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