diff options
author | Jordan Liu <jminjie@google.com> | 2018-02-06 03:35:04 +0000 |
---|---|---|
committer | android-build-merger <android-build-merger@google.com> | 2018-02-06 03:35:04 +0000 |
commit | 99a0928057ed6f6509cc39a5ea09477b547fa82d (patch) | |
tree | 2021011825375e1f49e05147e99dd339e8bad443 | |
parent | 7f20e0ed6361da0ead31a3622267a6be0d103570 (diff) | |
parent | e31a69531373f5ea8b9a46fa5df88bd1525b1226 (diff) | |
download | Stk-99a0928057ed6f6509cc39a5ea09477b547fa82d.tar.gz |
Merge "Wake up from the standby mode and display message on the keyguard screen" am: 5a2d65c45e
am: e31a695313
Change-Id: Ic055538f22a0d8271e07a9c697b7f8f82582c7f3
-rw-r--r-- | AndroidManifest.xml | 2 | ||||
-rw-r--r-- | src/com/android/stk/StkAppService.java | 206 |
2 files changed, 179 insertions, 29 deletions
diff --git a/AndroidManifest.xml b/AndroidManifest.xml index e8d1c74..f4652da 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -28,6 +28,8 @@ <uses-permission android:name="android.permission.GET_TASKS"/> <uses-permission android:name="android.permission.RECEIVE_STK_COMMANDS" /> <uses-permission android:name="android.permission.SET_ACTIVITY_WATCHER" /> + <uses-permission android:name="android.permission.VIBRATE" /> + <uses-permission android:name="android.permission.WAKE_LOCK" /> <application android:icon="@drawable/ic_launcher_sim_toolkit" android:label="@string/app_name" diff --git a/src/com/android/stk/StkAppService.java b/src/com/android/stk/StkAppService.java index dc551a6..a85402c 100644 --- a/src/com/android/stk/StkAppService.java +++ b/src/com/android/stk/StkAppService.java @@ -19,6 +19,7 @@ package com.android.stk; import android.app.ActivityManager; import android.app.ActivityManager.RunningTaskInfo; import android.app.AlertDialog; +import android.app.KeyguardManager; import android.app.Notification; import android.app.NotificationChannel; import android.app.NotificationManager; @@ -27,6 +28,7 @@ import android.app.Service; import android.app.Activity; import android.app.ActivityManagerNative; import android.app.IProcessObserver; +import android.content.BroadcastReceiver; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; @@ -37,6 +39,7 @@ import android.content.res.Resources; import android.content.res.Resources.NotFoundException; import android.graphics.Bitmap; import android.graphics.BitmapFactory; +import android.media.RingtoneManager; import android.net.Uri; import android.os.Bundle; import android.os.Handler; @@ -64,6 +67,7 @@ import android.widget.Toast; import android.content.IntentFilter; import com.android.internal.telephony.cat.AppInterface; +import com.android.internal.telephony.cat.Input; import com.android.internal.telephony.cat.LaunchBrowserMode; import com.android.internal.telephony.cat.Menu; import com.android.internal.telephony.cat.Item; @@ -108,7 +112,8 @@ public class StkAppService extends Service implements Runnable { protected boolean mIsInputPending = false; protected boolean mIsMenuPending = false; protected boolean mIsDialogPending = false; - protected boolean responseNeeded = true; + protected boolean mNotificationOnKeyguard = false; + protected boolean mNoResponseFromUser = false; protected boolean launchBrowser = false; protected BrowserSettings mBrowserSettings = null; protected LinkedList<DelayedCmd> mCmdsQ = null; @@ -264,6 +269,10 @@ public class StkAppService extends Service implements Runnable { // system property to set the STK specific default url for launch browser proactive cmds private static final String STK_BROWSER_DEFAULT_URL_SYSPROP = "persist.radio.stk.default_url"; + private static final int NOTIFICATION_ON_KEYGUARD = 1; + private static final long[] VIBRATION_PATTERN = new long[] { 0, 350, 250, 350 }; + private BroadcastReceiver mUserPresentReceiver = null; + @Override public void onCreate() { CatLog.d(LOG_TAG, "onCreate()+"); @@ -832,6 +841,7 @@ public class StkAppService extends Service implements Runnable { mStkContext[slotId].mIsInputPending = false; mStkContext[slotId].mIsMenuPending = false; mStkContext[slotId].mIsDialogPending = false; + mStkContext[slotId].mNoResponseFromUser = false; if (mStkContext[slotId].mMainCmd == null) { CatLog.d(LOG_TAG, "[handleSessionEnd][mMainCmd is null!]"); @@ -1251,6 +1261,24 @@ public class StkAppService extends Service implements Runnable { return; } + switch (args.getInt(RES_ID)) { + case RES_ID_MENU_SELECTION: + case RES_ID_INPUT: + case RES_ID_CONFIRM: + case RES_ID_CHOICE: + case RES_ID_BACKWARD: + case RES_ID_END_SESSION: + mStkContext[slotId].mNoResponseFromUser = false; + break; + case RES_ID_TIMEOUT: + cancelNotificationOnKeyguard(slotId); + mStkContext[slotId].mNoResponseFromUser = true; + break; + default: + // The other IDs cannot be used to judge if there is no response from user. + break; + } + if (null != mStkContext[slotId].mCurrentCmd && null != mStkContext[slotId].mCurrentCmd.getCmdType()) { CatLog.d(LOG_TAG, "handleCmdResponse- cmdName[" + @@ -1392,14 +1420,19 @@ public class StkAppService extends Service implements Runnable { String uriString = STK_INPUT_URI + System.currentTimeMillis(); //Set unique URI to create a new instance of activity for different slotId. Uri uriData = Uri.parse(uriString); + Input input = mStkContext[slotId].mCurrentCmd.geInput(); CatLog.d(LOG_TAG, "launchInputActivity, slotId: " + slotId); newIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | getFlagActivityNoUserAction(InitiatedByUserAction.unknown, slotId)); newIntent.setClassName(PACKAGE_NAME, targetActivity); - newIntent.putExtra("INPUT", mStkContext[slotId].mCurrentCmd.geInput()); + newIntent.putExtra("INPUT", input); newIntent.putExtra(SLOT_ID, slotId); newIntent.setData(uriData); + + if (input != null) { + notifyUserIfNecessary(slotId, input.text); + } mContext.startActivity(newIntent); } @@ -1411,22 +1444,133 @@ public class StkAppService extends Service implements Runnable { String uriString = STK_DIALOG_URI + System.currentTimeMillis(); //Set unique URI to create a new instance of activity for different slotId. Uri uriData = Uri.parse(uriString); - if (newIntent != null) { - newIntent.setClassName(PACKAGE_NAME, targetActivity); - newIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK - | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS - | getFlagActivityNoUserAction(InitiatedByUserAction.unknown, slotId)); - newIntent.setData(uriData); - newIntent.putExtra("TEXT", mStkContext[slotId].mCurrentCmd.geTextMessage()); - newIntent.putExtra(SLOT_ID, slotId); - startActivity(newIntent); - // For display texts with immediate response, send the terminal response - // immediately. responseNeeded will be false, if display text command has - // the immediate response tlv. - if (!mStkContext[slotId].mCurrentCmd.geTextMessage().responseNeeded) { - sendResponse(RES_ID_CONFIRM, slotId, true); + TextMessage textMessage = mStkContext[slotId].mCurrentCmd.geTextMessage(); + + newIntent.setClassName(PACKAGE_NAME, targetActivity); + newIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK + | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS + | getFlagActivityNoUserAction(InitiatedByUserAction.unknown, slotId)); + newIntent.setData(uriData); + newIntent.putExtra("TEXT", textMessage); + newIntent.putExtra(SLOT_ID, slotId); + + if (textMessage != null) { + notifyUserIfNecessary(slotId, textMessage.text); + } + startActivity(newIntent); + // For display texts with immediate response, send the terminal response + // immediately. responseNeeded will be false, if display text command has + // the immediate response tlv. + if (!mStkContext[slotId].mCurrentCmd.geTextMessage().responseNeeded) { + sendResponse(RES_ID_CONFIRM, slotId, true); + } + } + + private void notifyUserIfNecessary(int slotId, String message) { + createAllChannels(); + + if (mStkContext[slotId].mNoResponseFromUser) { + // No response from user was observed in the current session. + // Do nothing in that case in order to avoid turning on the screen again and again + // when the card repeatedly sends the same command in its retry procedure. + return; + } + + PowerManager pm = (PowerManager) getSystemService(POWER_SERVICE); + + if (((KeyguardManager) getSystemService(Context.KEYGUARD_SERVICE)).isKeyguardLocked()) { + // Display the notification on the keyguard screen + // if user cannot see the message from the card right now because of it. + // The notification can be dismissed if user removed the keyguard screen. + launchNotificationOnKeyguard(slotId, message); + } else if (!(pm.isInteractive() && isTopOfStack())) { + // User might be doing something but it is not related to the SIM Toolkit. + // Play the tone and do vibration in order to attract user's attention. + // User will see the input screen or the dialog soon in this case. + NotificationChannel channel = mNotificationManager + .getNotificationChannel(STK_NOTIFICATION_CHANNEL_ID); + Uri uri = channel.getSound(); + if (uri != null && !Uri.EMPTY.equals(uri) + && (NotificationManager.IMPORTANCE_LOW) < channel.getImportance()) { + RingtoneManager.getRingtone(getApplicationContext(), uri).play(); + } + long[] pattern = channel.getVibrationPattern(); + if (pattern != null && channel.shouldVibrate()) { + ((Vibrator) this.getSystemService(Context.VIBRATOR_SERVICE)) + .vibrate(pattern, -1); } } + + // Turn on the screen. + PowerManager.WakeLock wakelock = pm.newWakeLock(PowerManager.FULL_WAKE_LOCK + | PowerManager.ACQUIRE_CAUSES_WAKEUP | PowerManager.ON_AFTER_RELEASE, LOG_TAG); + wakelock.acquire(); + wakelock.release(); + } + + private void launchNotificationOnKeyguard(int slotId, String message) { + Notification.Builder builder = new Notification.Builder(this, STK_NOTIFICATION_CHANNEL_ID); + + builder.setStyle(new Notification.BigTextStyle(builder).bigText(message)); + builder.setContentText(message); + + Menu menu = getMainMenu(slotId); + if (menu == null || TextUtils.isEmpty(menu.title)) { + builder.setContentTitle(getResources().getString(R.string.app_name)); + } else { + builder.setContentTitle(menu.title); + } + + builder.setSmallIcon(com.android.internal.R.drawable.stat_notify_sim_toolkit); + builder.setOngoing(true); + builder.setOnlyAlertOnce(true); + builder.setColor(getResources().getColor( + com.android.internal.R.color.system_notification_accent_color)); + + registerUserPresentReceiver(); + mNotificationManager.notify(getNotificationId(NOTIFICATION_ON_KEYGUARD, slotId), + builder.build()); + mStkContext[slotId].mNotificationOnKeyguard = true; + } + + private void cancelNotificationOnKeyguard(int slotId) { + mNotificationManager.cancel(getNotificationId(NOTIFICATION_ON_KEYGUARD, slotId)); + mStkContext[slotId].mNotificationOnKeyguard = false; + unregisterUserPresentReceiver(slotId); + } + + private synchronized void registerUserPresentReceiver() { + if (mUserPresentReceiver == null) { + mUserPresentReceiver = new BroadcastReceiver() { + @Override public void onReceive(Context context, Intent intent) { + if (Intent.ACTION_USER_PRESENT.equals(intent.getAction())) { + for (int slot = 0; slot < mSimCount; slot++) { + cancelNotificationOnKeyguard(slot); + } + } + } + }; + registerReceiver(mUserPresentReceiver, new IntentFilter(Intent.ACTION_USER_PRESENT)); + } + } + + private synchronized void unregisterUserPresentReceiver(int slotId) { + if (mUserPresentReceiver != null) { + for (int slot = PhoneConstants.SIM_ID_1; slot < mSimCount; slot++) { + if (slot != slotId) { + if (mStkContext[slot].mNotificationOnKeyguard) { + // The broadcast receiver is still necessary for other SIM card. + return; + } + } + } + unregisterReceiver(mUserPresentReceiver); + mUserPresentReceiver = null; + } + } + + private int getNotificationId(int notificationType, int slotId) { + return getNotificationId(slotId) + (notificationType * mSimCount); } public boolean isStkDialogActivated(Context context) { @@ -1696,17 +1840,15 @@ public class StkAppService extends Service implements Runnable { //Set unique URI to create a new instance of activity for different slotId. Uri uriData = Uri.parse(uriString); - if (newIntent != null) { - newIntent.setClassName(this, targetActivity); - newIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK - | Intent.FLAG_ACTIVITY_NO_HISTORY - | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS - | getFlagActivityNoUserAction(InitiatedByUserAction.unknown, slotId)); - newIntent.putExtra("TEXT", msg); - newIntent.putExtra(SLOT_ID, slotId); - newIntent.setData(uriData); - startActivity(newIntent); - } + newIntent.setClassName(this, targetActivity); + newIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK + | Intent.FLAG_ACTIVITY_NO_HISTORY + | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS + | getFlagActivityNoUserAction(InitiatedByUserAction.unknown, slotId)); + newIntent.putExtra("TEXT", msg); + newIntent.putExtra(SLOT_ID, slotId); + newIntent.setData(uriData); + startActivity(newIntent); } private void launchBrowser(BrowserSettings settings) { @@ -1791,6 +1933,7 @@ public class StkAppService extends Service implements Runnable { .setSmallIcon(com.android.internal.R.drawable.stat_notify_sim_toolkit); notificationBuilder.setContentIntent(pendingIntent); notificationBuilder.setOngoing(true); + notificationBuilder.setOnlyAlertOnce(true); // Set text and icon for the status bar and notification body. if (mStkContext[slotId].mIdleModeTextCmd.hasIconLoadFailed() || !msg.iconSelfExplanatory) { @@ -1817,10 +1960,15 @@ public class StkAppService extends Service implements Runnable { * ignore this call. */ private void createAllChannels() { - mNotificationManager.createNotificationChannel(new NotificationChannel( + NotificationChannel notificationChannel = new NotificationChannel( STK_NOTIFICATION_CHANNEL_ID, getResources().getString(R.string.stk_channel_name), - NotificationManager.IMPORTANCE_MIN)); + NotificationManager.IMPORTANCE_DEFAULT); + + notificationChannel.enableVibration(true); + notificationChannel.setVibrationPattern(VIBRATION_PATTERN); + + mNotificationManager.createNotificationChannel(notificationChannel); } private void launchToneDialog(int slotId) { |