1 /*
2  * Copyright (C) 2008 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.internal.location;
18 
19 import android.app.Notification;
20 import android.app.NotificationManager;
21 import android.app.PendingIntent;
22 import android.compat.annotation.UnsupportedAppUsage;
23 import android.content.BroadcastReceiver;
24 import android.content.Context;
25 import android.content.Intent;
26 import android.content.IntentFilter;
27 import android.location.INetInitiatedListener;
28 import android.location.LocationManager;
29 import android.os.RemoteException;
30 import android.os.SystemClock;
31 import android.os.UserHandle;
32 import android.telephony.PhoneNumberUtils;
33 import android.telephony.PhoneStateListener;
34 import android.telephony.TelephonyManager;
35 import android.util.Log;
36 
37 import com.android.internal.R;
38 import com.android.internal.notification.SystemNotificationChannels;
39 import com.android.internal.telephony.GsmAlphabet;
40 
41 import java.io.UnsupportedEncodingException;
42 import java.util.concurrent.TimeUnit;
43 
44 /**
45  * A GPS Network-initiated Handler class used by LocationManager.
46  *
47  * {@hide}
48  */
49 public class GpsNetInitiatedHandler {
50 
51     private static final String TAG = "GpsNetInitiatedHandler";
52 
53     private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
54 
55     // NI verify activity for bringing up UI (not used yet)
56     public static final String ACTION_NI_VERIFY = "android.intent.action.NETWORK_INITIATED_VERIFY";
57 
58     // string constants for defining data fields in NI Intent
59     public static final String NI_INTENT_KEY_NOTIF_ID = "notif_id";
60     public static final String NI_INTENT_KEY_TITLE = "title";
61     public static final String NI_INTENT_KEY_MESSAGE = "message";
62     public static final String NI_INTENT_KEY_TIMEOUT = "timeout";
63     public static final String NI_INTENT_KEY_DEFAULT_RESPONSE = "default_resp";
64 
65     // the extra command to send NI response to GnssLocationProvider
66     public static final String NI_RESPONSE_EXTRA_CMD = "send_ni_response";
67 
68     // the extra command parameter names in the Bundle
69     public static final String NI_EXTRA_CMD_NOTIF_ID = "notif_id";
70     public static final String NI_EXTRA_CMD_RESPONSE = "response";
71 
72     // these need to match GpsNiType constants in gps_ni.h
73     public static final int GPS_NI_TYPE_VOICE = 1;
74     public static final int GPS_NI_TYPE_UMTS_SUPL = 2;
75     public static final int GPS_NI_TYPE_UMTS_CTRL_PLANE = 3;
76     public static final int GPS_NI_TYPE_EMERGENCY_SUPL = 4;
77 
78     // these need to match GpsUserResponseType constants in gps_ni.h
79     public static final int GPS_NI_RESPONSE_ACCEPT = 1;
80     public static final int GPS_NI_RESPONSE_DENY = 2;
81     public static final int GPS_NI_RESPONSE_NORESP = 3;
82     public static final int GPS_NI_RESPONSE_IGNORE = 4;
83 
84     // these need to match GpsNiNotifyFlags constants in gps_ni.h
85     public static final int GPS_NI_NEED_NOTIFY = 0x0001;
86     public static final int GPS_NI_NEED_VERIFY = 0x0002;
87     public static final int GPS_NI_PRIVACY_OVERRIDE = 0x0004;
88 
89     // these need to match GpsNiEncodingType in gps_ni.h
90     public static final int GPS_ENC_NONE = 0;
91     public static final int GPS_ENC_SUPL_GSM_DEFAULT = 1;
92     public static final int GPS_ENC_SUPL_UTF8 = 2;
93     public static final int GPS_ENC_SUPL_UCS2 = 3;
94     public static final int GPS_ENC_UNKNOWN = -1;
95 
96     private final Context mContext;
97     private final TelephonyManager mTelephonyManager;
98     private final PhoneStateListener mPhoneStateListener;
99 
100     // parent gps location provider
101     private final LocationManager mLocationManager;
102 
103     // configuration of notificaiton behavior
104     private boolean mPlaySounds = false;
105     private boolean mPopupImmediately = true;
106 
107     // read the SUPL_ES form gps.conf
108     private volatile boolean mIsSuplEsEnabled;
109 
110     // Set to true if the phone is having emergency call.
111     private volatile boolean mIsInEmergencyCall;
112 
113     // If Location function is enabled.
114     private volatile boolean mIsLocationEnabled = false;
115 
116     private final INetInitiatedListener mNetInitiatedListener;
117 
118     // Set to true if string from HAL is encoded as Hex, e.g., "3F0039"
119     @UnsupportedAppUsage
120     static private boolean mIsHexInput = true;
121 
122     // End time of emergency call, and extension, if set
123     private volatile long mCallEndElapsedRealtimeMillis = 0;
124     private volatile long mEmergencyExtensionMillis = 0;
125 
126     public static class GpsNiNotification
127     {
128         @android.compat.annotation.UnsupportedAppUsage
GpsNiNotification()129         public GpsNiNotification() {
130         }
131         public int notificationId;
132         public int niType;
133         public boolean needNotify;
134         public boolean needVerify;
135         public boolean privacyOverride;
136         public int timeout;
137         public int defaultResponse;
138         @UnsupportedAppUsage
139         public String requestorId;
140         @UnsupportedAppUsage
141         public String text;
142         @UnsupportedAppUsage
143         public int requestorIdEncoding;
144         @UnsupportedAppUsage
145         public int textEncoding;
146     };
147 
148     public static class GpsNiResponse {
149         /* User response, one of the values in GpsUserResponseType */
150         int userResponse;
151     };
152 
153     private final BroadcastReceiver mBroadcastReciever = new BroadcastReceiver() {
154 
155         @Override public void onReceive(Context context, Intent intent) {
156             String action = intent.getAction();
157             if (action.equals(Intent.ACTION_NEW_OUTGOING_CALL)) {
158                 String phoneNumber = intent.getStringExtra(Intent.EXTRA_PHONE_NUMBER);
159                 /*
160                    Tracks the emergency call:
161                        mIsInEmergencyCall records if the phone is in emergency call or not. It will
162                        be set to true when the phone is having emergency call, and then will
163                        be set to false by mPhoneStateListener when the emergency call ends.
164                 */
165                 mIsInEmergencyCall = PhoneNumberUtils.isEmergencyNumber(phoneNumber);
166                 if (DEBUG) Log.v(TAG, "ACTION_NEW_OUTGOING_CALL - " + getInEmergency());
167             } else if (action.equals(LocationManager.MODE_CHANGED_ACTION)) {
168                 updateLocationMode();
169                 if (DEBUG) Log.d(TAG, "location enabled :" + getLocationEnabled());
170             }
171         }
172     };
173 
174     /**
175      * The notification that is shown when a network-initiated notification
176      * (and verification) event is received.
177      * <p>
178      * This is lazily created, so use {@link #setNINotification()}.
179      */
180     private Notification.Builder mNiNotificationBuilder;
181 
GpsNetInitiatedHandler(Context context, INetInitiatedListener netInitiatedListener, boolean isSuplEsEnabled)182     public GpsNetInitiatedHandler(Context context,
183                                   INetInitiatedListener netInitiatedListener,
184                                   boolean isSuplEsEnabled) {
185         mContext = context;
186 
187         if (netInitiatedListener == null) {
188             throw new IllegalArgumentException("netInitiatedListener is null");
189         } else {
190             mNetInitiatedListener = netInitiatedListener;
191         }
192 
193         setSuplEsEnabled(isSuplEsEnabled);
194         mLocationManager = (LocationManager)context.getSystemService(Context.LOCATION_SERVICE);
195         updateLocationMode();
196         mTelephonyManager =
197             (TelephonyManager)context.getSystemService(Context.TELEPHONY_SERVICE);
198 
199         mPhoneStateListener = new PhoneStateListener() {
200             @Override
201             public void onCallStateChanged(int state, String incomingNumber) {
202                 if (DEBUG) Log.d(TAG, "onCallStateChanged(): state is "+ state);
203                 // listening for emergency call ends
204                 if (state == TelephonyManager.CALL_STATE_IDLE) {
205                     if (mIsInEmergencyCall) {
206                         mCallEndElapsedRealtimeMillis = SystemClock.elapsedRealtime();
207                         mIsInEmergencyCall = false;
208                     }
209                 }
210             }
211         };
212         mTelephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_CALL_STATE);
213 
214         IntentFilter intentFilter = new IntentFilter();
215         intentFilter.addAction(Intent.ACTION_NEW_OUTGOING_CALL);
216         intentFilter.addAction(LocationManager.MODE_CHANGED_ACTION);
217         mContext.registerReceiver(mBroadcastReciever, intentFilter);
218     }
219 
setSuplEsEnabled(boolean isEnabled)220     public void setSuplEsEnabled(boolean isEnabled) {
221         mIsSuplEsEnabled = isEnabled;
222     }
223 
getSuplEsEnabled()224     public boolean getSuplEsEnabled() {
225         return mIsSuplEsEnabled;
226     }
227 
228     /**
229      * Updates Location enabler based on location setting.
230      */
updateLocationMode()231     public void updateLocationMode() {
232         mIsLocationEnabled = mLocationManager.isProviderEnabled(LocationManager.GPS_PROVIDER);
233     }
234 
235     /**
236      * Checks if user agreed to use location.
237      */
getLocationEnabled()238     public boolean getLocationEnabled() {
239         return mIsLocationEnabled;
240     }
241 
242     /**
243      * Determines whether device is in user-initiated emergency session based on the following
244      * 1. If the user is making an emergency call, this is provided by actively
245      *    monitoring the outgoing phone number;
246      * 2. If the user has recently ended an emergency call, and the device is in a configured time
247      *    window after the end of that call.
248      * 3. If the device is in a emergency callback state, this is provided by querying
249      *    TelephonyManager.
250      * 4. If the user has recently sent an Emergency SMS and telephony reports that it is in
251      *    emergency SMS mode, this is provided by querying TelephonyManager.
252      * @return true if is considered in user initiated emergency mode for NI purposes
253      */
getInEmergency()254     public boolean getInEmergency() {
255         boolean isInEmergencyExtension =
256                 (mCallEndElapsedRealtimeMillis > 0)
257                 && ((SystemClock.elapsedRealtime() - mCallEndElapsedRealtimeMillis)
258                         < mEmergencyExtensionMillis);
259         boolean isInEmergencyCallback = mTelephonyManager.getEmergencyCallbackMode();
260         boolean isInEmergencySmsMode = mTelephonyManager.isInEmergencySmsMode();
261         return mIsInEmergencyCall || isInEmergencyCallback || isInEmergencyExtension
262                 || isInEmergencySmsMode;
263     }
264 
setEmergencyExtensionSeconds(int emergencyExtensionSeconds)265     public void setEmergencyExtensionSeconds(int emergencyExtensionSeconds) {
266         mEmergencyExtensionMillis = TimeUnit.SECONDS.toMillis(emergencyExtensionSeconds);
267     }
268 
269     // Handles NI events from HAL
270     @UnsupportedAppUsage
handleNiNotification(GpsNiNotification notif)271     public void handleNiNotification(GpsNiNotification notif) {
272         if (DEBUG) Log.d(TAG, "in handleNiNotification () :"
273                         + " notificationId: " + notif.notificationId
274                         + " requestorId: " + notif.requestorId
275                         + " text: " + notif.text
276                         + " mIsSuplEsEnabled" + getSuplEsEnabled()
277                         + " mIsLocationEnabled" + getLocationEnabled());
278 
279         if (getSuplEsEnabled()) {
280             handleNiInEs(notif);
281         } else {
282             handleNi(notif);
283         }
284 
285         //////////////////////////////////////////////////////////////////////////
286         //   A note about timeout
287         //   According to the protocol, in the need_notify and need_verify case,
288         //   a default response should be sent when time out.
289         //
290         //   In some GPS hardware, the GPS driver (under HAL) can handle the timeout case
291         //   and this class GpsNetInitiatedHandler does not need to do anything.
292         //
293         //   However, the UI should at least close the dialog when timeout. Further,
294         //   for more general handling, timeout response should be added to the Handler here.
295         //
296     }
297 
298     // handle NI form HAL when SUPL_ES is disabled.
handleNi(GpsNiNotification notif)299     private void handleNi(GpsNiNotification notif) {
300         if (DEBUG) Log.d(TAG, "in handleNi () :"
301                         + " needNotify: " + notif.needNotify
302                         + " needVerify: " + notif.needVerify
303                         + " privacyOverride: " + notif.privacyOverride
304                         + " mPopupImmediately: " + mPopupImmediately
305                         + " mInEmergency: " + getInEmergency());
306 
307         if (!getLocationEnabled() && !getInEmergency()) {
308             // Location is currently disabled, ignore all NI requests.
309             try {
310                 mNetInitiatedListener.sendNiResponse(notif.notificationId,
311                                                      GPS_NI_RESPONSE_IGNORE);
312             } catch (RemoteException e) {
313                 Log.e(TAG, "RemoteException in sendNiResponse");
314             }
315         }
316         if (notif.needNotify) {
317         // If NI does not need verify or the dialog is not requested
318         // to pop up immediately, the dialog box will not pop up.
319             if (notif.needVerify && mPopupImmediately) {
320                 // Popup the dialog box now
321                 openNiDialog(notif);
322             } else {
323                 // Show the notification
324                 setNiNotification(notif);
325             }
326         }
327         // ACCEPT cases: 1. Notify, no verify; 2. no notify, no verify;
328         // 3. privacy override.
329         if (!notif.needVerify || notif.privacyOverride) {
330             try {
331                 mNetInitiatedListener.sendNiResponse(notif.notificationId,
332                                                      GPS_NI_RESPONSE_ACCEPT);
333             } catch (RemoteException e) {
334                 Log.e(TAG, "RemoteException in sendNiResponse");
335             }
336         }
337     }
338 
339     // handle NI from HAL when the SUPL_ES is enabled
handleNiInEs(GpsNiNotification notif)340     private void handleNiInEs(GpsNiNotification notif) {
341 
342         if (DEBUG) Log.d(TAG, "in handleNiInEs () :"
343                     + " niType: " + notif.niType
344                     + " notificationId: " + notif.notificationId);
345 
346         // UE is in emergency mode when in emergency call mode or in emergency call back mode
347         /*
348            1. When SUPL ES bit is off and UE is not in emergency mode:
349                   Call handleNi() to do legacy behaviour.
350            2. When SUPL ES bit is on and UE is in emergency mode:
351                   Call handleNi() to do acceptance behaviour.
352            3. When SUPL ES bit is off but UE is in emergency mode:
353                   Ignore the emergency SUPL INIT.
354            4. When SUPL ES bit is on but UE is not in emergency mode:
355                   Ignore the emergency SUPL INIT.
356         */
357         boolean isNiTypeES = (notif.niType == GPS_NI_TYPE_EMERGENCY_SUPL);
358         if (isNiTypeES != getInEmergency()) {
359             try {
360                 mNetInitiatedListener.sendNiResponse(notif.notificationId,
361                                                      GPS_NI_RESPONSE_IGNORE);
362             } catch (RemoteException e) {
363                 Log.e(TAG, "RemoteException in sendNiResponse");
364             }
365         } else {
366             handleNi(notif);
367         }
368     }
369 
370     /**
371      * Posts a notification in the status bar using the contents in {@code notif} object.
372      */
setNiNotification(GpsNiNotification notif)373     private synchronized void setNiNotification(GpsNiNotification notif) {
374         NotificationManager notificationManager = (NotificationManager) mContext
375                 .getSystemService(Context.NOTIFICATION_SERVICE);
376         if (notificationManager == null) {
377             return;
378         }
379 
380         String title = getNotifTitle(notif, mContext);
381         String message = getNotifMessage(notif, mContext);
382 
383         if (DEBUG) Log.d(TAG, "setNiNotification, notifyId: " + notif.notificationId +
384                 ", title: " + title +
385                 ", message: " + message);
386 
387         // Construct Notification
388         if (mNiNotificationBuilder == null) {
389             mNiNotificationBuilder = new Notification.Builder(mContext,
390                 SystemNotificationChannels.NETWORK_ALERTS)
391                     .setSmallIcon(com.android.internal.R.drawable.stat_sys_gps_on)
392                     .setWhen(0)
393                     .setOngoing(true)
394                     .setAutoCancel(true)
395                     .setColor(mContext.getColor(
396                             com.android.internal.R.color.system_notification_accent_color));
397         }
398 
399         if (mPlaySounds) {
400             mNiNotificationBuilder.setDefaults(Notification.DEFAULT_SOUND);
401         } else {
402             mNiNotificationBuilder.setDefaults(0);
403         }
404 
405         // if not to popup dialog immediately, pending intent will open the dialog
406         Intent intent = !mPopupImmediately ? getDlgIntent(notif) : new Intent();
407         PendingIntent pi = PendingIntent.getBroadcast(mContext, 0, intent, 0);
408         mNiNotificationBuilder.setTicker(getNotifTicker(notif, mContext))
409                 .setContentTitle(title)
410                 .setContentText(message)
411                 .setContentIntent(pi);
412 
413         notificationManager.notifyAsUser(null, notif.notificationId, mNiNotificationBuilder.build(),
414                 UserHandle.ALL);
415     }
416 
417     // Opens the notification dialog and waits for user input
openNiDialog(GpsNiNotification notif)418     private void openNiDialog(GpsNiNotification notif)
419     {
420         Intent intent = getDlgIntent(notif);
421 
422         if (DEBUG) Log.d(TAG, "openNiDialog, notifyId: " + notif.notificationId +
423                 ", requestorId: " + notif.requestorId +
424                 ", text: " + notif.text);
425 
426         mContext.startActivity(intent);
427     }
428 
429     // Construct the intent for bringing up the dialog activity, which shows the
430     // notification and takes user input
getDlgIntent(GpsNiNotification notif)431     private Intent getDlgIntent(GpsNiNotification notif)
432     {
433         Intent intent = new Intent();
434         String title = getDialogTitle(notif, mContext);
435         String message = getDialogMessage(notif, mContext);
436 
437         // directly bring up the NI activity
438         intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
439         intent.setClass(mContext, com.android.internal.app.NetInitiatedActivity.class);
440 
441         // put data in the intent
442         intent.putExtra(NI_INTENT_KEY_NOTIF_ID, notif.notificationId);
443         intent.putExtra(NI_INTENT_KEY_TITLE, title);
444         intent.putExtra(NI_INTENT_KEY_MESSAGE, message);
445         intent.putExtra(NI_INTENT_KEY_TIMEOUT, notif.timeout);
446         intent.putExtra(NI_INTENT_KEY_DEFAULT_RESPONSE, notif.defaultResponse);
447 
448         if (DEBUG) Log.d(TAG, "generateIntent, title: " + title + ", message: " + message +
449                 ", timeout: " + notif.timeout);
450 
451         return intent;
452     }
453 
454     // Converts a string (or Hex string) to a char array
stringToByteArray(String original, boolean isHex)455     static byte[] stringToByteArray(String original, boolean isHex)
456     {
457         int length = isHex ? original.length() / 2 : original.length();
458         byte[] output = new byte[length];
459         int i;
460 
461         if (isHex)
462         {
463             for (i = 0; i < length; i++)
464             {
465                 output[i] = (byte) Integer.parseInt(original.substring(i*2, i*2+2), 16);
466             }
467         }
468         else {
469             for (i = 0; i < length; i++)
470             {
471                 output[i] = (byte) original.charAt(i);
472             }
473         }
474 
475         return output;
476     }
477 
478     /**
479      * Unpacks an byte array containing 7-bit packed characters into a String.
480      *
481      * @param input a 7-bit packed char array
482      * @return the unpacked String
483      */
decodeGSMPackedString(byte[] input)484     static String decodeGSMPackedString(byte[] input)
485     {
486         final char PADDING_CHAR = 0x00;
487         int lengthBytes = input.length;
488         int lengthSeptets = (lengthBytes * 8) / 7;
489         String decoded;
490 
491         /* Special case where the last 7 bits in the last byte could hold a valid
492          * 7-bit character or a padding character. Drop the last 7-bit character
493          * if it is a padding character.
494          */
495         if (lengthBytes % 7 == 0) {
496             if (lengthBytes > 0) {
497                 if ((input[lengthBytes - 1] >> 1) == PADDING_CHAR) {
498                     lengthSeptets = lengthSeptets - 1;
499                 }
500             }
501         }
502 
503         decoded = GsmAlphabet.gsm7BitPackedToString(input, 0, lengthSeptets);
504 
505         // Return "" if decoding of GSM packed string fails
506         if (null == decoded) {
507             Log.e(TAG, "Decoding of GSM packed string failed");
508             decoded = "";
509         }
510 
511         return decoded;
512     }
513 
decodeUTF8String(byte[] input)514     static String decodeUTF8String(byte[] input)
515     {
516         String decoded = "";
517         try {
518             decoded = new String(input, "UTF-8");
519         }
520         catch (UnsupportedEncodingException e)
521         {
522             throw new AssertionError();
523         }
524         return decoded;
525     }
526 
decodeUCS2String(byte[] input)527     static String decodeUCS2String(byte[] input)
528     {
529         String decoded = "";
530         try {
531             decoded = new String(input, "UTF-16");
532         }
533         catch (UnsupportedEncodingException e)
534         {
535             throw new AssertionError();
536         }
537         return decoded;
538     }
539 
540     /** Decode NI string
541      *
542      * @param original   The text string to be decoded
543      * @param isHex      Specifies whether the content of the string has been encoded as a Hex string. Encoding
544      *                   a string as Hex can allow zeros inside the coded text.
545      * @param coding     Specifies the coding scheme of the string, such as GSM, UTF8, UCS2, etc. This coding scheme
546      *                      needs to match those used passed to HAL from the native GPS driver. Decoding is done according
547      *                   to the <code> coding </code>, after a Hex string is decoded. Generally, if the
548      *                   notification strings don't need further decoding, <code> coding </code> encoding can be
549      *                   set to -1, and <code> isHex </code> can be false.
550      * @return the decoded string
551      */
552     @UnsupportedAppUsage
decodeString(String original, boolean isHex, int coding)553     static private String decodeString(String original, boolean isHex, int coding)
554     {
555         if (coding == GPS_ENC_NONE || coding == GPS_ENC_UNKNOWN) {
556             return original;
557         }
558 
559         byte[] input = stringToByteArray(original, isHex);
560 
561         switch (coding) {
562             case GPS_ENC_SUPL_GSM_DEFAULT:
563                 return decodeGSMPackedString(input);
564 
565             case GPS_ENC_SUPL_UTF8:
566                 return decodeUTF8String(input);
567 
568             case GPS_ENC_SUPL_UCS2:
569                 return decodeUCS2String(input);
570 
571             default:
572                 Log.e(TAG, "Unknown encoding " + coding + " for NI text " + original);
573                 return original;
574         }
575     }
576 
577     // change this to configure notification display
getNotifTicker(GpsNiNotification notif, Context context)578     static private String getNotifTicker(GpsNiNotification notif, Context context)
579     {
580         String ticker = String.format(context.getString(R.string.gpsNotifTicker),
581                 decodeString(notif.requestorId, mIsHexInput, notif.requestorIdEncoding),
582                 decodeString(notif.text, mIsHexInput, notif.textEncoding));
583         return ticker;
584     }
585 
586     // change this to configure notification display
getNotifTitle(GpsNiNotification notif, Context context)587     static private String getNotifTitle(GpsNiNotification notif, Context context)
588     {
589         String title = String.format(context.getString(R.string.gpsNotifTitle));
590         return title;
591     }
592 
593     // change this to configure notification display
getNotifMessage(GpsNiNotification notif, Context context)594     static private String getNotifMessage(GpsNiNotification notif, Context context)
595     {
596         String message = String.format(context.getString(R.string.gpsNotifMessage),
597                 decodeString(notif.requestorId, mIsHexInput, notif.requestorIdEncoding),
598                 decodeString(notif.text, mIsHexInput, notif.textEncoding));
599         return message;
600     }
601 
602     // change this to configure dialog display (for verification)
getDialogTitle(GpsNiNotification notif, Context context)603     static public String getDialogTitle(GpsNiNotification notif, Context context)
604     {
605         return getNotifTitle(notif, context);
606     }
607 
608     // change this to configure dialog display (for verification)
getDialogMessage(GpsNiNotification notif, Context context)609     static private String getDialogMessage(GpsNiNotification notif, Context context)
610     {
611         return getNotifMessage(notif, context);
612     }
613 
614 }
615