summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJordan Liu <jminjie@google.com>2018-02-06 03:35:04 +0000
committerandroid-build-merger <android-build-merger@google.com>2018-02-06 03:35:04 +0000
commit99a0928057ed6f6509cc39a5ea09477b547fa82d (patch)
tree2021011825375e1f49e05147e99dd339e8bad443
parent7f20e0ed6361da0ead31a3622267a6be0d103570 (diff)
parente31a69531373f5ea8b9a46fa5df88bd1525b1226 (diff)
downloadStk-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.xml2
-rw-r--r--src/com/android/stk/StkAppService.java206
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) {