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