PackageInstallerService.java revision bb580670350b76fa2fcc5ee873f99b7970759cbf
1/* 2 * Copyright (C) 2014 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 17package com.android.server.pm; 18 19import static android.content.pm.PackageManager.INSTALL_ALL_USERS; 20import static android.content.pm.PackageManager.INSTALL_FROM_ADB; 21import static android.content.pm.PackageManager.INSTALL_REPLACE_EXISTING; 22 23import android.app.AppOpsManager; 24import android.content.Context; 25import android.content.pm.IPackageDeleteObserver; 26import android.content.pm.IPackageInstaller; 27import android.content.pm.IPackageInstallerObserver; 28import android.content.pm.IPackageInstallerSession; 29import android.content.pm.InstallSessionInfo; 30import android.content.pm.InstallSessionParams; 31import android.os.Binder; 32import android.os.FileUtils; 33import android.os.HandlerThread; 34import android.os.Process; 35import android.os.RemoteException; 36import android.os.SELinux; 37import android.os.UserHandle; 38import android.os.UserManager; 39import android.system.ErrnoException; 40import android.system.Os; 41import android.util.ArraySet; 42import android.util.Slog; 43import android.util.SparseArray; 44 45import com.android.internal.annotations.GuardedBy; 46import com.android.internal.util.ArrayUtils; 47import com.android.server.IoThread; 48import com.google.android.collect.Sets; 49 50import java.io.File; 51import java.io.FilenameFilter; 52import java.io.IOException; 53import java.util.ArrayList; 54import java.util.List; 55 56public class PackageInstallerService extends IPackageInstaller.Stub { 57 private static final String TAG = "PackageInstaller"; 58 59 // TODO: destroy sessions with old timestamps 60 // TODO: remove outstanding sessions when installer package goes away 61 62 private final Context mContext; 63 private final PackageManagerService mPm; 64 private final AppOpsManager mAppOps; 65 66 private final File mStagingDir; 67 private final HandlerThread mInstallThread; 68 69 private final Callback mCallback = new Callback(); 70 71 @GuardedBy("mSessions") 72 private int mNextSessionId; 73 @GuardedBy("mSessions") 74 private final SparseArray<PackageInstallerSession> mSessions = new SparseArray<>(); 75 76 private static final FilenameFilter sStageFilter = new FilenameFilter() { 77 @Override 78 public boolean accept(File dir, String name) { 79 return name.startsWith("vmdl") && name.endsWith(".tmp"); 80 } 81 }; 82 83 public PackageInstallerService(Context context, PackageManagerService pm, File stagingDir) { 84 mContext = context; 85 mPm = pm; 86 mAppOps = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE); 87 88 mStagingDir = stagingDir; 89 90 mInstallThread = new HandlerThread(TAG); 91 mInstallThread.start(); 92 93 synchronized (mSessions) { 94 readSessionsLocked(); 95 96 // Clean up orphaned staging directories 97 final ArraySet<File> stages = Sets.newArraySet(mStagingDir.listFiles(sStageFilter)); 98 for (int i = 0; i < mSessions.size(); i++) { 99 final PackageInstallerSession session = mSessions.valueAt(i); 100 stages.remove(session.sessionStageDir); 101 } 102 for (File stage : stages) { 103 Slog.w(TAG, "Deleting orphan stage " + stage); 104 if (stage.isDirectory()) { 105 FileUtils.deleteContents(stage); 106 } 107 stage.delete(); 108 } 109 } 110 } 111 112 @Deprecated 113 public File allocateSessionDir() throws IOException { 114 synchronized (mSessions) { 115 try { 116 final int sessionId = allocateSessionIdLocked(); 117 return prepareSessionStageDir(sessionId); 118 } catch (IllegalStateException e) { 119 throw new IOException(e); 120 } 121 } 122 } 123 124 private void readSessionsLocked() { 125 // TODO: implement persisting 126 mSessions.clear(); 127 mNextSessionId = 1; 128 } 129 130 private void writeSessionsLocked() { 131 // TODO: implement persisting 132 } 133 134 private void writeSessionsAsync() { 135 IoThread.getHandler().post(new Runnable() { 136 @Override 137 public void run() { 138 synchronized (mSessions) { 139 writeSessionsLocked(); 140 } 141 } 142 }); 143 } 144 145 @Override 146 public int createSession(String installerPackageName, InstallSessionParams params, 147 int userId) { 148 final int callingUid = Binder.getCallingUid(); 149 mPm.enforceCrossUserPermission(callingUid, userId, false, TAG); 150 151 if (mPm.isUserRestricted(UserHandle.getUserId(callingUid), 152 UserManager.DISALLOW_INSTALL_APPS)) { 153 throw new SecurityException("User restriction prevents installing"); 154 } 155 156 if ((callingUid == Process.SHELL_UID) || (callingUid == 0)) { 157 params.installFlags |= INSTALL_FROM_ADB; 158 } else { 159 mAppOps.checkPackage(callingUid, installerPackageName); 160 161 params.installFlags &= ~INSTALL_FROM_ADB; 162 params.installFlags &= ~INSTALL_ALL_USERS; 163 params.installFlags |= INSTALL_REPLACE_EXISTING; 164 } 165 166 synchronized (mSessions) { 167 final long createdMillis = System.currentTimeMillis(); 168 final int sessionId = allocateSessionIdLocked(); 169 final File sessionStageDir = prepareSessionStageDir(sessionId); 170 171 final PackageInstallerSession session = new PackageInstallerSession(mCallback, mPm, 172 sessionId, userId, installerPackageName, callingUid, params, createdMillis, 173 sessionStageDir, mInstallThread.getLooper()); 174 mSessions.put(sessionId, session); 175 176 writeSessionsAsync(); 177 return sessionId; 178 } 179 } 180 181 @Override 182 public IPackageInstallerSession openSession(int sessionId) { 183 synchronized (mSessions) { 184 final PackageInstallerSession session = mSessions.get(sessionId); 185 if (session == null) { 186 throw new IllegalStateException("Missing session " + sessionId); 187 } 188 if (Binder.getCallingUid() != session.installerUid) { 189 throw new SecurityException("Caller has no access to session " + sessionId); 190 } 191 return session; 192 } 193 } 194 195 private int allocateSessionIdLocked() { 196 if (mSessions.get(mNextSessionId) != null) { 197 throw new IllegalStateException("Next session already allocated"); 198 } 199 return mNextSessionId++; 200 } 201 202 private File prepareSessionStageDir(int sessionId) { 203 final File file = new File(mStagingDir, "vmdl" + sessionId + ".tmp"); 204 205 if (file.exists()) { 206 throw new IllegalStateException(); 207 } 208 209 try { 210 Os.mkdir(file.getAbsolutePath(), 0755); 211 Os.chmod(file.getAbsolutePath(), 0755); 212 } catch (ErrnoException e) { 213 // This purposefully throws if directory already exists 214 throw new IllegalStateException("Failed to prepare session dir", e); 215 } 216 217 if (!SELinux.restorecon(file)) { 218 throw new IllegalStateException("Failed to prepare session dir"); 219 } 220 221 return file; 222 } 223 224 @Override 225 public List<InstallSessionInfo> getSessions(int userId) { 226 mPm.enforceCrossUserPermission(Binder.getCallingUid(), userId, false, TAG); 227 228 final List<InstallSessionInfo> result = new ArrayList<>(); 229 synchronized (mSessions) { 230 for (int i = 0; i < mSessions.size(); i++) { 231 final PackageInstallerSession session = mSessions.valueAt(i); 232 if (session.userId == userId) { 233 result.add(session.generateInfo()); 234 } 235 } 236 } 237 return result; 238 } 239 240 @Override 241 public void uninstall(String packageName, int flags, IPackageDeleteObserver observer, 242 int userId) { 243 mPm.deletePackageAsUser(packageName, observer, userId, flags); 244 } 245 246 @Override 247 public void uninstallSplit(String basePackageName, String overlayName, int flags, 248 IPackageDeleteObserver observer, int userId) { 249 // TODO: flesh out once PM has split support 250 throw new UnsupportedOperationException(); 251 } 252 253 @Override 254 public void registerObserver(IPackageInstallerObserver observer, int userId) { 255 throw new UnsupportedOperationException(); 256 } 257 258 @Override 259 public void unregisterObserver(IPackageInstallerObserver observer, int userId) { 260 throw new UnsupportedOperationException(); 261 } 262 263 class Callback { 264 public void onProgressChanged(PackageInstallerSession session) { 265 // TODO: notify listeners 266 } 267 268 public void onSessionInvalid(PackageInstallerSession session) { 269 writeSessionsAsync(); 270 } 271 } 272} 273