1 /* 2 * Copyright (C) 2022 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.manageapplications; 18 19 import static android.content.pm.PackageManager.INSTALL_ALL_WHITELIST_RESTRICTED_PERMISSIONS; 20 import static android.content.pm.PackageManager.INSTALL_FAILED_INVALID_URI; 21 import static android.content.pm.PackageManager.INSTALL_REASON_USER; 22 import static android.os.UserManager.USER_TYPE_PROFILE_CLONE; 23 24 import android.app.ActivityManagerNative; 25 import android.app.AppGlobals; 26 import android.app.IActivityManager; 27 import android.content.Context; 28 import android.content.Intent; 29 import android.net.Uri; 30 import android.os.RemoteException; 31 import android.os.UserHandle; 32 import android.os.UserManager; 33 import android.util.Log; 34 35 import androidx.fragment.app.FragmentActivity; 36 37 import com.android.settings.Utils; 38 39 import java.util.HashSet; 40 41 /** 42 * Handles clone user creation and clone app install/uninstall. 43 */ 44 public class CloneBackend { 45 46 public static final String TAG = "CloneBackend"; 47 public static final int SUCCESS = 0; 48 private static final int ERROR_CREATING_CLONE_USER = 1; 49 private static final int ERROR_STARTING_CLONE_USER = 2; 50 private static final int ERROR_CLONING_PACKAGE = 3; 51 private static CloneBackend sInstance; 52 private Context mContext; 53 private int mCloneUserId; 54 CloneBackend(Context context)55 private CloneBackend(Context context) { 56 mContext = context; 57 mCloneUserId = Utils.getCloneUserId(context); 58 } 59 60 /** 61 * @param context 62 * @return a CloneBackend object 63 */ getInstance(Context context)64 public static CloneBackend getInstance(Context context) { 65 if (sInstance == null) { 66 sInstance = new CloneBackend(context); 67 } 68 return sInstance; 69 } 70 71 /** 72 * Starts activity to uninstall cloned app. 73 * 74 * <p> Invokes {@link com.android.packageinstaller.UninstallerActivity} which then displays the 75 * dialog to the user and handles actual uninstall. 76 */ uninstallClonedApp(String packageName, boolean allUsers, FragmentActivity activity)77 void uninstallClonedApp(String packageName, boolean allUsers, FragmentActivity activity) { 78 // Create new intent to launch Uninstaller activity. 79 Uri packageUri = Uri.parse("package:" + packageName); 80 Intent uninstallIntent = new Intent(Intent.ACTION_UNINSTALL_PACKAGE, packageUri); 81 uninstallIntent.putExtra(Intent.EXTRA_UNINSTALL_ALL_USERS, allUsers); 82 uninstallIntent.putExtra(Intent.EXTRA_USER, UserHandle.of(mCloneUserId)); 83 // Trigger uninstall as clone user. 84 activity.startActivityAsUser(uninstallIntent, UserHandle.of(mCloneUserId)); 85 } 86 87 /** 88 * Installs another instance of given package in clone user. 89 * 90 * <p> Creates clone user if doesn't exist and starts the new user before installing app. 91 * @param packageName 92 * @return error/success code 93 */ installCloneApp(String packageName)94 public int installCloneApp(String packageName) { 95 String userName = "cloneUser"; 96 UserHandle cloneUserHandle = null; 97 boolean newlyCreated = false; 98 99 // Create clone user if not already exists. 100 if (mCloneUserId == -1) { 101 UserManager um = mContext.getSystemService(UserManager.class); 102 try { 103 cloneUserHandle = um.createProfile(userName, USER_TYPE_PROFILE_CLONE, 104 new HashSet<>()); 105 } catch (Exception e) { 106 if (ManageApplications.DEBUG) { 107 Log.e(TAG, "Error occurred creating clone user" + e.getMessage()); 108 } 109 return ERROR_CREATING_CLONE_USER; 110 } 111 112 if (cloneUserHandle != null) { 113 mCloneUserId = cloneUserHandle.getIdentifier(); 114 newlyCreated = true; 115 if (ManageApplications.DEBUG) { 116 Log.d(TAG, "Created clone user " + mCloneUserId); 117 } 118 } else { 119 mCloneUserId = -1; 120 } 121 } 122 123 if (mCloneUserId > 0) { 124 // If clone user is newly created for the first time, then start this user. 125 if (newlyCreated) { 126 IActivityManager am = ActivityManagerNative.getDefault(); 127 try { 128 am.startProfile(mCloneUserId); 129 } catch (RemoteException e) { 130 if (ManageApplications.DEBUG) { 131 Log.e(TAG, "Error starting clone user " + e.getMessage()); 132 } 133 return ERROR_STARTING_CLONE_USER; 134 } 135 } 136 137 // Install given app in clone user 138 int res = 0; 139 try { 140 res = AppGlobals.getPackageManager().installExistingPackageAsUser( 141 packageName, mCloneUserId, 142 INSTALL_ALL_WHITELIST_RESTRICTED_PERMISSIONS, INSTALL_REASON_USER, null); 143 } catch (RemoteException e) { 144 if (ManageApplications.DEBUG) { 145 Log.e(TAG, "Error installing package" + packageName + " in clone user." 146 + e.getMessage()); 147 } 148 return ERROR_CLONING_PACKAGE; 149 } 150 151 if (res == INSTALL_FAILED_INVALID_URI) { 152 if (ManageApplications.DEBUG) { 153 Log.e(TAG, "Package " + packageName + " doesn't exist."); 154 } 155 return ERROR_CLONING_PACKAGE; 156 } 157 } 158 159 if (ManageApplications.DEBUG) { 160 Log.i(TAG, "Package " + packageName + " cloned successfully."); 161 } 162 return SUCCESS; 163 } 164 getCloneUserId()165 public int getCloneUserId() { 166 return mCloneUserId; 167 } 168 169 /** 170 * Resets {@link #mCloneUserId} to -1. 171 * Typically called after the cloneUser is removed, so that the obsolete clonedUserId present 172 * with the CloneBackend instance can be cleared. 173 */ resetCloneUserId()174 public void resetCloneUserId() { 175 mCloneUserId = -1; 176 } 177 } 178