diff options
Diffstat (limited to 'cmds')
36 files changed, 2741 insertions, 584 deletions
diff --git a/cmds/am/src/com/android/commands/am/Am.java b/cmds/am/src/com/android/commands/am/Am.java index ffe7ac9..ba11a81 100644 --- a/cmds/am/src/com/android/commands/am/Am.java +++ b/cmds/am/src/com/android/commands/am/Am.java @@ -19,29 +19,44 @@ package com.android.commands.am; import android.app.ActivityManager; -import android.app.ActivityManager.StackBoxInfo; +import android.app.ActivityManager.StackInfo; import android.app.ActivityManagerNative; +import android.app.IActivityContainer; import android.app.IActivityController; import android.app.IActivityManager; import android.app.IInstrumentationWatcher; import android.app.Instrumentation; +import android.app.ProfilerInfo; import android.app.UiAutomationConnection; +import android.app.usage.ConfigurationStats; +import android.app.usage.IUsageStatsManager; +import android.app.usage.UsageStatsManager; import android.content.ComponentName; +import android.content.Context; import android.content.IIntentReceiver; import android.content.Intent; import android.content.pm.IPackageManager; +import android.content.pm.ParceledListSlice; import android.content.pm.ResolveInfo; +import android.content.res.Configuration; +import android.graphics.Rect; import android.net.Uri; import android.os.Binder; import android.os.Build; import android.os.Bundle; +import android.os.IBinder; import android.os.ParcelFileDescriptor; import android.os.RemoteException; import android.os.ServiceManager; +import android.os.SystemClock; import android.os.SystemProperties; import android.os.UserHandle; +import android.text.TextUtils; import android.util.AndroidException; +import android.util.ArrayMap; import android.view.IWindowManager; +import android.view.View; + import com.android.internal.os.BaseCommand; import dalvik.system.VMRuntime; @@ -53,6 +68,9 @@ import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintStream; import java.net.URISyntaxException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; import java.util.HashSet; import java.util.List; @@ -69,6 +87,8 @@ public class Am extends BaseCommand { private String mReceiverPermission; private String mProfileFile; + private int mSamplingInterval; + private boolean mAutoStop; /** * Command-line entry point. @@ -84,7 +104,7 @@ public class Am extends BaseCommand { out.println( "usage: am [subcommand] [options]\n" + "usage: am start [-D] [-W] [-P <FILE>] [--start-profiler <FILE>]\n" + - " [--R COUNT] [-S] [--opengl-trace]\n" + + " [--sampling INTERVAL] [-R COUNT] [-S] [--opengl-trace]\n" + " [--user <USER_ID> | current] <INTENT>\n" + " am startservice [--user <USER_ID> | current] <INTENT>\n" + " am stopservice [--user <USER_ID> | current] <INTENT>\n" + @@ -94,11 +114,7 @@ public class Am extends BaseCommand { " am broadcast [--user <USER_ID> | all | current] <INTENT>\n" + " am instrument [-r] [-e <NAME> <VALUE>] [-p <FILE>] [-w]\n" + " [--user <USER_ID> | current]\n" + - " [--no-window-animation]\n" + - " [--abi <ABI>]\n : Launch the instrumented process with the " + - " selected ABI. This assumes that the process supports the" + - " selected ABI." + - " <COMPONENT>\n" + + " [--no-window-animation] [--abi <ABI>] <COMPONENT>\n" + " am profile start [--user <USER_ID> current] <PROCESS> <FILE>\n" + " am profile stop [--user <USER_ID> current] [<PROCESS>]\n" + " am dumpheap [--user <USER_ID> current] [-n] <PROCESS> <FILE>\n" + @@ -112,17 +128,23 @@ public class Am extends BaseCommand { " am to-uri [INTENT]\n" + " am to-intent-uri [INTENT]\n" + " am switch-user <USER_ID>\n" + + " am start-user <USER_ID>\n" + " am stop-user <USER_ID>\n" + - " am stack create <TASK_ID> <RELATIVE_STACK_BOX_ID> <POSITION> <WEIGHT>\n" + + " am stack start <DISPLAY_ID> <INTENT>\n" + " am stack movetask <TASK_ID> <STACK_ID> [true|false]\n" + - " am stack resize <STACK_ID> <WEIGHT>\n" + - " am stack boxes\n" + - " am stack box <STACK_BOX_ID>\n" + + " am stack resize <STACK_ID> <LEFT,TOP,RIGHT,BOTTOM>\n" + + " am stack list\n" + + " am stack info <STACK_ID>\n" + + " am lock-task <TASK_ID>\n" + + " am lock-task stop\n" + + " am get-config\n" + "\n" + "am start: start an Activity. Options are:\n" + " -D: enable debugging\n" + " -W: wait for launch to complete\n" + " --start-profiler <FILE>: start profiler and send results to <FILE>\n" + + " --sampling INTERVAL: use sample profiling with INTERVAL microseconds\n" + + " between samples (use with --start-profiler)\n" + " -P <FILE>: like above, but profiling stops when app goes idle\n" + " -R: repeat the activity launch <COUNT> times. Prior to each repeat,\n" + " the top activity will be finished.\n" + @@ -168,6 +190,8 @@ public class Am extends BaseCommand { " --user <USER_ID> | current: Specify user instrumentation runs in;\n" + " current user if not specified.\n" + " --no-window-animation: turn off window animations while running.\n" + + " --abi <ABI>: Launch the instrumented process with the selected ABI.\n" + + " This assumes that the process supports the selected ABI.\n" + "\n" + "am profile: start and stop profiler on a process. The given <PROCESS> argument\n" + " may be either a process name or pid. Options are:\n" + @@ -208,27 +232,27 @@ public class Am extends BaseCommand { "am switch-user: switch to put USER_ID in the foreground, starting\n" + " execution of that user if it is currently stopped.\n" + "\n" + + "am start-user: start USER_ID in background if it is currently stopped,\n" + + " use switch-user if you want to start the user in foreground.\n" + + "\n" + "am stop-user: stop execution of USER_ID, not allowing it to run any\n" + - " code until a later explicit switch to it.\n" + + " code until a later explicit start or switch to it.\n" + "\n" + - "am stack create: create a new stack relative to an existing one.\n" + - " <TASK_ID>: the task to populate the new stack with. Must exist.\n" + - " <RELATIVE_STACK_BOX_ID>: existing stack box's id.\n" + - " <POSITION>: 0: before <RELATIVE_STACK_BOX_ID>, per RTL/LTR configuration,\n" + - " 1: after <RELATIVE_STACK_BOX_ID>, per RTL/LTR configuration,\n" + - " 2: to left of <RELATIVE_STACK_BOX_ID>,\n" + - " 3: to right of <RELATIVE_STACK_BOX_ID>," + - " 4: above <RELATIVE_STACK_BOX_ID>, 5: below <RELATIVE_STACK_BOX_ID>\n" + - " <WEIGHT>: float between 0.2 and 0.8 inclusive.\n" + + "am stack start: start a new activity on <DISPLAY_ID> using <INTENT>.\n" + "\n" + "am stack movetask: move <TASK_ID> from its current stack to the top (true) or" + " bottom (false) of <STACK_ID>.\n" + "\n" + - "am stack resize: change <STACK_ID> relative size to new <WEIGHT>.\n" + + "am stack resize: change <STACK_ID> size and position to <LEFT,TOP,RIGHT,BOTTOM>.\n" + + "\n" + + "am stack list: list all of the activity stacks and their sizes.\n" + "\n" + - "am stack boxes: list the hierarchy of stack boxes and their contents.\n" + + "am stack info: display the information about activity stack <STACK_ID>.\n" + "\n" + - "am stack box: list the hierarchy of stack boxes rooted at <STACK_BOX_ID>.\n" + + "am lock-task: bring <TASK_ID> to the front and don't allow other tasks to run\n" + + "\n" + + "am get-config: retrieve the configuration and any recent configurations\n" + + " of the device\n" + "\n" + "<INTENT> specifications include these flags and arguments:\n" + " [-a <ACTION>] [-d <DATA_URI>] [-t <MIME_TYPE>]\n" + @@ -244,8 +268,11 @@ public class Am extends BaseCommand { " [--eia <EXTRA_KEY> <EXTRA_INT_VALUE>[,<EXTRA_INT_VALUE...]]\n" + " [--ela <EXTRA_KEY> <EXTRA_LONG_VALUE>[,<EXTRA_LONG_VALUE...]]\n" + " [--efa <EXTRA_KEY> <EXTRA_FLOAT_VALUE>[,<EXTRA_FLOAT_VALUE...]]\n" + + " [--esa <EXTRA_KEY> <EXTRA_STRING_VALUE>[,<EXTRA_STRING_VALUE...]]\n" + + " (to embed a comma into a string escape it using \"\\,\")\n" + " [-n <COMPONENT>] [-f <FLAGS>]\n" + " [--grant-read-uri-permission] [--grant-write-uri-permission]\n" + + " [--grant-persistable-uri-permission] [--grant-prefix-uri-permission]\n" + " [--debug-log-resolution] [--exclude-stopped-packages]\n" + " [--include-stopped-packages]\n" + " [--activity-brought-to-front] [--activity-clear-top]\n" + @@ -315,10 +342,16 @@ public class Am extends BaseCommand { runToUri(true); } else if (op.equals("switch-user")) { runSwitchUser(); + } else if (op.equals("start-user")) { + runStartUserInBackground(); } else if (op.equals("stop-user")) { runStopUser(); } else if (op.equals("stack")) { runStack(); + } else if (op.equals("lock-task")) { + runLockTask(); + } else if (op.equals("get-config")) { + runGetConfig(); } else { showError("Error: unknown command '" + op + "'"); } @@ -346,6 +379,8 @@ public class Am extends BaseCommand { mStopOption = false; mRepeat = 0; mProfileFile = null; + mSamplingInterval = 0; + mAutoStop = false; mUserId = defUser; Uri data = null; String type = null; @@ -431,6 +466,15 @@ public class Am extends BaseCommand { } intent.putExtra(key, list); hasIntentInfo = true; + } else if (opt.equals("--esa")) { + String key = nextArgRequired(); + String value = nextArgRequired(); + // Split on commas unless they are preceeded by an escape. + // The escape character must be escaped for the string and + // again for the regex, thus four escape characters become one. + String[] strings = value.split("(?<!\\\\),"); + intent.putExtra(key, strings); + hasIntentInfo = true; } else if (opt.equals("--ez")) { String key = nextArgRequired(); String value = nextArgRequired(); @@ -450,6 +494,10 @@ public class Am extends BaseCommand { intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); } else if (opt.equals("--grant-write-uri-permission")) { intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION); + } else if (opt.equals("--grant-persistable-uri-permission")) { + intent.addFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION); + } else if (opt.equals("--grant-prefix-uri-permission")) { + intent.addFlags(Intent.FLAG_GRANT_PREFIX_URI_PERMISSION); } else if (opt.equals("--exclude-stopped-packages")) { intent.addFlags(Intent.FLAG_EXCLUDE_STOPPED_PACKAGES); } else if (opt.equals("--include-stopped-packages")) { @@ -499,10 +547,12 @@ public class Am extends BaseCommand { mWaitOption = true; } else if (opt.equals("-P")) { mProfileFile = nextArgRequired(); - mStartFlags |= ActivityManager.START_FLAG_AUTO_STOP_PROFILER; + mAutoStop = true; } else if (opt.equals("--start-profiler")) { mProfileFile = nextArgRequired(); - mStartFlags &= ~ActivityManager.START_FLAG_AUTO_STOP_PROFILER; + mAutoStop = false; + } else if (opt.equals("--sampling")) { + mSamplingInterval = Integer.parseInt(nextArgRequired()); } else if (opt.equals("-R")) { mRepeat = Integer.parseInt(nextArgRequired()); } else if (opt.equals("-S")) { @@ -658,12 +708,13 @@ public class Am extends BaseCommand { mAm.forceStopPackage(packageName, mUserId); Thread.sleep(250); } - + System.out.println("Starting: " + intent); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - + ParcelFileDescriptor fd = null; - + ProfilerInfo profilerInfo = null; + if (mProfileFile != null) { try { fd = ParcelFileDescriptor.open( @@ -675,18 +726,21 @@ public class Am extends BaseCommand { System.err.println("Error: Unable to open file: " + mProfileFile); return; } + profilerInfo = new ProfilerInfo(mProfileFile, fd, mSamplingInterval, mAutoStop); } IActivityManager.WaitResult result = null; int res; + final long startTime = SystemClock.uptimeMillis(); if (mWaitOption) { result = mAm.startActivityAndWait(null, null, intent, mimeType, - null, null, 0, mStartFlags, mProfileFile, fd, null, mUserId); + null, null, 0, mStartFlags, profilerInfo, null, mUserId); res = result.result; } else { res = mAm.startActivityAsUser(null, null, intent, mimeType, - null, null, 0, mStartFlags, mProfileFile, fd, null, mUserId); + null, null, 0, mStartFlags, profilerInfo, null, mUserId); } + final long endTime = SystemClock.uptimeMillis(); PrintStream out = mWaitOption ? System.out : System.err; boolean launched = false; switch (res) { @@ -739,6 +793,11 @@ public class Am extends BaseCommand { "Error: Activity not started, you do not " + "have permission to access it."); break; + case ActivityManager.START_NOT_VOICE_COMPATIBLE: + out.println( + "Error: Activity not started, voice control not allowed for: " + + intent); + break; default: out.println( "Error: Activity not started, unknown error code " + res); @@ -759,6 +818,7 @@ public class Am extends BaseCommand { if (result.totalTime >= 0) { System.out.println("TotalTime: " + result.totalTime); } + System.out.println("WaitTime: " + (endTime-startTime)); System.out.println("Complete"); } mRepeat--; @@ -803,7 +863,7 @@ public class Am extends BaseCommand { } private void sendBroadcast() throws Exception { - Intent intent = makeIntent(UserHandle.USER_ALL); + Intent intent = makeIntent(UserHandle.USER_CURRENT); IntentReceiver receiver = new IntentReceiver(); System.out.println("Broadcasting: " + intent); mAm.broadcastIntent(null, intent, null, receiver, 0, null, null, mReceiverPermission, @@ -887,8 +947,7 @@ public class Am extends BaseCommand { } } - if (!mAm.startInstrumentation(cn, profileFile, 0, args, watcher, connection, userId, - abi)) { + if (!mAm.startInstrumentation(cn, profileFile, 0, args, watcher, connection, userId, abi)) { throw new AndroidException("INSTRUMENTATION_FAILED: " + cn.flattenToString()); } @@ -911,7 +970,7 @@ public class Am extends BaseCommand { SystemProperties.set("dalvik.vm.extra-opts", props); } } - + private void runProfile() throws Exception { String profileFile = null; boolean start = false; @@ -965,6 +1024,7 @@ public class Am extends BaseCommand { } ParcelFileDescriptor fd = null; + ProfilerInfo profilerInfo = null; if (start) { profileFile = nextArgRequired(); @@ -978,6 +1038,7 @@ public class Am extends BaseCommand { System.err.println("Error: Unable to open file: " + profileFile); return; } + profilerInfo = new ProfilerInfo(profileFile, fd, 0, false); } try { @@ -991,7 +1052,7 @@ public class Am extends BaseCommand { } else if (start) { //removeWallOption(); } - if (!mAm.profileControl(process, userId, start, profileFile, fd, profileType)) { + if (!mAm.profileControl(process, userId, start, profilerInfo, profileType)) { wall = false; throw new AndroidException("PROFILE FAILED on process " + process); } @@ -1076,6 +1137,16 @@ public class Am extends BaseCommand { mAm.switchUser(Integer.parseInt(user)); } + private void runStartUserInBackground() throws Exception { + String user = nextArgRequired(); + boolean success = mAm.startUserInBackground(Integer.parseInt(user)); + if (success) { + System.out.println("Success: user started"); + } else { + System.err.println("Error: could not start user"); + } + } + private void runStopUser() throws Exception { String user = nextArgRequired(); int res = mAm.stopUser(Integer.parseInt(user), null); @@ -1434,7 +1505,7 @@ public class Am extends BaseCommand { System.out.println("Performing idle maintenance..."); Intent intent = new Intent( - "com.android.server.IdleMaintenanceService.action.FORCE_IDLE_MAINTENANCE"); + "com.android.server.task.controllers.IdleController.ACTION_TRIGGER_IDLE"); mAm.broadcastIntent(null, intent, null, null, 0, null, null, null, android.app.AppOpsManager.OP_NONE, true, false, UserHandle.USER_ALL); } @@ -1573,35 +1644,32 @@ public class Am extends BaseCommand { private void runStack() throws Exception { String op = nextArgRequired(); - if (op.equals("create")) { - runStackCreate(); + if (op.equals("start")) { + runStackStart(); } else if (op.equals("movetask")) { runStackMoveTask(); } else if (op.equals("resize")) { - runStackBoxResize(); - } else if (op.equals("boxes")) { - runStackBoxes(); - } else if (op.equals("box")) { - runStackBoxInfo(); + runStackResize(); + } else if (op.equals("list")) { + runStackList(); + } else if (op.equals("info")) { + runStackInfo(); } else { showError("Error: unknown command '" + op + "'"); return; } } - private void runStackCreate() throws Exception { - String taskIdStr = nextArgRequired(); - int taskId = Integer.valueOf(taskIdStr); - String relativeToStr = nextArgRequired(); - int relativeTo = Integer.valueOf(relativeToStr); - String positionStr = nextArgRequired(); - int position = Integer.valueOf(positionStr); - String weightStr = nextArgRequired(); - float weight = Float.valueOf(weightStr); + private void runStackStart() throws Exception { + String displayIdStr = nextArgRequired(); + int displayId = Integer.valueOf(displayIdStr); + Intent intent = makeIntent(UserHandle.USER_CURRENT); try { - int stackId = mAm.createStack(taskId, relativeTo, position, weight); - System.out.println("createStack returned new stackId=" + stackId + "\n\n"); + IBinder homeActivityToken = mAm.getHomeActivityToken(); + IActivityContainer container = mAm.createActivityContainer(homeActivityToken, null); + container.attachToDisplay(displayId); + container.startActivity(intent); } catch (RemoteException e) { } } @@ -1628,34 +1696,138 @@ public class Am extends BaseCommand { } } - private void runStackBoxResize() throws Exception { - String stackBoxIdStr = nextArgRequired(); - int stackBoxId = Integer.valueOf(stackBoxIdStr); - String weightStr = nextArgRequired(); - float weight = Float.valueOf(weightStr); + private void runStackResize() throws Exception { + String stackIdStr = nextArgRequired(); + int stackId = Integer.valueOf(stackIdStr); + String leftStr = nextArgRequired(); + int left = Integer.valueOf(leftStr); + String topStr = nextArgRequired(); + int top = Integer.valueOf(topStr); + String rightStr = nextArgRequired(); + int right = Integer.valueOf(rightStr); + String bottomStr = nextArgRequired(); + int bottom = Integer.valueOf(bottomStr); try { - mAm.resizeStackBox(stackBoxId, weight); + mAm.resizeStack(stackId, new Rect(left, top, right, bottom)); } catch (RemoteException e) { } } - private void runStackBoxes() throws Exception { + private void runStackList() throws Exception { try { - List<StackBoxInfo> stackBoxes = mAm.getStackBoxes(); - for (StackBoxInfo info : stackBoxes) { + List<StackInfo> stacks = mAm.getAllStackInfos(); + for (StackInfo info : stacks) { System.out.println(info); } } catch (RemoteException e) { } } - private void runStackBoxInfo() throws Exception { + private void runStackInfo() throws Exception { try { - String stackBoxIdStr = nextArgRequired(); - int stackBoxId = Integer.valueOf(stackBoxIdStr); - StackBoxInfo stackBoxInfo = mAm.getStackBoxInfo(stackBoxId); - System.out.println(stackBoxInfo); + String stackIdStr = nextArgRequired(); + int stackId = Integer.valueOf(stackIdStr); + StackInfo info = mAm.getStackInfo(stackId); + System.out.println(info); + } catch (RemoteException e) { + } + } + + private void runLockTask() throws Exception { + String taskIdStr = nextArgRequired(); + try { + if (taskIdStr.equals("stop")) { + mAm.stopLockTaskMode(); + } else { + int taskId = Integer.valueOf(taskIdStr); + mAm.startLockTaskMode(taskId); + } + System.err.println("Activity manager is " + (mAm.isInLockTaskMode() ? "" : "not ") + + "in lockTaskMode"); + } catch (RemoteException e) { + } + } + + private List<Configuration> getRecentConfigurations(int days) { + IUsageStatsManager usm = IUsageStatsManager.Stub.asInterface(ServiceManager.getService( + Context.USAGE_STATS_SERVICE)); + final long now = System.currentTimeMillis(); + final long nDaysAgo = now - (days * 24 * 60 * 60 * 1000); + try { + @SuppressWarnings("unchecked") + ParceledListSlice<ConfigurationStats> configStatsSlice = usm.queryConfigurationStats( + UsageStatsManager.INTERVAL_BEST, nDaysAgo, now, "com.android.shell"); + if (configStatsSlice == null) { + return Collections.emptyList(); + } + + final ArrayMap<Configuration, Integer> recentConfigs = new ArrayMap<>(); + final List<ConfigurationStats> configStatsList = configStatsSlice.getList(); + final int configStatsListSize = configStatsList.size(); + for (int i = 0; i < configStatsListSize; i++) { + final ConfigurationStats stats = configStatsList.get(i); + final int indexOfKey = recentConfigs.indexOfKey(stats.getConfiguration()); + if (indexOfKey < 0) { + recentConfigs.put(stats.getConfiguration(), stats.getActivationCount()); + } else { + recentConfigs.setValueAt(indexOfKey, + recentConfigs.valueAt(indexOfKey) + stats.getActivationCount()); + } + } + + final Comparator<Configuration> comparator = new Comparator<Configuration>() { + @Override + public int compare(Configuration a, Configuration b) { + return recentConfigs.get(b).compareTo(recentConfigs.get(a)); + } + }; + + ArrayList<Configuration> configs = new ArrayList<>(recentConfigs.size()); + configs.addAll(recentConfigs.keySet()); + Collections.sort(configs, comparator); + return configs; + + } catch (RemoteException e) { + return Collections.emptyList(); + } + } + + private void runGetConfig() throws Exception { + int days = 14; + String option = nextOption(); + if (option != null) { + if (!option.equals("--days")) { + throw new IllegalArgumentException("unrecognized option " + option); + } + + days = Integer.parseInt(nextArgRequired()); + if (days <= 0) { + throw new IllegalArgumentException("--days must be a positive integer"); + } + } + + try { + Configuration config = mAm.getConfiguration(); + if (config == null) { + System.err.println("Activity manager has no configuration"); + return; + } + + System.out.println("config: " + Configuration.resourceQualifierString(config)); + System.out.println("abi: " + TextUtils.join(",", Build.SUPPORTED_ABIS)); + + final List<Configuration> recentConfigs = getRecentConfigurations(days); + final int recentConfigSize = recentConfigs.size(); + if (recentConfigSize > 0) { + System.out.println("recentConfigs:"); + } + + for (int i = 0; i < recentConfigSize; i++) { + System.out.println(" config: " + Configuration.resourceQualifierString( + recentConfigs.get(i))); + } + } catch (RemoteException e) { } } diff --git a/cmds/app_process/app_main.cpp b/cmds/app_process/app_main.cpp index 6e77e13..1bb28c3 100644 --- a/cmds/app_process/app_main.cpp +++ b/cmds/app_process/app_main.cpp @@ -133,8 +133,7 @@ static size_t computeArgBlockSize(int argc, char* const argv[]) { // names if the zygote command line decreases in size. uintptr_t start = reinterpret_cast<uintptr_t>(argv[0]); uintptr_t end = reinterpret_cast<uintptr_t>(argv[argc - 1]); - end += strlen(argv[argc - 1]); - + end += strlen(argv[argc - 1]) + 1; return (end - start); } @@ -220,15 +219,27 @@ int main(int argc, char* const argv[]) // // For zygote starts, all remaining arguments are passed to the zygote. // main function. + // + // Note that we must copy argument string values since we will rewrite the + // entire argument block when we apply the nice name to argv0. - - int i = runtime.addVmArguments(argc, argv); + int i; + for (i = 0; i < argc; i++) { + if (argv[i][0] != '-') { + break; + } + if (argv[i][1] == '-' && argv[i][2] == 0) { + ++i; // Skip --. + break; + } + runtime.addOption(strdup(argv[i])); + } // Parse runtime arguments. Stop at first unrecognized option. bool zygote = false; bool startSystemServer = false; bool application = false; - const char* niceName = NULL; + String8 niceName; String8 className; ++i; // Skip unused "parent dir" argument. @@ -242,7 +253,7 @@ int main(int argc, char* const argv[]) } else if (strcmp(arg, "--application") == 0) { application = true; } else if (strncmp(arg, "--nice-name=", 12) == 0) { - niceName = arg + 12; + niceName.setTo(arg + 12); } else if (strncmp(arg, "--", 2) != 0) { className.setTo(arg); break; @@ -287,9 +298,9 @@ int main(int argc, char* const argv[]) } } - if (niceName && *niceName) { - runtime.setArgv0(niceName); - set_process_name(niceName); + if (!niceName.isEmpty()) { + runtime.setArgv0(niceName.string()); + set_process_name(niceName.string()); } if (zygote) { diff --git a/cmds/appops/Android.mk b/cmds/appops/Android.mk new file mode 100644 index 0000000..1e15204 --- /dev/null +++ b/cmds/appops/Android.mk @@ -0,0 +1,16 @@ +# Copyright 2014 The Android Open Source Project + +LOCAL_PATH:= $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_SRC_FILES := $(call all-subdir-java-files) +LOCAL_MODULE := appops +include $(BUILD_JAVA_LIBRARY) + +include $(CLEAR_VARS) +LOCAL_MODULE := appops +LOCAL_SRC_FILES := appops +LOCAL_MODULE_CLASS := EXECUTABLES +LOCAL_MODULE_TAGS := optional +include $(BUILD_PREBUILT) + diff --git a/cmds/appops/MODULE_LICENSE_APACHE2 b/cmds/appops/MODULE_LICENSE_APACHE2 new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/cmds/appops/MODULE_LICENSE_APACHE2 diff --git a/cmds/appops/NOTICE b/cmds/appops/NOTICE new file mode 100644 index 0000000..06a9081 --- /dev/null +++ b/cmds/appops/NOTICE @@ -0,0 +1,190 @@ + + Copyright (c) 2005-2014, The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + diff --git a/cmds/appops/appops b/cmds/appops/appops new file mode 100755 index 0000000..407e551 --- /dev/null +++ b/cmds/appops/appops @@ -0,0 +1,5 @@ +# Script to start "appwidget" on the device, which has a very rudimentary shell. +base=/system +export CLASSPATH=$base/framework/appops.jar +exec app_process $base/bin com.android.commands.appops.AppOpsCommand "$@" + diff --git a/cmds/appops/src/com/android/commands/appops/AppOpsCommand.java b/cmds/appops/src/com/android/commands/appops/AppOpsCommand.java new file mode 100644 index 0000000..c414f58 --- /dev/null +++ b/cmds/appops/src/com/android/commands/appops/AppOpsCommand.java @@ -0,0 +1,137 @@ +/* +** Copyright 2014, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +package com.android.commands.appops; + +import android.app.ActivityManager; +import android.app.ActivityThread; +import android.app.AppOpsManager; +import android.content.Context; +import android.content.pm.IPackageManager; +import android.os.ServiceManager; +import android.os.UserHandle; + +import com.android.internal.app.IAppOpsService; +import com.android.internal.os.BaseCommand; + +import java.io.PrintStream; + +/** + * This class is a command line utility for manipulating AppOps permissions. + */ +public class AppOpsCommand extends BaseCommand { + + public static void main(String[] args) { + new AppOpsCommand().run(args); + } + + @Override + public void onShowUsage(PrintStream out) { + out.println("usage: adb shell appops set <PACKAGE> <OP> " + + "<allow|ignore|deny|default> [--user <USER_ID>]\n" + + " <PACKAGE> an Android package name.\n" + + " <OP> an AppOps operation.\n" + + " <USER_ID> the user id under which the package is installed. If --user is not\n" + + " specified, the current user is assumed.\n"); + } + + private static final String COMMAND_SET = "set"; + + @Override + public void onRun() throws Exception { + String command = nextArgRequired(); + switch (command) { + case COMMAND_SET: + runSet(); + break; + + default: + throw new IllegalArgumentException("Unknown command '" + command + "'."); + } + } + + private static final String ARGUMENT_USER = "--user"; + + // Modes + private static final String MODE_ALLOW = "allow"; + private static final String MODE_DENY = "deny"; + private static final String MODE_IGNORE = "ignore"; + private static final String MODE_DEFAULT = "default"; + + private void runSet() throws Exception { + String packageName = null; + String op = null; + String mode = null; + int userId = UserHandle.USER_CURRENT; + for (String argument; (argument = nextArg()) != null;) { + if (ARGUMENT_USER.equals(argument)) { + userId = Integer.parseInt(nextArgRequired()); + } else { + if (packageName == null) { + packageName = argument; + } else if (op == null) { + op = argument; + } else if (mode == null) { + mode = argument; + } else { + throw new IllegalArgumentException("Unsupported argument: " + argument); + } + } + } + + if (packageName == null) { + throw new IllegalArgumentException("Package name not specified."); + } else if (op == null) { + throw new IllegalArgumentException("Operation not specified."); + } else if (mode == null) { + throw new IllegalArgumentException("Mode not specified."); + } + + final int opInt = AppOpsManager.strOpToOp(op); + final int modeInt; + switch (mode) { + case MODE_ALLOW: + modeInt = AppOpsManager.MODE_ALLOWED; + break; + case MODE_DENY: + modeInt = AppOpsManager.MODE_ERRORED; + break; + case MODE_IGNORE: + modeInt = AppOpsManager.MODE_IGNORED; + break; + case MODE_DEFAULT: + modeInt = AppOpsManager.MODE_DEFAULT; + break; + default: + throw new IllegalArgumentException("Mode is invalid."); + } + + // Parsing complete, let's execute the command. + + if (userId == UserHandle.USER_CURRENT) { + userId = ActivityManager.getCurrentUser(); + } + + final IPackageManager pm = ActivityThread.getPackageManager(); + final IAppOpsService appOpsService = IAppOpsService.Stub.asInterface( + ServiceManager.getService(Context.APP_OPS_SERVICE)); + final int uid = pm.getPackageUid(packageName, userId); + if (uid < 0) { + throw new Exception("No UID for " + packageName + " for user " + userId); + } + appOpsService.setMode(opInt, uid, packageName, modeInt); + } +} diff --git a/cmds/appwidget/Android.mk b/cmds/appwidget/Android.mk new file mode 100644 index 0000000..1fb258d --- /dev/null +++ b/cmds/appwidget/Android.mk @@ -0,0 +1,16 @@ +# Copyright 2014 The Android Open Source Project + +LOCAL_PATH:= $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_SRC_FILES := $(call all-subdir-java-files) +LOCAL_MODULE := appwidget +include $(BUILD_JAVA_LIBRARY) + +include $(CLEAR_VARS) +LOCAL_MODULE := appwidget +LOCAL_SRC_FILES := appwidget +LOCAL_MODULE_CLASS := EXECUTABLES +LOCAL_MODULE_TAGS := optional +include $(BUILD_PREBUILT) + diff --git a/cmds/appwidget/MODULE_LICENSE_APACHE2 b/cmds/appwidget/MODULE_LICENSE_APACHE2 new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/cmds/appwidget/MODULE_LICENSE_APACHE2 diff --git a/cmds/appwidget/NOTICE b/cmds/appwidget/NOTICE new file mode 100644 index 0000000..06a9081 --- /dev/null +++ b/cmds/appwidget/NOTICE @@ -0,0 +1,190 @@ + + Copyright (c) 2005-2014, The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + diff --git a/cmds/appwidget/appwidget b/cmds/appwidget/appwidget new file mode 100755 index 0000000..6105009 --- /dev/null +++ b/cmds/appwidget/appwidget @@ -0,0 +1,5 @@ +# Script to start "appwidget" on the device, which has a very rudimentary shell. +base=/system +export CLASSPATH=$base/framework/appwidget.jar +exec app_process $base/bin com.android.commands.appwidget.AppWidget "$@" + diff --git a/cmds/appwidget/src/com/android/commands/appwidget/AppWidget.java b/cmds/appwidget/src/com/android/commands/appwidget/AppWidget.java new file mode 100644 index 0000000..5ea7352 --- /dev/null +++ b/cmds/appwidget/src/com/android/commands/appwidget/AppWidget.java @@ -0,0 +1,167 @@ +/* +** Copyright 2014, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +package com.android.commands.appwidget; + +import android.content.Context; +import android.os.IBinder; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.os.UserHandle; +import android.text.TextUtils; + +import com.android.internal.appwidget.IAppWidgetService; + +/** + * This class is a command line utility for manipulating app widgets. A client + * can grant or revoke the permission for a given package to bind app widgets. + */ +public class AppWidget { + + private static final String USAGE = + "usage: adb shell appwidget [subcommand] [options]\n" + + "\n" + + "usage: adb shell appwidget grantbind --package <PACKAGE> " + + " [--user <USER_ID> | current]\n" + + " <PACKAGE> an Android package name.\n" + + " <USER_ID> The user id under which the package is installed.\n" + + " Example:\n" + + " # Grant the \"foo.bar.baz\" package to bind app widgets for the current user.\n" + + " adb shell grantbind --package foo.bar.baz --user current\n" + + "\n" + + "usage: adb shell appwidget revokebind --package <PACKAGE> " + + "[--user <USER_ID> | current]\n" + + " <PACKAGE> an Android package name.\n" + + " <USER_ID> The user id under which the package is installed.\n" + + " Example:\n" + + " # Revoke the permisison to bind app widgets from the \"foo.bar.baz\" package.\n" + + " adb shell revokebind --package foo.bar.baz --user current\n" + + "\n"; + + private static class Parser { + private static final String ARGUMENT_GRANT_BIND = "grantbind"; + private static final String ARGUMENT_REVOKE_BIND = "revokebind"; + private static final String ARGUMENT_PACKAGE = "--package"; + private static final String ARGUMENT_USER = "--user"; + private static final String ARGUMENT_PREFIX = "--"; + private static final String VALUE_USER_CURRENT = "current"; + + private final Tokenizer mTokenizer; + + public Parser(String[] args) { + mTokenizer = new Tokenizer(args); + } + + public Runnable parseCommand() { + try { + String operation = mTokenizer.nextArg(); + if (ARGUMENT_GRANT_BIND.equals(operation)) { + return parseSetGrantBindAppWidgetPermissionCommand(true); + } else if (ARGUMENT_REVOKE_BIND.equals(operation)) { + return parseSetGrantBindAppWidgetPermissionCommand(false); + } else { + throw new IllegalArgumentException("Unsupported operation: " + operation); + } + } catch (IllegalArgumentException iae) { + System.out.println(USAGE); + System.out.println("[ERROR] " + iae.getMessage()); + return null; + } + } + + private SetBindAppWidgetPermissionCommand parseSetGrantBindAppWidgetPermissionCommand( + boolean granted) { + String packageName = null; + int userId = UserHandle.USER_OWNER; + for (String argument; (argument = mTokenizer.nextArg()) != null;) { + if (ARGUMENT_PACKAGE.equals(argument)) { + packageName = argumentValueRequired(argument); + } else if (ARGUMENT_USER.equals(argument)) { + String user = argumentValueRequired(argument); + if (VALUE_USER_CURRENT.equals(user)) { + userId = UserHandle.USER_CURRENT; + } else { + userId = Integer.parseInt(user); + } + } else { + throw new IllegalArgumentException("Unsupported argument: " + argument); + } + } + if (packageName == null) { + throw new IllegalArgumentException("Package name not specified." + + " Did you specify --package argument?"); + } + return new SetBindAppWidgetPermissionCommand(packageName, granted, userId); + } + + private String argumentValueRequired(String argument) { + String value = mTokenizer.nextArg(); + if (TextUtils.isEmpty(value) || value.startsWith(ARGUMENT_PREFIX)) { + throw new IllegalArgumentException("No value for argument: " + argument); + } + return value; + } + } + + private static class Tokenizer { + private final String[] mArgs; + private int mNextArg; + + public Tokenizer(String[] args) { + mArgs = args; + } + + private String nextArg() { + if (mNextArg < mArgs.length) { + return mArgs[mNextArg++]; + } else { + return null; + } + } + } + + private static class SetBindAppWidgetPermissionCommand implements Runnable { + final String mPackageName; + final boolean mGranted; + final int mUserId; + + public SetBindAppWidgetPermissionCommand(String packageName, boolean granted, + int userId) { + mPackageName = packageName; + mGranted = granted; + mUserId = userId; + } + + @Override + public void run() { + IBinder binder = ServiceManager.getService(Context.APPWIDGET_SERVICE); + IAppWidgetService appWidgetService = IAppWidgetService.Stub.asInterface(binder); + try { + appWidgetService.setBindAppWidgetPermission(mPackageName, mUserId, mGranted); + } catch (RemoteException re) { + re.printStackTrace(); + } + } + } + + public static void main(String[] args) { + Parser parser = new Parser(args); + Runnable command = parser.parseCommand(); + if (command != null) { + command.run(); + } + } +} diff --git a/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java b/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java index db3d8bb..d683851 100644 --- a/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java +++ b/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java @@ -23,6 +23,7 @@ import android.app.backup.IRestoreSession; import android.os.RemoteException; import android.os.ServiceManager; +import java.util.ArrayList; import java.util.HashSet; public final class Bmgr { @@ -102,6 +103,11 @@ public final class Bmgr { return; } + if ("fullbackup".equals(op)) { + doFullTransportBackup(); + return; + } + System.err.println("Unknown command"); showUsage(); } @@ -165,6 +171,24 @@ public final class Bmgr { } } + private void doFullTransportBackup() { + System.out.println("Performing full transport backup"); + + String pkg; + ArrayList<String> allPkgs = new ArrayList<String>(); + while ((pkg = nextArg()) != null) { + allPkgs.add(pkg); + } + if (allPkgs.size() > 0) { + try { + mBmgr.fullTransportBackup(allPkgs.toArray(new String[allPkgs.size()])); + } catch (RemoteException e) { + System.err.println(e.toString()); + System.err.println(BMGR_NOT_RUNNING_ERR); + } + } + } + private void doTransport() { try { String which = nextArg(); @@ -453,6 +477,7 @@ public final class Bmgr { System.err.println(" bmgr restore PACKAGE"); System.err.println(" bmgr run"); System.err.println(" bmgr wipe TRANSPORT PACKAGE"); + System.err.println(" bmgr fullbackup PACKAGE..."); System.err.println(""); System.err.println("The 'backup' command schedules a backup pass for the named package."); System.err.println("Note that the backup pass will effectively be a no-op if the package"); @@ -468,11 +493,11 @@ public final class Bmgr { System.err.println(""); System.err.println("The 'list transports' command reports the names of the backup transports"); System.err.println("currently available on the device. These names can be passed as arguments"); - System.err.println("to the 'transport' and 'wipe' commands. The currently selected transport"); + System.err.println("to the 'transport' and 'wipe' commands. The currently active transport"); System.err.println("is indicated with a '*' character."); System.err.println(""); System.err.println("The 'list sets' command reports the token and name of each restore set"); - System.err.println("available to the device via the current transport."); + System.err.println("available to the device via the currently active transport."); System.err.println(""); System.err.println("The 'transport' command designates the named transport as the currently"); System.err.println("active one. This setting is persistent across reboots."); @@ -500,5 +525,8 @@ public final class Bmgr { System.err.println("erased from the given transport's storage. The next backup operation"); System.err.println("that the given application performs will rewrite its entire data set."); System.err.println("Transport names to use here are those reported by 'list transports'."); + System.err.println(""); + System.err.println("The 'fullbackup' command induces a full-data stream backup for one or more"); + System.err.println("packages. The data is sent via the currently active transport."); } } diff --git a/cmds/bootanimation/Android.mk b/cmds/bootanimation/Android.mk index dd987e0..d6ecbe3 100644 --- a/cmds/bootanimation/Android.mk +++ b/cmds/bootanimation/Android.mk @@ -3,10 +3,13 @@ include $(CLEAR_VARS) LOCAL_SRC_FILES:= \ bootanimation_main.cpp \ + AudioPlayer.cpp \ BootAnimation.cpp LOCAL_CFLAGS += -DGL_GLEXT_PROTOTYPES -DEGL_EGLEXT_PROTOTYPES +LOCAL_C_INCLUDES += external/tinyalsa/include + LOCAL_SHARED_LIBRARIES := \ libcutils \ liblog \ @@ -17,10 +20,8 @@ LOCAL_SHARED_LIBRARIES := \ libskia \ libEGL \ libGLESv1_CM \ - libgui - -LOCAL_C_INCLUDES := \ - $(call include-path-for, corecg graphics) + libgui \ + libtinyalsa LOCAL_MODULE:= bootanimation diff --git a/cmds/bootanimation/AudioPlayer.cpp b/cmds/bootanimation/AudioPlayer.cpp new file mode 100644 index 0000000..471b77f --- /dev/null +++ b/cmds/bootanimation/AudioPlayer.cpp @@ -0,0 +1,314 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_NDEBUG 0 +#define LOG_TAG "BootAnim_AudioPlayer" + +#include "AudioPlayer.h" + +#include <androidfw/ZipFileRO.h> +#include <tinyalsa/asoundlib.h> +#include <utils/Log.h> +#include <utils/String8.h> + +#define ID_RIFF 0x46464952 +#define ID_WAVE 0x45564157 +#define ID_FMT 0x20746d66 +#define ID_DATA 0x61746164 + +// Maximum line length for audio_conf.txt +// We only accept lines less than this length to avoid overflows using sscanf() +#define MAX_LINE_LENGTH 1024 + +struct riff_wave_header { + uint32_t riff_id; + uint32_t riff_sz; + uint32_t wave_id; +}; + +struct chunk_header { + uint32_t id; + uint32_t sz; +}; + +struct chunk_fmt { + uint16_t audio_format; + uint16_t num_channels; + uint32_t sample_rate; + uint32_t byte_rate; + uint16_t block_align; + uint16_t bits_per_sample; +}; + + +namespace android { + +AudioPlayer::AudioPlayer() + : mCard(-1), + mDevice(-1), + mPeriodSize(0), + mPeriodCount(0), + mCurrentFile(NULL) +{ +} + +AudioPlayer::~AudioPlayer() { +} + +static bool setMixerValue(struct mixer* mixer, const char* name, const char* values) +{ + if (!mixer) { + ALOGE("no mixer in setMixerValue"); + return false; + } + struct mixer_ctl *ctl = mixer_get_ctl_by_name(mixer, name); + if (!ctl) { + ALOGE("mixer_get_ctl_by_name failed for %s", name); + return false; + } + + enum mixer_ctl_type type = mixer_ctl_get_type(ctl); + int numValues = mixer_ctl_get_num_values(ctl); + int intValue; + char stringValue[MAX_LINE_LENGTH]; + + for (int i = 0; i < numValues && values; i++) { + // strip leading space + while (*values == ' ') values++; + if (*values == 0) break; + + switch (type) { + case MIXER_CTL_TYPE_BOOL: + case MIXER_CTL_TYPE_INT: + if (sscanf(values, "%d", &intValue) == 1) { + if (mixer_ctl_set_value(ctl, i, intValue) != 0) { + ALOGE("mixer_ctl_set_value failed for %s %d", name, intValue); + } + } else { + ALOGE("Could not parse %s as int for %d", intValue, name); + } + break; + case MIXER_CTL_TYPE_ENUM: + if (sscanf(values, "%s", stringValue) == 1) { + if (mixer_ctl_set_enum_by_string(ctl, stringValue) != 0) { + ALOGE("mixer_ctl_set_enum_by_string failed for %s %%s", name, stringValue); + } + } else { + ALOGE("Could not parse %s as enum for %d", stringValue, name); + } + break; + default: + ALOGE("unsupported mixer type %d for %s", type, name); + break; + } + + values = strchr(values, ' '); + } + + return true; +} + + +/* + * Parse the audio configuration file. + * The file is named audio_conf.txt and must begin with the following header: + * + * card=<ALSA card number> + * device=<ALSA device number> + * period_size=<period size> + * period_count=<period count> + * + * This header is followed by zero or more mixer settings, each with the format: + * mixer "<name>" = <value list> + * Since mixer names can contain spaces, the name must be enclosed in double quotes. + * The values in the value list can be integers, booleans (represented by 0 or 1) + * or strings for enum values. + */ +bool AudioPlayer::init(const char* config) +{ + int tempInt; + struct mixer* mixer = NULL; + char name[MAX_LINE_LENGTH]; + + for (;;) { + const char* endl = strstr(config, "\n"); + if (!endl) break; + String8 line(config, endl - config); + if (line.length() >= MAX_LINE_LENGTH) { + ALOGE("Line too long in audio_conf.txt"); + return false; + } + const char* l = line.string(); + + if (sscanf(l, "card=%d", &tempInt) == 1) { + ALOGD("card=%d", tempInt); + mCard = tempInt; + + mixer = mixer_open(mCard); + if (!mixer) { + ALOGE("could not open mixer for card %d", mCard); + return false; + } + } else if (sscanf(l, "device=%d", &tempInt) == 1) { + ALOGD("device=%d", tempInt); + mDevice = tempInt; + } else if (sscanf(l, "period_size=%d", &tempInt) == 1) { + ALOGD("period_size=%d", tempInt); + mPeriodSize = tempInt; + } else if (sscanf(l, "period_count=%d", &tempInt) == 1) { + ALOGD("period_count=%d", tempInt); + mPeriodCount = tempInt; + } else if (sscanf(l, "mixer \"%[0-9a-zA-Z _]s\"", name) == 1) { + const char* values = strchr(l, '='); + if (values) { + values++; // skip '=' + ALOGD("name: \"%s\" = %s", name, values); + setMixerValue(mixer, name, values); + } else { + ALOGE("values missing for name: \"%s\"", name); + } + } + config = ++endl; + } + + mixer_close(mixer); + + if (mCard >= 0 && mDevice >= 0) { + return true; + } + + return false; +} + +void AudioPlayer::playFile(struct FileMap* fileMap) { + // stop any currently playing sound + requestExitAndWait(); + + mCurrentFile = fileMap; + run("bootanim audio", PRIORITY_URGENT_AUDIO); +} + +bool AudioPlayer::threadLoop() +{ + struct pcm_config config; + struct pcm *pcm = NULL; + bool moreChunks = true; + const struct chunk_fmt* chunkFmt = NULL; + void* buffer = NULL; + int bufferSize; + const uint8_t* wavData; + size_t wavLength; + const struct riff_wave_header* wavHeader; + + if (mCurrentFile == NULL) { + ALOGE("mCurrentFile is NULL"); + return false; + } + + wavData = (const uint8_t *)mCurrentFile->getDataPtr(); + if (!wavData) { + ALOGE("Could not access WAV file data"); + goto exit; + } + wavLength = mCurrentFile->getDataLength(); + + wavHeader = (const struct riff_wave_header *)wavData; + if (wavLength < sizeof(*wavHeader) || (wavHeader->riff_id != ID_RIFF) || + (wavHeader->wave_id != ID_WAVE)) { + ALOGE("Error: audio file is not a riff/wave file\n"); + goto exit; + } + wavData += sizeof(*wavHeader); + wavLength -= sizeof(*wavHeader); + + do { + const struct chunk_header* chunkHeader = (const struct chunk_header*)wavData; + if (wavLength < sizeof(*chunkHeader)) { + ALOGE("EOF reading chunk headers"); + goto exit; + } + + wavData += sizeof(*chunkHeader); + wavLength -= sizeof(*chunkHeader); + + switch (chunkHeader->id) { + case ID_FMT: + chunkFmt = (const struct chunk_fmt *)wavData; + wavData += chunkHeader->sz; + wavLength -= chunkHeader->sz; + break; + case ID_DATA: + /* Stop looking for chunks */ + moreChunks = 0; + break; + default: + /* Unknown chunk, skip bytes */ + wavData += chunkHeader->sz; + wavLength -= chunkHeader->sz; + } + } while (moreChunks); + + if (!chunkFmt) { + ALOGE("format not found in WAV file"); + goto exit; + } + + + memset(&config, 0, sizeof(config)); + config.channels = chunkFmt->num_channels; + config.rate = chunkFmt->sample_rate; + config.period_size = mPeriodSize; + config.period_count = mPeriodCount; + config.start_threshold = mPeriodSize / 4; + config.stop_threshold = INT_MAX; + config.avail_min = config.start_threshold; + if (chunkFmt->bits_per_sample != 16) { + ALOGE("only 16 bit WAV files are supported"); + goto exit; + } + config.format = PCM_FORMAT_S16_LE; + + pcm = pcm_open(mCard, mDevice, PCM_OUT, &config); + if (!pcm || !pcm_is_ready(pcm)) { + ALOGE("Unable to open PCM device (%s)\n", pcm_get_error(pcm)); + goto exit; + } + + bufferSize = pcm_frames_to_bytes(pcm, pcm_get_buffer_size(pcm)); + + while (wavLength > 0) { + if (exitPending()) goto exit; + size_t count = bufferSize; + if (count > wavLength) + count = wavLength; + + if (pcm_write(pcm, wavData, count)) { + ALOGE("pcm_write failed (%s)", pcm_get_error(pcm)); + goto exit; + } + wavData += count; + wavLength -= count; + } + +exit: + if (pcm) + pcm_close(pcm); + mCurrentFile->release(); + mCurrentFile = NULL; + return false; +} + +} // namespace android diff --git a/cmds/bootanimation/AudioPlayer.h b/cmds/bootanimation/AudioPlayer.h new file mode 100644 index 0000000..7e82a07 --- /dev/null +++ b/cmds/bootanimation/AudioPlayer.h @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _BOOTANIMATION_AUDIOPLAYER_H +#define _BOOTANIMATION_AUDIOPLAYER_H + +#include <utils/Thread.h> + +namespace android { + +class AudioPlayer : public Thread +{ +public: + AudioPlayer(); + virtual ~AudioPlayer(); + bool init(const char* config); + + void playFile(struct FileMap* fileMap); + +private: + virtual bool threadLoop(); + +private: + int mCard; // ALSA card to use + int mDevice; // ALSA device to use + int mPeriodSize; + int mPeriodCount; + + struct FileMap* mCurrentFile; +}; + +} // namespace android + +#endif // _BOOTANIMATION_AUDIOPLAYER_H diff --git a/cmds/bootanimation/BootAnimation.cpp b/cmds/bootanimation/BootAnimation.cpp index 7f7ae2c..b2474f2 100644 --- a/cmds/bootanimation/BootAnimation.cpp +++ b/cmds/bootanimation/BootAnimation.cpp @@ -14,6 +14,7 @@ * limitations under the License. */ +#define LOG_NDEBUG 0 #define LOG_TAG "BootAnimation" #include <stdint.h> @@ -30,28 +31,28 @@ #include <utils/Atomic.h> #include <utils/Errors.h> #include <utils/Log.h> -#include <utils/threads.h> #include <ui/PixelFormat.h> #include <ui/Rect.h> #include <ui/Region.h> #include <ui/DisplayInfo.h> -#include <ui/FramebufferNativeWindow.h> #include <gui/ISurfaceComposer.h> #include <gui/Surface.h> #include <gui/SurfaceComposerClient.h> -#include <core/SkBitmap.h> -#include <core/SkStream.h> -#include <core/SkImageDecoder.h> +#include <SkBitmap.h> +#include <SkStream.h> +#include <SkImageDecoder.h> #include <GLES/gl.h> #include <GLES/glext.h> #include <EGL/eglext.h> #include "BootAnimation.h" +#include "AudioPlayer.h" +#define OEM_BOOTANIMATION_FILE "/oem/media/bootanimation.zip" #define SYSTEM_BOOTANIMATION_FILE "/system/media/bootanimation.zip" #define SYSTEM_ENCRYPTED_BOOTANIMATION_FILE "/system/media/bootanimation-encrypted.zip" #define EXIT_PROP_NAME "service.bootanim.exit" @@ -99,6 +100,9 @@ void BootAnimation::binderDied(const wp<IBinder>&) // might be blocked on a condition variable that will never be updated. kill( getpid(), SIGKILL ); requestExit(); + if (mAudioPlayer != NULL) { + mAudioPlayer->requestExit(); + } } status_t BootAnimation::initTexture(Texture* texture, AssetManager& assets, @@ -108,7 +112,7 @@ status_t BootAnimation::initTexture(Texture* texture, AssetManager& assets, return NO_INIT; SkBitmap bitmap; SkImageDecoder::DecodeMemory(asset->getBuffer(false), asset->getLength(), - &bitmap, SkBitmap::kNo_Config, SkImageDecoder::kDecodePixels_Mode); + &bitmap, kUnknown_SkColorType, SkImageDecoder::kDecodePixels_Mode); asset->close(); delete asset; @@ -127,20 +131,20 @@ status_t BootAnimation::initTexture(Texture* texture, AssetManager& assets, glGenTextures(1, &texture->name); glBindTexture(GL_TEXTURE_2D, texture->name); - switch (bitmap.getConfig()) { - case SkBitmap::kA8_Config: + switch (bitmap.colorType()) { + case kAlpha_8_SkColorType: glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, w, h, 0, GL_ALPHA, GL_UNSIGNED_BYTE, p); break; - case SkBitmap::kARGB_4444_Config: + case kARGB_4444_SkColorType: glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4, p); break; - case SkBitmap::kARGB_8888_Config: + case kN32_SkColorType: glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, p); break; - case SkBitmap::kRGB_565_Config: + case kRGB_565_SkColorType: glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, w, h, 0, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, p); break; @@ -166,7 +170,7 @@ status_t BootAnimation::initTexture(const Animation::Frame& frame) if (codec) { codec->setDitherImage(false); codec->decode(&stream, &bitmap, - SkBitmap::kARGB_8888_Config, + kN32_SkColorType, SkImageDecoder::kDecodePixels_Mode); delete codec; } @@ -190,8 +194,8 @@ status_t BootAnimation::initTexture(const Animation::Frame& frame) if (tw < w) tw <<= 1; if (th < h) th <<= 1; - switch (bitmap.getConfig()) { - case SkBitmap::kARGB_8888_Config: + switch (bitmap.colorType()) { + case kN32_SkColorType: if (tw != w || th != h) { glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, tw, th, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0); @@ -203,7 +207,7 @@ status_t BootAnimation::initTexture(const Animation::Frame& frame) } break; - case SkBitmap::kRGB_565_Config: + case kRGB_565_SkColorType: if (tw != w || th != h) { glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, tw, th, 0, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, 0); @@ -289,6 +293,9 @@ status_t BootAnimation::readyToRun() { (access(SYSTEM_ENCRYPTED_BOOTANIMATION_FILE, R_OK) == 0) && ((zipFile = ZipFileRO::open(SYSTEM_ENCRYPTED_BOOTANIMATION_FILE)) != NULL)) || + ((access(OEM_BOOTANIMATION_FILE, R_OK) == 0) && + ((zipFile = ZipFileRO::open(OEM_BOOTANIMATION_FILE)) != NULL)) || + ((access(SYSTEM_BOOTANIMATION_FILE, R_OK) == 0) && ((zipFile = ZipFileRO::open(SYSTEM_BOOTANIMATION_FILE)) != NULL))) { mZip = zipFile; @@ -391,29 +398,76 @@ void BootAnimation::checkExit() { int exitnow = atoi(value); if (exitnow) { requestExit(); + if (mAudioPlayer != NULL) { + mAudioPlayer->requestExit(); + } } } -bool BootAnimation::movie() +// Parse a color represented as an HTML-style 'RRGGBB' string: each pair of +// characters in str is a hex number in [0, 255], which are converted to +// floating point values in the range [0.0, 1.0] and placed in the +// corresponding elements of color. +// +// If the input string isn't valid, parseColor returns false and color is +// left unchanged. +static bool parseColor(const char str[7], float color[3]) { + float tmpColor[3]; + for (int i = 0; i < 3; i++) { + int val = 0; + for (int j = 0; j < 2; j++) { + val *= 16; + char c = str[2*i + j]; + if (c >= '0' && c <= '9') val += c - '0'; + else if (c >= 'A' && c <= 'F') val += (c - 'A') + 10; + else if (c >= 'a' && c <= 'f') val += (c - 'a') + 10; + else return false; + } + tmpColor[i] = static_cast<float>(val) / 255.0f; + } + memcpy(color, tmpColor, sizeof(tmpColor)); + return true; +} + +bool BootAnimation::readFile(const char* name, String8& outString) { - ZipEntryRO desc = mZip->findEntryByName("desc.txt"); - ALOGE_IF(!desc, "couldn't find desc.txt"); - if (!desc) { + ZipEntryRO entry = mZip->findEntryByName(name); + ALOGE_IF(!entry, "couldn't find %s", name); + if (!entry) { return false; } - FileMap* descMap = mZip->createEntryFileMap(desc); - mZip->releaseEntry(desc); - ALOGE_IF(!descMap, "descMap is null"); - if (!descMap) { + FileMap* entryMap = mZip->createEntryFileMap(entry); + mZip->releaseEntry(entry); + ALOGE_IF(!entryMap, "entryMap is null"); + if (!entryMap) { return false; } - String8 desString((char const*)descMap->getDataPtr(), - descMap->getDataLength()); - descMap->release(); + outString.setTo((char const*)entryMap->getDataPtr(), entryMap->getDataLength()); + entryMap->release(); + return true; +} + +bool BootAnimation::movie() +{ + String8 desString; + + if (!readFile("desc.txt", desString)) { + return false; + } char const* s = desString.string(); + // Create and initialize an AudioPlayer if we have an audio_conf.txt file + String8 audioConf; + if (readFile("audio_conf.txt", audioConf)) { + mAudioPlayer = new AudioPlayer; + if (!mAudioPlayer->init(audioConf.string())) { + ALOGE("mAudioPlayer.init failed"); + mAudioPlayer = NULL; + } + } + Animation animation; // Parse the description file @@ -424,20 +478,29 @@ bool BootAnimation::movie() const char* l = line.string(); int fps, width, height, count, pause; char path[ANIM_ENTRY_NAME_MAX]; + char color[7] = "000000"; // default to black if unspecified + char pathType; if (sscanf(l, "%d %d %d", &width, &height, &fps) == 3) { - //LOGD("> w=%d, h=%d, fps=%d", width, height, fps); + // ALOGD("> w=%d, h=%d, fps=%d", width, height, fps); animation.width = width; animation.height = height; animation.fps = fps; } - else if (sscanf(l, " %c %d %d %s", &pathType, &count, &pause, path) == 4) { - //LOGD("> type=%c, count=%d, pause=%d, path=%s", pathType, count, pause, path); + else if (sscanf(l, " %c %d %d %s #%6s", &pathType, &count, &pause, path, color) >= 4) { + // ALOGD("> type=%c, count=%d, pause=%d, path=%s, color=%s", pathType, count, pause, path, color); Animation::Part part; part.playUntilComplete = pathType == 'c'; part.count = count; part.pause = pause; part.path = path; + part.audioFile = NULL; + if (!parseColor(color, part.backgroundColor)) { + ALOGE("> invalid color '#%s'", color); + part.backgroundColor[0] = 0.0f; + part.backgroundColor[1] = 0.0f; + part.backgroundColor[2] = 0.0f; + } animation.parts.add(part); } @@ -472,11 +535,16 @@ bool BootAnimation::movie() if (method == ZipFileRO::kCompressStored) { FileMap* map = mZip->createEntryFileMap(entry); if (map) { - Animation::Frame frame; - frame.name = leaf; - frame.map = map; Animation::Part& part(animation.parts.editItemAt(j)); - part.frames.add(frame); + if (leaf == "audio.wav") { + // a part may have at most one audio file + part.audioFile = map; + } else { + Animation::Frame frame; + frame.name = leaf; + frame.map = map; + part.frames.add(frame); + } } } } @@ -523,6 +591,17 @@ bool BootAnimation::movie() if(exitPending() && !part.playUntilComplete) break; + // only play audio file the first time we animate the part + if (r == 0 && mAudioPlayer != NULL && part.audioFile) { + mAudioPlayer->playFile(part.audioFile); + } + + glClearColor( + part.backgroundColor[0], + part.backgroundColor[1], + part.backgroundColor[2], + 1.0f); + for (size_t j=0 ; j<fcount && (!exitPending() || part.playUntilComplete) ; j++) { const Animation::Frame& frame(part.frames[j]); nsecs_t lastFrame = systemTime(); diff --git a/cmds/bootanimation/BootAnimation.h b/cmds/bootanimation/BootAnimation.h index ba1c507..f968b25 100644 --- a/cmds/bootanimation/BootAnimation.h +++ b/cmds/bootanimation/BootAnimation.h @@ -21,7 +21,7 @@ #include <sys/types.h> #include <androidfw/AssetManager.h> -#include <utils/threads.h> +#include <utils/Thread.h> #include <EGL/egl.h> #include <GLES/gl.h> @@ -30,6 +30,7 @@ class SkBitmap; namespace android { +class AudioPlayer; class Surface; class SurfaceComposerClient; class SurfaceControl; @@ -71,6 +72,8 @@ private: String8 path; SortedVector<Frame> frames; bool playUntilComplete; + float backgroundColor[3]; + FileMap* audioFile; }; int fps; int width; @@ -81,11 +84,13 @@ private: status_t initTexture(Texture* texture, AssetManager& asset, const char* name); status_t initTexture(const Animation::Frame& frame); bool android(); + bool readFile(const char* name, String8& outString); bool movie(); void checkExit(); sp<SurfaceComposerClient> mSession; + sp<AudioPlayer> mAudioPlayer; AssetManager mAssets; Texture mAndroid[2]; int mWidth; diff --git a/cmds/bu/src/com/android/commands/bu/Backup.java b/cmds/bu/src/com/android/commands/bu/Backup.java index 73fd660..ffc0f87 100644 --- a/cmds/bu/src/com/android/commands/bu/Backup.java +++ b/cmds/bu/src/com/android/commands/bu/Backup.java @@ -20,6 +20,7 @@ import android.app.backup.IBackupManager; import android.os.ParcelFileDescriptor; import android.os.RemoteException; import android.os.ServiceManager; +import android.system.OsConstants; import android.util.Log; import java.io.IOException; @@ -50,13 +51,11 @@ public final class Backup { return; } - int socketFd = Integer.parseInt(nextArg()); - String arg = nextArg(); if (arg.equals("backup")) { - doFullBackup(socketFd); + doFullBackup(OsConstants.STDOUT_FILENO); } else if (arg.equals("restore")) { - doFullRestore(socketFd); + doFullRestore(OsConstants.STDIN_FILENO); } else { Log.e(TAG, "Invalid operation '" + arg + "'"); } @@ -68,7 +67,9 @@ public final class Backup { boolean saveObbs = false; boolean saveShared = false; boolean doEverything = false; + boolean doWidgets = false; boolean allIncludesSystem = true; + boolean doCompress = true; String arg; while ((arg = nextArg()) != null) { @@ -89,8 +90,16 @@ public final class Backup { allIncludesSystem = true; } else if ("-nosystem".equals(arg)) { allIncludesSystem = false; + } else if ("-widgets".equals(arg)) { + doWidgets = true; + } else if ("-nowidgets".equals(arg)) { + doWidgets = false; } else if ("-all".equals(arg)) { doEverything = true; + } else if ("-compress".equals(arg)) { + doCompress = true; + } else if ("-nocompress".equals(arg)) { + doCompress = false; } else { Log.w(TAG, "Unknown backup flag " + arg); continue; @@ -114,8 +123,8 @@ public final class Backup { try { fd = ParcelFileDescriptor.adoptFd(socketFd); String[] packArray = new String[packages.size()]; - mBackupManager.fullBackup(fd, saveApks, saveObbs, saveShared, doEverything, - allIncludesSystem, packages.toArray(packArray)); + mBackupManager.fullBackup(fd, saveApks, saveObbs, saveShared, doWidgets, + doEverything, allIncludesSystem, doCompress, packages.toArray(packArray)); } catch (RemoteException e) { Log.e(TAG, "Unable to invoke backup manager for backup"); } finally { diff --git a/cmds/content/src/com/android/commands/content/Content.java b/cmds/content/src/com/android/commands/content/Content.java index e66bdf4..948c9a2 100644 --- a/cmds/content/src/com/android/commands/content/Content.java +++ b/cmds/content/src/com/android/commands/content/Content.java @@ -26,9 +26,17 @@ import android.net.Uri; import android.os.Binder; import android.os.Bundle; import android.os.IBinder; +import android.os.ParcelFileDescriptor; import android.os.UserHandle; import android.text.TextUtils; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +import libcore.io.IoUtils; + /** * This class is a command line utility for manipulating content. A client * can insert, update, and remove records in a content provider. For example, @@ -109,6 +117,12 @@ public class Content { + " <METHOD> is the name of a provider-defined method\n" + " <ARG> is an optional string argument\n" + " <BINDING> is like --bind above, typed data of the form <KEY>:{b,s,i,l,f,d}:<VAL>\n" + + "\n" + + "usage: adb shell content read --uri <URI> [--user <USER_ID>]\n" + + " Example:\n" + + " # cat default ringtone to a file, then pull to host\n" + + " adb shell 'content read --uri content://settings/system/ringtone >" + + " /mnt/sdcard/tmp.ogg' && adb pull /mnt/sdcard/tmp.ogg\n" + "\n"; private static class Parser { @@ -117,6 +131,7 @@ public class Content { private static final String ARGUMENT_UPDATE = "update"; private static final String ARGUMENT_QUERY = "query"; private static final String ARGUMENT_CALL = "call"; + private static final String ARGUMENT_READ = "read"; private static final String ARGUMENT_WHERE = "--where"; private static final String ARGUMENT_BIND = "--bind"; private static final String ARGUMENT_URI = "--uri"; @@ -154,6 +169,8 @@ public class Content { return parseQueryCommand(); } else if (ARGUMENT_CALL.equals(operation)) { return parseCallCommand(); + } else if (ARGUMENT_READ.equals(operation)) { + return parseReadCommand(); } else { throw new IllegalArgumentException("Unsupported operation: " + operation); } @@ -273,6 +290,25 @@ public class Content { return new CallCommand(uri, userId, method, arg, values); } + private ReadCommand parseReadCommand() { + Uri uri = null; + int userId = UserHandle.USER_OWNER; + for (String argument; (argument = mTokenizer.nextArg())!= null;) { + if (ARGUMENT_URI.equals(argument)) { + uri = Uri.parse(argumentValueRequired(argument)); + } else if (ARGUMENT_USER.equals(argument)) { + userId = Integer.parseInt(argumentValueRequired(argument)); + } else { + throw new IllegalArgumentException("Unsupported argument: " + argument); + } + } + if (uri == null) { + throw new IllegalArgumentException("Content provider URI not specified." + + " Did you specify --uri argument?"); + } + return new ReadCommand(uri, userId); + } + public QueryCommand parseQueryCommand() { Uri uri = null; int userId = UserHandle.USER_OWNER; @@ -458,6 +494,31 @@ public class Content { } } + private static class ReadCommand extends Command { + public ReadCommand(Uri uri, int userId) { + super(uri, userId); + } + + @Override + public void onExecute(IContentProvider provider) throws Exception { + final ParcelFileDescriptor fd = provider.openFile(null, mUri, "r", null); + copy(new FileInputStream(fd.getFileDescriptor()), System.out); + } + + private static void copy(InputStream is, OutputStream os) throws IOException { + final byte[] buffer = new byte[8 * 1024]; + int read; + try { + while ((read = is.read(buffer)) > -1) { + os.write(buffer, 0, read); + } + } finally { + IoUtils.closeQuietly(is); + IoUtils.closeQuietly(os); + } + } + } + private static class QueryCommand extends DeleteCommand { final String[] mProjection; final String mSortOrder; @@ -498,7 +559,7 @@ public class Content { columnValue = String.valueOf(cursor.getFloat(columnIndex)); break; case Cursor.FIELD_TYPE_INTEGER: - columnValue = String.valueOf(cursor.getInt(columnIndex)); + columnValue = String.valueOf(cursor.getLong(columnIndex)); break; case Cursor.FIELD_TYPE_STRING: columnValue = cursor.getString(columnIndex); diff --git a/cmds/dpm/Android.mk b/cmds/dpm/Android.mk new file mode 100644 index 0000000..9f5aee4 --- /dev/null +++ b/cmds/dpm/Android.mk @@ -0,0 +1,15 @@ +# Copyright 2014 The Android Open Source Project +# +LOCAL_PATH:= $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_SRC_FILES := $(call all-subdir-java-files) +LOCAL_MODULE := dpm +include $(BUILD_JAVA_LIBRARY) + +include $(CLEAR_VARS) +LOCAL_MODULE := dpm +LOCAL_SRC_FILES := dpm +LOCAL_MODULE_CLASS := EXECUTABLES +LOCAL_MODULE_TAGS := optional +include $(BUILD_PREBUILT) diff --git a/cmds/dpm/MODULE_LICENSE_APACHE2 b/cmds/dpm/MODULE_LICENSE_APACHE2 new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/cmds/dpm/MODULE_LICENSE_APACHE2 diff --git a/cmds/dpm/NOTICE b/cmds/dpm/NOTICE new file mode 100644 index 0000000..316b4eb --- /dev/null +++ b/cmds/dpm/NOTICE @@ -0,0 +1,190 @@ + + Copyright (c) 2014, The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + diff --git a/cmds/dpm/dpm b/cmds/dpm/dpm new file mode 100755 index 0000000..c2e5cbb --- /dev/null +++ b/cmds/dpm/dpm @@ -0,0 +1,6 @@ +# Script to start "dpm" on the device +# +base=/system +export CLASSPATH=$base/framework/dpm.jar +exec app_process $base/bin com.android.commands.dpm.Dpm "$@" + diff --git a/cmds/dpm/src/com/android/commands/dpm/Dpm.java b/cmds/dpm/src/com/android/commands/dpm/Dpm.java new file mode 100644 index 0000000..3b9a785 --- /dev/null +++ b/cmds/dpm/src/com/android/commands/dpm/Dpm.java @@ -0,0 +1,135 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.commands.dpm; + +import android.app.admin.IDevicePolicyManager; +import android.content.ComponentName; +import android.content.Context; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.os.UserHandle; + +import com.android.internal.os.BaseCommand; + +import java.io.PrintStream; + +public final class Dpm extends BaseCommand { + + /** + * Command-line entry point. + * + * @param args The command-line arguments + */ + public static void main(String[] args) { + (new Dpm()).run(args); + } + + private static final String COMMAND_SET_DEVICE_OWNER = "set-device-owner"; + private static final String COMMAND_SET_PROFILE_OWNER = "set-profile-owner"; + + private IDevicePolicyManager mDevicePolicyManager; + + @Override + public void onShowUsage(PrintStream out) { + out.println( + "usage: dpm [subcommand] [options]\n" + + "usage: dpm set-device-owner <COMPONENT>\n" + + "usage: dpm set-profile-owner <COMPONENT> <USER_ID>\n" + + "\n" + + "dpm set-device-owner: Sets the given component as active admin, and its\n" + + " package as device owner.\n" + + "\n" + + "dpm set-profile-owner: Sets the given component as active admin and profile" + + " owner for an existing user.\n"); + } + + @Override + public void onRun() throws Exception { + mDevicePolicyManager = IDevicePolicyManager.Stub.asInterface( + ServiceManager.getService(Context.DEVICE_POLICY_SERVICE)); + if (mDevicePolicyManager == null) { + showError("Error: Could not access the Device Policy Manager. Is the system running?"); + return; + } + + String command = nextArgRequired(); + switch (command) { + case COMMAND_SET_DEVICE_OWNER: + runSetDeviceOwner(); + break; + case COMMAND_SET_PROFILE_OWNER: + runSetProfileOwner(); + break; + default: + throw new IllegalArgumentException ("unknown command '" + command + "'"); + } + } + + private void runSetDeviceOwner() throws RemoteException { + ComponentName component = parseComponentName(nextArgRequired()); + mDevicePolicyManager.setActiveAdmin(component, true /*refreshing*/, UserHandle.USER_OWNER); + + String packageName = component.getPackageName(); + try { + if (!mDevicePolicyManager.setDeviceOwner(packageName, null /*ownerName*/)) { + throw new RuntimeException( + "Can't set package " + packageName + " as device owner."); + } + } catch (Exception e) { + // Need to remove the admin that we just added. + mDevicePolicyManager.removeActiveAdmin(component, UserHandle.USER_OWNER); + throw e; + } + System.out.println("Success: Device owner set to package " + packageName); + System.out.println("Active admin set to component " + component.toShortString()); + } + + private void runSetProfileOwner() throws RemoteException { + ComponentName component = parseComponentName(nextArgRequired()); + int userId = parseInt(nextArgRequired()); + mDevicePolicyManager.setActiveAdmin(component, true /*refreshing*/, userId); + + try { + if (!mDevicePolicyManager.setProfileOwner(component, "" /*ownerName*/, userId)) { + throw new RuntimeException("Can't set component " + component.toShortString() + + " as profile owner for user " + userId); + } + } catch (Exception e) { + // Need to remove the admin that we just added. + mDevicePolicyManager.removeActiveAdmin(component, userId); + throw e; + } + System.out.println("Success: Active admin and profile owner set to " + + component.toShortString() + " for user " + userId); + } + + private ComponentName parseComponentName(String component) { + ComponentName cn = ComponentName.unflattenFromString(component); + if (cn == null) { + throw new IllegalArgumentException ("Invalid component " + component); + } + return cn; + } + + private int parseInt(String argument) { + try { + return Integer.parseInt(argument); + } catch (NumberFormatException e) { + throw new IllegalArgumentException ("Invalid integer argument '" + argument + "'", e); + } + } +}
\ No newline at end of file diff --git a/cmds/idmap/create.cpp b/cmds/idmap/create.cpp index ae35f7b..593a197 100644 --- a/cmds/idmap/create.cpp +++ b/cmds/idmap/create.cpp @@ -105,7 +105,7 @@ fail: uint32_t cached_target_crc, cached_overlay_crc; String8 cached_target_path, cached_overlay_path; - if (!ResTable::getIdmapInfo(buf, N, &cached_target_crc, &cached_overlay_crc, + if (!ResTable::getIdmapInfo(buf, N, NULL, &cached_target_crc, &cached_overlay_crc, &cached_target_path, &cached_overlay_path)) { return true; } diff --git a/cmds/idmap/idmap.cpp b/cmds/idmap/idmap.cpp index 46c0edc..90cfa2c 100644 --- a/cmds/idmap/idmap.cpp +++ b/cmds/idmap/idmap.cpp @@ -66,26 +66,32 @@ EXAMPLES \n\ Display an idmap file: \n\ \n\ $ adb shell idmap --inspect /data/resource-cache/vendor@overlay@overlay.apk@idmap \n\ - SECTION ENTRY VALUE OFFSET COMMENT \n\ - IDMAP HEADER magic 0x706d6469 0x0 \n\ - base crc 0x484aa77f 0x1 \n\ - overlay crc 0x03c66fa5 0x2 \n\ - base path .......... 0x03-0x42 /system/app/target.apk \n\ - overlay path .......... 0x43-0x82 /vendor/overlay/overlay.apk \n\ - DATA HEADER types count 0x00000003 0x83 \n\ - padding 0x00000000 0x84 \n\ - type offset 0x00000004 0x85 absolute offset 0x87, xml \n\ - type offset 0x00000007 0x86 absolute offset 0x8a, string \n\ - DATA BLOCK entry count 0x00000001 0x87 \n\ - entry offset 0x00000000 0x88 \n\ - entry 0x7f020000 0x89 xml/integer \n\ - DATA BLOCK entry count 0x00000002 0x8a \n\ - entry offset 0x00000000 0x8b \n\ - entry 0x7f030000 0x8c string/str \n\ - entry 0x7f030001 0x8d string/str2 \n\ + SECTION ENTRY VALUE COMMENT \n\ + IDMAP HEADER magic 0x706d6469 \n\ + base crc 0xb65a383f \n\ + overlay crc 0x7b9675e8 \n\ + base path .......... /path/to/target.apk \n\ + overlay path .......... /path/to/overlay.apk \n\ + DATA HEADER target pkg 0x0000007f \n\ + types count 0x00000003 \n\ + DATA BLOCK target type 0x00000002 \n\ + overlay type 0x00000002 \n\ + entry count 0x00000001 \n\ + entry offset 0x00000000 \n\ + entry 0x00000000 drawable/drawable \n\ + DATA BLOCK target type 0x00000003 \n\ + overlay type 0x00000003 \n\ + entry count 0x00000001 \n\ + entry offset 0x00000000 \n\ + entry 0x00000000 xml/integer \n\ + DATA BLOCK target type 0x00000004 \n\ + overlay type 0x00000004 \n\ + entry count 0x00000001 \n\ + entry offset 0x00000000 \n\ + entry 0x00000000 raw/lorem_ipsum \n\ \n\ In this example, the overlay package provides three alternative resource values:\n\ - xml/integer, string/str and string/str2.\n\ + drawable/drawable, xml/integer, and raw/lorem_ipsum \n\ \n\ NOTES \n\ This tool and its expected invocation from installd is modelled on dexopt."; diff --git a/cmds/idmap/inspect.cpp b/cmds/idmap/inspect.cpp index a59f5d3..b9ac8a5 100644 --- a/cmds/idmap/inspect.cpp +++ b/cmds/idmap/inspect.cpp @@ -10,92 +10,108 @@ using namespace android; -#define NEXT(b, i, o) do { if (buf.next(&i, &o) < 0) { return -1; } } while (0) - namespace { - static const uint32_t IDMAP_MAGIC = 0x706d6469; + static const uint32_t IDMAP_MAGIC = 0x504D4449; static const size_t PATH_LENGTH = 256; - static const uint32_t IDMAP_HEADER_SIZE = (3 + 2 * (PATH_LENGTH / sizeof(uint32_t))); void printe(const char *fmt, ...); class IdmapBuffer { private: - char *buf_; + const char* buf_; size_t len_; - mutable size_t pos_; + size_t pos_; public: - IdmapBuffer() : buf_((char *)MAP_FAILED), len_(0), pos_(0) {} + IdmapBuffer() : buf_((const char *)MAP_FAILED), len_(0), pos_(0) {} ~IdmapBuffer() { if (buf_ != MAP_FAILED) { - munmap(buf_, len_); + munmap(const_cast<char*>(buf_), len_); } } - int init(const char *idmap_path) - { + status_t init(const char *idmap_path) { struct stat st; int fd; if (stat(idmap_path, &st) < 0) { printe("failed to stat idmap '%s': %s\n", idmap_path, strerror(errno)); - return -1; + return UNKNOWN_ERROR; } len_ = st.st_size; if ((fd = TEMP_FAILURE_RETRY(open(idmap_path, O_RDONLY))) < 0) { printe("failed to open idmap '%s': %s\n", idmap_path, strerror(errno)); - return -1; + return UNKNOWN_ERROR; } - if ((buf_ = (char*)mmap(NULL, len_, PROT_READ, MAP_PRIVATE, fd, 0)) == MAP_FAILED) { + if ((buf_ = (const char*)mmap(NULL, len_, PROT_READ, MAP_PRIVATE, fd, 0)) == MAP_FAILED) { close(fd); printe("failed to mmap idmap: %s\n", strerror(errno)); - return -1; + return UNKNOWN_ERROR; } close(fd); - return 0; + return NO_ERROR; } - int next(uint32_t *i, uint32_t *offset) const - { + status_t nextUint32(uint32_t* i) { if (!buf_) { printe("failed to read next uint32_t: buffer not initialized\n"); - return -1; + return UNKNOWN_ERROR; } - if (pos_ + 4 > len_) { + + if (pos_ + sizeof(uint32_t) > len_) { printe("failed to read next uint32_t: end of buffer reached at pos=0x%08x\n", pos_); - return -1; + return UNKNOWN_ERROR; + } + + if ((reinterpret_cast<uintptr_t>(buf_ + pos_) & 0x3) != 0) { + printe("failed to read next uint32_t: not aligned on 4-byte boundary\n"); + return UNKNOWN_ERROR; } - *offset = pos_ / sizeof(uint32_t); - char a = buf_[pos_++]; - char b = buf_[pos_++]; - char c = buf_[pos_++]; - char d = buf_[pos_++]; - *i = (d << 24) | (c << 16) | (b << 8) | a; - return 0; + + *i = dtohl(*reinterpret_cast<const uint32_t*>(buf_ + pos_)); + pos_ += sizeof(uint32_t); + return NO_ERROR; } - int nextPath(char *b, uint32_t *offset_start, uint32_t *offset_end) const - { + status_t nextUint16(uint16_t* i) { + if (!buf_) { + printe("failed to read next uint16_t: buffer not initialized\n"); + return UNKNOWN_ERROR; + } + + if (pos_ + sizeof(uint16_t) > len_) { + printe("failed to read next uint16_t: end of buffer reached at pos=0x%08x\n", + pos_); + return UNKNOWN_ERROR; + } + + if ((reinterpret_cast<uintptr_t>(buf_ + pos_) & 0x1) != 0) { + printe("failed to read next uint32_t: not aligned on 2-byte boundary\n"); + return UNKNOWN_ERROR; + } + + *i = dtohs(*reinterpret_cast<const uint16_t*>(buf_ + pos_)); + pos_ += sizeof(uint16_t); + return NO_ERROR; + } + + status_t nextPath(char *b) { if (!buf_) { printe("failed to read next path: buffer not initialized\n"); - return -1; + return UNKNOWN_ERROR; } if (pos_ + PATH_LENGTH > len_) { printe("failed to read next path: end of buffer reached at pos=0x%08x\n", pos_); - return -1; + return UNKNOWN_ERROR; } memcpy(b, buf_ + pos_, PATH_LENGTH); - *offset_start = pos_ / sizeof(uint32_t); pos_ += PATH_LENGTH; - *offset_end = pos_ / sizeof(uint32_t) - 1; - return 0; + return NO_ERROR; } }; - void printe(const char *fmt, ...) - { + void printe(const char *fmt, ...) { va_list ap; va_start(ap, fmt); @@ -104,44 +120,37 @@ namespace { va_end(ap); } - void print_header() - { - printf("SECTION ENTRY VALUE OFFSET COMMENT\n"); + void print_header() { + printf("SECTION ENTRY VALUE COMMENT\n"); } - void print(const char *section, const char *subsection, uint32_t value, uint32_t offset, - const char *fmt, ...) - { + void print(const char *section, const char *subsection, uint32_t value, const char *fmt, ...) { va_list ap; va_start(ap, fmt); - printf("%-12s %-12s 0x%08x 0x%-4x ", section, subsection, value, offset); + printf("%-12s %-12s 0x%08x ", section, subsection, value); vprintf(fmt, ap); printf("\n"); va_end(ap); } - void print_path(const char *section, const char *subsection, uint32_t offset_start, - uint32_t offset_end, const char *fmt, ...) - { + void print_path(const char *section, const char *subsection, const char *fmt, ...) { va_list ap; va_start(ap, fmt); - printf("%-12s %-12s .......... 0x%02x-0x%02x ", section, subsection, offset_start, - offset_end); + printf("%-12s %-12s .......... ", section, subsection); vprintf(fmt, ap); printf("\n"); va_end(ap); } - int resource_metadata(const AssetManager& am, uint32_t res_id, - String8 *package, String8 *type, String8 *name) - { + status_t resource_metadata(const AssetManager& am, uint32_t res_id, + String8 *package, String8 *type, String8 *name) { const ResTable& rt = am.getResources(); struct ResTable::resource_name data; if (!rt.getResourceName(res_id, false, &data)) { printe("failed to get resource name id=0x%08x\n", res_id); - return -1; + return UNKNOWN_ERROR; } if (package) { *package = String8(String16(data.package, data.packageLen)); @@ -152,140 +161,150 @@ namespace { if (name) { *name = String8(String16(data.name, data.nameLen)); } - return 0; - } - - int package_id(const AssetManager& am) - { - return (am.getResources().getBasePackageId(0)) << 24; + return NO_ERROR; } - int parse_idmap_header(const IdmapBuffer& buf, AssetManager& am) - { - uint32_t i, o, e; + status_t parse_idmap_header(IdmapBuffer& buf, AssetManager& am) { + uint32_t i; char path[PATH_LENGTH]; - NEXT(buf, i, o); + status_t err = buf.nextUint32(&i); + if (err != NO_ERROR) { + return err; + } + if (i != IDMAP_MAGIC) { printe("not an idmap file: actual magic constant 0x%08x does not match expected magic " "constant 0x%08x\n", i, IDMAP_MAGIC); - return -1; + return UNKNOWN_ERROR; } + print_header(); - print("IDMAP HEADER", "magic", i, o, ""); + print("IDMAP HEADER", "magic", i, ""); + + err = buf.nextUint32(&i); + if (err != NO_ERROR) { + return err; + } + print("", "version", i, ""); - NEXT(buf, i, o); - print("", "base crc", i, o, ""); + err = buf.nextUint32(&i); + if (err != NO_ERROR) { + return err; + } + print("", "base crc", i, ""); - NEXT(buf, i, o); - print("", "overlay crc", i, o, ""); + err = buf.nextUint32(&i); + if (err != NO_ERROR) { + return err; + } + print("", "overlay crc", i, ""); - if (buf.nextPath(path, &o, &e) < 0) { + err = buf.nextPath(path); + if (err != NO_ERROR) { // printe done from IdmapBuffer::nextPath - return -1; + return err; } - print_path("", "base path", o, e, "%s", path); + print_path("", "base path", "%s", path); + if (!am.addAssetPath(String8(path), NULL)) { printe("failed to add '%s' as asset path\n", path); - return -1; + return UNKNOWN_ERROR; } - if (buf.nextPath(path, &o, &e) < 0) { + err = buf.nextPath(path); + if (err != NO_ERROR) { // printe done from IdmapBuffer::nextPath - return -1; + return err; } - print_path("", "overlay path", o, e, "%s", path); + print_path("", "overlay path", "%s", path); - return 0; + return NO_ERROR; } - int parse_data_header(const IdmapBuffer& buf, const AssetManager& am, Vector<uint32_t>& types) - { - uint32_t i, o; - const uint32_t numeric_package = package_id(am); + status_t parse_data(IdmapBuffer& buf, const AssetManager& am) { + const uint32_t packageId = am.getResources().getBasePackageId(0); - NEXT(buf, i, o); - print("DATA HEADER", "types count", i, o, ""); - const uint32_t N = i; + uint16_t data16; + status_t err = buf.nextUint16(&data16); + if (err != NO_ERROR) { + return err; + } + print("DATA HEADER", "target pkg", static_cast<uint32_t>(data16), ""); - for (uint32_t j = 0; j < N; ++j) { - NEXT(buf, i, o); - if (i == 0) { - print("", "padding", i, o, ""); - } else { - String8 type; - const uint32_t numeric_type = (j + 1) << 16; - const uint32_t res_id = numeric_package | numeric_type; - if (resource_metadata(am, res_id, NULL, &type, NULL) < 0) { - // printe done from resource_metadata - return -1; - } - print("", "type offset", i, o, "absolute offset 0x%02x, %s", - i + IDMAP_HEADER_SIZE, type.string()); - types.add(numeric_type); - } + err = buf.nextUint16(&data16); + if (err != NO_ERROR) { + return err; } + print("", "types count", static_cast<uint32_t>(data16), ""); - return 0; - } + uint32_t typeCount = static_cast<uint32_t>(data16); + while (typeCount > 0) { + typeCount--; + + err = buf.nextUint16(&data16); + if (err != NO_ERROR) { + return err; + } + const uint32_t targetTypeId = static_cast<uint32_t>(data16); + print("DATA BLOCK", "target type", targetTypeId, ""); - int parse_data_block(const IdmapBuffer& buf, const AssetManager& am, size_t numeric_type) - { - uint32_t i, o, n, id_offset; - const uint32_t numeric_package = package_id(am); - - NEXT(buf, i, o); - print("DATA BLOCK", "entry count", i, o, ""); - n = i; - - NEXT(buf, i, o); - print("", "entry offset", i, o, ""); - id_offset = i; - - for ( ; n > 0; --n) { - String8 type, name; - - NEXT(buf, i, o); - if (i == 0) { - print("", "padding", i, o, ""); - } else { - uint32_t res_id = numeric_package | numeric_type | id_offset; - if (resource_metadata(am, res_id, NULL, &type, &name) < 0) { - // printe done from resource_metadata - return -1; + err = buf.nextUint16(&data16); + if (err != NO_ERROR) { + return err; + } + print("", "overlay type", static_cast<uint32_t>(data16), ""); + + err = buf.nextUint16(&data16); + if (err != NO_ERROR) { + return err; + } + const uint32_t entryCount = static_cast<uint32_t>(data16); + print("", "entry count", entryCount, ""); + + err = buf.nextUint16(&data16); + if (err != NO_ERROR) { + return err; + } + const uint32_t entryOffset = static_cast<uint32_t>(data16); + print("", "entry offset", entryOffset, ""); + + for (uint32_t i = 0; i < entryCount; i++) { + uint32_t data32; + err = buf.nextUint32(&data32); + if (err != NO_ERROR) { + return err; } - print("", "entry", i, o, "%s/%s", type.string(), name.string()); + + uint32_t resID = (packageId << 24) | (targetTypeId << 16) | (entryOffset + i); + String8 type; + String8 name; + err = resource_metadata(am, resID, NULL, &type, &name); + if (err != NO_ERROR) { + return err; + } + print("", "entry", data32, "%s/%s", type.string(), name.string()); } - ++id_offset; } - return 0; + return NO_ERROR; } } -int idmap_inspect(const char *idmap_path) -{ +int idmap_inspect(const char *idmap_path) { IdmapBuffer buf; if (buf.init(idmap_path) < 0) { // printe done from IdmapBuffer::init return EXIT_FAILURE; } AssetManager am; - if (parse_idmap_header(buf, am) < 0) { + if (parse_idmap_header(buf, am) != NO_ERROR) { // printe done from parse_idmap_header return EXIT_FAILURE; } - Vector<uint32_t> types; - if (parse_data_header(buf, am, types) < 0) { + if (parse_data(buf, am) != NO_ERROR) { // printe done from parse_data_header return EXIT_FAILURE; } - const size_t N = types.size(); - for (size_t i = 0; i < N; ++i) { - if (parse_data_block(buf, am, types.itemAt(i)) < 0) { - // printe done from parse_data_block - return EXIT_FAILURE; - } - } return EXIT_SUCCESS; } diff --git a/cmds/idmap/scan.cpp b/cmds/idmap/scan.cpp index c5fc941..1153f38 100644 --- a/cmds/idmap/scan.cpp +++ b/cmds/idmap/scan.cpp @@ -119,7 +119,8 @@ namespace { int parse_manifest(const void *data, size_t size, const char *target_package_name) { - ResXMLTree parser(data, size); + ResXMLTree parser; + parser.setTo(data, size); if (parser.getError() != NO_ERROR) { ALOGD("%s failed to init xml parser, error=0x%08x\n", __FUNCTION__, parser.getError()); return -1; diff --git a/cmds/media/src/com/android/commands/media/Media.java b/cmds/media/src/com/android/commands/media/Media.java index 92c6a51..6a8fb05 100644 --- a/cmds/media/src/com/android/commands/media/Media.java +++ b/cmds/media/src/com/android/commands/media/Media.java @@ -17,12 +17,19 @@ package com.android.commands.media; -import android.app.PendingIntent; +import android.app.ActivityManager; import android.content.Context; -import android.graphics.Bitmap; -import android.media.IAudioService; -import android.media.IRemoteControlDisplay; +import android.content.pm.ParceledListSlice; +import android.media.MediaMetadata; +import android.media.session.ISessionController; +import android.media.session.ISessionControllerCallback; +import android.media.session.ISessionManager; +import android.media.session.MediaController; +import android.media.session.ParcelableVolumeInfo; +import android.media.session.PlaybackState; import android.os.Bundle; +import android.os.HandlerThread; +import android.os.IBinder; import android.os.RemoteException; import android.os.ServiceManager; import android.os.SystemClock; @@ -30,16 +37,17 @@ import android.util.AndroidException; import android.view.InputDevice; import android.view.KeyCharacterMap; import android.view.KeyEvent; + import com.android.internal.os.BaseCommand; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintStream; +import java.util.List; public class Media extends BaseCommand { - - private IAudioService mAudioService; + private ISessionManager mSessionService; /** * Command-line entry point. @@ -54,29 +62,35 @@ public class Media extends BaseCommand { out.println( "usage: media [subcommand] [options]\n" + " media dispatch KEY\n" + - " media remote-display\n" + + " media list-sessions\n" + + " media monitor <tag>\n" + "\n" + - "media dispatch: dispatch a media key to the current media client.\n" + + "media dispatch: dispatch a media key to the system.\n" + " KEY may be: play, pause, play-pause, mute, headsethook,\n" + - " stop, next, previous, rewind, recordm fast-forword.\n" + - "media remote-display: monitor remote display updates.\n" + " stop, next, previous, rewind, record, fast-forword.\n" + + "media list-sessions: print a list of the current sessions.\n" + + "media monitor: monitor updates to the specified session.\n" + + " Use the tag from list-sessions.\n" ); } public void onRun() throws Exception { - mAudioService = IAudioService.Stub.asInterface(ServiceManager.checkService( - Context.AUDIO_SERVICE)); - if (mAudioService == null) { + mSessionService = ISessionManager.Stub.asInterface(ServiceManager.checkService( + Context.MEDIA_SESSION_SERVICE)); + if (mSessionService == null) { System.err.println(NO_SYSTEM_ERROR_CODE); - throw new AndroidException("Can't connect to audio service; is the system running?"); + throw new AndroidException( + "Can't connect to media session service; is the system running?"); } String op = nextArgRequired(); if (op.equals("dispatch")) { runDispatch(); - } else if (op.equals("remote-display")) { - runRemoteDisplay(); + } else if (op.equals("list-sessions")) { + runListSessions(); + } else if (op.equals("monitor")) { + runMonitor(); } else { showError("Error: unknown command '" + op + "'"); return; @@ -85,11 +99,42 @@ public class Media extends BaseCommand { private void sendMediaKey(KeyEvent event) { try { - mAudioService.dispatchMediaKeyEvent(event); + mSessionService.dispatchMediaKeyEvent(event, false); } catch (RemoteException e) { } } + private void runMonitor() throws Exception { + String id = nextArgRequired(); + if (id == null) { + showError("Error: must include a session id"); + return; + } + boolean success = false; + try { + List<IBinder> sessions = mSessionService + .getSessions(null, ActivityManager.getCurrentUser()); + for (IBinder session : sessions) { + ISessionController controller = ISessionController.Stub.asInterface(session); + try { + if (controller != null && id.equals(controller.getTag())) { + ControllerMonitor monitor = new ControllerMonitor(controller); + monitor.run(); + success = true; + break; + } + } catch (RemoteException e) { + // ignore + } + } + } catch (Exception e) { + System.out.println("***Error monitoring session*** " + e.getMessage()); + } + if (!success) { + System.out.println("No session found with id " + id); + } + } + private void runDispatch() throws Exception { String cmd = nextArgRequired(); int keycode; @@ -127,65 +172,80 @@ public class Media extends BaseCommand { KeyCharacterMap.VIRTUAL_KEYBOARD, 0, 0, InputDevice.SOURCE_KEYBOARD)); } - class RemoteDisplayMonitor extends IRemoteControlDisplay.Stub { - RemoteDisplayMonitor() { + class ControllerMonitor extends ISessionControllerCallback.Stub { + private final ISessionController mController; + + public ControllerMonitor(ISessionController controller) { + mController = controller; } + @Override + public void onSessionDestroyed() { + System.out.println("onSessionDestroyed. Enter q to quit."); + + } @Override - public void setCurrentClientId(int clientGeneration, PendingIntent clientMediaIntent, - boolean clearing) { - System.out.println("New client: id=" + clientGeneration - + " intent=" + clientMediaIntent + " clearing=" + clearing); + public void onEvent(String event, Bundle extras) { + System.out.println("onSessionEvent event=" + event + ", extras=" + extras); } @Override - public void setEnabled(boolean enabled) { - System.out.println("New enable state= " + (enabled ? "enabled" : "disabled")); + public void onPlaybackStateChanged(PlaybackState state) { + System.out.println("onPlaybackStateChanged " + state); } @Override - public void setPlaybackState(int generationId, int state, long stateChangeTimeMs, - long currentPosMs, float speed) { - System.out.println("New state: id=" + generationId + " state=" + state - + " time=" + stateChangeTimeMs + " pos=" + currentPosMs + " speed=" + speed); + public void onMetadataChanged(MediaMetadata metadata) { + String mmString = metadata == null ? null : "title=" + metadata + .getDescription(); + System.out.println("onMetadataChanged " + mmString); } @Override - public void setTransportControlInfo(int generationId, int transportControlFlags, - int posCapabilities) { - System.out.println("New control info: id=" + generationId - + " flags=0x" + Integer.toHexString(transportControlFlags) - + " cap=0x" + Integer.toHexString(posCapabilities)); + public void onQueueChanged(ParceledListSlice queue) throws RemoteException { + System.out.println("onQueueChanged, " + + (queue == null ? "null queue" : " size=" + queue.getList().size())); } @Override - public void setMetadata(int generationId, Bundle metadata) { - System.out.println("New metadata: id=" + generationId - + " data=" + metadata); + public void onQueueTitleChanged(CharSequence title) throws RemoteException { + System.out.println("onQueueTitleChange " + title); } @Override - public void setArtwork(int generationId, Bitmap artwork) { - System.out.println("New artwork: id=" + generationId - + " art=" + artwork); + public void onExtrasChanged(Bundle extras) throws RemoteException { + System.out.println("onExtrasChanged " + extras); } @Override - public void setAllMetadata(int generationId, Bundle metadata, Bitmap artwork) { - System.out.println("New metadata+artwork: id=" + generationId - + " data=" + metadata + " art=" + artwork); + public void onVolumeInfoChanged(ParcelableVolumeInfo info) throws RemoteException { + System.out.println("onVolumeInfoChanged " + info); } void printUsageMessage() { - System.out.println("Monitoring remote control displays... available commands:"); + try { + System.out.println("V2Monitoring session " + mController.getTag() + + "... available commands: play, pause, next, previous"); + } catch (RemoteException e) { + System.out.println("Error trying to monitor session!"); + } System.out.println("(q)uit: finish monitoring"); } void run() throws RemoteException { printUsageMessage(); - - mAudioService.registerRemoteControlDisplay(this, 0, 0); + HandlerThread cbThread = new HandlerThread("MediaCb") { + @Override + protected void onLooperPrepared() { + try { + mController.registerCallbackListener(ControllerMonitor.this); + } catch (RemoteException e) { + System.out.println("Error registering monitor callback"); + } + } + }; + cbThread.start(); try { InputStreamReader converter = new InputStreamReader(System.in); @@ -198,6 +258,14 @@ public class Media extends BaseCommand { addNewline = false; } else if ("q".equals(line) || "quit".equals(line)) { break; + } else if ("play".equals(line)) { + mController.play(); + } else if ("pause".equals(line)) { + mController.pause(); + } else if ("next".equals(line)) { + mController.next(); + } else if ("previous".equals(line)) { + mController.previous(); } else { System.out.println("Invalid command: " + line); } @@ -209,17 +277,38 @@ public class Media extends BaseCommand { printUsageMessage(); } } - } catch (IOException e) { e.printStackTrace(); } finally { - mAudioService.unregisterRemoteControlDisplay(this); + cbThread.getLooper().quit(); + try { + mController.unregisterCallbackListener(this); + } catch (Exception e) { + // ignoring + } } } } - private void runRemoteDisplay() throws Exception { - RemoteDisplayMonitor monitor = new RemoteDisplayMonitor(); - monitor.run(); + private void runListSessions() { + System.out.println("Sessions:"); + try { + List<IBinder> sessions = mSessionService + .getSessions(null, ActivityManager.getCurrentUser()); + for (IBinder session : sessions) { + + ISessionController controller = ISessionController.Stub.asInterface(session); + if (controller != null) { + try { + System.out.println(" tag=" + controller.getTag() + + ", package=" + controller.getPackageName()); + } catch (RemoteException e) { + // ignore + } + } + } + } catch (Exception e) { + System.out.println("***Error listing sessions***"); + } } } diff --git a/cmds/pm/src/com/android/commands/pm/Pm.java b/cmds/pm/src/com/android/commands/pm/Pm.java index 75b0a82..5e9d8f7 100644 --- a/cmds/pm/src/com/android/commands/pm/Pm.java +++ b/cmds/pm/src/com/android/commands/pm/Pm.java @@ -18,16 +18,23 @@ package com.android.commands.pm; import android.app.ActivityManager; import android.app.ActivityManagerNative; +import android.app.IActivityManager; +import android.app.PackageInstallObserver; import android.content.ComponentName; +import android.content.IIntentReceiver; +import android.content.IIntentSender; +import android.content.Intent; +import android.content.IntentSender; import android.content.pm.ApplicationInfo; -import android.content.pm.ContainerEncryptionParams; import android.content.pm.FeatureInfo; import android.content.pm.IPackageDataObserver; -import android.content.pm.IPackageDeleteObserver; -import android.content.pm.IPackageInstallObserver; +import android.content.pm.IPackageInstaller; import android.content.pm.IPackageManager; import android.content.pm.InstrumentationInfo; import android.content.pm.PackageInfo; +import android.content.pm.PackageInstaller; +import android.content.pm.PackageInstaller.SessionInfo; +import android.content.pm.PackageInstaller.SessionParams; import android.content.pm.PackageItemInfo; import android.content.pm.PackageManager; import android.content.pm.ParceledListSlice; @@ -39,30 +46,43 @@ import android.content.res.AssetManager; import android.content.res.Resources; import android.net.Uri; import android.os.Build; +import android.os.Bundle; import android.os.IUserManager; +import android.os.Process; import android.os.RemoteException; import android.os.ServiceManager; import android.os.UserHandle; import android.os.UserManager; +import android.text.TextUtils; +import android.util.Log; + +import com.android.internal.content.PackageHelper; +import com.android.internal.util.ArrayUtils; +import com.android.internal.util.SizedInputStream; + +import libcore.io.IoUtils; import java.io.File; import java.io.FileDescriptor; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; import java.lang.reflect.Field; import java.lang.reflect.Modifier; -import java.security.InvalidAlgorithmParameterException; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.List; import java.util.WeakHashMap; -import javax.crypto.SecretKey; -import javax.crypto.spec.IvParameterSpec; -import javax.crypto.spec.SecretKeySpec; - -import com.android.internal.content.PackageHelper; +import java.util.concurrent.SynchronousQueue; +import java.util.concurrent.TimeUnit; public final class Pm { + private static final String TAG = "Pm"; + IPackageManager mPm; + IPackageInstaller mInstaller; IUserManager mUm; private WeakHashMap<String, Resources> mResourceCache @@ -76,10 +96,18 @@ public final class Pm { "Error: Could not access the Package Manager. Is the system running?"; public static void main(String[] args) { - new Pm().run(args); + try { + new Pm().run(args); + } catch (Exception e) { + Log.e(TAG, "Error", e); + System.err.println("Error: " + e); + if (e instanceof RemoteException) { + System.err.println(PM_NOT_RUNNING_ERR); + } + } } - public void run(String[] args) { + public void run(String[] args) throws IOException, RemoteException { boolean validCommand = false; if (args.length < 1) { showUsage(); @@ -92,6 +120,7 @@ public final class Pm { System.err.println(PM_NOT_RUNNING_ERR); return; } + mInstaller = mPm.getPackageInstaller(); mArgs = args; String op = args[0]; @@ -117,6 +146,31 @@ public final class Pm { return; } + if ("install-create".equals(op)) { + runInstallCreate(); + return; + } + + if ("install-write".equals(op)) { + runInstallWrite(); + return; + } + + if ("install-commit".equals(op)) { + runInstallCommit(); + return; + } + + if ("install-abandon".equals(op) || "install-destroy".equals(op)) { + runInstallAbandon(); + return; + } + + if ("set-installer".equals(op)) { + runSetInstaller(); + return; + } + if ("uninstall".equals(op)) { runUninstall(); return; @@ -147,13 +201,13 @@ public final class Pm { return; } - if ("block".equals(op)) { - runSetBlockedSetting(true); + if ("hide".equals(op)) { + runSetHiddenSetting(true); return; } - if ("unblock".equals(op)) { - runSetBlockedSetting(false); + if ("unhide".equals(op)) { + runSetHiddenSetting(false); return; } @@ -202,6 +256,11 @@ public final class Pm { return; } + if ("force-dex-opt".equals(op)) { + runForceDexOpt(); + return; + } + try { if (args.length == 1) { if (args[0].equalsIgnoreCase("-l")) { @@ -698,14 +757,23 @@ public final class Pm { ActivityManager.dumpPackageStateStatic(FileDescriptor.out, pkg); } - class PackageInstallObserver extends IPackageInstallObserver.Stub { + class LocalPackageInstallObserver extends PackageInstallObserver { boolean finished; int result; + String extraPermission; + String extraPackage; - public void packageInstalled(String name, int status) { - synchronized( this) { + @Override + public void onPackageInstalled(String name, int status, String msg, Bundle extras) { + synchronized (this) { finished = true; result = status; + if (status == PackageManager.INSTALL_FAILED_DUPLICATE_PERMISSION) { + extraPermission = extras.getString( + PackageManager.EXTRA_FAILURE_EXISTING_PERMISSION); + extraPackage = extras.getString( + PackageManager.EXTRA_FAILURE_EXISTING_PACKAGE); + } notifyAll(); } } @@ -715,7 +783,8 @@ public final class Pm { * Converts a failure code into a string by using reflection to find a matching constant * in PackageManager. */ - private String installFailureToString(int result) { + private String installFailureToString(LocalPackageInstallObserver obs) { + final int result = obs.result; Field[] fields = PackageManager.class.getFields(); for (Field f: fields) { if (f.getType() == int.class) { @@ -730,7 +799,16 @@ public final class Pm { // get the int value and compare it to result. try { if (result == f.getInt(null)) { - return fieldName; + StringBuilder sb = new StringBuilder(64); + sb.append(fieldName); + if (obs.extraPermission != null) { + sb.append(" perm="); + sb.append(obs.extraPermission); + } + if (obs.extraPackage != null) { + sb.append(" pkg=" + obs.extraPackage); + } + return sb.toString(); } } catch (IllegalAccessException e) { // this shouldn't happen since we only look for public static fields. @@ -787,18 +865,12 @@ public final class Pm { } private void runInstall() { - int installFlags = PackageManager.INSTALL_ALL_USERS; + int installFlags = 0; + int userId = UserHandle.USER_ALL; String installerPackageName = null; String opt; - String algo = null; - byte[] iv = null; - byte[] key = null; - - String macAlgo = null; - byte[] macKey = null; - byte[] tag = null; String originatingUriString = null; String referrer = null; String abi = null; @@ -824,42 +896,6 @@ public final class Pm { installFlags |= PackageManager.INSTALL_INTERNAL; } else if (opt.equals("-d")) { installFlags |= PackageManager.INSTALL_ALLOW_DOWNGRADE; - } else if (opt.equals("--algo")) { - algo = nextOptionData(); - if (algo == null) { - System.err.println("Error: must supply argument for --algo"); - return; - } - } else if (opt.equals("--iv")) { - iv = hexToBytes(nextOptionData()); - if (iv == null) { - System.err.println("Error: must supply argument for --iv"); - return; - } - } else if (opt.equals("--key")) { - key = hexToBytes(nextOptionData()); - if (key == null) { - System.err.println("Error: must supply argument for --key"); - return; - } - } else if (opt.equals("--macalgo")) { - macAlgo = nextOptionData(); - if (macAlgo == null) { - System.err.println("Error: must supply argument for --macalgo"); - return; - } - } else if (opt.equals("--mackey")) { - macKey = hexToBytes(nextOptionData()); - if (macKey == null) { - System.err.println("Error: must supply argument for --mackey"); - return; - } - } else if (opt.equals("--tag")) { - tag = hexToBytes(nextOptionData()); - if (tag == null) { - System.err.println("Error: must supply argument for --tag"); - return; - } } else if (opt.equals("--originating-uri")) { originatingUriString = nextOptionData(); if (originatingUriString == null) { @@ -873,70 +909,20 @@ public final class Pm { return; } } else if (opt.equals("--abi")) { - abi = nextOptionData(); - if (abi == null) { - System.err.println("Error: must supply argument for --abi"); - return; - } + abi = checkAbiArgument(nextOptionData()); + } else if (opt.equals("--user")) { + userId = Integer.parseInt(nextOptionData()); } else { System.err.println("Error: Unknown option: " + opt); return; } } - if (abi != null) { - final String[] supportedAbis = Build.SUPPORTED_ABIS; - boolean matched = false; - for (String supportedAbi : supportedAbis) { - if (supportedAbi.equals(abi)) { - matched = true; - break; - } - } - - if (!matched) { - System.err.println("Error: abi " + abi + " not supported on this device."); - return; - } - } - - final ContainerEncryptionParams encryptionParams; - if (algo != null || iv != null || key != null || macAlgo != null || macKey != null - || tag != null) { - if (algo == null || iv == null || key == null) { - System.err.println("Error: all of --algo, --iv, and --key must be specified"); - return; - } - - if (macAlgo != null || macKey != null || tag != null) { - if (macAlgo == null || macKey == null || tag == null) { - System.err.println("Error: all of --macalgo, --mackey, and --tag must " - + "be specified"); - return; - } - } - - try { - final SecretKey encKey = new SecretKeySpec(key, "RAW"); - - final SecretKey macSecretKey; - if (macKey == null || macKey.length == 0) { - macSecretKey = null; - } else { - macSecretKey = new SecretKeySpec(macKey, "RAW"); - } - - encryptionParams = new ContainerEncryptionParams(algo, new IvParameterSpec(iv), - encKey, macAlgo, null, macSecretKey, tag, -1, -1, -1); - } catch (InvalidAlgorithmParameterException e) { - e.printStackTrace(); - return; - } - } else { - encryptionParams = null; + if (userId == UserHandle.USER_ALL) { + userId = UserHandle.USER_OWNER; + installFlags |= PackageManager.INSTALL_ALL_USERS; } - final Uri apkURI; final Uri verificationURI; final Uri originatingURI; final Uri referrerURI; @@ -956,9 +942,7 @@ public final class Pm { // Populate apkURI, must be present final String apkFilePath = nextArg(); System.err.println("\tpkg: " + apkFilePath); - if (apkFilePath != null) { - apkURI = Uri.fromFile(new File(apkFilePath)); - } else { + if (apkFilePath == null) { System.err.println("Error: no package specified"); return; } @@ -972,14 +956,13 @@ public final class Pm { verificationURI = null; } - PackageInstallObserver obs = new PackageInstallObserver(); + LocalPackageInstallObserver obs = new LocalPackageInstallObserver(); try { VerificationParams verificationParams = new VerificationParams(verificationURI, originatingURI, referrerURI, VerificationParams.NO_UID, null); - mPm.installPackageWithVerificationEncryptionAndAbiOverride(apkURI, obs, - installFlags, installerPackageName, verificationParams, - encryptionParams, abi); + mPm.installPackageAsUser(apkFilePath, obs.getBinder(), installFlags, + installerPackageName, verificationParams, abi, userId); synchronized (obs) { while (!obs.finished) { @@ -992,7 +975,7 @@ public final class Pm { System.out.println("Success"); } else { System.err.println("Failure [" - + installFailureToString(obs.result) + + installFailureToString(obs) + "]"); } } @@ -1002,39 +985,200 @@ public final class Pm { } } - /** - * Convert a string containing hex-encoded bytes to a byte array. - * - * @param input String containing hex-encoded bytes - * @return input as an array of bytes - */ - private byte[] hexToBytes(String input) { - if (input == null) { - return null; + private void runInstallCreate() throws RemoteException { + int userId = UserHandle.USER_ALL; + String installerPackageName = null; + + final SessionParams params = new SessionParams(SessionParams.MODE_FULL_INSTALL); + + String opt; + while ((opt = nextOption()) != null) { + if (opt.equals("-l")) { + params.installFlags |= PackageManager.INSTALL_FORWARD_LOCK; + } else if (opt.equals("-r")) { + params.installFlags |= PackageManager.INSTALL_REPLACE_EXISTING; + } else if (opt.equals("-i")) { + installerPackageName = nextArg(); + if (installerPackageName == null) { + throw new IllegalArgumentException("Missing installer package"); + } + } else if (opt.equals("-t")) { + params.installFlags |= PackageManager.INSTALL_ALLOW_TEST; + } else if (opt.equals("-s")) { + params.installFlags |= PackageManager.INSTALL_EXTERNAL; + } else if (opt.equals("-f")) { + params.installFlags |= PackageManager.INSTALL_INTERNAL; + } else if (opt.equals("-d")) { + params.installFlags |= PackageManager.INSTALL_ALLOW_DOWNGRADE; + } else if (opt.equals("--originating-uri")) { + params.originatingUri = Uri.parse(nextOptionData()); + } else if (opt.equals("--referrer")) { + params.referrerUri = Uri.parse(nextOptionData()); + } else if (opt.equals("-p")) { + params.mode = SessionParams.MODE_INHERIT_EXISTING; + params.appPackageName = nextOptionData(); + if (params.appPackageName == null) { + throw new IllegalArgumentException("Missing inherit package name"); + } + } else if (opt.equals("-S")) { + params.setSize(Long.parseLong(nextOptionData())); + } else if (opt.equals("--abi")) { + params.abiOverride = checkAbiArgument(nextOptionData()); + } else if (opt.equals("--user")) { + userId = Integer.parseInt(nextOptionData()); + } else { + throw new IllegalArgumentException("Unknown option " + opt); + } } - final int inputLength = input.length(); - if ((inputLength % 2) != 0) { - System.err.print("Invalid length; must be multiple of 2"); - return null; + if (userId == UserHandle.USER_ALL) { + userId = UserHandle.USER_OWNER; + params.installFlags |= PackageManager.INSTALL_ALL_USERS; + } + + final int sessionId = mInstaller.createSession(params, installerPackageName, userId); + + // NOTE: adb depends on parsing this string + System.out.println("Success: created install session [" + sessionId + "]"); + } + + private void runInstallWrite() throws IOException, RemoteException { + long sizeBytes = -1; + + String opt; + while ((opt = nextOption()) != null) { + if (opt.equals("-S")) { + sizeBytes = Long.parseLong(nextOptionData()); + } else { + throw new IllegalArgumentException("Unknown option: " + opt); + } + } + + final int sessionId = Integer.parseInt(nextArg()); + final String splitName = nextArg(); + + String path = nextArg(); + if ("-".equals(path)) { + path = null; + } else if (path != null) { + final File file = new File(path); + if (file.isFile()) { + sizeBytes = file.length(); + } } - final int byteLength = inputLength / 2; - final byte[] output = new byte[byteLength]; + final SessionInfo info = mInstaller.getSessionInfo(sessionId); - int inputIndex = 0; - int byteIndex = 0; - while (inputIndex < inputLength) { - output[byteIndex++] = (byte) Integer.parseInt( - input.substring(inputIndex, inputIndex + 2), 16); - inputIndex += 2; + PackageInstaller.Session session = null; + InputStream in = null; + OutputStream out = null; + try { + session = new PackageInstaller.Session(mInstaller.openSession(sessionId)); + + if (path != null) { + in = new FileInputStream(path); + } else { + in = new SizedInputStream(System.in, sizeBytes); + } + out = session.openWrite(splitName, 0, sizeBytes); + + int total = 0; + byte[] buffer = new byte[65536]; + int c; + while ((c = in.read(buffer)) != -1) { + total += c; + out.write(buffer, 0, c); + + if (info.sizeBytes > 0) { + final float fraction = ((float) c / (float) info.sizeBytes); + session.addProgress(fraction); + } + } + session.fsync(out); + + System.out.println("Success: streamed " + total + " bytes"); + } finally { + IoUtils.closeQuietly(out); + IoUtils.closeQuietly(in); + IoUtils.closeQuietly(session); } + } - return output; + private void runInstallCommit() throws RemoteException { + final int sessionId = Integer.parseInt(nextArg()); + + PackageInstaller.Session session = null; + try { + session = new PackageInstaller.Session(mInstaller.openSession(sessionId)); + + final LocalIntentReceiver receiver = new LocalIntentReceiver(); + session.commit(receiver.getIntentSender()); + + final Intent result = receiver.getResult(); + final int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS, + PackageInstaller.STATUS_FAILURE); + if (status == PackageInstaller.STATUS_SUCCESS) { + System.out.println("Success"); + } else { + Log.e(TAG, "Failure details: " + result.getExtras()); + System.out.println("Failure [" + + result.getStringExtra(PackageInstaller.EXTRA_STATUS_MESSAGE) + "]"); + return; + } + } finally { + IoUtils.closeQuietly(session); + } + } + + private void runInstallAbandon() throws RemoteException { + final int sessionId = Integer.parseInt(nextArg()); + + PackageInstaller.Session session = null; + try { + session = new PackageInstaller.Session(mInstaller.openSession(sessionId)); + session.abandon(); + System.out.println("Success"); + } finally { + IoUtils.closeQuietly(session); + } + } + + private void runSetInstaller() throws RemoteException { + final String targetPackage = nextArg(); + final String installerPackageName = nextArg(); + + if (targetPackage == null || installerPackageName == null) { + throw new IllegalArgumentException( + "must provide both target and installer package names"); + } + + mPm.setInstallerPackageName(targetPackage, installerPackageName); + System.out.println("Success"); } public void runCreateUser() { String name; + int userId = -1; + int flags = 0; + String opt; + while ((opt = nextOption()) != null) { + if ("--profileOf".equals(opt)) { + String optionData = nextOptionData(); + if (optionData == null || !isNumber(optionData)) { + System.err.println("Error: no USER_ID specified"); + showUsage(); + return; + } else { + userId = Integer.parseInt(optionData); + } + } else if ("--managed".equals(opt)) { + flags |= UserInfo.FLAG_MANAGED_PROFILE; + } else { + System.err.println("Error: unknown option " + opt); + showUsage(); + return; + } + } String arg = nextArg(); if (arg == null) { System.err.println("Error: no user name specified."); @@ -1042,7 +1186,12 @@ public final class Pm { } name = arg; try { - final UserInfo info = mUm.createUser(name, 0); + UserInfo info = null; + if (userId < 0) { + info = mUm.createUser(name, flags); + } else { + info = mUm.createProfileForUser(name, flags, userId); + } if (info != null) { System.out.println("Success: created user id " + info.id); } else { @@ -1082,13 +1231,16 @@ public final class Pm { public void runListUsers() { try { + IActivityManager am = ActivityManagerNative.getDefault(); + List<UserInfo> users = mUm.getUsers(false); if (users == null) { System.err.println("Error: couldn't get users"); } else { System.out.println("Users:"); for (int i = 0; i < users.size(); i++) { - System.out.println("\t" + users.get(i).toString()); + String running = am.isUserRunning(users.get(i).id, false) ? " running" : ""; + System.out.println("\t" + users.get(i).toString() + running); } } } catch (RemoteException e) { @@ -1101,26 +1253,32 @@ public final class Pm { System.out.println("Maximum supported users: " + UserManager.getMaxSupportedUsers()); } - class PackageDeleteObserver extends IPackageDeleteObserver.Stub { - boolean finished; - boolean result; - - public void packageDeleted(String packageName, int returnCode) { - synchronized (this) { - finished = true; - result = returnCode == PackageManager.DELETE_SUCCEEDED; - notifyAll(); - } + public void runForceDexOpt() { + final String packageName = nextArg(); + try { + mPm.forceDexOpt(packageName); + } catch (RemoteException e) { + throw e.rethrowAsRuntimeException(); } } - private void runUninstall() { - int unInstallFlags = PackageManager.DELETE_ALL_USERS; + private void runUninstall() throws RemoteException { + int flags = 0; + int userId = UserHandle.USER_ALL; String opt; while ((opt=nextOption()) != null) { if (opt.equals("-k")) { - unInstallFlags |= PackageManager.DELETE_KEEP_DATA; + flags |= PackageManager.DELETE_KEEP_DATA; + } else if (opt.equals("--user")) { + String param = nextArg(); + if (isNumber(param)) { + userId = Integer.parseInt(param); + } else { + showUsage(); + System.err.println("Error: Invalid user: " + param); + return; + } } else { System.err.println("Error: Unknown option: " + opt); return; @@ -1133,32 +1291,46 @@ public final class Pm { showUsage(); return; } - boolean result = deletePackage(pkg, unInstallFlags); - if (result) { - System.out.println("Success"); + + if (userId == UserHandle.USER_ALL) { + userId = UserHandle.USER_OWNER; + flags |= PackageManager.DELETE_ALL_USERS; } else { - System.out.println("Failure"); + PackageInfo info; + try { + info = mPm.getPackageInfo(pkg, 0, userId); + } catch (RemoteException e) { + System.err.println(e.toString()); + System.err.println(PM_NOT_RUNNING_ERR); + return; + } + if (info == null) { + System.err.println("Failure - not installed for " + userId); + return; + } + final boolean isSystem = + (info.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0; + // If we are being asked to delete a system app for just one + // user set flag so it disables rather than reverting to system + // version of the app. + if (isSystem) { + flags |= PackageManager.DELETE_SYSTEM_APP; + } } - } - private boolean deletePackage(String pkg, int unInstallFlags) { - PackageDeleteObserver obs = new PackageDeleteObserver(); - try { - mPm.deletePackageAsUser(pkg, obs, UserHandle.USER_OWNER, unInstallFlags); + final LocalIntentReceiver receiver = new LocalIntentReceiver(); + mInstaller.uninstall(pkg, flags, receiver.getIntentSender(), userId); - synchronized (obs) { - while (!obs.finished) { - try { - obs.wait(); - } catch (InterruptedException e) { - } - } - } - } catch (RemoteException e) { - System.err.println(e.toString()); - System.err.println(PM_NOT_RUNNING_ERR); + final Intent result = receiver.getResult(); + final int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS, + PackageInstaller.STATUS_FAILURE); + if (status == PackageInstaller.STATUS_SUCCESS) { + System.out.println("Success"); + } else { + Log.e(TAG, "Failure details: " + result.getExtras()); + System.out.println("Failure [" + + result.getStringExtra(PackageInstaller.EXTRA_STATUS_MESSAGE) + "]"); } - return obs.result; } static class ClearDataObserver extends IPackageDataObserver.Stub { @@ -1173,7 +1345,6 @@ public final class Pm { notifyAll(); } } - } private void runClear() { @@ -1290,7 +1461,7 @@ public final class Pm { } } - private void runSetBlockedSetting(boolean state) { + private void runSetHiddenSetting(boolean state) { int userId = 0; String option = nextOption(); if (option != null && option.equals("--user")) { @@ -1311,9 +1482,9 @@ public final class Pm { return; } try { - mPm.setApplicationBlockedSettingAsUser(pkg, state, userId); - System.err.println("Package " + pkg + " new blocked state: " - + mPm.getApplicationBlockedSettingAsUser(pkg, userId)); + mPm.setApplicationHiddenSettingAsUser(pkg, state, userId); + System.err.println("Package " + pkg + " new hidden state: " + + mPm.getApplicationHiddenSettingAsUser(pkg, userId)); } catch (RemoteException e) { System.err.println(e.toString()); System.err.println(PM_NOT_RUNNING_ERR); @@ -1456,6 +1627,12 @@ public final class Pm { if (info != null && info.applicationInfo != null) { System.out.print("package:"); System.out.println(info.applicationInfo.sourceDir); + if (!ArrayUtils.isEmpty(info.applicationInfo.splitSourceDirs)) { + for (String splitSourceDir : info.applicationInfo.splitSourceDirs) { + System.out.print("package:"); + System.out.println(splitSourceDir); + } + } } } catch (RemoteException e) { System.err.println(e.toString()); @@ -1481,6 +1658,54 @@ public final class Pm { } } + private static String checkAbiArgument(String abi) { + if (TextUtils.isEmpty(abi)) { + throw new IllegalArgumentException("Missing ABI argument"); + } + + if ("-".equals(abi)) { + return abi; + } + + final String[] supportedAbis = Build.SUPPORTED_ABIS; + for (String supportedAbi : supportedAbis) { + if (supportedAbi.equals(abi)) { + return abi; + } + } + + throw new IllegalArgumentException("ABI " + abi + " not supported on this device"); + } + + private static class LocalIntentReceiver { + private final SynchronousQueue<Intent> mResult = new SynchronousQueue<>(); + + private IIntentSender.Stub mLocalSender = new IIntentSender.Stub() { + @Override + public int send(int code, Intent intent, String resolvedType, + IIntentReceiver finishedReceiver, String requiredPermission) { + try { + mResult.offer(intent, 5, TimeUnit.SECONDS); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + return 0; + } + }; + + public IntentSender getIntentSender() { + return new IntentSender((IIntentSender) mLocalSender); + } + + public Intent getResult() { + try { + return mResult.take(); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + } + private String nextOption() { if (mNextArg >= mArgs.length) { return null; @@ -1537,24 +1762,27 @@ public final class Pm { System.err.println(" pm list users"); System.err.println(" pm path PACKAGE"); System.err.println(" pm dump PACKAGE"); - System.err.println(" pm install [-l] [-r] [-t] [-i INSTALLER_PACKAGE_NAME] [-s] [-f]"); - System.err.println(" [--algo <algorithm name> --key <key-in-hex> --iv <IV-in-hex>]"); - System.err.println(" [--originating-uri <URI>] [--referrer <URI>] PATH"); - System.err.println(" pm uninstall [-k] PACKAGE"); + System.err.println(" pm install [-lrtsfd] [-i PACKAGE] [PATH]"); + System.err.println(" pm install-create [-lrtsfdp] [-i PACKAGE] [-S BYTES]"); + System.err.println(" pm install-write [-S BYTES] SESSION_ID SPLIT_NAME [PATH]"); + System.err.println(" pm install-commit SESSION_ID"); + System.err.println(" pm install-abandon SESSION_ID"); + System.err.println(" pm uninstall [-k] [--user USER_ID] PACKAGE"); + System.err.println(" pm set-installer PACKAGE INSTALLER"); System.err.println(" pm clear [--user USER_ID] PACKAGE"); System.err.println(" pm enable [--user USER_ID] PACKAGE_OR_COMPONENT"); System.err.println(" pm disable [--user USER_ID] PACKAGE_OR_COMPONENT"); System.err.println(" pm disable-user [--user USER_ID] PACKAGE_OR_COMPONENT"); System.err.println(" pm disable-until-used [--user USER_ID] PACKAGE_OR_COMPONENT"); - System.err.println(" pm block [--user USER_ID] PACKAGE_OR_COMPONENT"); - System.err.println(" pm unblock [--user USER_ID] PACKAGE_OR_COMPONENT"); + System.err.println(" pm hide [--user USER_ID] PACKAGE_OR_COMPONENT"); + System.err.println(" pm unhide [--user USER_ID] PACKAGE_OR_COMPONENT"); System.err.println(" pm grant PACKAGE PERMISSION"); System.err.println(" pm revoke PACKAGE PERMISSION"); System.err.println(" pm set-install-location [0/auto] [1/internal] [2/external]"); System.err.println(" pm get-install-location"); System.err.println(" pm set-permission-enforced PERMISSION [true|false]"); System.err.println(" pm trim-caches DESIRED_FREE_SPACE"); - System.err.println(" pm create-user USER_NAME"); + System.err.println(" pm create-user [--profileOf USER_ID] [--managed] USER_NAME"); System.err.println(" pm remove-user USER_ID"); System.err.println(" pm get-max-users"); System.err.println(""); @@ -1589,16 +1817,28 @@ public final class Pm { System.err.println(""); System.err.println("pm path: print the path to the .apk of the given PACKAGE."); System.err.println(""); - System.err.println("pm dump: print system state associated w ith the given PACKAGE."); + System.err.println("pm dump: print system state associated with the given PACKAGE."); + System.err.println(""); + System.err.println("pm install: install a single legacy package"); + System.err.println("pm install-create: create an install session"); + System.err.println(" -l: forward lock application"); + System.err.println(" -r: replace existing application"); + System.err.println(" -t: allow test packages"); + System.err.println(" -i: specify the installer package name"); + System.err.println(" -s: install application on sdcard"); + System.err.println(" -f: install application on internal flash"); + System.err.println(" -d: allow version code downgrade"); + System.err.println(" -p: partial application install"); + System.err.println(" -S: size in bytes of entire session"); System.err.println(""); - System.err.println("pm install: installs a package to the system. Options:"); - System.err.println(" -l: install the package with FORWARD_LOCK."); - System.err.println(" -r: reinstall an exisiting app, keeping its data."); - System.err.println(" -t: allow test .apks to be installed."); - System.err.println(" -i: specify the installer package name."); - System.err.println(" -s: install package on sdcard."); - System.err.println(" -f: install package on internal flash."); - System.err.println(" -d: allow version code downgrade."); + System.err.println("pm install-write: write a package into existing session; path may"); + System.err.println(" be '-' to read from stdin"); + System.err.println(" -S: size in bytes of package, required for stdin"); + System.err.println(""); + System.err.println("pm install-commit: perform install of fully staged session"); + System.err.println("pm install-abandon: abandon session"); + System.err.println(""); + System.err.println("pm set-installer: set installer package name"); System.err.println(""); System.err.println("pm uninstall: removes a package from the system. Options:"); System.err.println(" -k: keep the data and cache directories around after package removal."); @@ -1632,5 +1872,6 @@ public final class Pm { System.err.println(""); System.err.println("pm remove-user: remove the user with the given USER_IDENTIFIER,"); System.err.println(" deleting all data associated with that user"); + System.err.println(""); } } diff --git a/cmds/screencap/Android.mk b/cmds/screencap/Android.mk index ca8008b..5c11b75 100644 --- a/cmds/screencap/Android.mk +++ b/cmds/screencap/Android.mk @@ -16,11 +16,4 @@ LOCAL_MODULE:= screencap LOCAL_MODULE_TAGS := optional -LOCAL_C_INCLUDES += \ - external/skia/include/core \ - external/skia/include/effects \ - external/skia/include/images \ - external/skia/src/ports \ - external/skia/include/utils - include $(BUILD_EXECUTABLE) diff --git a/cmds/screencap/screencap.cpp b/cmds/screencap/screencap.cpp index 2b365d8..1ddbecb 100644 --- a/cmds/screencap/screencap.cpp +++ b/cmds/screencap/screencap.cpp @@ -54,13 +54,13 @@ static void usage(const char* pname) ); } -static SkBitmap::Config flinger2skia(PixelFormat f) +static SkColorType flinger2skia(PixelFormat f) { switch (f) { case PIXEL_FORMAT_RGB_565: - return SkBitmap::kRGB_565_Config; + return kRGB_565_SkColorType; default: - return SkBitmap::kARGB_8888_Config; + return kN32_SkColorType; } } @@ -159,7 +159,7 @@ int main(int argc, char** argv) ScreenshotClient screenshot; sp<IBinder> display = SurfaceComposerClient::getBuiltInDisplay(displayId); - if (display != NULL && screenshot.update(display) == NO_ERROR) { + if (display != NULL && screenshot.update(display, Rect(), false) == NO_ERROR) { base = screenshot.getPixels(); w = screenshot.getWidth(); h = screenshot.getHeight(); @@ -192,9 +192,10 @@ int main(int argc, char** argv) if (base) { if (png) { + const SkImageInfo info = SkImageInfo::Make(w, h, flinger2skia(f), + kPremul_SkAlphaType); SkBitmap b; - b.setConfig(flinger2skia(f), w, h, s*bytesPerPixel(f)); - b.setPixels((void*)base); + b.installPixels(info, const_cast<void*>(base), s*bytesPerPixel(f)); SkDynamicMemoryWStream stream; SkImageEncoder::EncodeStream(&stream, b, SkImageEncoder::kPNG_Type, SkImageEncoder::kDefaultQuality); diff --git a/cmds/settings/src/com/android/commands/settings/SettingsCmd.java b/cmds/settings/src/com/android/commands/settings/SettingsCmd.java index dce0a75..e6847a9 100644 --- a/cmds/settings/src/com/android/commands/settings/SettingsCmd.java +++ b/cmds/settings/src/com/android/commands/settings/SettingsCmd.java @@ -20,6 +20,7 @@ import android.app.ActivityManagerNative; import android.app.IActivityManager; import android.app.IActivityManager.ContentProviderHolder; import android.content.IContentProvider; +import android.net.Uri; import android.os.Binder; import android.os.Bundle; import android.os.IBinder; @@ -33,7 +34,8 @@ public final class SettingsCmd { enum CommandVerb { UNSPECIFIED, GET, - PUT + PUT, + DELETE } static String[] mArgs; @@ -74,6 +76,8 @@ public final class SettingsCmd { mVerb = CommandVerb.GET; } else if ("put".equalsIgnoreCase(arg)) { mVerb = CommandVerb.PUT; + } else if ("delete".equalsIgnoreCase(arg)) { + mVerb = CommandVerb.DELETE; } else { // invalid System.err.println("Invalid command: " + arg); @@ -87,7 +91,7 @@ public final class SettingsCmd { break; // invalid } mTable = arg.toLowerCase(); - } else if (mVerb == CommandVerb.GET) { + } else if (mVerb == CommandVerb.GET || mVerb == CommandVerb.DELETE) { mKey = arg; if (mNextArg >= mArgs.length) { valid = true; @@ -136,6 +140,10 @@ public final class SettingsCmd { case PUT: putForUser(provider, mUser, mTable, mKey, mValue); break; + case DELETE: + System.out.println("Deleted " + + deleteForUser(provider, mUser, mTable, mKey) + " rows"); + break; default: System.err.println("Unspecified command"); break; @@ -211,9 +219,31 @@ public final class SettingsCmd { } } + int deleteForUser(IContentProvider provider, int userHandle, + final String table, final String key) { + Uri targetUri; + if ("system".equals(table)) targetUri = Settings.System.getUriFor(key); + else if ("secure".equals(table)) targetUri = Settings.Secure.getUriFor(key); + else if ("global".equals(table)) targetUri = Settings.Global.getUriFor(key); + else { + System.err.println("Invalid table; no delete performed"); + throw new IllegalArgumentException("Invalid table " + table); + } + + int num = 0; + try { + num = provider.delete(null, targetUri, null, null); + } catch (RemoteException e) { + System.err.println("Can't clear key " + key + " in " + table + " for user " + + userHandle); + } + return num; + } + private static void printUsage() { System.err.println("usage: settings [--user NUM] get namespace key"); System.err.println(" settings [--user NUM] put namespace key value"); + System.err.println(" settings [--user NUM] delete namespace key"); System.err.println("\n'namespace' is one of {system, secure, global}, case-insensitive"); System.err.println("If '--user NUM' is not given, the operations are performed on the owner user."); } diff --git a/cmds/svc/src/com/android/commands/svc/DataCommand.java b/cmds/svc/src/com/android/commands/svc/DataCommand.java index 72cb86d..406e33b 100644 --- a/cmds/svc/src/com/android/commands/svc/DataCommand.java +++ b/cmds/svc/src/com/android/commands/svc/DataCommand.java @@ -36,9 +36,7 @@ public class DataCommand extends Svc.Command { return shortHelp() + "\n" + "\n" + "usage: svc data [enable|disable]\n" - + " Turn mobile data on or off.\n\n" - + " svc data prefer\n" - + " Set mobile as the preferred data network\n"; + + " Turn mobile data on or off.\n\n"; } public void run(String[] args) { @@ -51,15 +49,6 @@ public class DataCommand extends Svc.Command { } else if ("disable".equals(args[1])) { flag = false; validCommand = true; - } else if ("prefer".equals(args[1])) { - IConnectivityManager connMgr = - IConnectivityManager.Stub.asInterface(ServiceManager.getService(Context.CONNECTIVITY_SERVICE)); - try { - connMgr.setNetworkPreference(ConnectivityManager.TYPE_MOBILE); - } catch (RemoteException e) { - System.err.println("Failed to set preferred network: " + e); - } - return; } if (validCommand) { ITelephony phoneMgr @@ -78,4 +67,4 @@ public class DataCommand extends Svc.Command { } System.err.println(longHelp()); } -}
\ No newline at end of file +} diff --git a/cmds/svc/src/com/android/commands/svc/WifiCommand.java b/cmds/svc/src/com/android/commands/svc/WifiCommand.java index d29e8b2..39f0e35 100644 --- a/cmds/svc/src/com/android/commands/svc/WifiCommand.java +++ b/cmds/svc/src/com/android/commands/svc/WifiCommand.java @@ -36,9 +36,7 @@ public class WifiCommand extends Svc.Command { return shortHelp() + "\n" + "\n" + "usage: svc wifi [enable|disable]\n" - + " Turn Wi-Fi on or off.\n\n" - + " svc wifi prefer\n" - + " Set Wi-Fi as the preferred data network\n"; + + " Turn Wi-Fi on or off.\n\n"; } public void run(String[] args) { @@ -51,15 +49,6 @@ public class WifiCommand extends Svc.Command { } else if ("disable".equals(args[1])) { flag = false; validCommand = true; - } else if ("prefer".equals(args[1])) { - IConnectivityManager connMgr = - IConnectivityManager.Stub.asInterface(ServiceManager.getService(Context.CONNECTIVITY_SERVICE)); - try { - connMgr.setNetworkPreference(ConnectivityManager.TYPE_WIFI); - } catch (RemoteException e) { - System.err.println("Failed to set preferred network: " + e); - } - return; } if (validCommand) { IWifiManager wifiMgr @@ -75,4 +64,4 @@ public class WifiCommand extends Svc.Command { } System.err.println(longHelp()); } -}
\ No newline at end of file +} |