blob: 7e1bc79399b9db1eb06ae1e79a9d7488f5719b96 [file] [log] [blame]
Dario Freni2e8dffc2019-02-06 14:55:16 +00001/*
2 * Copyright (C) 2019 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
Nikita Ioffe278af8f2019-07-04 20:48:25 +010014 * limitations under the License.
Dario Freni2e8dffc2019-02-06 14:55:16 +000015 */
16
17package com.android.server.pm;
18
Gavin Corkeryef441722019-05-09 17:02:10 +010019import android.annotation.IntDef;
Dario Freni2e8dffc2019-02-06 14:55:16 +000020import android.annotation.NonNull;
21import android.annotation.Nullable;
22import android.apex.ApexInfo;
23import android.apex.ApexInfoList;
24import android.apex.ApexSessionInfo;
25import android.apex.IApexService;
Mohammad Samiul Islambdccb752019-05-08 14:41:53 +000026import android.content.BroadcastReceiver;
Dario Freni14f885b2019-02-25 12:48:47 +000027import android.content.Context;
Mohammad Samiul Islambdccb752019-05-08 14:41:53 +000028import android.content.Intent;
29import android.content.IntentFilter;
Gavin Corkeryef441722019-05-09 17:02:10 +010030import android.content.pm.ApplicationInfo;
Dario Freni2e8dffc2019-02-06 14:55:16 +000031import android.content.pm.PackageInfo;
Mohammad Samiul Islam7aa7d2e2019-03-27 12:23:47 +000032import android.content.pm.PackageManager;
Dario Freni2e8dffc2019-02-06 14:55:16 +000033import android.content.pm.PackageParser;
Dario Freni2e8dffc2019-02-06 14:55:16 +000034import android.os.RemoteException;
35import android.os.ServiceManager;
Dario Freni2ce84342019-04-26 13:06:22 +010036import android.sysprop.ApexProperties;
Dario Freni2e8dffc2019-02-06 14:55:16 +000037import android.util.Slog;
38
Mohammad Samiul Islambdccb752019-05-08 14:41:53 +000039import com.android.internal.annotations.GuardedBy;
Dario Freni2e8dffc2019-02-06 14:55:16 +000040import com.android.internal.util.IndentingPrintWriter;
41
42import java.io.File;
43import java.io.PrintWriter;
Gavin Corkeryef441722019-05-09 17:02:10 +010044import java.lang.annotation.Retention;
45import java.lang.annotation.RetentionPolicy;
46import java.util.ArrayList;
Nikita Ioffef012a222019-03-05 22:37:55 +000047import java.util.Collections;
Gavin Corkeryef441722019-05-09 17:02:10 +010048import java.util.HashSet;
49import java.util.List;
50import java.util.stream.Collectors;
Dario Freni2e8dffc2019-02-06 14:55:16 +000051
52/**
53 * ApexManager class handles communications with the apex service to perform operation and queries,
54 * as well as providing caching to avoid unnecessary calls to the service.
55 */
Nikita Ioffe278af8f2019-07-04 20:48:25 +010056abstract class ApexManager {
Gavin Corkeryef441722019-05-09 17:02:10 +010057
Nikita Ioffe278af8f2019-07-04 20:48:25 +010058 private static final String TAG = "ApexManager";
Dario Freni14f885b2019-02-25 12:48:47 +000059
Gavin Corkeryef441722019-05-09 17:02:10 +010060 static final int MATCH_ACTIVE_PACKAGE = 1 << 0;
61 static final int MATCH_FACTORY_PACKAGE = 1 << 1;
Gavin Corkeryef441722019-05-09 17:02:10 +010062
Nikita Ioffe278af8f2019-07-04 20:48:25 +010063 /**
64 * Returns an instance of either {@link ApexManagerImpl} or {@link ApexManagerNoOp} depending
65 * on whenever this device supports APEX, i.e. {@link ApexProperties#updatable()} evaluates to
66 * {@code true}.
67 */
68 static ApexManager create(Context systemContext) {
69 if (ApexProperties.updatable().orElse(false)) {
Martijn Coenen3f629942019-02-22 13:15:46 +010070 try {
Nikita Ioffe278af8f2019-07-04 20:48:25 +010071 return new ApexManagerImpl(systemContext, IApexService.Stub.asInterface(
72 ServiceManager.getServiceOrThrow("apexservice")));
73 } catch (ServiceManager.ServiceNotFoundException e) {
74 throw new IllegalStateException("Required service apexservice not available");
Martijn Coenen3f629942019-02-22 13:15:46 +010075 }
Nikita Ioffe278af8f2019-07-04 20:48:25 +010076 } else {
77 return new ApexManagerNoOp();
Dario Freni2e8dffc2019-02-06 14:55:16 +000078 }
79 }
80
Nikita Ioffe278af8f2019-07-04 20:48:25 +010081 abstract void systemReady();
82
Dario Freni2e8dffc2019-02-06 14:55:16 +000083 /**
Gavin Corkeryef441722019-05-09 17:02:10 +010084 * Retrieves information about an APEX package.
Dario Freni2e8dffc2019-02-06 14:55:16 +000085 *
86 * @param packageName the package name to look for. Note that this is the package name reported
87 * in the APK container manifest (i.e. AndroidManifest.xml), which might
88 * differ from the one reported in the APEX manifest (i.e.
89 * apex_manifest.json).
Gavin Corkeryef441722019-05-09 17:02:10 +010090 * @param flags the type of package to return. This may match to active packages
91 * and factory (pre-installed) packages.
Dario Freni2e8dffc2019-02-06 14:55:16 +000092 * @return a PackageInfo object with the information about the package, or null if the package
93 * is not found.
94 */
Nikita Ioffe278af8f2019-07-04 20:48:25 +010095 @Nullable
96 abstract PackageInfo getPackageInfo(String packageName, @PackageInfoFlags int flags);
Dario Freni2e8dffc2019-02-06 14:55:16 +000097
98 /**
99 * Retrieves information about all active APEX packages.
100 *
Gavin Corkeryef441722019-05-09 17:02:10 +0100101 * @return a List of PackageInfo object, each one containing information about a different
Dario Freni2e8dffc2019-02-06 14:55:16 +0000102 * active package.
103 */
Nikita Ioffe278af8f2019-07-04 20:48:25 +0100104 abstract List<PackageInfo> getActivePackages();
Gavin Corkeryef441722019-05-09 17:02:10 +0100105
106 /**
107 * Retrieves information about all active pre-installed APEX packages.
108 *
109 * @return a List of PackageInfo object, each one containing information about a different
110 * active pre-installed package.
111 */
Nikita Ioffe278af8f2019-07-04 20:48:25 +0100112 abstract List<PackageInfo> getFactoryPackages();
Gavin Corkeryef441722019-05-09 17:02:10 +0100113
114 /**
115 * Retrieves information about all inactive APEX packages.
116 *
117 * @return a List of PackageInfo object, each one containing information about a different
118 * inactive package.
119 */
Nikita Ioffe278af8f2019-07-04 20:48:25 +0100120 abstract List<PackageInfo> getInactivePackages();
Dario Freni2e8dffc2019-02-06 14:55:16 +0000121
122 /**
Nikita Ioffef012a222019-03-05 22:37:55 +0000123 * Checks if {@code packageName} is an apex package.
124 *
125 * @param packageName package to check.
126 * @return {@code true} if {@code packageName} is an apex package.
127 */
Nikita Ioffe278af8f2019-07-04 20:48:25 +0100128 abstract boolean isApexPackage(String packageName);
Nikita Ioffef012a222019-03-05 22:37:55 +0000129
130 /**
Dario Freni2e8dffc2019-02-06 14:55:16 +0000131 * Retrieves information about an apexd staged session i.e. the internal state used by apexd to
132 * track the different states of a session.
133 *
134 * @param sessionId the identifier of the session.
135 * @return an ApexSessionInfo object, or null if the session is not known.
136 */
Nikita Ioffe278af8f2019-07-04 20:48:25 +0100137 @Nullable
138 abstract ApexSessionInfo getStagedSessionInfo(int sessionId);
Dario Freni2e8dffc2019-02-06 14:55:16 +0000139
140 /**
141 * Submit a staged session to apex service. This causes the apex service to perform some initial
142 * verification and accept or reject the session. Submitting a session successfully is not
143 * enough for it to be activated at the next boot, the caller needs to call
144 * {@link #markStagedSessionReady(int)}.
145 *
146 * @param sessionId the identifier of the {@link PackageInstallerSession} being submitted.
147 * @param childSessionIds if {@code sessionId} is a multi-package session, this should contain
148 * an array of identifiers of all the child sessions. Otherwise it should
149 * be an empty array.
150 * @param apexInfoList this is an output parameter, which needs to be initialized by tha caller
151 * and will be filled with a list of {@link ApexInfo} objects, each of which
152 * contains metadata about one of the packages being submitted as part of
153 * the session.
154 * @return whether the submission of the session was successful.
155 */
Nikita Ioffe278af8f2019-07-04 20:48:25 +0100156 abstract boolean submitStagedSession(
157 int sessionId, @NonNull int[] childSessionIds, @NonNull ApexInfoList apexInfoList);
Dario Freni2e8dffc2019-02-06 14:55:16 +0000158
159 /**
Dario Frenia0e3dda2019-02-18 20:58:54 +0000160 * Mark a staged session previously submitted using {@code submitStagedSession} as ready to be
Dario Freni2e8dffc2019-02-06 14:55:16 +0000161 * applied at next reboot.
162 *
163 * @param sessionId the identifier of the {@link PackageInstallerSession} being marked as ready.
164 * @return true upon success, false if the session is unknown.
165 */
Nikita Ioffe278af8f2019-07-04 20:48:25 +0100166 abstract boolean markStagedSessionReady(int sessionId);
Dario Freni2e8dffc2019-02-06 14:55:16 +0000167
168 /**
Nikita Ioffea820bd92019-02-15 14:22:44 +0000169 * Marks a staged session as successful.
170 *
171 * <p>Only activated session can be marked as successful.
172 *
173 * @param sessionId the identifier of the {@link PackageInstallerSession} being marked as
174 * successful.
175 */
Nikita Ioffe278af8f2019-07-04 20:48:25 +0100176 abstract void markStagedSessionSuccessful(int sessionId);
Nikita Ioffea820bd92019-02-15 14:22:44 +0000177
178 /**
Dario Freni83620602019-02-18 14:30:57 +0000179 * Whether the current device supports the management of APEX packages.
180 *
181 * @return true if APEX packages can be managed on this device, false otherwise.
182 */
Nikita Ioffe278af8f2019-07-04 20:48:25 +0100183 abstract boolean isApexSupported();
Dario Freni83620602019-02-18 14:30:57 +0000184
185 /**
shafik07205e32019-02-07 20:12:33 +0000186 * Abandons the (only) active session previously submitted.
187 *
188 * @return {@code true} upon success, {@code false} if any remote exception occurs
189 */
Nikita Ioffe278af8f2019-07-04 20:48:25 +0100190 abstract boolean abortActiveSession();
shafik07205e32019-02-07 20:12:33 +0000191
192 /**
Nikita Ioffef012a222019-03-05 22:37:55 +0000193 * Uninstalls given {@code apexPackage}.
194 *
195 * <p>NOTE. Device must be rebooted in order for uninstall to take effect.
196 *
197 * @param apexPackagePath package to uninstall.
198 * @return {@code true} upon successful uninstall, {@code false} otherwise.
199 */
Nikita Ioffe278af8f2019-07-04 20:48:25 +0100200 abstract boolean uninstallApex(String apexPackagePath);
Gavin Corkeryef441722019-05-09 17:02:10 +0100201
202 /**
Dario Freni2e8dffc2019-02-06 14:55:16 +0000203 * Dumps various state information to the provided {@link PrintWriter} object.
204 *
205 * @param pw the {@link PrintWriter} object to send information to.
206 * @param packageName a {@link String} containing a package name, or {@code null}. If set, only
207 * information about that specific package will be dumped.
208 */
Nikita Ioffe278af8f2019-07-04 20:48:25 +0100209 abstract void dump(PrintWriter pw, @Nullable String packageName);
210
211 @IntDef(
212 flag = true,
213 prefix = { "MATCH_"},
214 value = {MATCH_ACTIVE_PACKAGE, MATCH_FACTORY_PACKAGE})
215 @Retention(RetentionPolicy.SOURCE)
216 @interface PackageInfoFlags{}
217
218 /**
219 * An implementation of {@link ApexManager} that should be used in case device supports updating
220 * APEX packages.
221 */
222 private static class ApexManagerImpl extends ApexManager {
223 private final IApexService mApexService;
224 private final Context mContext;
225 private final Object mLock = new Object();
226 /**
227 * A map from {@code APEX packageName} to the {@Link PackageInfo} generated from the {@code
228 * AndroidManifest.xml}
229 *
230 * <p>Note that key of this map is {@code packageName} field of the corresponding {@code
231 * AndroidManifest.xml}.
232 */
233 @GuardedBy("mLock")
234 private List<PackageInfo> mAllPackagesCache;
235
236 ApexManagerImpl(Context context, IApexService apexService) {
237 mContext = context;
238 mApexService = apexService;
239 }
240
241 /**
242 * Whether an APEX package is active or not.
243 *
244 * @param packageInfo the package to check
245 * @return {@code true} if this package is active, {@code false} otherwise.
246 */
247 private static boolean isActive(PackageInfo packageInfo) {
248 return (packageInfo.applicationInfo.flags & ApplicationInfo.FLAG_INSTALLED) != 0;
249 }
250
251 /**
252 * Whether the APEX package is pre-installed or not.
253 *
254 * @param packageInfo the package to check
255 * @return {@code true} if this package is pre-installed, {@code false} otherwise.
256 */
257 private static boolean isFactory(PackageInfo packageInfo) {
258 return (packageInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0;
259 }
260
261 @Override
262 void systemReady() {
263 mContext.registerReceiver(new BroadcastReceiver() {
264 @Override
265 public void onReceive(Context context, Intent intent) {
266 populateAllPackagesCacheIfNeeded();
267 mContext.unregisterReceiver(this);
Dario Freni2e8dffc2019-02-06 14:55:16 +0000268 }
Nikita Ioffe278af8f2019-07-04 20:48:25 +0100269 }, new IntentFilter(Intent.ACTION_BOOT_COMPLETED));
270 }
271
272 private void populateAllPackagesCacheIfNeeded() {
273 synchronized (mLock) {
274 if (mAllPackagesCache != null) {
275 return;
276 }
277 try {
278 mAllPackagesCache = new ArrayList<>();
279 HashSet<String> activePackagesSet = new HashSet<>();
280 HashSet<String> factoryPackagesSet = new HashSet<>();
281 final ApexInfo[] allPkgs = mApexService.getAllPackages();
282 for (ApexInfo ai : allPkgs) {
283 // If the device is using flattened APEX, don't report any APEX
284 // packages since they won't be managed or updated by PackageManager.
285 if ((new File(ai.modulePath)).isDirectory()) {
286 break;
287 }
288 try {
289 final PackageInfo pkg = PackageParser.generatePackageInfoFromApex(
290 ai, PackageManager.GET_META_DATA
291 | PackageManager.GET_SIGNING_CERTIFICATES);
292 mAllPackagesCache.add(pkg);
293 if (ai.isActive) {
294 if (activePackagesSet.contains(pkg.packageName)) {
295 throw new IllegalStateException(
296 "Two active packages have the same name: "
297 + pkg.packageName);
298 }
299 activePackagesSet.add(pkg.packageName);
300 }
301 if (ai.isFactory) {
302 if (factoryPackagesSet.contains(pkg.packageName)) {
303 throw new IllegalStateException(
304 "Two factory packages have the same name: "
305 + pkg.packageName);
306 }
307 factoryPackagesSet.add(pkg.packageName);
308 }
309 } catch (PackageParser.PackageParserException pe) {
310 throw new IllegalStateException("Unable to parse: " + ai, pe);
311 }
312 }
313 } catch (RemoteException re) {
314 Slog.e(TAG, "Unable to retrieve packages from apexservice: " + re.toString());
315 throw new RuntimeException(re);
316 }
317 }
318 }
319
320 @Override
321 @Nullable PackageInfo getPackageInfo(String packageName, @PackageInfoFlags int flags) {
322 populateAllPackagesCacheIfNeeded();
323 boolean matchActive = (flags & MATCH_ACTIVE_PACKAGE) != 0;
324 boolean matchFactory = (flags & MATCH_FACTORY_PACKAGE) != 0;
325 for (PackageInfo packageInfo: mAllPackagesCache) {
326 if (!packageInfo.packageName.equals(packageName)) {
327 continue;
328 }
329 if ((!matchActive || isActive(packageInfo))
330 && (!matchFactory || isFactory(packageInfo))) {
331 return packageInfo;
332 }
333 }
334 return null;
335 }
336
337 @Override
338 List<PackageInfo> getActivePackages() {
339 populateAllPackagesCacheIfNeeded();
340 return mAllPackagesCache
341 .stream()
342 .filter(item -> isActive(item))
343 .collect(Collectors.toList());
344 }
345
346 @Override
347 List<PackageInfo> getFactoryPackages() {
348 populateAllPackagesCacheIfNeeded();
349 return mAllPackagesCache
350 .stream()
351 .filter(item -> isFactory(item))
352 .collect(Collectors.toList());
353 }
354
355 @Override
356 List<PackageInfo> getInactivePackages() {
357 populateAllPackagesCacheIfNeeded();
358 return mAllPackagesCache
359 .stream()
360 .filter(item -> !isActive(item))
361 .collect(Collectors.toList());
362 }
363
364 @Override
365 boolean isApexPackage(String packageName) {
366 if (!isApexSupported()) return false;
367 populateAllPackagesCacheIfNeeded();
368 for (PackageInfo packageInfo : mAllPackagesCache) {
369 if (packageInfo.packageName.equals(packageName)) {
370 return true;
371 }
372 }
373 return false;
374 }
375
376 @Override
377 @Nullable ApexSessionInfo getStagedSessionInfo(int sessionId) {
378 try {
379 ApexSessionInfo apexSessionInfo = mApexService.getStagedSessionInfo(sessionId);
380 if (apexSessionInfo.isUnknown) {
381 return null;
382 }
383 return apexSessionInfo;
384 } catch (RemoteException re) {
385 Slog.e(TAG, "Unable to contact apexservice", re);
386 throw new RuntimeException(re);
387 }
388 }
389
390 @Override
391 boolean submitStagedSession(
392 int sessionId, @NonNull int[] childSessionIds, @NonNull ApexInfoList apexInfoList) {
393 try {
394 return mApexService.submitStagedSession(sessionId, childSessionIds, apexInfoList);
395 } catch (RemoteException re) {
396 Slog.e(TAG, "Unable to contact apexservice", re);
397 throw new RuntimeException(re);
398 }
399 }
400
401 @Override
402 boolean markStagedSessionReady(int sessionId) {
403 try {
404 return mApexService.markStagedSessionReady(sessionId);
405 } catch (RemoteException re) {
406 Slog.e(TAG, "Unable to contact apexservice", re);
407 throw new RuntimeException(re);
408 }
409 }
410
411 @Override
412 void markStagedSessionSuccessful(int sessionId) {
413 try {
414 mApexService.markStagedSessionSuccessful(sessionId);
415 } catch (RemoteException re) {
416 Slog.e(TAG, "Unable to contact apexservice", re);
417 throw new RuntimeException(re);
418 } catch (Exception e) {
419 // It is fine to just log an exception in this case. APEXd will be able to recover
420 // in case markStagedSessionSuccessful fails.
421 Slog.e(TAG, "Failed to mark session " + sessionId + " as successful", e);
422 }
423 }
424
425 @Override
426 boolean isApexSupported() {
427 return true;
428 }
429
430 @Override
431 boolean abortActiveSession() {
432 try {
433 mApexService.abortActiveSession();
434 return true;
435 } catch (RemoteException re) {
436 Slog.e(TAG, "Unable to contact apexservice", re);
437 return false;
438 }
439 }
440
441 @Override
442 boolean uninstallApex(String apexPackagePath) {
443 try {
444 mApexService.unstagePackages(Collections.singletonList(apexPackagePath));
445 return true;
446 } catch (Exception e) {
447 return false;
448 }
449 }
450
451 /**
452 * Dump information about the packages contained in a particular cache
453 * @param packagesCache the cache to print information about.
454 * @param packageName a {@link String} containing a package name, or {@code null}. If set,
455 * only information about that specific package will be dumped.
456 * @param ipw the {@link IndentingPrintWriter} object to send information to.
457 */
458 void dumpFromPackagesCache(
459 List<PackageInfo> packagesCache,
460 @Nullable String packageName,
461 IndentingPrintWriter ipw) {
462 ipw.println();
463 ipw.increaseIndent();
464 for (PackageInfo pi : packagesCache) {
465 if (packageName != null && !packageName.equals(pi.packageName)) {
466 continue;
467 }
468 ipw.println(pi.packageName);
469 ipw.increaseIndent();
470 ipw.println("Version: " + pi.versionCode);
471 ipw.println("Path: " + pi.applicationInfo.sourceDir);
472 ipw.println("IsActive: " + isActive(pi));
473 ipw.println("IsFactory: " + isFactory(pi));
Dario Freni2e8dffc2019-02-06 14:55:16 +0000474 ipw.decreaseIndent();
475 }
476 ipw.decreaseIndent();
Nikita Ioffe278af8f2019-07-04 20:48:25 +0100477 ipw.println();
478 }
479
480 @Override
481 void dump(PrintWriter pw, @Nullable String packageName) {
482 final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ", 120);
483 try {
484 populateAllPackagesCacheIfNeeded();
485 ipw.println();
486 ipw.println("Active APEX packages:");
487 dumpFromPackagesCache(getActivePackages(), packageName, ipw);
488 ipw.println("Inactive APEX packages:");
489 dumpFromPackagesCache(getInactivePackages(), packageName, ipw);
490 ipw.println("Factory APEX packages:");
491 dumpFromPackagesCache(getFactoryPackages(), packageName, ipw);
492 ipw.increaseIndent();
493 ipw.println("APEX session state:");
494 ipw.increaseIndent();
495 final ApexSessionInfo[] sessions = mApexService.getSessions();
496 for (ApexSessionInfo si : sessions) {
497 ipw.println("Session ID: " + si.sessionId);
498 ipw.increaseIndent();
499 if (si.isUnknown) {
500 ipw.println("State: UNKNOWN");
501 } else if (si.isVerified) {
502 ipw.println("State: VERIFIED");
503 } else if (si.isStaged) {
504 ipw.println("State: STAGED");
505 } else if (si.isActivated) {
506 ipw.println("State: ACTIVATED");
507 } else if (si.isActivationFailed) {
508 ipw.println("State: ACTIVATION FAILED");
509 } else if (si.isSuccess) {
510 ipw.println("State: SUCCESS");
511 } else if (si.isRollbackInProgress) {
512 ipw.println("State: ROLLBACK IN PROGRESS");
513 } else if (si.isRolledBack) {
514 ipw.println("State: ROLLED BACK");
515 } else if (si.isRollbackFailed) {
516 ipw.println("State: ROLLBACK FAILED");
517 }
518 ipw.decreaseIndent();
519 }
520 ipw.decreaseIndent();
521 } catch (RemoteException e) {
522 ipw.println("Couldn't communicate with apexd.");
523 }
Dario Freni2e8dffc2019-02-06 14:55:16 +0000524 }
525 }
Mohammad Samiul Islambdccb752019-05-08 14:41:53 +0000526
Nikita Ioffe278af8f2019-07-04 20:48:25 +0100527 /**
528 * An implementation of {@link ApexManager} that should be used in case device does not support
529 * updating APEX packages.
530 */
531 private static final class ApexManagerNoOp extends ApexManager {
532
533 @Override
534 void systemReady() {
535 // No-op
536 }
537
538 @Override
539 PackageInfo getPackageInfo(String packageName, int flags) {
540 return null;
541 }
542
543 @Override
544 List<PackageInfo> getActivePackages() {
545 return Collections.emptyList();
546 }
547
548 @Override
549 List<PackageInfo> getFactoryPackages() {
550 return Collections.emptyList();
551 }
552
553 @Override
554 List<PackageInfo> getInactivePackages() {
555 return Collections.emptyList();
556 }
557
558 @Override
559 boolean isApexPackage(String packageName) {
560 return false;
561 }
562
563 @Override
564 ApexSessionInfo getStagedSessionInfo(int sessionId) {
565 throw new UnsupportedOperationException();
566 }
567
568 @Override
569 boolean submitStagedSession(int sessionId, int[] childSessionIds,
570 ApexInfoList apexInfoList) {
571 throw new UnsupportedOperationException();
572 }
573
574 @Override
575 boolean markStagedSessionReady(int sessionId) {
576 throw new UnsupportedOperationException();
577 }
578
579 @Override
580 void markStagedSessionSuccessful(int sessionId) {
581 throw new UnsupportedOperationException();
582 }
583
584 @Override
585 boolean isApexSupported() {
586 return false;
587 }
588
589 @Override
590 boolean abortActiveSession() {
591 throw new UnsupportedOperationException();
592 }
593
594 @Override
595 boolean uninstallApex(String apexPackagePath) {
596 throw new UnsupportedOperationException();
597 }
598
599 @Override
600 void dump(PrintWriter pw, String packageName) {
601 // No-op
602 }
Mohammad Samiul Islambdccb752019-05-08 14:41:53 +0000603 }
Jiyong Parkc53878f2019-05-25 01:31:32 +0900604}