1 /* 2 * Copyright (C) 2015 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.settings.applications; 18 19 import static com.android.settingslib.RestrictedLockUtils.EnforcedAdmin; 20 21 import android.Manifest; 22 import android.app.Activity; 23 import android.app.ActivityManager; 24 import android.app.Dialog; 25 import android.app.admin.DevicePolicyManager; 26 import android.app.settings.SettingsEnums; 27 import android.content.BroadcastReceiver; 28 import android.content.Context; 29 import android.content.Intent; 30 import android.content.IntentFilter; 31 import android.content.pm.ApplicationInfo; 32 import android.content.pm.PackageInfo; 33 import android.content.pm.PackageManager; 34 import android.content.pm.PackageManager.NameNotFoundException; 35 import android.hardware.usb.IUsbManager; 36 import android.os.Bundle; 37 import android.os.IBinder; 38 import android.os.RemoteException; 39 import android.os.ServiceManager; 40 import android.os.UserHandle; 41 import android.os.UserManager; 42 import android.text.TextUtils; 43 import android.util.Log; 44 45 import androidx.annotation.VisibleForTesting; 46 import androidx.appcompat.app.AlertDialog; 47 import androidx.fragment.app.DialogFragment; 48 import androidx.fragment.app.Fragment; 49 50 import com.android.settings.SettingsActivity; 51 import com.android.settings.SettingsPreferenceFragment; 52 import com.android.settings.applications.manageapplications.ManageApplications; 53 import com.android.settings.core.SubSettingLauncher; 54 import com.android.settings.core.instrumentation.InstrumentedDialogFragment; 55 import com.android.settings.overlay.FeatureFactory; 56 import com.android.settingslib.RestrictedLockUtilsInternal; 57 import com.android.settingslib.applications.ApplicationsState; 58 import com.android.settingslib.applications.ApplicationsState.AppEntry; 59 60 import java.util.ArrayList; 61 62 public abstract class AppInfoBase extends SettingsPreferenceFragment 63 implements ApplicationsState.Callbacks { 64 65 public static final String ARG_PACKAGE_NAME = "package"; 66 public static final String ARG_PACKAGE_UID = "uid"; 67 68 private static final String TAG = "AppInfoBase"; 69 70 protected EnforcedAdmin mAppsControlDisallowedAdmin; 71 protected boolean mAppsControlDisallowedBySystem; 72 73 protected ApplicationFeatureProvider mApplicationFeatureProvider; 74 protected ApplicationsState mState; 75 protected ApplicationsState.Session mSession; 76 protected ApplicationsState.AppEntry mAppEntry; 77 protected PackageInfo mPackageInfo; 78 protected int mUserId; 79 protected String mPackageName; 80 81 protected IUsbManager mUsbManager; 82 protected DevicePolicyManager mDpm; 83 protected UserManager mUserManager; 84 protected PackageManager mPm; 85 86 // Dialog identifiers used in showDialog 87 protected static final int DLG_BASE = 0; 88 89 protected boolean mFinishing; 90 protected boolean mListeningToPackageRemove; 91 92 @Override onCreate(Bundle savedInstanceState)93 public void onCreate(Bundle savedInstanceState) { 94 super.onCreate(savedInstanceState); 95 mFinishing = false; 96 final Activity activity = getActivity(); 97 mApplicationFeatureProvider = FeatureFactory.getFeatureFactory() 98 .getApplicationFeatureProvider(); 99 mState = ApplicationsState.getInstance(activity.getApplication()); 100 mSession = mState.newSession(this, getSettingsLifecycle()); 101 mDpm = (DevicePolicyManager) activity.getSystemService(Context.DEVICE_POLICY_SERVICE); 102 mUserManager = (UserManager) activity.getSystemService(Context.USER_SERVICE); 103 mPm = activity.getPackageManager(); 104 IBinder b = ServiceManager.getService(Context.USB_SERVICE); 105 mUsbManager = IUsbManager.Stub.asInterface(b); 106 107 retrieveAppEntry(); 108 startListeningToPackageRemove(); 109 } 110 111 @Override onResume()112 public void onResume() { 113 super.onResume(); 114 mAppsControlDisallowedAdmin = RestrictedLockUtilsInternal.checkIfRestrictionEnforced( 115 getActivity(), UserManager.DISALLOW_APPS_CONTROL, mUserId); 116 mAppsControlDisallowedBySystem = RestrictedLockUtilsInternal.hasBaseUserRestriction( 117 getActivity(), UserManager.DISALLOW_APPS_CONTROL, mUserId); 118 119 if (!refreshUi()) { 120 setIntentAndFinish(true /* appChanged */); 121 } 122 } 123 124 125 @Override onDestroy()126 public void onDestroy() { 127 stopListeningToPackageRemove(); 128 super.onDestroy(); 129 } 130 retrieveAppEntry()131 protected String retrieveAppEntry() { 132 final Bundle args = getArguments(); 133 mPackageName = (args != null) ? args.getString(ARG_PACKAGE_NAME) : null; 134 Intent intent = (args == null) ? 135 getIntent() : (Intent) args.getParcelable("intent"); 136 if (mPackageName == null) { 137 if (intent != null && intent.getData() != null) { 138 mPackageName = intent.getData().getSchemeSpecificPart(); 139 } 140 } 141 if (intent != null && intent.hasExtra(Intent.EXTRA_USER_HANDLE)) { 142 mUserId = ((UserHandle) intent.getParcelableExtra(Intent.EXTRA_USER_HANDLE)) 143 .getIdentifier(); 144 if (mUserId != UserHandle.myUserId() && !hasInteractAcrossUsersFullPermission()) { 145 Log.w(TAG, "Intent not valid."); 146 finish(); 147 return ""; 148 } 149 } else { 150 mUserId = UserHandle.myUserId(); 151 } 152 mAppEntry = mState.getEntry(mPackageName, mUserId); 153 if (mAppEntry != null) { 154 // Get application info again to refresh changed properties of application 155 try { 156 mPackageInfo = mPm.getPackageInfoAsUser( 157 mAppEntry.info.packageName, 158 PackageManager.PackageInfoFlags.of( 159 PackageManager.MATCH_DISABLED_COMPONENTS 160 | PackageManager.GET_SIGNING_CERTIFICATES 161 | PackageManager.GET_PERMISSIONS 162 | PackageManager.MATCH_ARCHIVED_PACKAGES), 163 mUserId); 164 } catch (NameNotFoundException e) { 165 Log.e(TAG, "Exception when retrieving package:" + mAppEntry.info.packageName, e); 166 } 167 } else { 168 Log.w(TAG, "Missing AppEntry; maybe reinstalling?"); 169 mPackageInfo = null; 170 } 171 172 return mPackageName; 173 } 174 175 @VisibleForTesting hasInteractAcrossUsersFullPermission()176 protected boolean hasInteractAcrossUsersFullPermission() { 177 Activity activity = getActivity(); 178 if (!(activity instanceof SettingsActivity)) { 179 return false; 180 } 181 try { 182 int callerUid = ActivityManager.getService().getLaunchedFromUid( 183 activity.getActivityToken()); 184 if (ActivityManager.checkUidPermission(Manifest.permission.INTERACT_ACROSS_USERS_FULL, 185 callerUid) != PackageManager.PERMISSION_GRANTED) { 186 Log.w(TAG, "Uid " + callerUid + " does not have required permission " 187 + Manifest.permission.INTERACT_ACROSS_USERS_FULL); 188 return false; 189 } 190 return true; 191 } catch (RemoteException e) { 192 return false; 193 } 194 } 195 setIntentAndFinish(boolean appChanged)196 protected void setIntentAndFinish(boolean appChanged) { 197 Log.i(TAG, "appChanged=" + appChanged); 198 Intent intent = new Intent(); 199 intent.putExtra(ManageApplications.APP_CHG, appChanged); 200 SettingsActivity sa = (SettingsActivity) getActivity(); 201 sa.finishPreferencePanel(Activity.RESULT_OK, intent); 202 mFinishing = true; 203 } 204 showDialogInner(int id, int moveErrorCode)205 protected void showDialogInner(int id, int moveErrorCode) { 206 DialogFragment newFragment = MyAlertDialogFragment.newInstance(id, moveErrorCode); 207 newFragment.setTargetFragment(this, 0); 208 newFragment.show(getFragmentManager(), "dialog " + id); 209 } 210 refreshUi()211 protected abstract boolean refreshUi(); 212 createDialog(int id, int errorCode)213 protected abstract AlertDialog createDialog(int id, int errorCode); 214 215 @Override onRunningStateChanged(boolean running)216 public void onRunningStateChanged(boolean running) { 217 // No op. 218 } 219 220 @Override onRebuildComplete(ArrayList<AppEntry> apps)221 public void onRebuildComplete(ArrayList<AppEntry> apps) { 222 // No op. 223 } 224 225 @Override onPackageIconChanged()226 public void onPackageIconChanged() { 227 // No op. 228 } 229 230 @Override onPackageSizeChanged(String packageName)231 public void onPackageSizeChanged(String packageName) { 232 // No op. 233 } 234 235 @Override onAllSizesComputed()236 public void onAllSizesComputed() { 237 // No op. 238 } 239 240 @Override onLauncherInfoChanged()241 public void onLauncherInfoChanged() { 242 // No op. 243 } 244 245 @Override onLoadEntriesCompleted()246 public void onLoadEntriesCompleted() { 247 // No op. 248 } 249 250 @Override onPackageListChanged()251 public void onPackageListChanged() { 252 if (!refreshUi()) { 253 setIntentAndFinish(true /* appChanged */); 254 } 255 } 256 startAppInfoFragment(Class<?> fragment, String title, String pkg, int uid, Fragment source, int request, int sourceMetricsCategory)257 public static void startAppInfoFragment(Class<?> fragment, String title, 258 String pkg, int uid, Fragment source, int request, int sourceMetricsCategory) { 259 final Bundle args = new Bundle(); 260 args.putString(AppInfoBase.ARG_PACKAGE_NAME, pkg); 261 args.putInt(AppInfoBase.ARG_PACKAGE_UID, uid); 262 263 new SubSettingLauncher(source.getContext()) 264 .setDestination(fragment.getName()) 265 .setSourceMetricsCategory(sourceMetricsCategory) 266 .setTitleText(title) 267 .setArguments(args) 268 .setUserHandle(new UserHandle(UserHandle.getUserId(uid))) 269 .setResultListener(source, request) 270 .launch(); 271 } 272 273 /** Starts app info fragment from SPA pages. */ startAppInfoFragment(Class<?> fragment, String title, ApplicationInfo app, Context context, int sourceMetricsCategory)274 public static void startAppInfoFragment(Class<?> fragment, String title, ApplicationInfo app, 275 Context context, int sourceMetricsCategory) { 276 final Bundle args = new Bundle(); 277 args.putString(AppInfoBase.ARG_PACKAGE_NAME, app.packageName); 278 args.putInt(AppInfoBase.ARG_PACKAGE_UID, app.uid); 279 280 new SubSettingLauncher(context) 281 .setDestination(fragment.getName()) 282 .setSourceMetricsCategory(sourceMetricsCategory) 283 .setTitleText(title) 284 .setArguments(args) 285 .setUserHandle(UserHandle.getUserHandleForUid(app.uid)) 286 .launch(); 287 } 288 289 public static class MyAlertDialogFragment extends InstrumentedDialogFragment { 290 291 private static final String ARG_ID = "id"; 292 293 @Override getMetricsCategory()294 public int getMetricsCategory() { 295 return SettingsEnums.DIALOG_BASE_APP_INFO_ACTION; 296 } 297 298 @Override onCreateDialog(Bundle savedInstanceState)299 public Dialog onCreateDialog(Bundle savedInstanceState) { 300 int id = getArguments().getInt(ARG_ID); 301 int errorCode = getArguments().getInt("moveError"); 302 Dialog dialog = ((AppInfoBase) getTargetFragment()).createDialog(id, errorCode); 303 if (dialog == null) { 304 throw new IllegalArgumentException("unknown id " + id); 305 } 306 return dialog; 307 } 308 newInstance(int id, int errorCode)309 public static MyAlertDialogFragment newInstance(int id, int errorCode) { 310 MyAlertDialogFragment dialogFragment = new MyAlertDialogFragment(); 311 Bundle args = new Bundle(); 312 args.putInt(ARG_ID, id); 313 args.putInt("moveError", errorCode); 314 dialogFragment.setArguments(args); 315 return dialogFragment; 316 } 317 } 318 startListeningToPackageRemove()319 protected void startListeningToPackageRemove() { 320 if (mListeningToPackageRemove) { 321 return; 322 } 323 mListeningToPackageRemove = true; 324 final IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_REMOVED); 325 filter.addDataScheme("package"); 326 getContext().registerReceiver(mPackageRemovedReceiver, filter); 327 } 328 stopListeningToPackageRemove()329 protected void stopListeningToPackageRemove() { 330 if (!mListeningToPackageRemove) { 331 return; 332 } 333 mListeningToPackageRemove = false; 334 getContext().unregisterReceiver(mPackageRemovedReceiver); 335 } 336 onPackageRemoved()337 protected void onPackageRemoved() { 338 getActivity().finishAndRemoveTask(); 339 } 340 341 protected final BroadcastReceiver mPackageRemovedReceiver = new BroadcastReceiver() { 342 @Override 343 public void onReceive(Context context, Intent intent) { 344 String packageName = intent.getData().getSchemeSpecificPart(); 345 if (!mFinishing && (mAppEntry == null || mAppEntry.info == null 346 || TextUtils.equals(mAppEntry.info.packageName, packageName))) { 347 onPackageRemoved(); 348 } 349 } 350 }; 351 352 } 353