1 /* 2 * Copyright (C) 2018 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.app; 18 19 import static android.content.res.Resources.ID_NULL; 20 21 import android.Manifest; 22 import android.app.AlertDialog; 23 import android.content.DialogInterface; 24 import android.content.Intent; 25 import android.content.pm.PackageManager; 26 import android.content.pm.ResolveInfo; 27 import android.content.pm.SuspendDialogInfo; 28 import android.content.res.Resources; 29 import android.graphics.drawable.Drawable; 30 import android.os.Bundle; 31 import android.os.UserHandle; 32 import android.util.Slog; 33 import android.view.WindowManager; 34 35 import com.android.internal.R; 36 37 public class SuspendedAppActivity extends AlertActivity 38 implements DialogInterface.OnClickListener { 39 private static final String TAG = SuspendedAppActivity.class.getSimpleName(); 40 private static final String PACKAGE_NAME = "com.android.internal.app"; 41 42 public static final String EXTRA_SUSPENDED_PACKAGE = PACKAGE_NAME + ".extra.SUSPENDED_PACKAGE"; 43 public static final String EXTRA_SUSPENDING_PACKAGE = 44 PACKAGE_NAME + ".extra.SUSPENDING_PACKAGE"; 45 public static final String EXTRA_DIALOG_INFO = PACKAGE_NAME + ".extra.DIALOG_INFO"; 46 47 private Intent mMoreDetailsIntent; 48 private int mUserId; 49 private PackageManager mPm; 50 private Resources mSuspendingAppResources; 51 private SuspendDialogInfo mSuppliedDialogInfo; 52 getAppLabel(String packageName)53 private CharSequence getAppLabel(String packageName) { 54 try { 55 return mPm.getApplicationInfoAsUser(packageName, 0, mUserId).loadLabel(mPm); 56 } catch (PackageManager.NameNotFoundException ne) { 57 Slog.e(TAG, "Package " + packageName + " not found", ne); 58 } 59 return packageName; 60 } 61 getMoreDetailsActivity(String suspendingPackage, String suspendedPackage, int userId)62 private Intent getMoreDetailsActivity(String suspendingPackage, String suspendedPackage, 63 int userId) { 64 final Intent moreDetailsIntent = new Intent(Intent.ACTION_SHOW_SUSPENDED_APP_DETAILS) 65 .setPackage(suspendingPackage); 66 final String requiredPermission = Manifest.permission.SEND_SHOW_SUSPENDED_APP_DETAILS; 67 final ResolveInfo resolvedInfo = mPm.resolveActivityAsUser(moreDetailsIntent, 0, userId); 68 if (resolvedInfo != null && resolvedInfo.activityInfo != null 69 && requiredPermission.equals(resolvedInfo.activityInfo.permission)) { 70 moreDetailsIntent.putExtra(Intent.EXTRA_PACKAGE_NAME, suspendedPackage) 71 .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP); 72 return moreDetailsIntent; 73 } 74 return null; 75 } 76 resolveIcon()77 private Drawable resolveIcon() { 78 final int iconId = (mSuppliedDialogInfo != null) ? mSuppliedDialogInfo.getIconResId() 79 : ID_NULL; 80 if (iconId != ID_NULL && mSuspendingAppResources != null) { 81 try { 82 return mSuspendingAppResources.getDrawable(iconId, getTheme()); 83 } catch (Resources.NotFoundException nfe) { 84 Slog.e(TAG, "Could not resolve drawable resource id " + iconId); 85 } 86 } 87 return null; 88 } 89 resolveTitle()90 private String resolveTitle() { 91 final int titleId = (mSuppliedDialogInfo != null) ? mSuppliedDialogInfo.getTitleResId() 92 : ID_NULL; 93 if (titleId != ID_NULL && mSuspendingAppResources != null) { 94 try { 95 return mSuspendingAppResources.getString(titleId); 96 } catch (Resources.NotFoundException nfe) { 97 Slog.e(TAG, "Could not resolve string resource id " + titleId); 98 } 99 } 100 return getString(R.string.app_suspended_title); 101 } 102 resolveDialogMessage(String suspendingPkg, String suspendedPkg)103 private String resolveDialogMessage(String suspendingPkg, String suspendedPkg) { 104 final CharSequence suspendedAppLabel = getAppLabel(suspendedPkg); 105 if (mSuppliedDialogInfo != null) { 106 final int messageId = mSuppliedDialogInfo.getDialogMessageResId(); 107 final String message = mSuppliedDialogInfo.getDialogMessage(); 108 if (messageId != ID_NULL && mSuspendingAppResources != null) { 109 try { 110 return mSuspendingAppResources.getString(messageId, suspendedAppLabel); 111 } catch (Resources.NotFoundException nfe) { 112 Slog.e(TAG, "Could not resolve string resource id " + messageId); 113 } 114 } else if (message != null) { 115 return String.format(getResources().getConfiguration().getLocales().get(0), message, 116 suspendedAppLabel); 117 } 118 } 119 return getString(R.string.app_suspended_default_message, suspendedAppLabel, 120 getAppLabel(suspendingPkg)); 121 } 122 resolveNeutralButtonText()123 private String resolveNeutralButtonText() { 124 final int buttonTextId = (mSuppliedDialogInfo != null) 125 ? mSuppliedDialogInfo.getNeutralButtonTextResId() : ID_NULL; 126 if (buttonTextId != ID_NULL && mSuspendingAppResources != null) { 127 try { 128 return mSuspendingAppResources.getString(buttonTextId); 129 } catch (Resources.NotFoundException nfe) { 130 Slog.e(TAG, "Could not resolve string resource id " + buttonTextId); 131 } 132 } 133 return getString(R.string.app_suspended_more_details); 134 } 135 136 @Override onCreate(Bundle icicle)137 public void onCreate(Bundle icicle) { 138 super.onCreate(icicle); 139 mPm = getPackageManager(); 140 getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG); 141 142 final Intent intent = getIntent(); 143 mUserId = intent.getIntExtra(Intent.EXTRA_USER_ID, -1); 144 if (mUserId < 0) { 145 Slog.wtf(TAG, "Invalid user: " + mUserId); 146 finish(); 147 return; 148 } 149 final String suspendedPackage = intent.getStringExtra(EXTRA_SUSPENDED_PACKAGE); 150 final String suspendingPackage = intent.getStringExtra(EXTRA_SUSPENDING_PACKAGE); 151 mSuppliedDialogInfo = intent.getParcelableExtra(EXTRA_DIALOG_INFO); 152 if (mSuppliedDialogInfo != null) { 153 try { 154 mSuspendingAppResources = mPm.getResourcesForApplicationAsUser(suspendingPackage, 155 mUserId); 156 } catch (PackageManager.NameNotFoundException ne) { 157 Slog.e(TAG, "Could not find resources for " + suspendingPackage, ne); 158 } 159 } 160 161 final AlertController.AlertParams ap = mAlertParams; 162 ap.mIcon = resolveIcon(); 163 ap.mTitle = resolveTitle(); 164 ap.mMessage = resolveDialogMessage(suspendingPackage, suspendedPackage); 165 ap.mPositiveButtonText = getString(android.R.string.ok); 166 mMoreDetailsIntent = getMoreDetailsActivity(suspendingPackage, suspendedPackage, mUserId); 167 if (mMoreDetailsIntent != null) { 168 ap.mNeutralButtonText = resolveNeutralButtonText(); 169 } 170 ap.mPositiveButtonListener = ap.mNeutralButtonListener = this; 171 setupAlert(); 172 } 173 174 @Override onClick(DialogInterface dialog, int which)175 public void onClick(DialogInterface dialog, int which) { 176 switch (which) { 177 case AlertDialog.BUTTON_NEUTRAL: 178 startActivityAsUser(mMoreDetailsIntent, UserHandle.of(mUserId)); 179 Slog.i(TAG, "Started more details activity"); 180 break; 181 } 182 finish(); 183 } 184 createSuspendedAppInterceptIntent(String suspendedPackage, String suspendingPackage, SuspendDialogInfo dialogInfo, int userId)185 public static Intent createSuspendedAppInterceptIntent(String suspendedPackage, 186 String suspendingPackage, SuspendDialogInfo dialogInfo, int userId) { 187 return new Intent() 188 .setClassName("android", SuspendedAppActivity.class.getName()) 189 .putExtra(EXTRA_SUSPENDED_PACKAGE, suspendedPackage) 190 .putExtra(EXTRA_DIALOG_INFO, dialogInfo) 191 .putExtra(EXTRA_SUSPENDING_PACKAGE, suspendingPackage) 192 .putExtra(Intent.EXTRA_USER_ID, userId) 193 .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK 194 | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS); 195 } 196 } 197