summaryrefslogtreecommitdiff
path: root/cmds
diff options
context:
space:
mode:
Diffstat (limited to 'cmds')
-rw-r--r--cmds/am/Android.mk16
-rwxr-xr-xcmds/am/am14
-rw-r--r--cmds/am/proto/instrumentation_data.proto66
-rw-r--r--cmds/am/src/com/android/commands/am/Am.java2527
-rw-r--r--cmds/am/src/com/android/commands/am/Instrument.java435
-rw-r--r--cmds/app_process/Android.mk1
-rw-r--r--cmds/app_process/app_main.cpp4
-rwxr-xr-xcmds/appops/appops3
-rw-r--r--cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java145
-rw-r--r--cmds/bootanimation/Android.mk3
-rw-r--r--cmds/bootanimation/BootAnimation.cpp100
-rw-r--r--cmds/bootanimation/BootAnimation.h17
-rw-r--r--cmds/bootanimation/bootanim.rc4
-rw-r--r--cmds/bootanimation/bootanimation_main.cpp29
-rw-r--r--cmds/bu/src/com/android/commands/bu/Backup.java36
-rw-r--r--cmds/content/src/com/android/commands/content/Content.java157
-rw-r--r--cmds/dpm/src/com/android/commands/dpm/Dpm.java4
-rw-r--r--cmds/idmap/Android.mk2
-rw-r--r--cmds/idmap/create.cpp6
-rw-r--r--cmds/idmap/idmap.cpp35
-rw-r--r--cmds/idmap/idmap.h2
-rw-r--r--cmds/idmap/inspect.cpp1
-rw-r--r--cmds/idmap/scan.cpp53
-rw-r--r--cmds/incident/Android.mk48
-rw-r--r--cmds/incident/incident_sections.h29
-rw-r--r--cmds/incident/main.cpp236
-rw-r--r--cmds/incidentd/Android.mk56
-rw-r--r--cmds/incidentd/incidentd.rc16
-rw-r--r--cmds/incidentd/src/FdBuffer.cpp129
-rw-r--r--cmds/incidentd/src/FdBuffer.h86
-rw-r--r--cmds/incidentd/src/IncidentService.cpp244
-rw-r--r--cmds/incidentd/src/IncidentService.h112
-rw-r--r--cmds/incidentd/src/Reporter.cpp352
-rw-r--r--cmds/incidentd/src/Reporter.h101
-rw-r--r--cmds/incidentd/src/Section.cpp292
-rw-r--r--cmds/incidentd/src/Section.h106
-rw-r--r--cmds/incidentd/src/main.cpp64
-rw-r--r--cmds/incidentd/src/protobuf.cpp41
-rw-r--r--cmds/incidentd/src/protobuf.h40
-rw-r--r--cmds/incidentd/src/report_directory.cpp173
-rw-r--r--cmds/incidentd/src/report_directory.h29
-rw-r--r--cmds/incidentd/src/section_list.cpp29
-rw-r--r--cmds/incidentd/src/section_list.h28
-rw-r--r--cmds/input/src/com/android/commands/input/Input.java41
-rw-r--r--cmds/locksettings/Android.mk30
-rwxr-xr-xcmds/locksettings/locksettings5
-rw-r--r--cmds/locksettings/src/com/android/commands/locksettings/LockSettingsCmd.java65
-rw-r--r--cmds/media/src/com/android/commands/media/Media.java14
-rwxr-xr-xcmds/media/src/com/android/commands/media/VolumeCtrl.java185
-rw-r--r--cmds/pm/src/com/android/commands/pm/Pm.java131
-rw-r--r--cmds/screencap/screencap.cpp30
-rw-r--r--cmds/settings/Android.mk5
-rwxr-xr-xcmds/settings/settings7
-rw-r--r--cmds/settings/src/com/android/commands/settings/SettingsCmd.java320
-rw-r--r--cmds/sm/src/com/android/commands/sm/Sm.java24
-rw-r--r--cmds/svc/src/com/android/commands/svc/PowerCommand.java15
-rw-r--r--cmds/uiautomator/library/Android.mk2
-rw-r--r--cmds/uiautomator/library/testrunner-src/com/android/uiautomator/core/ShellUiAutomatorBridge.java19
-rw-r--r--cmds/uiautomator/library/testrunner-src/com/android/uiautomator/core/UiAutomationShellWrapper.java3
-rw-r--r--cmds/vr/Android.mk15
-rw-r--r--cmds/vr/MODULE_LICENSE_APACHE20
-rw-r--r--cmds/vr/NOTICE190
-rw-r--r--cmds/vr/src/com/android/commands/vr/Vr.java106
-rwxr-xr-xcmds/vr/vr6
-rw-r--r--cmds/wm/src/com/android/commands/wm/Wm.java29
65 files changed, 4137 insertions, 2976 deletions
diff --git a/cmds/am/Android.mk b/cmds/am/Android.mk
index f8350dce7f50..5586dd4e5b18 100644
--- a/cmds/am/Android.mk
+++ b/cmds/am/Android.mk
@@ -3,8 +3,11 @@
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
-LOCAL_SRC_FILES := $(call all-subdir-java-files)
+LOCAL_SRC_FILES := \
+ $(call all-java-files-under, src) \
+ $(call all-proto-files-under, proto)
LOCAL_MODULE := am
+LOCAL_PROTOC_OPTIMIZE_TYPE := stream
include $(BUILD_JAVA_LIBRARY)
include $(CLEAR_VARS)
@@ -13,3 +16,14 @@ LOCAL_SRC_FILES := am
LOCAL_MODULE_CLASS := EXECUTABLES
LOCAL_MODULE_TAGS := optional
include $(BUILD_PREBUILT)
+
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := \
+ $(call all-proto-files-under, proto)
+LOCAL_MODULE := libinstrumentation
+LOCAL_PROTOC_OPTIMIZE_TYPE := full
+LOCAL_EXPORT_C_INCLUDE_DIRS := \
+ $(call intermediates-dir-for,STATIC_LIBRARIES,libinstrumentation,HOST,,,)/proto/$(LOCAL_PATH)/proto
+include $(BUILD_HOST_STATIC_LIBRARY)
+
diff --git a/cmds/am/am b/cmds/am/am
index 1d426bc8c8e1..54c2d394be2c 100755
--- a/cmds/am/am
+++ b/cmds/am/am
@@ -1,9 +1,9 @@
#!/system/bin/sh
-#
-# Script to start "am" on the device, which has a very rudimentary
-# shell.
-#
-base=/system
-export CLASSPATH=$base/framework/am.jar
-exec app_process $base/bin com.android.commands.am.Am "$@"
+if [ "$1" != "instrument" ] ; then
+ cmd activity "$@"
+else
+ base=/system
+ export CLASSPATH=$base/framework/am.jar
+ exec app_process $base/bin com.android.commands.am.Am "$@"
+fi
diff --git a/cmds/am/proto/instrumentation_data.proto b/cmds/am/proto/instrumentation_data.proto
new file mode 100644
index 000000000000..12a18a2a035f
--- /dev/null
+++ b/cmds/am/proto/instrumentation_data.proto
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+syntax = "proto2";
+package android.am;
+
+option java_package = "com.android.commands.am";
+
+message ResultsBundleEntry {
+ optional string key = 1;
+
+ optional string value_string = 2;
+ optional sint32 value_int = 3;
+ optional float value_float = 4;
+ optional double value_double = 5;
+ optional sint64 value_long = 6;
+ optional ResultsBundle value_bundle = 7;
+}
+
+message ResultsBundle {
+ repeated ResultsBundleEntry entries = 1;
+}
+
+message TestStatus {
+ optional sint32 result_code = 3;
+ optional ResultsBundle results = 4;
+}
+
+enum SessionStatusCode {
+ /**
+ * The command ran successfully. This does not imply that the tests passed.
+ */
+ SESSION_FINISHED = 0;
+
+ /**
+ * There was an unrecoverable error running the tests.
+ */
+ SESSION_ABORTED = 1;
+}
+
+message SessionStatus {
+ optional SessionStatusCode status_code = 1;
+ optional string error_text = 2;
+ optional sint32 result_code = 3;
+ optional ResultsBundle results = 4;
+}
+
+message Session {
+ repeated TestStatus test_status = 1;
+ optional SessionStatus session_status = 2;
+}
+
+
diff --git a/cmds/am/src/com/android/commands/am/Am.java b/cmds/am/src/com/android/commands/am/Am.java
index 4e1b6db71f30..f003061cec3a 100644
--- a/cmds/am/src/com/android/commands/am/Am.java
+++ b/cmds/am/src/com/android/commands/am/Am.java
@@ -1,20 +1,18 @@
/*
-**
-** Copyright 2007, 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.
-*/
-
+ * Copyright (C) 2007 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.am;
@@ -25,8 +23,6 @@ import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
import android.app.ActivityManager;
import android.app.ActivityManager.StackInfo;
-import android.app.ActivityManagerNative;
-import android.app.ActivityOptions;
import android.app.IActivityContainer;
import android.app.IActivityController;
import android.app.IActivityManager;
@@ -46,7 +42,6 @@ import android.content.Intent;
import android.content.pm.IPackageManager;
import android.content.pm.InstrumentationInfo;
import android.content.pm.ParceledListSlice;
-import android.content.pm.ResolveInfo;
import android.content.pm.UserInfo;
import android.content.res.Configuration;
import android.graphics.Rect;
@@ -55,15 +50,17 @@ import android.os.Build;
import android.os.Bundle;
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
+import android.os.ResultReceiver;
import android.os.SELinux;
import android.os.ServiceManager;
+import android.os.ShellCallback;
import android.os.ShellCommand;
-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.util.Log;
import android.view.IWindowManager;
import com.android.internal.os.BaseCommand;
@@ -72,6 +69,7 @@ import com.android.internal.util.Preconditions;
import java.io.BufferedReader;
import java.io.File;
+import java.io.FileDescriptor;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
@@ -85,34 +83,9 @@ import java.util.List;
public class Am extends BaseCommand {
- private static final String SHELL_PACKAGE_NAME = "com.android.shell";
-
- // Is the object moving in a positive direction?
- private static final boolean MOVING_FORWARD = true;
- // Is the object moving in the horizontal plan?
- private static final boolean MOVING_HORIZONTALLY = true;
- // Is the object current point great then its target point?
- private static final boolean GREATER_THAN_TARGET = true;
- // Amount we reduce the stack size by when testing a task re-size.
- private static final int STACK_BOUNDS_INSET = 10;
-
private IActivityManager mAm;
private IPackageManager mPm;
- private int mStartFlags = 0;
- private boolean mWaitOption = false;
- private boolean mStopOption = false;
-
- private int mRepeat = 0;
- private int mUserId;
- private String mReceiverPermission;
-
- private String mProfileFile;
- private int mSamplingInterval;
- private boolean mAutoStop;
- private boolean mStreaming; // Streaming the profiling output to a file.
- private int mStackId;
-
/**
* Command-line entry point.
*
@@ -124,261 +97,17 @@ public class Am extends BaseCommand {
@Override
public void onShowUsage(PrintStream out) {
- PrintWriter pw = new PrintWriter(out);
- pw.println(
- "usage: am [subcommand] [options]\n" +
- "usage: am start [-D] [-N] [-W] [-P <FILE>] [--start-profiler <FILE>]\n" +
- " [--sampling INTERVAL] [--streaming] [-R COUNT] [-S]\n" +
- " [--track-allocation] [--user <USER_ID> | current] <INTENT>\n" +
- " am startservice [--user <USER_ID> | current] <INTENT>\n" +
- " am stopservice [--user <USER_ID> | current] <INTENT>\n" +
- " am force-stop [--user <USER_ID> | all | current] <PACKAGE>\n" +
- " am kill [--user <USER_ID> | all | current] <PACKAGE>\n" +
- " am kill-all\n" +
- " 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] [--abi <ABI>] <COMPONENT>\n" +
- " am profile start [--user <USER_ID> current] [--sampling INTERVAL]\n"+
- " [--streaming] <PROCESS> <FILE>\n" +
- " am profile stop [--user <USER_ID> current] [<PROCESS>]\n" +
- " am dumpheap [--user <USER_ID> current] [-n] <PROCESS> <FILE>\n" +
- " am set-debug-app [-w] [--persistent] <PACKAGE>\n" +
- " am clear-debug-app\n" +
- " am set-watch-heap <PROCESS> <MEM-LIMIT>\n" +
- " am clear-watch-heap\n" +
- " am bug-report [--progress | --telephony]\n" +
- " am monitor [--gdb <port>]\n" +
- " am hang [--allow-restart]\n" +
- " am restart\n" +
- " am idle-maintenance\n" +
- " am screen-compat [on|off] <PACKAGE>\n" +
- " am package-importance <PACKAGE>\n" +
- " am to-uri [INTENT]\n" +
- " am to-intent-uri [INTENT]\n" +
- " am to-app-uri [INTENT]\n" +
- " am switch-user <USER_ID>\n" +
- " am start-user <USER_ID>\n" +
- " am unlock-user <USER_ID> [TOKEN_HEX]\n" +
- " am stop-user [-w] [-f] <USER_ID>\n" +
- " am stack start <DISPLAY_ID> <INTENT>\n" +
- " am stack movetask <TASK_ID> <STACK_ID> [true|false]\n" +
- " am stack resize <STACK_ID> <LEFT,TOP,RIGHT,BOTTOM>\n" +
- " am stack resize-animated <STACK_ID> <LEFT,TOP,RIGHT,BOTTOM>\n" +
- " am stack resize-docked-stack <LEFT,TOP,RIGHT,BOTTOM> [<TASK_LEFT,TASK_TOP,TASK_RIGHT,TASK_BOTTOM>]\n" +
- " am stack size-docked-stack-test: <STEP_SIZE> <l|t|r|b> [DELAY_MS]\n" +
- " am stack move-top-activity-to-pinned-stack: <STACK_ID> <LEFT,TOP,RIGHT,BOTTOM>\n" +
- " am stack positiontask <TASK_ID> <STACK_ID> <POSITION>\n" +
- " am stack list\n" +
- " am stack info <STACK_ID>\n" +
- " am stack remove <STACK_ID>\n" +
- " am task lock <TASK_ID>\n" +
- " am task lock stop\n" +
- " am task resizeable <TASK_ID> [0 (unresizeable) | 1 (crop_windows) | 2 (resizeable) | 3 (resizeable_and_pipable)]\n" +
- " am task resize <TASK_ID> <LEFT,TOP,RIGHT,BOTTOM>\n" +
- " am task drag-task-test <TASK_ID> <STEP_SIZE> [DELAY_MS] \n" +
- " am task size-task-test <TASK_ID> <STEP_SIZE> [DELAY_MS] \n" +
- " am get-config\n" +
- " am suppress-resize-config-changes <true|false>\n" +
- " am set-inactive [--user <USER_ID>] <PACKAGE> true|false\n" +
- " am get-inactive [--user <USER_ID>] <PACKAGE>\n" +
- " am send-trim-memory [--user <USER_ID>] <PROCESS>\n" +
- " [HIDDEN|RUNNING_MODERATE|BACKGROUND|RUNNING_LOW|MODERATE|RUNNING_CRITICAL|COMPLETE]\n" +
- " am get-current-user\n" +
- "\n" +
- "am start: start an Activity. Options are:\n" +
- " -D: enable debugging\n" +
- " -N: enable native 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" +
- " --streaming: stream the profiling output to the specified file (use\n" +
- " 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" +
- " -S: force stop the target app before starting the activity\n" +
- " --track-allocation: enable tracking of object allocations\n" +
- " --user <USER_ID> | current: Specify which user to run as; if not\n" +
- " specified then run as the current user.\n" +
- " --stack <STACK_ID>: Specify into which stack should the activity be put." +
- "\n" +
- "am startservice: start a Service. Options are:\n" +
- " --user <USER_ID> | current: Specify which user to run as; if not\n" +
- " specified then run as the current user.\n" +
- "\n" +
- "am stopservice: stop a Service. Options are:\n" +
- " --user <USER_ID> | current: Specify which user to run as; if not\n" +
- " specified then run as the current user.\n" +
- "\n" +
- "am force-stop: force stop everything associated with <PACKAGE>.\n" +
- " --user <USER_ID> | all | current: Specify user to force stop;\n" +
- " all users if not specified.\n" +
- "\n" +
- "am kill: Kill all processes associated with <PACKAGE>. Only kills.\n" +
- " processes that are safe to kill -- that is, will not impact the user\n" +
- " experience.\n" +
- " --user <USER_ID> | all | current: Specify user whose processes to kill;\n" +
- " all users if not specified.\n" +
- "\n" +
- "am kill-all: Kill all background processes.\n" +
- "\n" +
- "am broadcast: send a broadcast Intent. Options are:\n" +
- " --user <USER_ID> | all | current: Specify which user to send to; if not\n" +
- " specified then send to all users.\n" +
- " --receiver-permission <PERMISSION>: Require receiver to hold permission.\n" +
- "\n" +
- "am instrument: start an Instrumentation. Typically this target <COMPONENT>\n" +
- " is the form <TEST_PACKAGE>/<RUNNER_CLASS> or only <TEST_PACKAGE> if there \n" +
- " is only one instrumentation. Options are:\n" +
- " -r: print raw results (otherwise decode REPORT_KEY_STREAMRESULT). Use with\n" +
- " [-e perf true] to generate raw output for performance measurements.\n" +
- " -e <NAME> <VALUE>: set argument <NAME> to <VALUE>. For test runners a\n" +
- " common form is [-e <testrunner_flag> <value>[,<value>...]].\n" +
- " -p <FILE>: write profiling data to <FILE>\n" +
- " -w: wait for instrumentation to finish before returning. Required for\n" +
- " test runners.\n" +
- " --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 trace-ipc: Trace IPC transactions.\n" +
- " start: start tracing IPC transactions.\n" +
- " stop: stop tracing IPC transactions and dump the results to file.\n" +
- " --dump-file <FILE>: Specify the file the trace should be dumped to.\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" +
- " --user <USER_ID> | current: When supplying a process name,\n" +
- " specify user of process to profile; uses current user if not specified.\n" +
- " --sampling INTERVAL: use sample profiling with INTERVAL microseconds\n" +
- " between samples\n" +
- " --streaming: stream the profiling output to the specified file\n" +
- "\n" +
- "am dumpheap: dump the heap of a process. The given <PROCESS> argument may\n" +
- " be either a process name or pid. Options are:\n" +
- " -n: dump native heap instead of managed heap\n" +
- " --user <USER_ID> | current: When supplying a process name,\n" +
- " specify user of process to dump; uses current user if not specified.\n" +
- "\n" +
- "am set-debug-app: set application <PACKAGE> to debug. Options are:\n" +
- " -w: wait for debugger when application starts\n" +
- " --persistent: retain this value\n" +
- "\n" +
- "am clear-debug-app: clear the previously set-debug-app.\n" +
- "\n" +
- "am set-watch-heap: start monitoring pss size of <PROCESS>, if it is at or\n" +
- " above <HEAP-LIMIT> then a heap dump is collected for the user to report\n" +
- "\n" +
- "am clear-watch-heap: clear the previously set-watch-heap.\n" +
- "\n" +
- "am bug-report: request bug report generation; will launch a notification\n" +
- " when done to select where it should be delivered. Options are: \n" +
- " --progress: will launch a notification right away to show its progress.\n" +
- " --telephony: will dump only telephony sections.\n" +
- "\n" +
- "am monitor: start monitoring for crashes or ANRs.\n" +
- " --gdb: start gdbserv on the given port at crash/ANR\n" +
- "\n" +
- "am hang: hang the system.\n" +
- " --allow-restart: allow watchdog to perform normal system restart\n" +
- "\n" +
- "am restart: restart the user-space system.\n" +
- "\n" +
- "am idle-maintenance: perform idle maintenance now.\n" +
- "\n" +
- "am screen-compat: control screen compatibility mode of <PACKAGE>.\n" +
- "\n" +
- "am package-importance: print current importance of <PACKAGE>.\n" +
- "\n" +
- "am to-uri: print the given Intent specification as a URI.\n" +
- "\n" +
- "am to-intent-uri: print the given Intent specification as an intent: URI.\n" +
- "\n" +
- "am to-app-uri: print the given Intent specification as an android-app: URI.\n" +
- "\n" +
- "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 start or switch to it.\n" +
- " -w: wait for stop-user to complete.\n" +
- " -f: force stop even if there are related users that cannot be stopped.\n" +
- "\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> size and position to <LEFT,TOP,RIGHT,BOTTOM>.\n" +
- "\n" +
- "am stack resize-docked-stack: change docked stack to <LEFT,TOP,RIGHT,BOTTOM>\n" +
- " and supplying temporary different task bounds indicated by\n" +
- " <TASK_LEFT,TOP,RIGHT,BOTTOM>\n" +
- "\n" +
- "am stack size-docked-stack-test: test command for sizing docked stack by\n" +
- " <STEP_SIZE> increments from the side <l>eft, <t>op, <r>ight, or <b>ottom\n" +
- " applying the optional [DELAY_MS] between each step.\n" +
- "\n" +
- "am stack move-top-activity-to-pinned-stack: moves the top activity from\n" +
- " <STACK_ID> to the pinned stack using <LEFT,TOP,RIGHT,BOTTOM> for the\n" +
- " bounds of the pinned stack.\n" +
- "\n" +
- "am stack positiontask: place <TASK_ID> in <STACK_ID> at <POSITION>" +
- "\n" +
- "am stack list: list all of the activity stacks and their sizes.\n" +
- "\n" +
- "am stack info: display the information about activity stack <STACK_ID>.\n" +
- "\n" +
- "am stack remove: remove stack <STACK_ID>.\n" +
- "\n" +
- "am task lock: bring <TASK_ID> to the front and don't allow other tasks to run.\n" +
- "\n" +
- "am task lock stop: end the current task lock.\n" +
- "\n" +
- "am task resizeable: change resizeable mode of <TASK_ID>.\n" +
- " 0 (unresizeable) | 1 (crop_windows) | 2 (resizeable) | 3 (resizeable_and_pipable)\n" +
- "\n" +
- "am task resize: makes sure <TASK_ID> is in a stack with the specified bounds.\n" +
- " Forces the task to be resizeable and creates a stack if no existing stack\n" +
- " has the specified bounds.\n" +
- "\n" +
- "am task drag-task-test: test command for dragging/moving <TASK_ID> by\n" +
- " <STEP_SIZE> increments around the screen applying the optional [DELAY_MS]\n" +
- " between each step.\n" +
- "\n" +
- "am task size-task-test: test command for sizing <TASK_ID> by <STEP_SIZE>" +
- " increments within the screen applying the optional [DELAY_MS] between\n" +
- " each step.\n" +
- "\n" +
- "am get-config: retrieve the configuration and any recent configurations\n" +
- " of the device.\n" +
- "am suppress-resize-config-changes: suppresses configuration changes due to\n" +
- " user resizing an activity/task.\n" +
- "\n" +
- "am set-inactive: sets the inactive state of an app.\n" +
- "\n" +
- "am get-inactive: returns the inactive state of an app.\n" +
- "\n" +
- "am send-trim-memory: send a memory trim event to a <PROCESS>.\n" +
- "\n" +
- "am get-current-user: returns id of the current foreground user.\n" +
- "\n"
- );
- Intent.printIntentArgsHelp(pw, "");
- pw.flush();
+ try {
+ runAmCmd(new String[] { "help" });
+ } catch (AndroidException e) {
+ e.printStackTrace(System.err);
+ }
}
@Override
public void onRun() throws Exception {
- mAm = ActivityManagerNative.getDefault();
+ mAm = ActivityManager.getService();
if (mAm == null) {
System.err.println(NO_SYSTEM_ERROR_CODE);
throw new AndroidException("Can't connect to activity manager; is the system running?");
@@ -392,82 +121,10 @@ public class Am extends BaseCommand {
String op = nextArgRequired();
- if (op.equals("start")) {
- runStart();
- } else if (op.equals("startservice")) {
- runStartService();
- } else if (op.equals("stopservice")) {
- runStopService();
- } else if (op.equals("force-stop")) {
- runForceStop();
- } else if (op.equals("kill")) {
- runKill();
- } else if (op.equals("kill-all")) {
- runKillAll();
- } else if (op.equals("instrument")) {
+ if (op.equals("instrument")) {
runInstrument();
- } else if (op.equals("trace-ipc")) {
- runTraceIpc();
- } else if (op.equals("broadcast")) {
- sendBroadcast();
- } else if (op.equals("profile")) {
- runProfile();
- } else if (op.equals("dumpheap")) {
- runDumpHeap();
- } else if (op.equals("set-debug-app")) {
- runSetDebugApp();
- } else if (op.equals("clear-debug-app")) {
- runClearDebugApp();
- } else if (op.equals("set-watch-heap")) {
- runSetWatchHeap();
- } else if (op.equals("clear-watch-heap")) {
- runClearWatchHeap();
- } else if (op.equals("bug-report")) {
- runBugReport();
- } else if (op.equals("monitor")) {
- runMonitor();
- } else if (op.equals("hang")) {
- runHang();
- } else if (op.equals("restart")) {
- runRestart();
- } else if (op.equals("idle-maintenance")) {
- runIdleMaintenance();
- } else if (op.equals("screen-compat")) {
- runScreenCompat();
- } else if (op.equals("package-importance")) {
- runPackageImportance();
- } else if (op.equals("to-uri")) {
- runToUri(0);
- } else if (op.equals("to-intent-uri")) {
- runToUri(Intent.URI_INTENT_SCHEME);
- } else if (op.equals("to-app-uri")) {
- runToUri(Intent.URI_ANDROID_APP_SCHEME);
- } else if (op.equals("switch-user")) {
- runSwitchUser();
- } else if (op.equals("start-user")) {
- runStartUserInBackground();
- } else if (op.equals("unlock-user")) {
- runUnlockUser();
- } else if (op.equals("stop-user")) {
- runStopUser();
- } else if (op.equals("stack")) {
- runStack();
- } else if (op.equals("task")) {
- runTask();
- } else if (op.equals("get-config")) {
- runGetConfig();
- } else if (op.equals("suppress-resize-config-changes")) {
- runSuppressResizeConfigChanges();
- } else if (op.equals("set-inactive")) {
- runSetInactive();
- } else if (op.equals("get-inactive")) {
- runGetInactive();
- } else if (op.equals("send-trim-memory")) {
- runSendTrimMemory();
- } else if (op.equals("get-current-user")) {
- runGetCurrentUser();
} else {
- showError("Error: unknown command '" + op + "'");
+ runAmCmd(getRawArgs());
}
}
@@ -483,2113 +140,95 @@ public class Am extends BaseCommand {
return userId;
}
- private Intent makeIntent(int defUser) throws URISyntaxException {
- mStartFlags = 0;
- mWaitOption = false;
- mStopOption = false;
- mRepeat = 0;
- mProfileFile = null;
- mSamplingInterval = 0;
- mAutoStop = false;
- mStreaming = false;
- mUserId = defUser;
- mStackId = INVALID_STACK_ID;
-
- return Intent.parseCommandArgs(mArgs, new Intent.CommandOptionHandler() {
- @Override
- public boolean handleOption(String opt, ShellCommand cmd) {
- if (opt.equals("-D")) {
- mStartFlags |= ActivityManager.START_FLAG_DEBUG;
- } else if (opt.equals("-N")) {
- mStartFlags |= ActivityManager.START_FLAG_NATIVE_DEBUGGING;
- } else if (opt.equals("-W")) {
- mWaitOption = true;
- } else if (opt.equals("-P")) {
- mProfileFile = nextArgRequired();
- mAutoStop = true;
- } else if (opt.equals("--start-profiler")) {
- mProfileFile = nextArgRequired();
- mAutoStop = false;
- } else if (opt.equals("--sampling")) {
- mSamplingInterval = Integer.parseInt(nextArgRequired());
- } else if (opt.equals("--streaming")) {
- mStreaming = true;
- } else if (opt.equals("-R")) {
- mRepeat = Integer.parseInt(nextArgRequired());
- } else if (opt.equals("-S")) {
- mStopOption = true;
- } else if (opt.equals("--track-allocation")) {
- mStartFlags |= ActivityManager.START_FLAG_TRACK_ALLOCATION;
- } else if (opt.equals("--user")) {
- mUserId = parseUserArg(nextArgRequired());
- } else if (opt.equals("--receiver-permission")) {
- mReceiverPermission = nextArgRequired();
- } else if (opt.equals("--stack")) {
- mStackId = Integer.parseInt(nextArgRequired());
- } else {
- return false;
- }
- return true;
- }
- });
- }
-
- private void runStartService() throws Exception {
- Intent intent = makeIntent(UserHandle.USER_CURRENT);
- if (mUserId == UserHandle.USER_ALL) {
- System.err.println("Error: Can't start activity with user 'all'");
- return;
- }
- System.out.println("Starting service: " + intent);
- ComponentName cn = mAm.startService(null, intent, intent.getType(),
- SHELL_PACKAGE_NAME, mUserId);
- if (cn == null) {
- System.err.println("Error: Not found; no service started.");
- } else if (cn.getPackageName().equals("!")) {
- System.err.println("Error: Requires permission " + cn.getClassName());
- } else if (cn.getPackageName().equals("!!")) {
- System.err.println("Error: " + cn.getClassName());
- }
- }
-
- private void runStopService() throws Exception {
- Intent intent = makeIntent(UserHandle.USER_CURRENT);
- if (mUserId == UserHandle.USER_ALL) {
- System.err.println("Error: Can't stop activity with user 'all'");
- return;
- }
- System.out.println("Stopping service: " + intent);
- int result = mAm.stopService(null, intent, intent.getType(), mUserId);
- if (result == 0) {
- System.err.println("Service not stopped: was not running.");
- } else if (result == 1) {
- System.err.println("Service stopped");
- } else if (result == -1) {
- System.err.println("Error stopping service");
- }
- }
-
- private void runStart() throws Exception {
- Intent intent = makeIntent(UserHandle.USER_CURRENT);
-
- if (mUserId == UserHandle.USER_ALL) {
- System.err.println("Error: Can't start service with user 'all'");
- return;
- }
+ static final class MyShellCallback extends ShellCallback {
+ boolean mActive = true;
- String mimeType = intent.getType();
- if (mimeType == null && intent.getData() != null
- && "content".equals(intent.getData().getScheme())) {
- mimeType = mAm.getProviderMimeType(intent.getData(), mUserId);
- }
-
-
- do {
- if (mStopOption) {
- String packageName;
- if (intent.getComponent() != null) {
- packageName = intent.getComponent().getPackageName();
- } else {
- List<ResolveInfo> activities = mPm.queryIntentActivities(intent, mimeType, 0,
- mUserId).getList();
- if (activities == null || activities.size() <= 0) {
- System.err.println("Error: Intent does not match any activities: "
- + intent);
- return;
- } else if (activities.size() > 1) {
- System.err.println("Error: Intent matches multiple activities; can't stop: "
- + intent);
- return;
- }
- packageName = activities.get(0).activityInfo.packageName;
- }
- System.out.println("Stopping: " + packageName);
- mAm.forceStopPackage(packageName, mUserId);
- Thread.sleep(250);
+ @Override public ParcelFileDescriptor onOpenOutputFile(String path, String seLinuxContext) {
+ if (!mActive) {
+ System.err.println("Open attempt after active for: " + path);
+ return null;
}
-
- System.out.println("Starting: " + intent);
- intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-
- ParcelFileDescriptor fd = null;
- ProfilerInfo profilerInfo = null;
-
- if (mProfileFile != null) {
- try {
- fd = openForSystemServer(
- new File(mProfileFile),
- ParcelFileDescriptor.MODE_CREATE |
- ParcelFileDescriptor.MODE_TRUNCATE |
- ParcelFileDescriptor.MODE_WRITE_ONLY);
- } catch (FileNotFoundException e) {
- System.err.println("Error: Unable to open file: " + mProfileFile);
- System.err.println("Consider using a file under /data/local/tmp/");
- return;
- }
- profilerInfo = new ProfilerInfo(mProfileFile, fd, mSamplingInterval, mAutoStop,
- mStreaming);
- }
-
- IActivityManager.WaitResult result = null;
- int res;
- final long startTime = SystemClock.uptimeMillis();
- ActivityOptions options = null;
- if (mStackId != INVALID_STACK_ID) {
- options = ActivityOptions.makeBasic();
- options.setLaunchStackId(mStackId);
- }
- if (mWaitOption) {
- result = mAm.startActivityAndWait(null, null, intent, mimeType,
- null, null, 0, mStartFlags, profilerInfo,
- options != null ? options.toBundle() : null, mUserId);
- res = result.result;
- } else {
- res = mAm.startActivityAsUser(null, null, intent, mimeType,
- null, null, 0, mStartFlags, profilerInfo,
- options != null ? options.toBundle() : null, mUserId);
- }
- final long endTime = SystemClock.uptimeMillis();
- PrintStream out = mWaitOption ? System.out : System.err;
- boolean launched = false;
- switch (res) {
- case ActivityManager.START_SUCCESS:
- launched = true;
- break;
- case ActivityManager.START_SWITCHES_CANCELED:
- launched = true;
- out.println(
- "Warning: Activity not started because the "
- + " current activity is being kept for the user.");
- break;
- case ActivityManager.START_DELIVERED_TO_TOP:
- launched = true;
- out.println(
- "Warning: Activity not started, intent has "
- + "been delivered to currently running "
- + "top-most instance.");
- break;
- case ActivityManager.START_RETURN_INTENT_TO_CALLER:
- launched = true;
- out.println(
- "Warning: Activity not started because intent "
- + "should be handled by the caller");
- break;
- case ActivityManager.START_TASK_TO_FRONT:
- launched = true;
- out.println(
- "Warning: Activity not started, its current "
- + "task has been brought to the front");
- break;
- case ActivityManager.START_INTENT_NOT_RESOLVED:
- out.println(
- "Error: Activity not started, unable to "
- + "resolve " + intent.toString());
- break;
- case ActivityManager.START_CLASS_NOT_FOUND:
- out.println(NO_CLASS_ERROR_CODE);
- out.println("Error: Activity class " +
- intent.getComponent().toShortString()
- + " does not exist.");
- break;
- case ActivityManager.START_FORWARD_AND_REQUEST_CONFLICT:
- out.println(
- "Error: Activity not started, you requested to "
- + "both forward and receive its result");
- break;
- case ActivityManager.START_PERMISSION_DENIED:
- out.println(
- "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;
- case ActivityManager.START_NOT_CURRENT_USER_ACTIVITY:
- out.println(
- "Error: Not allowed to start background user activity"
- + " that shouldn't be displayed for all users.");
- break;
- default:
- out.println(
- "Error: Activity not started, unknown error code " + res);
- break;
+ File file = new File(path);
+ //System.err.println("Opening file: " + file.getAbsolutePath());
+ //Log.i("Am", "Opening file: " + file.getAbsolutePath());
+ final ParcelFileDescriptor fd;
+ try {
+ fd = ParcelFileDescriptor.open(file,
+ ParcelFileDescriptor.MODE_CREATE |
+ ParcelFileDescriptor.MODE_TRUNCATE |
+ ParcelFileDescriptor.MODE_WRITE_ONLY);
+ } catch (FileNotFoundException e) {
+ String msg = "Unable to open file " + path + ": " + e;
+ System.err.println(msg);
+ throw new IllegalArgumentException(msg);
}
- if (mWaitOption && launched) {
- if (result == null) {
- result = new IActivityManager.WaitResult();
- result.who = intent.getComponent();
- }
- System.out.println("Status: " + (result.timeout ? "timeout" : "ok"));
- if (result.who != null) {
- System.out.println("Activity: " + result.who.flattenToShortString());
- }
- if (result.thisTime >= 0) {
- System.out.println("ThisTime: " + result.thisTime);
- }
- if (result.totalTime >= 0) {
- System.out.println("TotalTime: " + result.totalTime);
+ if (seLinuxContext != null) {
+ final String tcon = SELinux.getFileContext(file.getAbsolutePath());
+ if (!SELinux.checkSELinuxAccess(seLinuxContext, tcon, "file", "write")) {
+ try {
+ fd.close();
+ } catch (IOException e) {
+ }
+ String msg = "System server has no access to file context " + tcon;
+ System.err.println(msg + " (from path " + file.getAbsolutePath()
+ + ", context " + seLinuxContext + ")");
+ throw new IllegalArgumentException(msg);
}
- System.out.println("WaitTime: " + (endTime-startTime));
- System.out.println("Complete");
- }
- mRepeat--;
- if (mRepeat > 0) {
- mAm.unhandledBack();
- }
- } while (mRepeat > 0);
- }
-
- private void runForceStop() throws Exception {
- int userId = UserHandle.USER_ALL;
-
- String opt;
- while ((opt=nextOption()) != null) {
- if (opt.equals("--user")) {
- userId = parseUserArg(nextArgRequired());
- } else {
- System.err.println("Error: Unknown option: " + opt);
- return;
}
+ return fd;
}
- mAm.forceStopPackage(nextArgRequired(), userId);
}
- private void runKill() throws Exception {
- int userId = UserHandle.USER_ALL;
-
- String opt;
- while ((opt=nextOption()) != null) {
- if (opt.equals("--user")) {
- userId = parseUserArg(nextArgRequired());
- } else {
- System.err.println("Error: Unknown option: " + opt);
- return;
- }
+ void runAmCmd(String[] args) throws AndroidException {
+ final MyShellCallback cb = new MyShellCallback();
+ try {
+ mAm.asBinder().shellCommand(FileDescriptor.in, FileDescriptor.out, FileDescriptor.err,
+ args, cb, new ResultReceiver(null) { });
+ } catch (RemoteException e) {
+ System.err.println(NO_SYSTEM_ERROR_CODE);
+ throw new AndroidException("Can't call activity manager; is the system running?");
+ } finally {
+ cb.mActive = false;
}
- mAm.killBackgroundProcesses(nextArgRequired(), userId);
}
- private void runKillAll() throws Exception {
- mAm.killAllBackgroundProcesses();
- }
-
- private void sendBroadcast() throws Exception {
- Intent intent = makeIntent(UserHandle.USER_CURRENT);
- IntentReceiver receiver = new IntentReceiver();
- String[] requiredPermissions = mReceiverPermission == null ? null
- : new String[] {mReceiverPermission};
- System.out.println("Broadcasting: " + intent);
- mAm.broadcastIntent(null, intent, null, receiver, 0, null, null, requiredPermissions,
- android.app.AppOpsManager.OP_NONE, null, true, false, mUserId);
- receiver.waitForFinish();
- }
-
- private void runInstrument() throws Exception {
- String profileFile = null;
- boolean wait = false;
- boolean rawMode = false;
- boolean no_window_animation = false;
- int userId = UserHandle.USER_CURRENT;
- Bundle args = new Bundle();
- String argKey = null, argValue = null;
- IWindowManager wm = IWindowManager.Stub.asInterface(ServiceManager.getService("window"));
- String abi = null;
+ public void runInstrument() throws Exception {
+ Instrument instrument = new Instrument(mAm, mPm);
String opt;
while ((opt=nextOption()) != null) {
if (opt.equals("-p")) {
- profileFile = nextArgRequired();
+ instrument.profileFile = nextArgRequired();
} else if (opt.equals("-w")) {
- wait = true;
+ instrument.wait = true;
} else if (opt.equals("-r")) {
- rawMode = true;
+ instrument.rawMode = true;
+ } else if (opt.equals("-m")) {
+ instrument.proto = true;
} else if (opt.equals("-e")) {
- argKey = nextArgRequired();
- argValue = nextArgRequired();
- args.putString(argKey, argValue);
+ final String argKey = nextArgRequired();
+ final String argValue = nextArgRequired();
+ instrument.args.putString(argKey, argValue);
} else if (opt.equals("--no_window_animation")
|| opt.equals("--no-window-animation")) {
- no_window_animation = true;
+ instrument.noWindowAnimation = true;
} else if (opt.equals("--user")) {
- userId = parseUserArg(nextArgRequired());
+ instrument.userId = parseUserArg(nextArgRequired());
} else if (opt.equals("--abi")) {
- abi = nextArgRequired();
+ instrument.abi = nextArgRequired();
} else {
System.err.println("Error: Unknown option: " + opt);
return;
}
}
- if (userId == UserHandle.USER_ALL) {
+ if (instrument.userId == UserHandle.USER_ALL) {
System.err.println("Error: Can't start instrumentation with user 'all'");
return;
}
- String cnArg = nextArgRequired();
-
- ComponentName cn;
- if (cnArg.contains("/")) {
- cn = ComponentName.unflattenFromString(cnArg);
- if (cn == null) throw new IllegalArgumentException("Bad component name: " + cnArg);
- } else {
- List<InstrumentationInfo> infos = mPm.queryInstrumentation(null, 0).getList();
-
- final int numInfos = infos == null ? 0: infos.size();
- List<ComponentName> cns = new ArrayList<>();
- for (int i = 0; i < numInfos; i++) {
- InstrumentationInfo info = infos.get(i);
-
- ComponentName c = new ComponentName(info.packageName, info.name);
- if (cnArg.equals(info.packageName)) {
- cns.add(c);
- }
- }
-
- if (cns.size() == 0) {
- throw new IllegalArgumentException("No instrumentation found for: " + cnArg);
- } else if (cns.size() == 1) {
- cn = cns.get(0);
- } else {
- StringBuilder cnsStr = new StringBuilder();
- final int numCns = cns.size();
- for (int i = 0; i < numCns; i++) {
- cnsStr.append(cns.get(i).flattenToString());
- cnsStr.append(", ");
- }
-
- // Remove last ", "
- cnsStr.setLength(cnsStr.length() - 2);
-
- throw new IllegalArgumentException("Found multiple instrumentations: "
- + cnsStr.toString());
- }
- }
-
- InstrumentationWatcher watcher = null;
- UiAutomationConnection connection = null;
- if (wait) {
- watcher = new InstrumentationWatcher();
- watcher.setRawOutput(rawMode);
- connection = new UiAutomationConnection();
- }
-
- float[] oldAnims = null;
- if (no_window_animation) {
- oldAnims = wm.getAnimationScales();
- wm.setAnimationScale(0, 0.0f);
- wm.setAnimationScale(1, 0.0f);
- }
-
- 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) {
- throw new AndroidException(
- "INSTRUMENTATION_FAILED: Unsupported instruction set " + abi);
- }
- }
-
- if (!mAm.startInstrumentation(cn, profileFile, 0, args, watcher, connection, userId, abi)) {
- throw new AndroidException("INSTRUMENTATION_FAILED: " + cn.flattenToString());
- }
-
- if (watcher != null) {
- if (!watcher.waitForFinish()) {
- System.out.println("INSTRUMENTATION_ABORTED: System has crashed.");
- }
- }
-
- if (oldAnims != null) {
- wm.setAnimationScales(oldAnims);
- }
- }
-
- private void runTraceIpc() throws Exception {
- String op = nextArgRequired();
- if (op.equals("start")) {
- runTraceIpcStart();
- } else if (op.equals("stop")) {
- runTraceIpcStop();
- } else {
- showError("Error: unknown command '" + op + "'");
- return;
- }
- }
-
- private void runTraceIpcStart() throws Exception {
- System.out.println("Starting IPC tracing.");
- mAm.startBinderTracking();
- }
-
- private void runTraceIpcStop() throws Exception {
- String opt;
- String filename = null;
- while ((opt=nextOption()) != null) {
- if (opt.equals("--dump-file")) {
- filename = nextArgRequired();
- } else {
- System.err.println("Error: Unknown option: " + opt);
- return;
- }
- }
- if (filename == null) {
- System.err.println("Error: Specify filename to dump logs to.");
- return;
- }
-
- ParcelFileDescriptor fd = null;
-
- try {
- File file = new File(filename);
- file.delete();
- fd = openForSystemServer(file,
- ParcelFileDescriptor.MODE_CREATE |
- ParcelFileDescriptor.MODE_TRUNCATE |
- ParcelFileDescriptor.MODE_WRITE_ONLY);
- } catch (FileNotFoundException e) {
- System.err.println("Error: Unable to open file: " + filename);
- System.err.println("Consider using a file under /data/local/tmp/");
- return;
- }
-
- ;
- if (!mAm.stopBinderTrackingAndDump(fd)) {
- throw new AndroidException("STOP TRACE FAILED.");
- }
-
- System.out.println("Stopped IPC tracing. Dumping logs to: " + filename);
- }
-
- static void removeWallOption() {
- String props = SystemProperties.get("dalvik.vm.extra-opts");
- if (props != null && props.contains("-Xprofile:wallclock")) {
- props = props.replace("-Xprofile:wallclock", "");
- props = props.trim();
- SystemProperties.set("dalvik.vm.extra-opts", props);
- }
- }
-
- private void runProfile() throws Exception {
- String profileFile = null;
- boolean start = false;
- boolean wall = false;
- int userId = UserHandle.USER_CURRENT;
- int profileType = 0;
- mSamplingInterval = 0;
- mStreaming = false;
-
- String process = null;
-
- String cmd = nextArgRequired();
-
- if ("start".equals(cmd)) {
- start = true;
- String opt;
- while ((opt=nextOption()) != null) {
- if (opt.equals("--user")) {
- userId = parseUserArg(nextArgRequired());
- } else if (opt.equals("--wall")) {
- wall = true;
- } else if (opt.equals("--streaming")) {
- mStreaming = true;
- } else if (opt.equals("--sampling")) {
- mSamplingInterval = Integer.parseInt(nextArgRequired());
- } else {
- System.err.println("Error: Unknown option: " + opt);
- return;
- }
- }
- process = nextArgRequired();
- } else if ("stop".equals(cmd)) {
- String opt;
- while ((opt=nextOption()) != null) {
- if (opt.equals("--user")) {
- userId = parseUserArg(nextArgRequired());
- } else {
- System.err.println("Error: Unknown option: " + opt);
- return;
- }
- }
- process = nextArg();
- } else {
- // Compatibility with old syntax: process is specified first.
- process = cmd;
- cmd = nextArgRequired();
- if ("start".equals(cmd)) {
- start = true;
- } else if (!"stop".equals(cmd)) {
- throw new IllegalArgumentException("Profile command " + process + " not valid");
- }
- }
-
- if (userId == UserHandle.USER_ALL) {
- System.err.println("Error: Can't profile with user 'all'");
- return;
- }
-
- ParcelFileDescriptor fd = null;
- ProfilerInfo profilerInfo = null;
-
- if (start) {
- profileFile = nextArgRequired();
- try {
- fd = openForSystemServer(
- new File(profileFile),
- ParcelFileDescriptor.MODE_CREATE |
- ParcelFileDescriptor.MODE_TRUNCATE |
- ParcelFileDescriptor.MODE_WRITE_ONLY);
- } catch (FileNotFoundException e) {
- System.err.println("Error: Unable to open file: " + profileFile);
- System.err.println("Consider using a file under /data/local/tmp/");
- return;
- }
- profilerInfo = new ProfilerInfo(profileFile, fd, mSamplingInterval, false, mStreaming);
- }
-
- try {
- if (wall) {
- // XXX doesn't work -- this needs to be set before booting.
- String props = SystemProperties.get("dalvik.vm.extra-opts");
- if (props == null || !props.contains("-Xprofile:wallclock")) {
- props = props + " -Xprofile:wallclock";
- //SystemProperties.set("dalvik.vm.extra-opts", props);
- }
- } else if (start) {
- //removeWallOption();
- }
- if (!mAm.profileControl(process, userId, start, profilerInfo, profileType)) {
- wall = false;
- throw new AndroidException("PROFILE FAILED on process " + process);
- }
- } finally {
- if (!wall) {
- //removeWallOption();
- }
- }
- }
-
- private void runDumpHeap() throws Exception {
- boolean managed = true;
- int userId = UserHandle.USER_CURRENT;
-
- String opt;
- while ((opt=nextOption()) != null) {
- if (opt.equals("--user")) {
- userId = parseUserArg(nextArgRequired());
- if (userId == UserHandle.USER_ALL) {
- System.err.println("Error: Can't dump heap with user 'all'");
- return;
- }
- } else if (opt.equals("-n")) {
- managed = false;
- } else {
- System.err.println("Error: Unknown option: " + opt);
- return;
- }
- }
- String process = nextArgRequired();
- String heapFile = nextArgRequired();
- ParcelFileDescriptor fd = null;
-
- try {
- File file = new File(heapFile);
- file.delete();
- fd = openForSystemServer(file,
- ParcelFileDescriptor.MODE_CREATE |
- ParcelFileDescriptor.MODE_TRUNCATE |
- ParcelFileDescriptor.MODE_WRITE_ONLY);
- } catch (FileNotFoundException e) {
- System.err.println("Error: Unable to open file: " + heapFile);
- System.err.println("Consider using a file under /data/local/tmp/");
- return;
- }
-
- if (!mAm.dumpHeap(process, userId, managed, heapFile, fd)) {
- throw new AndroidException("HEAP DUMP FAILED on process " + process);
- }
- }
-
- private void runSetDebugApp() throws Exception {
- boolean wait = false;
- boolean persistent = false;
-
- String opt;
- while ((opt=nextOption()) != null) {
- if (opt.equals("-w")) {
- wait = true;
- } else if (opt.equals("--persistent")) {
- persistent = true;
- } else {
- System.err.println("Error: Unknown option: " + opt);
- return;
- }
- }
-
- String pkg = nextArgRequired();
- mAm.setDebugApp(pkg, wait, persistent);
- }
-
- private void runClearDebugApp() throws Exception {
- mAm.setDebugApp(null, false, true);
- }
-
- private void runSetWatchHeap() throws Exception {
- String proc = nextArgRequired();
- String limit = nextArgRequired();
- mAm.setDumpHeapDebugLimit(proc, 0, Long.parseLong(limit), null);
- }
-
- private void runClearWatchHeap() throws Exception {
- String proc = nextArgRequired();
- mAm.setDumpHeapDebugLimit(proc, 0, -1, null);
- }
-
- private void runBugReport() throws Exception {
- String opt;
- int bugreportType = ActivityManager.BUGREPORT_OPTION_FULL;
- while ((opt=nextOption()) != null) {
- if (opt.equals("--progress")) {
- bugreportType = ActivityManager.BUGREPORT_OPTION_INTERACTIVE;
- } else if (opt.equals("--telephony")) {
- bugreportType = ActivityManager.BUGREPORT_OPTION_TELEPHONY;
- } else {
- System.err.println("Error: Unknown option: " + opt);
- return;
- }
- }
- mAm.requestBugReport(bugreportType);
- System.out.println("Your lovely bug report is being created; please be patient.");
- }
-
- private void runSwitchUser() throws Exception {
- String user = nextArgRequired();
- 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 byte[] argToBytes(String arg) {
- if (arg.equals("!")) {
- return null;
- } else {
- return HexDump.hexStringToByteArray(arg);
- }
- }
-
- private void runUnlockUser() throws Exception {
- int userId = Integer.parseInt(nextArgRequired());
- byte[] token = argToBytes(nextArgRequired());
- byte[] secret = argToBytes(nextArgRequired());
- boolean success = mAm.unlockUser(userId, token, secret, null);
- if (success) {
- System.out.println("Success: user unlocked");
- } else {
- System.err.println("Error: could not unlock user");
- }
- }
-
- private static class StopUserCallback extends IStopUserCallback.Stub {
- private boolean mFinished = false;
-
- public synchronized void waitForFinish() {
- try {
- while (!mFinished) wait();
- } catch (InterruptedException e) {
- throw new IllegalStateException(e);
- }
- }
-
- @Override
- public synchronized void userStopped(int userId) {
- mFinished = true;
- notifyAll();
- }
-
- @Override
- public synchronized void userStopAborted(int userId) {
- mFinished = true;
- notifyAll();
- }
- }
-
- private void runStopUser() throws Exception {
- boolean wait = false;
- boolean force = false;
- String opt;
- while ((opt = nextOption()) != null) {
- if ("-w".equals(opt)) {
- wait = true;
- } else if ("-f".equals(opt)) {
- force = true;
- } else {
- System.err.println("Error: unknown option: " + opt);
- return;
- }
- }
- int user = Integer.parseInt(nextArgRequired());
- StopUserCallback callback = wait ? new StopUserCallback() : null;
-
- int res = mAm.stopUser(user, force, callback);
- if (res != ActivityManager.USER_OP_SUCCESS) {
- String txt = "";
- switch (res) {
- case ActivityManager.USER_OP_IS_CURRENT:
- txt = " (Can't stop current user)";
- break;
- case ActivityManager.USER_OP_UNKNOWN_USER:
- txt = " (Unknown user " + user + ")";
- break;
- case ActivityManager.USER_OP_ERROR_IS_SYSTEM:
- txt = " (System user cannot be stopped)";
- break;
- case ActivityManager.USER_OP_ERROR_RELATED_USERS_CANNOT_STOP:
- txt = " (Can't stop user " + user
- + " - one of its related users can't be stopped)";
- break;
- }
- System.err.println("Switch failed: " + res + txt);
- } else if (callback != null) {
- callback.waitForFinish();
- }
- }
-
- class MyActivityController extends IActivityController.Stub {
- final String mGdbPort;
- final boolean mMonkey;
-
- static final int STATE_NORMAL = 0;
- static final int STATE_CRASHED = 1;
- static final int STATE_EARLY_ANR = 2;
- static final int STATE_ANR = 3;
-
- int mState;
-
- static final int RESULT_DEFAULT = 0;
+ instrument.componentNameArg = nextArgRequired();
- static final int RESULT_CRASH_DIALOG = 0;
- static final int RESULT_CRASH_KILL = 1;
-
- static final int RESULT_EARLY_ANR_CONTINUE = 0;
- static final int RESULT_EARLY_ANR_KILL = 1;
-
- static final int RESULT_ANR_DIALOG = 0;
- static final int RESULT_ANR_KILL = 1;
- static final int RESULT_ANR_WAIT = 1;
-
- int mResult;
-
- Process mGdbProcess;
- Thread mGdbThread;
- boolean mGotGdbPrint;
-
- MyActivityController(String gdbPort, boolean monkey) {
- mGdbPort = gdbPort;
- mMonkey = monkey;
- }
-
- @Override
- public boolean activityResuming(String pkg) {
- synchronized (this) {
- System.out.println("** Activity resuming: " + pkg);
- }
- return true;
- }
-
- @Override
- public boolean activityStarting(Intent intent, String pkg) {
- synchronized (this) {
- System.out.println("** Activity starting: " + pkg);
- }
- return true;
- }
-
- @Override
- public boolean appCrashed(String processName, int pid, String shortMsg, String longMsg,
- long timeMillis, String stackTrace) {
- synchronized (this) {
- System.out.println("** ERROR: PROCESS CRASHED");
- System.out.println("processName: " + processName);
- System.out.println("processPid: " + pid);
- System.out.println("shortMsg: " + shortMsg);
- System.out.println("longMsg: " + longMsg);
- System.out.println("timeMillis: " + timeMillis);
- System.out.println("stack:");
- System.out.print(stackTrace);
- System.out.println("#");
- int result = waitControllerLocked(pid, STATE_CRASHED);
- return result == RESULT_CRASH_KILL ? false : true;
- }
- }
-
- @Override
- public int appEarlyNotResponding(String processName, int pid, String annotation) {
- synchronized (this) {
- System.out.println("** ERROR: EARLY PROCESS NOT RESPONDING");
- System.out.println("processName: " + processName);
- System.out.println("processPid: " + pid);
- System.out.println("annotation: " + annotation);
- int result = waitControllerLocked(pid, STATE_EARLY_ANR);
- if (result == RESULT_EARLY_ANR_KILL) return -1;
- return 0;
- }
- }
-
- @Override
- public int appNotResponding(String processName, int pid, String processStats) {
- synchronized (this) {
- System.out.println("** ERROR: PROCESS NOT RESPONDING");
- System.out.println("processName: " + processName);
- System.out.println("processPid: " + pid);
- System.out.println("processStats:");
- System.out.print(processStats);
- System.out.println("#");
- int result = waitControllerLocked(pid, STATE_ANR);
- if (result == RESULT_ANR_KILL) return -1;
- if (result == RESULT_ANR_WAIT) return 1;
- return 0;
- }
- }
-
- @Override
- public int systemNotResponding(String message) {
- synchronized (this) {
- System.out.println("** ERROR: PROCESS NOT RESPONDING");
- System.out.println("message: " + message);
- System.out.println("#");
- System.out.println("Allowing system to die.");
- return -1;
- }
- }
-
- void killGdbLocked() {
- mGotGdbPrint = false;
- if (mGdbProcess != null) {
- System.out.println("Stopping gdbserver");
- mGdbProcess.destroy();
- mGdbProcess = null;
- }
- if (mGdbThread != null) {
- mGdbThread.interrupt();
- mGdbThread = null;
- }
- }
-
- int waitControllerLocked(int pid, int state) {
- if (mGdbPort != null) {
- killGdbLocked();
-
- try {
- System.out.println("Starting gdbserver on port " + mGdbPort);
- System.out.println("Do the following:");
- System.out.println(" adb forward tcp:" + mGdbPort + " tcp:" + mGdbPort);
- System.out.println(" gdbclient app_process :" + mGdbPort);
-
- mGdbProcess = Runtime.getRuntime().exec(new String[] {
- "gdbserver", ":" + mGdbPort, "--attach", Integer.toString(pid)
- });
- final InputStreamReader converter = new InputStreamReader(
- mGdbProcess.getInputStream());
- mGdbThread = new Thread() {
- @Override
- public void run() {
- BufferedReader in = new BufferedReader(converter);
- String line;
- int count = 0;
- while (true) {
- synchronized (MyActivityController.this) {
- if (mGdbThread == null) {
- return;
- }
- if (count == 2) {
- mGotGdbPrint = true;
- MyActivityController.this.notifyAll();
- }
- }
- try {
- line = in.readLine();
- if (line == null) {
- return;
- }
- System.out.println("GDB: " + line);
- count++;
- } catch (IOException e) {
- return;
- }
- }
- }
- };
- mGdbThread.start();
-
- // Stupid waiting for .5s. Doesn't matter if we end early.
- try {
- this.wait(500);
- } catch (InterruptedException e) {
- }
-
- } catch (IOException e) {
- System.err.println("Failure starting gdbserver: " + e);
- killGdbLocked();
- }
- }
- mState = state;
- System.out.println("");
- printMessageForState();
-
- while (mState != STATE_NORMAL) {
- try {
- wait();
- } catch (InterruptedException e) {
- }
- }
-
- killGdbLocked();
-
- return mResult;
- }
-
- void resumeController(int result) {
- synchronized (this) {
- mState = STATE_NORMAL;
- mResult = result;
- notifyAll();
- }
- }
-
- void printMessageForState() {
- switch (mState) {
- case STATE_NORMAL:
- System.out.println("Monitoring activity manager... available commands:");
- break;
- case STATE_CRASHED:
- System.out.println("Waiting after crash... available commands:");
- System.out.println("(c)ontinue: show crash dialog");
- System.out.println("(k)ill: immediately kill app");
- break;
- case STATE_EARLY_ANR:
- System.out.println("Waiting after early ANR... available commands:");
- System.out.println("(c)ontinue: standard ANR processing");
- System.out.println("(k)ill: immediately kill app");
- break;
- case STATE_ANR:
- System.out.println("Waiting after ANR... available commands:");
- System.out.println("(c)ontinue: show ANR dialog");
- System.out.println("(k)ill: immediately kill app");
- System.out.println("(w)ait: wait some more");
- break;
- }
- System.out.println("(q)uit: finish monitoring");
- }
-
- void run() throws RemoteException {
- try {
- printMessageForState();
-
- mAm.setActivityController(this, mMonkey);
- mState = STATE_NORMAL;
-
- InputStreamReader converter = new InputStreamReader(System.in);
- BufferedReader in = new BufferedReader(converter);
- String line;
-
- while ((line = in.readLine()) != null) {
- boolean addNewline = true;
- if (line.length() <= 0) {
- addNewline = false;
- } else if ("q".equals(line) || "quit".equals(line)) {
- resumeController(RESULT_DEFAULT);
- break;
- } else if (mState == STATE_CRASHED) {
- if ("c".equals(line) || "continue".equals(line)) {
- resumeController(RESULT_CRASH_DIALOG);
- } else if ("k".equals(line) || "kill".equals(line)) {
- resumeController(RESULT_CRASH_KILL);
- } else {
- System.out.println("Invalid command: " + line);
- }
- } else if (mState == STATE_ANR) {
- if ("c".equals(line) || "continue".equals(line)) {
- resumeController(RESULT_ANR_DIALOG);
- } else if ("k".equals(line) || "kill".equals(line)) {
- resumeController(RESULT_ANR_KILL);
- } else if ("w".equals(line) || "wait".equals(line)) {
- resumeController(RESULT_ANR_WAIT);
- } else {
- System.out.println("Invalid command: " + line);
- }
- } else if (mState == STATE_EARLY_ANR) {
- if ("c".equals(line) || "continue".equals(line)) {
- resumeController(RESULT_EARLY_ANR_CONTINUE);
- } else if ("k".equals(line) || "kill".equals(line)) {
- resumeController(RESULT_EARLY_ANR_KILL);
- } else {
- System.out.println("Invalid command: " + line);
- }
- } else {
- System.out.println("Invalid command: " + line);
- }
-
- synchronized (this) {
- if (addNewline) {
- System.out.println("");
- }
- printMessageForState();
- }
- }
-
- } catch (IOException e) {
- e.printStackTrace();
- } finally {
- mAm.setActivityController(null, mMonkey);
- }
- }
- }
-
- private void runMonitor() throws Exception {
- String opt;
- String gdbPort = null;
- boolean monkey = false;
- while ((opt=nextOption()) != null) {
- if (opt.equals("--gdb")) {
- gdbPort = nextArgRequired();
- } else if (opt.equals("-m")) {
- monkey = true;
- } else {
- System.err.println("Error: Unknown option: " + opt);
- return;
- }
- }
-
- MyActivityController controller = new MyActivityController(gdbPort, monkey);
- controller.run();
- }
-
- private void runHang() throws Exception {
- String opt;
- boolean allowRestart = false;
- while ((opt=nextOption()) != null) {
- if (opt.equals("--allow-restart")) {
- allowRestart = true;
- } else {
- System.err.println("Error: Unknown option: " + opt);
- return;
- }
- }
-
- System.out.println("Hanging the system...");
- mAm.hang(new Binder(), allowRestart);
- }
-
- private void runRestart() throws Exception {
- String opt;
- while ((opt=nextOption()) != null) {
- System.err.println("Error: Unknown option: " + opt);
- return;
- }
-
- System.out.println("Restart the system...");
- mAm.restart();
- }
-
- private void runIdleMaintenance() throws Exception {
- String opt;
- while ((opt=nextOption()) != null) {
- System.err.println("Error: Unknown option: " + opt);
- return;
- }
-
- System.out.println("Performing idle maintenance...");
- try {
- mAm.sendIdleJobTrigger();
- } catch (RemoteException e) {
- }
- }
-
- private void runScreenCompat() throws Exception {
- String mode = nextArgRequired();
- boolean enabled;
- if ("on".equals(mode)) {
- enabled = true;
- } else if ("off".equals(mode)) {
- enabled = false;
- } else {
- System.err.println("Error: enabled mode must be 'on' or 'off' at " + mode);
- return;
- }
-
- String packageName = nextArgRequired();
- do {
- try {
- mAm.setPackageScreenCompatMode(packageName, enabled
- ? ActivityManager.COMPAT_MODE_ENABLED
- : ActivityManager.COMPAT_MODE_DISABLED);
- } catch (RemoteException e) {
- }
- packageName = nextArg();
- } while (packageName != null);
- }
-
- private void runPackageImportance() throws Exception {
- String packageName = nextArgRequired();
- try {
- int procState = mAm.getPackageProcessState(packageName, "com.android.shell");
- System.out.println(
- ActivityManager.RunningAppProcessInfo.procStateToImportance(procState));
- } catch (RemoteException e) {
- }
- }
-
- private void runToUri(int flags) throws Exception {
- Intent intent = makeIntent(UserHandle.USER_CURRENT);
- System.out.println(intent.toUri(flags));
- }
-
- private class IntentReceiver extends IIntentReceiver.Stub {
- private boolean mFinished = false;
-
- @Override
- public void performReceive(Intent intent, int resultCode, String data, Bundle extras,
- boolean ordered, boolean sticky, int sendingUser) {
- String line = "Broadcast completed: result=" + resultCode;
- if (data != null) line = line + ", data=\"" + data + "\"";
- if (extras != null) line = line + ", extras: " + extras;
- System.out.println(line);
- synchronized (this) {
- mFinished = true;
- notifyAll();
- }
- }
-
- public synchronized void waitForFinish() {
- try {
- while (!mFinished) wait();
- } catch (InterruptedException e) {
- throw new IllegalStateException(e);
- }
- }
- }
-
- private class InstrumentationWatcher extends IInstrumentationWatcher.Stub {
- private boolean mFinished = false;
- private boolean mRawMode = false;
-
- /**
- * Set or reset "raw mode". In "raw mode", all bundles are dumped. In "pretty mode",
- * if a bundle includes Instrumentation.REPORT_KEY_STREAMRESULT, just print that.
- * @param rawMode true for raw mode, false for pretty mode.
- */
- public void setRawOutput(boolean rawMode) {
- mRawMode = rawMode;
- }
-
- @Override
- public void instrumentationStatus(ComponentName name, int resultCode, Bundle results) {
- synchronized (this) {
- // pretty printer mode?
- String pretty = null;
- if (!mRawMode && results != null) {
- pretty = results.getString(Instrumentation.REPORT_KEY_STREAMRESULT);
- }
- if (pretty != null) {
- System.out.print(pretty);
- } else {
- if (results != null) {
- for (String key : results.keySet()) {
- System.out.println(
- "INSTRUMENTATION_STATUS: " + key + "=" + results.get(key));
- }
- }
- System.out.println("INSTRUMENTATION_STATUS_CODE: " + resultCode);
- }
- notifyAll();
- }
- }
-
- @Override
- public void instrumentationFinished(ComponentName name, int resultCode,
- Bundle results) {
- synchronized (this) {
- // pretty printer mode?
- String pretty = null;
- if (!mRawMode && results != null) {
- pretty = results.getString(Instrumentation.REPORT_KEY_STREAMRESULT);
- }
- if (pretty != null) {
- System.out.println(pretty);
- } else {
- if (results != null) {
- for (String key : results.keySet()) {
- System.out.println(
- "INSTRUMENTATION_RESULT: " + key + "=" + results.get(key));
- }
- }
- System.out.println("INSTRUMENTATION_CODE: " + resultCode);
- }
- mFinished = true;
- notifyAll();
- }
- }
-
- public boolean waitForFinish() {
- synchronized (this) {
- while (!mFinished) {
- try {
- if (!mAm.asBinder().pingBinder()) {
- return false;
- }
- wait(1000);
- } catch (InterruptedException e) {
- throw new IllegalStateException(e);
- }
- }
- }
- return true;
- }
- }
-
- private void runStack() throws Exception {
- String op = nextArgRequired();
- switch (op) {
- case "start":
- runStackStart();
- break;
- case "movetask":
- runStackMoveTask();
- break;
- case "resize":
- runStackResize();
- break;
- case "resize-animated":
- runStackResizeAnimated();
- break;
- case "resize-docked-stack":
- runStackResizeDocked();
- break;
- case "positiontask":
- runStackPositionTask();
- break;
- case "list":
- runStackList();
- break;
- case "info":
- runStackInfo();
- break;
- case "move-top-activity-to-pinned-stack":
- runMoveTopActivityToPinnedStack();
- break;
- case "size-docked-stack-test":
- runStackSizeDockedStackTest();
- break;
- case "remove":
- runStackRemove();
- break;
- default:
- showError("Error: unknown command '" + op + "'");
- break;
- }
- }
-
- private void runStackStart() throws Exception {
- String displayIdStr = nextArgRequired();
- int displayId = Integer.parseInt(displayIdStr);
- Intent intent = makeIntent(UserHandle.USER_CURRENT);
-
- try {
- IActivityContainer container = mAm.createStackOnDisplay(displayId);
- if (container != null) {
- container.startActivity(intent);
- }
- } catch (RemoteException e) {
- }
- }
-
- private void runStackMoveTask() throws Exception {
- String taskIdStr = nextArgRequired();
- int taskId = Integer.parseInt(taskIdStr);
- String stackIdStr = nextArgRequired();
- int stackId = Integer.parseInt(stackIdStr);
- String toTopStr = nextArgRequired();
- final boolean toTop;
- if ("true".equals(toTopStr)) {
- toTop = true;
- } else if ("false".equals(toTopStr)) {
- toTop = false;
- } else {
- System.err.println("Error: bad toTop arg: " + toTopStr);
- return;
- }
-
- try {
- mAm.moveTaskToStack(taskId, stackId, toTop);
- } catch (RemoteException e) {
- }
- }
-
- private void runStackResize() throws Exception {
- String stackIdStr = nextArgRequired();
- int stackId = Integer.parseInt(stackIdStr);
- final Rect bounds = getBounds();
- if (bounds == null) {
- System.err.println("Error: invalid input bounds");
- return;
- }
- resizeStack(stackId, bounds, 0);
- }
-
- private void runStackResizeAnimated() throws Exception {
- String stackIdStr = nextArgRequired();
- int stackId = Integer.parseInt(stackIdStr);
- final Rect bounds;
- if ("null".equals(mArgs.peekNextArg())) {
- bounds = null;
- } else {
- bounds = getBounds();
- if (bounds == null) {
- System.err.println("Error: invalid input bounds");
- return;
- }
- }
- resizeStackUnchecked(stackId, bounds, 0, true);
- }
-
- private void resizeStackUnchecked(int stackId, Rect bounds, int delayMs, boolean animate) {
- try {
- mAm.resizeStack(stackId, bounds, false, false, animate, -1);
- Thread.sleep(delayMs);
- } catch (RemoteException e) {
- showError("Error: resizing stack " + e);
- } catch (InterruptedException e) {
- }
- }
-
- private void runStackResizeDocked() throws Exception {
- final Rect bounds = getBounds();
- final Rect taskBounds = getBounds();
- if (bounds == null || taskBounds == null) {
- System.err.println("Error: invalid input bounds");
- return;
- }
- try {
- mAm.resizeDockedStack(bounds, taskBounds, null, null, null);
- } catch (RemoteException e) {
- showError("Error: resizing docked stack " + e);
- }
- }
-
- private void resizeStack(int stackId, Rect bounds, int delayMs)
- throws Exception {
- if (bounds == null) {
- showError("Error: invalid input bounds");
- return;
- }
- resizeStackUnchecked(stackId, bounds, delayMs, false);
- }
-
- private void runStackPositionTask() throws Exception {
- String taskIdStr = nextArgRequired();
- int taskId = Integer.parseInt(taskIdStr);
- String stackIdStr = nextArgRequired();
- int stackId = Integer.parseInt(stackIdStr);
- String positionStr = nextArgRequired();
- int position = Integer.parseInt(positionStr);
-
- try {
- mAm.positionTaskInStack(taskId, stackId, position);
- } catch (RemoteException e) {
- }
- }
-
- private void runStackList() throws Exception {
- try {
- List<StackInfo> stacks = mAm.getAllStackInfos();
- for (StackInfo info : stacks) {
- System.out.println(info);
- }
- } catch (RemoteException e) {
- }
- }
-
- private void runStackInfo() throws Exception {
- try {
- String stackIdStr = nextArgRequired();
- int stackId = Integer.parseInt(stackIdStr);
- StackInfo info = mAm.getStackInfo(stackId);
- System.out.println(info);
- } catch (RemoteException e) {
- }
- }
-
- private void runStackRemove() throws Exception {
- String stackIdStr = nextArgRequired();
- int stackId = Integer.parseInt(stackIdStr);
- mAm.removeStack(stackId);
- }
-
- private void runMoveTopActivityToPinnedStack() throws Exception {
- int stackId = Integer.parseInt(nextArgRequired());
- final Rect bounds = getBounds();
- if (bounds == null) {
- System.err.println("Error: invalid input bounds");
- return;
- }
-
- try {
- if (!mAm.moveTopActivityToPinnedStack(stackId, bounds)) {
- showError("Didn't move top activity to pinned stack.");
- }
- } catch (RemoteException e) {
- showError("Unable to move top activity: " + e);
- return;
- }
- }
-
- private void runStackSizeDockedStackTest() throws Exception {
- final int stepSize = Integer.parseInt(nextArgRequired());
- final String side = nextArgRequired();
- final String delayStr = nextArg();
- final int delayMs = (delayStr != null) ? Integer.parseInt(delayStr) : 0;
-
- Rect bounds;
- try {
- StackInfo info = mAm.getStackInfo(DOCKED_STACK_ID);
- if (info == null) {
- showError("Docked stack doesn't exist");
- return;
- }
- if (info.bounds == null) {
- showError("Docked stack doesn't have a bounds");
- return;
- }
- bounds = info.bounds;
- } catch (RemoteException e) {
- showError("Unable to get docked stack info:" + e);
- return;
- }
-
- final boolean horizontalGrowth = "l".equals(side) || "r".equals(side);
- final int changeSize = (horizontalGrowth ? bounds.width() : bounds.height()) / 2;
- int currentPoint;
- switch (side) {
- case "l":
- currentPoint = bounds.left;
- break;
- case "r":
- currentPoint = bounds.right;
- break;
- case "t":
- currentPoint = bounds.top;
- break;
- case "b":
- currentPoint = bounds.bottom;
- break;
- default:
- showError("Unknown growth side: " + side);
- return;
- }
-
- final int startPoint = currentPoint;
- final int minPoint = currentPoint - changeSize;
- final int maxPoint = currentPoint + changeSize;
-
- int maxChange;
- System.out.println("Shrinking docked stack side=" + side);
- while (currentPoint > minPoint) {
- maxChange = Math.min(stepSize, currentPoint - minPoint);
- currentPoint -= maxChange;
- setBoundsSide(bounds, side, currentPoint);
- resizeStack(DOCKED_STACK_ID, bounds, delayMs);
- }
-
- System.out.println("Growing docked stack side=" + side);
- while (currentPoint < maxPoint) {
- maxChange = Math.min(stepSize, maxPoint - currentPoint);
- currentPoint += maxChange;
- setBoundsSide(bounds, side, currentPoint);
- resizeStack(DOCKED_STACK_ID, bounds, delayMs);
- }
-
- System.out.println("Back to Original size side=" + side);
- while (currentPoint > startPoint) {
- maxChange = Math.min(stepSize, currentPoint - startPoint);
- currentPoint -= maxChange;
- setBoundsSide(bounds, side, currentPoint);
- resizeStack(DOCKED_STACK_ID, bounds, delayMs);
- }
- }
-
- private void setBoundsSide(Rect bounds, String side, int value) {
- switch (side) {
- case "l":
- bounds.left = value;
- break;
- case "r":
- bounds.right = value;
- break;
- case "t":
- bounds.top = value;
- break;
- case "b":
- bounds.bottom = value;
- break;
- default:
- showError("Unknown set side: " + side);
- break;
- }
- }
-
- private void runTask() throws Exception {
- String op = nextArgRequired();
- if (op.equals("lock")) {
- runTaskLock();
- } else if (op.equals("resizeable")) {
- runTaskResizeable();
- } else if (op.equals("resize")) {
- runTaskResize();
- } else if (op.equals("drag-task-test")) {
- runTaskDragTaskTest();
- } else if (op.equals("size-task-test")) {
- runTaskSizeTaskTest();
- } else {
- showError("Error: unknown command '" + op + "'");
- return;
- }
- }
-
- private void runTaskLock() throws Exception {
- String taskIdStr = nextArgRequired();
- try {
- if (taskIdStr.equals("stop")) {
- mAm.stopLockTaskMode();
- } else {
- int taskId = Integer.parseInt(taskIdStr);
- mAm.startLockTaskMode(taskId);
- }
- System.err.println("Activity manager is " + (mAm.isInLockTaskMode() ? "" : "not ") +
- "in lockTaskMode");
- } catch (RemoteException e) {
- }
- }
-
- private void runTaskResizeable() throws Exception {
- final String taskIdStr = nextArgRequired();
- final int taskId = Integer.parseInt(taskIdStr);
- final String resizeableStr = nextArgRequired();
- final int resizeableMode = Integer.parseInt(resizeableStr);
-
- try {
- mAm.setTaskResizeable(taskId, resizeableMode);
- } catch (RemoteException e) {
- }
- }
-
- private void runTaskResize() throws Exception {
- final String taskIdStr = nextArgRequired();
- final int taskId = Integer.parseInt(taskIdStr);
- final Rect bounds = getBounds();
- if (bounds == null) {
- System.err.println("Error: invalid input bounds");
- return;
- }
- taskResize(taskId, bounds, 0, false);
- }
-
- private void taskResize(int taskId, Rect bounds, int delay_ms, boolean pretendUserResize) {
- try {
- final int resizeMode = pretendUserResize ? RESIZE_MODE_USER : RESIZE_MODE_SYSTEM;
- mAm.resizeTask(taskId, bounds, resizeMode);
- Thread.sleep(delay_ms);
- } catch (RemoteException e) {
- System.err.println("Error changing task bounds: " + e);
- } catch (InterruptedException e) {
- }
- }
-
- private void runTaskDragTaskTest() {
- final int taskId = Integer.parseInt(nextArgRequired());
- final int stepSize = Integer.parseInt(nextArgRequired());
- final String delayStr = nextArg();
- final int delay_ms = (delayStr != null) ? Integer.parseInt(delayStr) : 0;
- final StackInfo stackInfo;
- Rect taskBounds;
- try {
- stackInfo = mAm.getStackInfo(mAm.getFocusedStackId());
- taskBounds = mAm.getTaskBounds(taskId);
- } catch (RemoteException e) {
- System.err.println("Error getting focus stack info or task bounds: " + e);
- return;
- }
- final Rect stackBounds = stackInfo.bounds;
- int travelRight = stackBounds.width() - taskBounds.width();
- int travelLeft = -travelRight;
- int travelDown = stackBounds.height() - taskBounds.height();
- int travelUp = -travelDown;
- int passes = 0;
-
- // We do 2 passes to get back to the original location of the task.
- while (passes < 2) {
- // Move right
- System.out.println("Moving right...");
- travelRight = moveTask(taskId, taskBounds, stackBounds, stepSize,
- travelRight, MOVING_FORWARD, MOVING_HORIZONTALLY, delay_ms);
- System.out.println("Still need to travel right by " + travelRight);
-
- // Move down
- System.out.println("Moving down...");
- travelDown = moveTask(taskId, taskBounds, stackBounds, stepSize,
- travelDown, MOVING_FORWARD, !MOVING_HORIZONTALLY, delay_ms);
- System.out.println("Still need to travel down by " + travelDown);
-
- // Move left
- System.out.println("Moving left...");
- travelLeft = moveTask(taskId, taskBounds, stackBounds, stepSize,
- travelLeft, !MOVING_FORWARD, MOVING_HORIZONTALLY, delay_ms);
- System.out.println("Still need to travel left by " + travelLeft);
-
- // Move up
- System.out.println("Moving up...");
- travelUp = moveTask(taskId, taskBounds, stackBounds, stepSize,
- travelUp, !MOVING_FORWARD, !MOVING_HORIZONTALLY, delay_ms);
- System.out.println("Still need to travel up by " + travelUp);
-
- try {
- taskBounds = mAm.getTaskBounds(taskId);
- } catch (RemoteException e) {
- System.err.println("Error getting task bounds: " + e);
- return;
- }
- passes++;
- }
- }
-
- private int moveTask(int taskId, Rect taskRect, Rect stackRect, int stepSize,
- int maxToTravel, boolean movingForward, boolean horizontal, int delay_ms) {
- int maxMove;
- if (movingForward) {
- while (maxToTravel > 0
- && ((horizontal && taskRect.right < stackRect.right)
- ||(!horizontal && taskRect.bottom < stackRect.bottom))) {
- if (horizontal) {
- maxMove = Math.min(stepSize, stackRect.right - taskRect.right);
- maxToTravel -= maxMove;
- taskRect.right += maxMove;
- taskRect.left += maxMove;
- } else {
- maxMove = Math.min(stepSize, stackRect.bottom - taskRect.bottom);
- maxToTravel -= maxMove;
- taskRect.top += maxMove;
- taskRect.bottom += maxMove;
- }
- taskResize(taskId, taskRect, delay_ms, false);
- }
- } else {
- while (maxToTravel < 0
- && ((horizontal && taskRect.left > stackRect.left)
- ||(!horizontal && taskRect.top > stackRect.top))) {
- if (horizontal) {
- maxMove = Math.min(stepSize, taskRect.left - stackRect.left);
- maxToTravel -= maxMove;
- taskRect.right -= maxMove;
- taskRect.left -= maxMove;
- } else {
- maxMove = Math.min(stepSize, taskRect.top - stackRect.top);
- maxToTravel -= maxMove;
- taskRect.top -= maxMove;
- taskRect.bottom -= maxMove;
- }
- taskResize(taskId, taskRect, delay_ms, false);
- }
- }
- // Return the remaining distance we didn't travel because we reached the target location.
- return maxToTravel;
- }
-
- private void runTaskSizeTaskTest() {
- final int taskId = Integer.parseInt(nextArgRequired());
- final int stepSize = Integer.parseInt(nextArgRequired());
- final String delayStr = nextArg();
- final int delay_ms = (delayStr != null) ? Integer.parseInt(delayStr) : 0;
- final StackInfo stackInfo;
- final Rect initialTaskBounds;
- try {
- stackInfo = mAm.getStackInfo(mAm.getFocusedStackId());
- initialTaskBounds = mAm.getTaskBounds(taskId);
- } catch (RemoteException e) {
- System.err.println("Error getting focus stack info or task bounds: " + e);
- return;
- }
- final Rect stackBounds = stackInfo.bounds;
- stackBounds.inset(STACK_BOUNDS_INSET, STACK_BOUNDS_INSET);
- final Rect currentTaskBounds = new Rect(initialTaskBounds);
-
- // Size by top-left
- System.out.println("Growing top-left");
- do {
- currentTaskBounds.top -= getStepSize(
- currentTaskBounds.top, stackBounds.top, stepSize, GREATER_THAN_TARGET);
-
- currentTaskBounds.left -= getStepSize(
- currentTaskBounds.left, stackBounds.left, stepSize, GREATER_THAN_TARGET);
-
- taskResize(taskId, currentTaskBounds, delay_ms, true);
- } while (stackBounds.top < currentTaskBounds.top
- || stackBounds.left < currentTaskBounds.left);
-
- // Back to original size
- System.out.println("Shrinking top-left");
- do {
- currentTaskBounds.top += getStepSize(
- currentTaskBounds.top, initialTaskBounds.top, stepSize, !GREATER_THAN_TARGET);
-
- currentTaskBounds.left += getStepSize(
- currentTaskBounds.left, initialTaskBounds.left, stepSize, !GREATER_THAN_TARGET);
-
- taskResize(taskId, currentTaskBounds, delay_ms, true);
- } while (initialTaskBounds.top > currentTaskBounds.top
- || initialTaskBounds.left > currentTaskBounds.left);
-
- // Size by top-right
- System.out.println("Growing top-right");
- do {
- currentTaskBounds.top -= getStepSize(
- currentTaskBounds.top, stackBounds.top, stepSize, GREATER_THAN_TARGET);
-
- currentTaskBounds.right += getStepSize(
- currentTaskBounds.right, stackBounds.right, stepSize, !GREATER_THAN_TARGET);
-
- taskResize(taskId, currentTaskBounds, delay_ms, true);
- } while (stackBounds.top < currentTaskBounds.top
- || stackBounds.right > currentTaskBounds.right);
-
- // Back to original size
- System.out.println("Shrinking top-right");
- do {
- currentTaskBounds.top += getStepSize(
- currentTaskBounds.top, initialTaskBounds.top, stepSize, !GREATER_THAN_TARGET);
-
- currentTaskBounds.right -= getStepSize(currentTaskBounds.right, initialTaskBounds.right,
- stepSize, GREATER_THAN_TARGET);
-
- taskResize(taskId, currentTaskBounds, delay_ms, true);
- } while (initialTaskBounds.top > currentTaskBounds.top
- || initialTaskBounds.right < currentTaskBounds.right);
-
- // Size by bottom-left
- System.out.println("Growing bottom-left");
- do {
- currentTaskBounds.bottom += getStepSize(
- currentTaskBounds.bottom, stackBounds.bottom, stepSize, !GREATER_THAN_TARGET);
-
- currentTaskBounds.left -= getStepSize(
- currentTaskBounds.left, stackBounds.left, stepSize, GREATER_THAN_TARGET);
-
- taskResize(taskId, currentTaskBounds, delay_ms, true);
- } while (stackBounds.bottom > currentTaskBounds.bottom
- || stackBounds.left < currentTaskBounds.left);
-
- // Back to original size
- System.out.println("Shrinking bottom-left");
- do {
- currentTaskBounds.bottom -= getStepSize(currentTaskBounds.bottom,
- initialTaskBounds.bottom, stepSize, GREATER_THAN_TARGET);
-
- currentTaskBounds.left += getStepSize(
- currentTaskBounds.left, initialTaskBounds.left, stepSize, !GREATER_THAN_TARGET);
-
- taskResize(taskId, currentTaskBounds, delay_ms, true);
- } while (initialTaskBounds.bottom < currentTaskBounds.bottom
- || initialTaskBounds.left > currentTaskBounds.left);
-
- // Size by bottom-right
- System.out.println("Growing bottom-right");
- do {
- currentTaskBounds.bottom += getStepSize(
- currentTaskBounds.bottom, stackBounds.bottom, stepSize, !GREATER_THAN_TARGET);
-
- currentTaskBounds.right += getStepSize(
- currentTaskBounds.right, stackBounds.right, stepSize, !GREATER_THAN_TARGET);
-
- taskResize(taskId, currentTaskBounds, delay_ms, true);
- } while (stackBounds.bottom > currentTaskBounds.bottom
- || stackBounds.right > currentTaskBounds.right);
-
- // Back to original size
- System.out.println("Shrinking bottom-right");
- do {
- currentTaskBounds.bottom -= getStepSize(currentTaskBounds.bottom,
- initialTaskBounds.bottom, stepSize, GREATER_THAN_TARGET);
-
- currentTaskBounds.right -= getStepSize(currentTaskBounds.right, initialTaskBounds.right,
- stepSize, GREATER_THAN_TARGET);
-
- taskResize(taskId, currentTaskBounds, delay_ms, true);
- } while (initialTaskBounds.bottom < currentTaskBounds.bottom
- || initialTaskBounds.right < currentTaskBounds.right);
- }
-
- private int getStepSize(int current, int target, int inStepSize, boolean greaterThanTarget) {
- int stepSize = 0;
- if (greaterThanTarget && target < current) {
- current -= inStepSize;
- stepSize = inStepSize;
- if (target > current) {
- stepSize -= (target - current);
- }
- }
- if (!greaterThanTarget && target > current) {
- current += inStepSize;
- stepSize = inStepSize;
- if (target < current) {
- stepSize += (current - target);
- }
- }
- return stepSize;
- }
-
- 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) {
- }
- }
-
- private void runSuppressResizeConfigChanges() throws Exception {
- boolean suppress = Boolean.valueOf(nextArgRequired());
-
- try {
- mAm.suppressResizeConfigChanges(suppress);
- } catch (RemoteException e) {
- System.err.println("Error suppressing resize config changes: " + e);
- }
- }
-
- private void runSetInactive() throws Exception {
- int userId = UserHandle.USER_CURRENT;
-
- String opt;
- while ((opt=nextOption()) != null) {
- if (opt.equals("--user")) {
- userId = parseUserArg(nextArgRequired());
- } else {
- System.err.println("Error: Unknown option: " + opt);
- return;
- }
- }
- String packageName = nextArgRequired();
- String value = nextArgRequired();
-
- IUsageStatsManager usm = IUsageStatsManager.Stub.asInterface(ServiceManager.getService(
- Context.USAGE_STATS_SERVICE));
- usm.setAppInactive(packageName, Boolean.parseBoolean(value), userId);
- }
-
- private void runGetInactive() throws Exception {
- int userId = UserHandle.USER_CURRENT;
-
- String opt;
- while ((opt=nextOption()) != null) {
- if (opt.equals("--user")) {
- userId = parseUserArg(nextArgRequired());
- } else {
- System.err.println("Error: Unknown option: " + opt);
- return;
- }
- }
- String packageName = nextArgRequired();
-
- IUsageStatsManager usm = IUsageStatsManager.Stub.asInterface(ServiceManager.getService(
- Context.USAGE_STATS_SERVICE));
- boolean isIdle = usm.isAppInactive(packageName, userId);
- System.out.println("Idle=" + isIdle);
- }
-
- private void runSendTrimMemory() throws Exception {
- int userId = UserHandle.USER_CURRENT;
- String opt;
- while ((opt = nextOption()) != null) {
- if (opt.equals("--user")) {
- userId = parseUserArg(nextArgRequired());
- if (userId == UserHandle.USER_ALL) {
- System.err.println("Error: Can't use user 'all'");
- return;
- }
- } else {
- System.err.println("Error: Unknown option: " + opt);
- return;
- }
- }
-
- String proc = nextArgRequired();
- String levelArg = nextArgRequired();
- int level;
- switch (levelArg) {
- case "HIDDEN":
- level = ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN;
- break;
- case "RUNNING_MODERATE":
- level = ComponentCallbacks2.TRIM_MEMORY_RUNNING_MODERATE;
- break;
- case "BACKGROUND":
- level = ComponentCallbacks2.TRIM_MEMORY_BACKGROUND;
- break;
- case "RUNNING_LOW":
- level = ComponentCallbacks2.TRIM_MEMORY_RUNNING_LOW;
- break;
- case "MODERATE":
- level = ComponentCallbacks2.TRIM_MEMORY_MODERATE;
- break;
- case "RUNNING_CRITICAL":
- level = ComponentCallbacks2.TRIM_MEMORY_RUNNING_CRITICAL;
- break;
- case "COMPLETE":
- level = ComponentCallbacks2.TRIM_MEMORY_COMPLETE;
- break;
- default:
- System.err.println("Error: Unknown level option: " + levelArg);
- return;
- }
- if (!mAm.setProcessMemoryTrimLevel(proc, userId, level)) {
- System.err.println("Error: Failure to set the level - probably Unknown Process: " +
- proc);
- }
- }
-
- private void runGetCurrentUser() throws Exception {
- UserInfo currentUser = Preconditions.checkNotNull(mAm.getCurrentUser(),
- "Current user not set");
- System.out.println(currentUser.id);
- }
-
- /**
- * Open the given file for sending into the system process. This verifies
- * with SELinux that the system will have access to the file.
- */
- private static ParcelFileDescriptor openForSystemServer(File file, int mode)
- throws FileNotFoundException {
- final ParcelFileDescriptor fd = ParcelFileDescriptor.open(file, mode);
- final String tcon = SELinux.getFileContext(file.getAbsolutePath());
- if (!SELinux.checkSELinuxAccess("u:r:system_server:s0", tcon, "file", "read")) {
- throw new FileNotFoundException("System server has no access to file context " + tcon);
- }
- return fd;
- }
-
- private Rect getBounds() {
- String leftStr = nextArgRequired();
- int left = Integer.parseInt(leftStr);
- String topStr = nextArgRequired();
- int top = Integer.parseInt(topStr);
- String rightStr = nextArgRequired();
- int right = Integer.parseInt(rightStr);
- String bottomStr = nextArgRequired();
- int bottom = Integer.parseInt(bottomStr);
- if (left < 0) {
- System.err.println("Error: bad left arg: " + leftStr);
- return null;
- }
- if (top < 0) {
- System.err.println("Error: bad top arg: " + topStr);
- return null;
- }
- if (right <= 0) {
- System.err.println("Error: bad right arg: " + rightStr);
- return null;
- }
- if (bottom <= 0) {
- System.err.println("Error: bad bottom arg: " + bottomStr);
- return null;
- }
- return new Rect(left, top, right, bottom);
+ instrument.run();
}
}
diff --git a/cmds/am/src/com/android/commands/am/Instrument.java b/cmds/am/src/com/android/commands/am/Instrument.java
new file mode 100644
index 000000000000..8eefd256a69d
--- /dev/null
+++ b/cmds/am/src/com/android/commands/am/Instrument.java
@@ -0,0 +1,435 @@
+/*
+ * Copyright (C) 2007 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.am;
+
+import android.app.IActivityManager;
+import android.app.IInstrumentationWatcher;
+import android.app.Instrumentation;
+import android.app.UiAutomationConnection;
+import android.content.ComponentName;
+import android.content.pm.IPackageManager;
+import android.content.pm.InstrumentationInfo;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.UserHandle;
+import android.util.AndroidException;
+import android.util.proto.ProtoOutputStream;
+import android.view.IWindowManager;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+
+/**
+ * Runs the am instrument command
+ */
+public class Instrument {
+ private final IActivityManager mAm;
+ private final IPackageManager mPm;
+ private final IWindowManager mWm;
+
+ // Command line arguments
+ public String profileFile = null;
+ public boolean wait = false;
+ public boolean rawMode = false;
+ public boolean proto = false;
+ public boolean noWindowAnimation = false;
+ public String abi = null;
+ public int userId = UserHandle.USER_CURRENT;
+ public Bundle args = new Bundle();
+ // Required
+ public String componentNameArg;
+
+ /**
+ * Construct the instrument command runner.
+ */
+ public Instrument(IActivityManager am, IPackageManager pm) {
+ mAm = am;
+ mPm = pm;
+ mWm = IWindowManager.Stub.asInterface(ServiceManager.getService("window"));
+ }
+
+ /**
+ * Base class for status reporting.
+ *
+ * All the methods on this interface are called within the synchronized block
+ * of the InstrumentationWatcher, so calls are in order. However, that means
+ * you must be careful not to do blocking operations because you don't know
+ * exactly the locking dependencies.
+ */
+ private interface StatusReporter {
+ /**
+ * Status update for tests.
+ */
+ public void onInstrumentationStatusLocked(ComponentName name, int resultCode,
+ Bundle results);
+
+ /**
+ * The tests finished.
+ */
+ public void onInstrumentationFinishedLocked(ComponentName name, int resultCode,
+ Bundle results);
+
+ /**
+ * @param errorText a description of the error
+ * @param commandError True if the error is related to the commandline, as opposed
+ * to a test failing.
+ */
+ public void onError(String errorText, boolean commandError);
+ }
+
+ /**
+ * Printer for the 'classic' text based status reporting.
+ */
+ private class TextStatusReporter implements StatusReporter {
+ private boolean mRawMode;
+
+ /**
+ * Human-ish readable output.
+ *
+ * @param rawMode In "raw mode" (true), all bundles are dumped.
+ * In "pretty mode" (false), if a bundle includes
+ * Instrumentation.REPORT_KEY_STREAMRESULT, just print that.
+ */
+ public TextStatusReporter(boolean rawMode) {
+ mRawMode = rawMode;
+ }
+
+ @Override
+ public void onInstrumentationStatusLocked(ComponentName name, int resultCode,
+ Bundle results) {
+ // pretty printer mode?
+ String pretty = null;
+ if (!mRawMode && results != null) {
+ pretty = results.getString(Instrumentation.REPORT_KEY_STREAMRESULT);
+ }
+ if (pretty != null) {
+ System.out.print(pretty);
+ } else {
+ if (results != null) {
+ for (String key : results.keySet()) {
+ System.out.println(
+ "INSTRUMENTATION_STATUS: " + key + "=" + results.get(key));
+ }
+ }
+ System.out.println("INSTRUMENTATION_STATUS_CODE: " + resultCode);
+ }
+ }
+
+ @Override
+ public void onInstrumentationFinishedLocked(ComponentName name, int resultCode,
+ Bundle results) {
+ // pretty printer mode?
+ String pretty = null;
+ if (!mRawMode && results != null) {
+ pretty = results.getString(Instrumentation.REPORT_KEY_STREAMRESULT);
+ }
+ if (pretty != null) {
+ System.out.println(pretty);
+ } else {
+ if (results != null) {
+ for (String key : results.keySet()) {
+ System.out.println(
+ "INSTRUMENTATION_RESULT: " + key + "=" + results.get(key));
+ }
+ }
+ System.out.println("INSTRUMENTATION_CODE: " + resultCode);
+ }
+ }
+
+ @Override
+ public void onError(String errorText, boolean commandError) {
+ // The regular BaseCommand error printing will print the commandErrors.
+ if (!commandError) {
+ System.out.println(errorText);
+ }
+ }
+ }
+
+ /**
+ * Printer for the protobuf based status reporting.
+ */
+ private class ProtoStatusReporter implements StatusReporter {
+ @Override
+ public void onInstrumentationStatusLocked(ComponentName name, int resultCode,
+ Bundle results) {
+ final ProtoOutputStream proto = new ProtoOutputStream();
+
+ final long token = proto.startRepeatedObject(InstrumentationData.Session.TEST_STATUS);
+
+ proto.writeSInt32(InstrumentationData.TestStatus.RESULT_CODE, resultCode);
+ writeBundle(proto, InstrumentationData.TestStatus.RESULTS, results);
+
+ proto.endRepeatedObject(token);
+ writeProtoToStdout(proto);
+ }
+
+ @Override
+ public void onInstrumentationFinishedLocked(ComponentName name, int resultCode,
+ Bundle results) {
+ final ProtoOutputStream proto = new ProtoOutputStream();
+
+ final long token = proto.startObject(InstrumentationData.Session.SESSION_STATUS);
+
+ proto.writeEnum(InstrumentationData.SessionStatus.STATUS_CODE,
+ InstrumentationData.SESSION_FINISHED);
+ proto.writeSInt32(InstrumentationData.SessionStatus.RESULT_CODE, resultCode);
+ writeBundle(proto, InstrumentationData.SessionStatus.RESULTS, results);
+
+ proto.endObject(token);
+ writeProtoToStdout(proto);
+ }
+
+ @Override
+ public void onError(String errorText, boolean commandError) {
+ final ProtoOutputStream proto = new ProtoOutputStream();
+
+ final long token = proto.startObject(InstrumentationData.Session.SESSION_STATUS);
+
+ proto.writeEnum(InstrumentationData.SessionStatus.STATUS_CODE,
+ InstrumentationData.SESSION_ABORTED);
+ proto.writeString(InstrumentationData.SessionStatus.ERROR_TEXT, errorText);
+
+ proto.endObject(token);
+ writeProtoToStdout(proto);
+ }
+
+ private void writeBundle(ProtoOutputStream proto, long fieldId, Bundle bundle) {
+ final long bundleToken = proto.startObject(fieldId);
+
+ for (final String key: bundle.keySet()) {
+ final long entryToken = proto.startRepeatedObject(
+ InstrumentationData.ResultsBundle.ENTRIES);
+
+ proto.writeString(InstrumentationData.ResultsBundleEntry.KEY, key);
+
+ final Object val = bundle.get(key);
+ if (val instanceof String) {
+ proto.writeString(InstrumentationData.ResultsBundleEntry.VALUE_STRING,
+ (String)val);
+ } else if (val instanceof Byte) {
+ proto.writeSInt32(InstrumentationData.ResultsBundleEntry.VALUE_INT,
+ ((Byte)val).intValue());
+ } else if (val instanceof Double) {
+ proto.writeDouble(InstrumentationData.ResultsBundleEntry.VALUE_DOUBLE,
+ ((Double)val).doubleValue());
+ } else if (val instanceof Float) {
+ proto.writeFloat(InstrumentationData.ResultsBundleEntry.VALUE_FLOAT,
+ ((Float)val).floatValue());
+ } else if (val instanceof Integer) {
+ proto.writeSInt32(InstrumentationData.ResultsBundleEntry.VALUE_INT,
+ ((Integer)val).intValue());
+ } else if (val instanceof Long) {
+ proto.writeSInt64(InstrumentationData.ResultsBundleEntry.VALUE_LONG,
+ ((Long)val).longValue());
+ } else if (val instanceof Short) {
+ proto.writeSInt32(InstrumentationData.ResultsBundleEntry.VALUE_INT,
+ ((Short)val).intValue());
+ } else if (val instanceof Bundle) {
+ writeBundle(proto, InstrumentationData.ResultsBundleEntry.VALUE_BUNDLE,
+ (Bundle)val);
+ }
+
+ proto.endRepeatedObject(entryToken);
+ }
+
+ proto.endObject(bundleToken);
+ }
+
+ private void writeProtoToStdout(ProtoOutputStream proto) {
+ try {
+ System.out.write(proto.getBytes());
+ System.out.flush();
+ } catch (IOException ex) {
+ System.err.println("Error writing finished response: ");
+ ex.printStackTrace(System.err);
+ }
+ }
+ }
+
+
+ /**
+ * Callbacks from the remote instrumentation instance.
+ */
+ private class InstrumentationWatcher extends IInstrumentationWatcher.Stub {
+ private final StatusReporter mReporter;
+
+ private boolean mFinished = false;
+
+ public InstrumentationWatcher(StatusReporter reporter) {
+ mReporter = reporter;
+ }
+
+ @Override
+ public void instrumentationStatus(ComponentName name, int resultCode, Bundle results) {
+ synchronized (this) {
+ mReporter.onInstrumentationStatusLocked(name, resultCode, results);
+ notifyAll();
+ }
+ }
+
+ @Override
+ public void instrumentationFinished(ComponentName name, int resultCode, Bundle results) {
+ synchronized (this) {
+ mReporter.onInstrumentationFinishedLocked(name, resultCode, results);
+ mFinished = true;
+ notifyAll();
+ }
+ }
+
+ public boolean waitForFinish() {
+ synchronized (this) {
+ while (!mFinished) {
+ try {
+ if (!mAm.asBinder().pingBinder()) {
+ return false;
+ }
+ wait(1000);
+ } catch (InterruptedException e) {
+ throw new IllegalStateException(e);
+ }
+ }
+ }
+ return true;
+ }
+ }
+
+ /**
+ * Figure out which component they really meant.
+ */
+ private ComponentName parseComponentName(String cnArg) throws Exception {
+ if (cnArg.contains("/")) {
+ ComponentName cn = ComponentName.unflattenFromString(cnArg);
+ if (cn == null) throw new IllegalArgumentException("Bad component name: " + cnArg);
+ return cn;
+ } else {
+ List<InstrumentationInfo> infos = mPm.queryInstrumentation(null, 0).getList();
+
+ final int numInfos = infos == null ? 0: infos.size();
+ ArrayList<ComponentName> cns = new ArrayList<>();
+ for (int i = 0; i < numInfos; i++) {
+ InstrumentationInfo info = infos.get(i);
+
+ ComponentName c = new ComponentName(info.packageName, info.name);
+ if (cnArg.equals(info.packageName)) {
+ cns.add(c);
+ }
+ }
+
+ if (cns.size() == 0) {
+ throw new IllegalArgumentException("No instrumentation found for: " + cnArg);
+ } else if (cns.size() == 1) {
+ return cns.get(0);
+ } else {
+ StringBuilder cnsStr = new StringBuilder();
+ final int numCns = cns.size();
+ for (int i = 0; i < numCns; i++) {
+ cnsStr.append(cns.get(i).flattenToString());
+ cnsStr.append(", ");
+ }
+
+ // Remove last ", "
+ cnsStr.setLength(cnsStr.length() - 2);
+
+ throw new IllegalArgumentException("Found multiple instrumentations: "
+ + cnsStr.toString());
+ }
+ }
+ }
+
+ /**
+ * Run the instrumentation.
+ */
+ public void run() throws Exception {
+ StatusReporter reporter = null;
+ float[] oldAnims = null;
+
+ try {
+ // Choose which output we will do.
+ if (proto) {
+ reporter = new ProtoStatusReporter();
+ } else if (wait) {
+ reporter = new TextStatusReporter(rawMode);
+ }
+
+ // Choose whether we have to wait for the results.
+ InstrumentationWatcher watcher = null;
+ UiAutomationConnection connection = null;
+ if (reporter != null) {
+ watcher = new InstrumentationWatcher(reporter);
+ connection = new UiAutomationConnection();
+ }
+
+ // Set the window animation if necessary
+ if (noWindowAnimation) {
+ oldAnims = mWm.getAnimationScales();
+ mWm.setAnimationScale(0, 0.0f);
+ mWm.setAnimationScale(1, 0.0f);
+ }
+
+ // Figure out which component we are tring to do.
+ final ComponentName cn = parseComponentName(componentNameArg);
+
+ // Choose an ABI if necessary
+ 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) {
+ throw new AndroidException(
+ "INSTRUMENTATION_FAILED: Unsupported instruction set " + abi);
+ }
+ }
+
+ // Start the instrumentation
+ if (!mAm.startInstrumentation(cn, profileFile, 0, args, watcher, connection, userId,
+ abi)) {
+ throw new AndroidException("INSTRUMENTATION_FAILED: " + cn.flattenToString());
+ }
+
+ // If we have been requested to wait, do so until the instrumentation is finished.
+ if (watcher != null) {
+ if (!watcher.waitForFinish()) {
+ reporter.onError("INSTRUMENTATION_ABORTED: System has crashed.", false);
+ return;
+ }
+ }
+ } catch (Exception ex) {
+ // Report failures
+ if (reporter != null) {
+ reporter.onError(ex.getMessage(), true);
+ }
+
+ // And re-throw the exception
+ throw ex;
+ } finally {
+ // Clean up
+ if (oldAnims != null) {
+ mWm.setAnimationScales(oldAnims);
+ }
+ }
+ }
+}
+
diff --git a/cmds/app_process/Android.mk b/cmds/app_process/Android.mk
index eaad3a7ec529..72fe0516f480 100644
--- a/cmds/app_process/Android.mk
+++ b/cmds/app_process/Android.mk
@@ -5,6 +5,7 @@ app_process_common_shared_libs := \
libbinder \
libcutils \
libdl \
+ libhwbinder \
liblog \
libnativeloader \
libutils \
diff --git a/cmds/app_process/app_main.cpp b/cmds/app_process/app_main.cpp
index 0ea141c292dd..e56417b82fc0 100644
--- a/cmds/app_process/app_main.cpp
+++ b/cmds/app_process/app_main.cpp
@@ -14,7 +14,7 @@
#include <unistd.h>
#include <binder/IPCThreadState.h>
-#include <binder/ProcessState.h>
+#include <hwbinder/IPCThreadState.h>
#include <utils/Log.h>
#include <cutils/memory.h>
#include <cutils/properties.h>
@@ -85,6 +85,7 @@ public:
ar->callMain(mClassName, mClass, mArgs);
IPCThreadState::self()->stopProcess();
+ hardware::IPCThreadState::self()->stopProcess();
}
virtual void onZygoteInit()
@@ -99,6 +100,7 @@ public:
if (mClassName.isEmpty()) {
// if zygote
IPCThreadState::self()->stopProcess();
+ hardware::IPCThreadState::self()->stopProcess();
}
AndroidRuntime::onExit(code);
diff --git a/cmds/appops/appops b/cmds/appops/appops
index 25d20311aae2..5dc85aa0979c 100755
--- a/cmds/appops/appops
+++ b/cmds/appops/appops
@@ -1 +1,2 @@
-cmd appops $@
+#!/system/bin/sh
+cmd appops "$@"
diff --git a/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java b/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java
index 5bf80765a7de..1bcfb22fe2f2 100644
--- a/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java
+++ b/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java
@@ -17,21 +17,26 @@
package com.android.commands.bmgr;
import android.app.backup.BackupManager;
+import android.app.backup.BackupManagerMonitor;
import android.app.backup.BackupProgress;
-import android.app.backup.RestoreSet;
import android.app.backup.IBackupManager;
import android.app.backup.IBackupObserver;
import android.app.backup.IRestoreObserver;
import android.app.backup.IRestoreSession;
+import android.app.backup.RestoreSet;
+import android.app.backup.ISelectBackupTransportCallback;
+import android.content.ComponentName;
import android.content.pm.IPackageManager;
import android.content.pm.PackageInfo;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.UserHandle;
+import android.util.Log;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
+import java.util.concurrent.CountDownLatch;
public final class Bmgr {
IBackupManager mBmgr;
@@ -122,6 +127,11 @@ public final class Bmgr {
return;
}
+ if ("cancel".equals(op)) {
+ doCancel();
+ return;
+ }
+
if ("whitelist".equals(op)) {
doPrintWhitelist();
return;
@@ -265,12 +275,14 @@ public final class Bmgr {
return "Agent error";
case BackupManager.ERROR_TRANSPORT_QUOTA_EXCEEDED:
return "Size quota exceeded";
+ case BackupManager.ERROR_BACKUP_CANCELLED:
+ return "Backup Cancelled";
default:
return "Unknown error";
}
}
- private void backupNowAllPackages() {
+ private void backupNowAllPackages(boolean nonIncrementalBackup) {
int userId = UserHandle.USER_SYSTEM;
IPackageManager mPm =
IPackageManager.Stub.asInterface(ServiceManager.getService("package"));
@@ -297,14 +309,20 @@ public final class Bmgr {
System.err.println(BMGR_NOT_RUNNING_ERR);
}
}
- backupNowPackages(packages);
+ backupNowPackages(packages, nonIncrementalBackup);
}
}
- private void backupNowPackages(List<String> packages) {
+ private void backupNowPackages(List<String> packages, boolean nonIncrementalBackup) {
+ int flags = 0;
+ if (nonIncrementalBackup) {
+ flags |= BackupManager.FLAG_NON_INCREMENTAL_BACKUP;
+ }
try {
BackupObserver observer = new BackupObserver();
- int err = mBmgr.requestBackup(packages.toArray(new String[packages.size()]), observer);
+ // TODO: implement monitor here?
+ int err = mBmgr.requestBackup(packages.toArray(new String[packages.size()]), observer,
+ null, flags);
if (err == 0) {
// Off and running -- wait for the backup to complete
observer.waitForCompletion();
@@ -320,29 +338,51 @@ public final class Bmgr {
private void doBackupNow() {
String pkg;
boolean backupAll = false;
+ boolean nonIncrementalBackup = false;
ArrayList<String> allPkgs = new ArrayList<String>();
while ((pkg = nextArg()) != null) {
if (pkg.equals("--all")) {
backupAll = true;
+ } else if (pkg.equals("--non-incremental")) {
+ nonIncrementalBackup = true;
+ } else if (pkg.equals("--incremental")) {
+ nonIncrementalBackup = false;
} else {
allPkgs.add(pkg);
}
}
if (backupAll) {
if (allPkgs.size() == 0) {
- System.out.println("Running backup for all packages.");
- backupNowAllPackages();
+ System.out.println("Running " + (nonIncrementalBackup ? "non-" : "") +
+ "incremental backup for all packages.");
+ backupNowAllPackages(nonIncrementalBackup);
} else {
System.err.println("Provide only '--all' flag or list of packages.");
}
} else if (allPkgs.size() > 0) {
- System.out.println("Running backup for " + allPkgs.size() +" requested packages.");
- backupNowPackages(allPkgs);
+ System.out.println("Running " + (nonIncrementalBackup ? "non-" : "") +
+ "incremental backup for " + allPkgs.size() +" requested packages.");
+ backupNowPackages(allPkgs, nonIncrementalBackup);
} else {
System.err.println("Provide '--all' flag or list of packages.");
}
}
+ private void doCancel() {
+ String arg = nextArg();
+ if ("backups".equals(arg)) {
+ try {
+ mBmgr.cancelBackups();
+ } catch (RemoteException e) {
+ System.err.println(e.toString());
+ System.err.println(BMGR_NOT_RUNNING_ERR);
+ }
+ return;
+ }
+
+ System.err.println("Unknown command.");
+ }
+
private void doTransport() {
try {
String which = nextArg();
@@ -351,6 +391,11 @@ public final class Bmgr {
return;
}
+ if ("-c".equals(which)) {
+ doTransportByComponent();
+ return;
+ }
+
String old = mBmgr.selectBackupTransport(which);
if (old == null) {
System.out.println("Unknown transport '" + which
@@ -358,9 +403,47 @@ public final class Bmgr {
} else {
System.out.println("Selected transport " + which + " (formerly " + old + ")");
}
+
+ } catch (RemoteException e) {
+ System.err.println(e.toString());
+ System.err.println(BMGR_NOT_RUNNING_ERR);
+ }
+ }
+
+ private void doTransportByComponent() {
+ String which = nextArg();
+ if (which == null) {
+ showUsage();
+ return;
+ }
+
+ final CountDownLatch latch = new CountDownLatch(1);
+
+ try {
+ mBmgr.selectBackupTransportAsync(ComponentName.unflattenFromString(which),
+ new ISelectBackupTransportCallback.Stub() {
+ @Override
+ public void onSuccess(String transportName) {
+ System.out.println("Success. Selected transport: " + transportName);
+ latch.countDown();
+ }
+
+ @Override
+ public void onFailure(int reason) {
+ System.err.println("Failure. error=" + reason);
+ latch.countDown();
+ }
+ });
} catch (RemoteException e) {
System.err.println(e.toString());
System.err.println(BMGR_NOT_RUNNING_ERR);
+ return;
+ }
+
+ try {
+ latch.await();
+ } catch (InterruptedException e) {
+ System.err.println("Operation interrupted.");
}
}
@@ -415,7 +498,16 @@ public final class Bmgr {
}
private void doListTransports() {
+ String arg = nextArg();
+
try {
+ if ("-c".equals(arg)) {
+ for (ComponentName transport : mBmgr.listAllTransportComponents()) {
+ System.out.println(transport.flattenToShortString());
+ }
+ return;
+ }
+
String current = mBmgr.getCurrentTransport();
String[] transports = mBmgr.listAllTransports();
if (transports == null || transports.length == 0) {
@@ -436,7 +528,8 @@ public final class Bmgr {
private void doListRestoreSets() {
try {
RestoreObserver observer = new RestoreObserver();
- int err = mRestore.getAvailableRestoreSets(observer);
+ // TODO implement monitor here
+ int err = mRestore.getAvailableRestoreSets(observer, null);
if (err != 0) {
System.out.println("Unable to request restore sets");
} else {
@@ -487,6 +580,11 @@ public final class Bmgr {
}
}
+ /**
+ * Wait until either {@link #restoreFinished} or {@link #restoreStarting} is called.
+ * Once one is called, it clears the internal flag again, so that the same observer intance
+ * can be reused for a next operation.
+ */
public void waitForCompletion() {
// The restoreFinished() callback will throw the 'done' flag; we
// just sit and wait on that notification.
@@ -497,6 +595,7 @@ public final class Bmgr {
} catch (InterruptedException ex) {
}
}
+ done = false;
}
}
}
@@ -539,7 +638,8 @@ public final class Bmgr {
}
RestoreObserver observer = new RestoreObserver();
- int err = mRestore.restorePackage(pkg, observer);
+ // TODO implement monitor here
+ int err = mRestore.restorePackage(pkg, observer, null );
if (err == 0) {
// Off and running -- wait for the restore to complete
observer.waitForCompletion();
@@ -566,7 +666,8 @@ public final class Bmgr {
return;
}
RestoreSet[] sets = null;
- int err = mRestore.getAvailableRestoreSets(observer);
+ // TODO implement monitor here
+ int err = mRestore.getAvailableRestoreSets(observer, null);
if (err == 0) {
observer.waitForCompletion();
sets = observer.sets;
@@ -575,11 +676,12 @@ public final class Bmgr {
if (s.token == token) {
System.out.println("Scheduling restore: " + s.name);
if (filter == null) {
- didRestore = (mRestore.restoreAll(token, observer) == 0);
+ didRestore = (mRestore.restoreAll(token, observer, null) == 0);
} else {
String[] names = new String[filter.size()];
filter.toArray(names);
- didRestore = (mRestore.restoreSome(token, observer, names) == 0);
+ didRestore = (mRestore.restoreSome(token, observer,
+ null, names) == 0);
}
break;
}
@@ -637,9 +739,9 @@ public final class Bmgr {
System.err.println(" bmgr backup PACKAGE");
System.err.println(" bmgr enable BOOL");
System.err.println(" bmgr enabled");
- System.err.println(" bmgr list transports");
+ System.err.println(" bmgr list transports [-c]");
System.err.println(" bmgr list sets");
- System.err.println(" bmgr transport WHICH");
+ System.err.println(" bmgr transport WHICH|-c WHICH_COMPONENT");
System.err.println(" bmgr restore TOKEN");
System.err.println(" bmgr restore TOKEN PACKAGE...");
System.err.println(" bmgr restore PACKAGE");
@@ -647,6 +749,7 @@ public final class Bmgr {
System.err.println(" bmgr wipe TRANSPORT PACKAGE");
System.err.println(" bmgr fullbackup PACKAGE...");
System.err.println(" bmgr backupnow --all|PACKAGE...");
+ System.err.println(" bmgr cancel backups");
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");
@@ -661,15 +764,18 @@ public final class Bmgr {
System.err.println("the backup mechanism.");
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("BackupManager is currently bound to. These names can be passed as arguments");
System.err.println("to the 'transport' and 'wipe' commands. The currently active transport");
- System.err.println("is indicated with a '*' character.");
+ System.err.println("is indicated with a '*' character. If -c flag is used, all available");
+ System.err.println("transport components on the device are listed. These can be used with");
+ System.err.println("the component variant of 'transport' command.");
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 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.");
+ System.err.println("active one. This setting is persistent across reboots. If -c flag is");
+ System.err.println("specified, the following string is treated as a component name.");
System.err.println("");
System.err.println("The 'restore' command when given just a restore token initiates a full-system");
System.err.println("restore operation from the currently active transport. It will deliver");
@@ -703,5 +809,6 @@ public final class Bmgr {
System.err.println("For each package it will run key/value or full data backup ");
System.err.println("depending on the package's manifest declarations.");
System.err.println("The data is sent via the currently active transport.");
+ System.err.println("The 'cancel backups' command cancels all running backups.");
}
}
diff --git a/cmds/bootanimation/Android.mk b/cmds/bootanimation/Android.mk
index 3a92b9e74144..0e2c13ee1719 100644
--- a/cmds/bootanimation/Android.mk
+++ b/cmds/bootanimation/Android.mk
@@ -26,7 +26,8 @@ LOCAL_SHARED_LIBRARIES := \
libGLESv1_CM \
libgui \
libOpenSLES \
- libtinyalsa
+ libtinyalsa \
+ libbase
LOCAL_MODULE:= bootanimation
diff --git a/cmds/bootanimation/BootAnimation.cpp b/cmds/bootanimation/BootAnimation.cpp
index 9cfef47b2ea1..7394490fc8d4 100644
--- a/cmds/bootanimation/BootAnimation.cpp
+++ b/cmds/bootanimation/BootAnimation.cpp
@@ -18,6 +18,7 @@
#define LOG_TAG "BootAnimation"
#include <stdint.h>
+#include <inttypes.h>
#include <sys/inotify.h>
#include <sys/poll.h>
#include <sys/stat.h>
@@ -35,6 +36,9 @@
#include <utils/Atomic.h>
#include <utils/Errors.h>
#include <utils/Log.h>
+#include <utils/SystemClock.h>
+
+#include <android-base/properties.h>
#include <ui/PixelFormat.h>
#include <ui/Rect.h>
@@ -49,8 +53,8 @@
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-parameter"
#include <SkBitmap.h>
+#include <SkImage.h>
#include <SkStream.h>
-#include <SkImageDecoder.h>
#pragma GCC diagnostic pop
#include <GLES/gl.h>
@@ -65,6 +69,9 @@ namespace android {
static const char OEM_BOOTANIMATION_FILE[] = "/oem/media/bootanimation.zip";
static const char SYSTEM_BOOTANIMATION_FILE[] = "/system/media/bootanimation.zip";
static const char SYSTEM_ENCRYPTED_BOOTANIMATION_FILE[] = "/system/media/bootanimation-encrypted.zip";
+static const char OEM_SHUTDOWNANIMATION_FILE[] = "/oem/media/shutdownanimation.zip";
+static const char SYSTEM_SHUTDOWNANIMATION_FILE[] = "/system/media/shutdownanimation.zip";
+
static const char SYSTEM_DATA_DIR_PATH[] = "/data/system";
static const char SYSTEM_TIME_DIR_NAME[] = "time";
static const char SYSTEM_TIME_DIR_PATH[] = "/data/system/time";
@@ -104,11 +111,15 @@ BootAnimation::BootAnimation() : Thread(false), mClockEnabled(true), mTimeIsAccu
mSession = new SurfaceComposerClient();
// If the system has already booted, the animation is not being used for a boot.
- mSystemBoot = !property_get_bool(BOOT_COMPLETED_PROP_NAME, 0);
+ mSystemBoot = !android::base::GetBoolProperty(BOOT_COMPLETED_PROP_NAME, false);
+ std::string powerCtl = android::base::GetProperty("sys.powerctl", "");
+ if (powerCtl.empty()) {
+ mShuttingDown = false;
+ } else {
+ mShuttingDown = true;
+ }
}
-BootAnimation::~BootAnimation() {}
-
void BootAnimation::onFirstRef() {
status_t err = mSession->linkToComposerDeath(this);
ALOGE_IF(err, "linkToComposerDeath failed (%s) ", strerror(-err));
@@ -140,8 +151,10 @@ status_t BootAnimation::initTexture(Texture* texture, AssetManager& assets,
if (asset == NULL)
return NO_INIT;
SkBitmap bitmap;
- SkImageDecoder::DecodeMemory(asset->getBuffer(false), asset->getLength(),
- &bitmap, kUnknown_SkColorType, SkImageDecoder::kDecodePixels_Mode);
+ sk_sp<SkData> data = SkData::MakeWithoutCopy(asset->getBuffer(false),
+ asset->getLength());
+ sk_sp<SkImage> image = SkImage::MakeFromEncoded(data);
+ image->asLegacyBitmap(&bitmap, SkImage::kRO_LegacyBitmapMode);
asset->close();
delete asset;
@@ -193,15 +206,10 @@ status_t BootAnimation::initTexture(Texture* texture, AssetManager& assets,
status_t BootAnimation::initTexture(FileMap* map, int* width, int* height)
{
SkBitmap bitmap;
- SkMemoryStream stream(map->getDataPtr(), map->getDataLength());
- SkImageDecoder* codec = SkImageDecoder::Factory(&stream);
- if (codec != NULL) {
- codec->setDitherImage(false);
- codec->decode(&stream, &bitmap,
- kN32_SkColorType,
- SkImageDecoder::kDecodePixels_Mode);
- delete codec;
- }
+ sk_sp<SkData> data = SkData::MakeWithoutCopy(map->getDataPtr(),
+ map->getDataLength());
+ sk_sp<SkImage> image = SkImage::MakeFromEncoded(data);
+ image->asLegacyBitmap(&bitmap, SkImage::kRO_LegacyBitmapMode);
// FileMap memory is never released until application exit.
// Release it now as the texture is already loaded and the memory used for
@@ -317,16 +325,23 @@ status_t BootAnimation::readyToRun() {
char decrypt[PROPERTY_VALUE_MAX];
property_get("vold.decrypt", decrypt, "");
- bool encryptedAnimation = atoi(decrypt) != 0 || !strcmp("trigger_restart_min_framework", decrypt);
+ bool encryptedAnimation = atoi(decrypt) != 0 ||
+ !strcmp("trigger_restart_min_framework", decrypt);
- if (encryptedAnimation && (access(SYSTEM_ENCRYPTED_BOOTANIMATION_FILE, R_OK) == 0)) {
+ if (!mShuttingDown && encryptedAnimation &&
+ (access(SYSTEM_ENCRYPTED_BOOTANIMATION_FILE, R_OK) == 0)) {
mZipFileName = SYSTEM_ENCRYPTED_BOOTANIMATION_FILE;
+ return NO_ERROR;
}
- else if (access(OEM_BOOTANIMATION_FILE, R_OK) == 0) {
- mZipFileName = OEM_BOOTANIMATION_FILE;
- }
- else if (access(SYSTEM_BOOTANIMATION_FILE, R_OK) == 0) {
- mZipFileName = SYSTEM_BOOTANIMATION_FILE;
+ static const char* bootFiles[] = {OEM_BOOTANIMATION_FILE, SYSTEM_BOOTANIMATION_FILE};
+ static const char* shutdownFiles[] =
+ {OEM_SHUTDOWNANIMATION_FILE, SYSTEM_SHUTDOWNANIMATION_FILE};
+
+ for (const char* f : (!mShuttingDown ? bootFiles : shutdownFiles)) {
+ if (access(f, R_OK) == 0) {
+ mZipFileName = f;
+ return NO_ERROR;
+ }
}
return NO_ERROR;
}
@@ -355,6 +370,8 @@ bool BootAnimation::threadLoop()
bool BootAnimation::android()
{
+ ALOGD("%sAnimationShownTiming start time: %" PRId64 "ms", mShuttingDown ? "Shutdown" : "Boot",
+ elapsedRealtime());
initTexture(&mAndroid[0], mAssets, "images/android-logo-mask.png");
initTexture(&mAndroid[1], mAssets, "images/android-logo-shine.png");
@@ -781,11 +798,12 @@ bool BootAnimation::preloadZip(Animation& animation)
}
// Create and initialize audioplay if there is a wav file in any of the animations.
+ // Do it on a separate thread so we don't hold up the animation intro.
if (partWithAudio != NULL) {
ALOGD("found audio.wav, creating playback engine");
- if (!audioplay::create(partWithAudio->audioData, partWithAudio->audioLength)) {
- return false;
- }
+ mInitAudioThread = new InitAudioThread(partWithAudio->audioData,
+ partWithAudio->audioLength);
+ mInitAudioThread->run("BootAnimation::InitAudioThread", PRIORITY_NORMAL);
}
zip->endIteration(cookie);
@@ -853,9 +871,14 @@ bool BootAnimation::movie()
playAnimation(*animation);
- if (mTimeCheckThread != NULL) {
+ if (mTimeCheckThread != nullptr) {
mTimeCheckThread->requestExit();
- mTimeCheckThread = NULL;
+ mTimeCheckThread = nullptr;
+ }
+
+ // We should have joined mInitAudioThread thread in playAnimation
+ if (mInitAudioThread != nullptr) {
+ mInitAudioThread = nullptr;
}
releaseAnimation(animation);
@@ -874,6 +897,8 @@ bool BootAnimation::playAnimation(const Animation& animation)
const int animationX = (mWidth - animation.width) / 2;
const int animationY = (mHeight - animation.height) / 2;
+ ALOGD("%sAnimationShownTiming start time: %" PRId64 "ms", mShuttingDown ? "Shutdown" : "Boot",
+ elapsedRealtime());
for (size_t i=0 ; i<pcount ; i++) {
const Animation::Part& part(animation.parts[i]);
const size_t fcount = part.frames.size();
@@ -895,6 +920,10 @@ bool BootAnimation::playAnimation(const Animation& animation)
// only play audio file the first time we animate the part
if (r == 0 && part.audioData && playSoundsAllowed()) {
ALOGD("playing clip for part%d, size=%d", (int) i, part.audioLength);
+ // Block until the audio engine is finished initializing.
+ if (mInitAudioThread != nullptr) {
+ mInitAudioThread->join();
+ }
audioplay::playClip(part.audioData, part.audioLength);
}
@@ -1038,7 +1067,9 @@ bool BootAnimation::playSoundsAllowed() const {
if (!mSystemBoot) {
return false;
}
-
+ if (mShuttingDown) { // no audio while shutting down
+ return false;
+ }
// Read the system property to see if we should play the sound.
// If it's not present, default to allowed.
if (!property_get_bool(PLAY_SOUND_PROP_NAME, 1)) {
@@ -1064,7 +1095,7 @@ bool BootAnimation::updateIsTimeAccurate() {
if (mTimeIsAccurate) {
return true;
}
-
+ if (mShuttingDown) return true;
struct stat statResult;
if(stat(TIME_FORMAT_12_HOUR_FLAG_FILE_PATH, &statResult) == 0) {
@@ -1188,6 +1219,17 @@ status_t BootAnimation::TimeCheckThread::readyToRun() {
return NO_ERROR;
}
+BootAnimation::InitAudioThread::InitAudioThread(uint8_t* exampleAudioData, int exampleAudioLength)
+ : Thread(false),
+ mExampleAudioData(exampleAudioData),
+ mExampleAudioLength(exampleAudioLength) {}
+
+bool BootAnimation::InitAudioThread::threadLoop() {
+ audioplay::create(mExampleAudioData, mExampleAudioLength);
+ // Exit immediately
+ return false;
+}
+
// ---------------------------------------------------------------------------
}
diff --git a/cmds/bootanimation/BootAnimation.h b/cmds/bootanimation/BootAnimation.h
index 7a2e4c28f767..181ef1c596d1 100644
--- a/cmds/bootanimation/BootAnimation.h
+++ b/cmds/bootanimation/BootAnimation.h
@@ -39,8 +39,7 @@ class SurfaceControl;
class BootAnimation : public Thread, public IBinder::DeathRecipient
{
public:
- BootAnimation();
- virtual ~BootAnimation();
+ BootAnimation();
sp<SurfaceComposerClient> session() const;
@@ -68,6 +67,16 @@ private:
BootAnimation* mBootAnimation;
};
+ class InitAudioThread : public Thread {
+ public:
+ InitAudioThread(uint8_t* exampleAudioData, int mExampleAudioLength);
+ private:
+ virtual bool threadLoop();
+
+ uint8_t* mExampleAudioData;
+ int mExampleAudioLength;
+ };
+
struct Texture {
GLint w;
GLint h;
@@ -154,9 +163,11 @@ private:
bool mTimeIsAccurate;
bool mTimeFormat12Hour;
bool mSystemBoot;
+ bool mShuttingDown;
String8 mZipFileName;
SortedVector<String8> mLoadedFiles;
- sp<TimeCheckThread> mTimeCheckThread;
+ sp<TimeCheckThread> mTimeCheckThread = nullptr;
+ sp<InitAudioThread> mInitAudioThread = nullptr;
};
// ---------------------------------------------------------------------------
diff --git a/cmds/bootanimation/bootanim.rc b/cmds/bootanimation/bootanim.rc
index 7344ba74f70b..469c9646a4aa 100644
--- a/cmds/bootanimation/bootanim.rc
+++ b/cmds/bootanimation/bootanim.rc
@@ -1,7 +1,7 @@
service bootanim /system/bin/bootanimation
- class core
+ class core animation
user graphics
group graphics audio
disabled
oneshot
- writepid /dev/stune/top-app/tasks \ No newline at end of file
+ writepid /dev/stune/top-app/tasks
diff --git a/cmds/bootanimation/bootanimation_main.cpp b/cmds/bootanimation/bootanimation_main.cpp
index 48a34e7dbf62..3689d5ed937e 100644
--- a/cmds/bootanimation/bootanimation_main.cpp
+++ b/cmds/bootanimation/bootanimation_main.cpp
@@ -16,12 +16,16 @@
#define LOG_TAG "BootAnimation"
+#include <stdint.h>
+#include <inttypes.h>
+
#include <binder/IPCThreadState.h>
#include <binder/ProcessState.h>
#include <binder/IServiceManager.h>
#include <cutils/properties.h>
#include <sys/resource.h>
#include <utils/Log.h>
+#include <utils/SystemClock.h>
#include <utils/threads.h>
#include "BootAnimation.h"
@@ -37,17 +41,40 @@ int main()
char value[PROPERTY_VALUE_MAX];
property_get("debug.sf.nobootanimation", value, "0");
int noBootAnimation = atoi(value);
+ if (!noBootAnimation) {
+ property_get("ro.boot.quiescent", value, "0");
+ noBootAnimation = atoi(value);
+ }
ALOGI_IF(noBootAnimation, "boot animation disabled");
if (!noBootAnimation) {
sp<ProcessState> proc(ProcessState::self());
ProcessState::self()->startThreadPool();
+ // TODO: replace this with better waiting logic in future, b/35253872
+ int64_t waitStartTime = elapsedRealtime();
+ sp<IServiceManager> sm = defaultServiceManager();
+ const String16 name("SurfaceFlinger");
+ const int SERVICE_WAIT_SLEEP_MS = 100;
+ const int LOG_PER_RETRIES = 10;
+ int retry = 0;
+ while (sm->checkService(name) == nullptr) {
+ retry++;
+ if ((retry % LOG_PER_RETRIES) == 0) {
+ ALOGW("Waiting for SurfaceFlinger, waited for %" PRId64 " ms",
+ elapsedRealtime() - waitStartTime);
+ }
+ usleep(SERVICE_WAIT_SLEEP_MS * 1000);
+ };
+ int64_t totalWaited = elapsedRealtime() - waitStartTime;
+ if (totalWaited > SERVICE_WAIT_SLEEP_MS) {
+ ALOGI("Waiting for SurfaceFlinger took %" PRId64 " ms", totalWaited);
+ }
+
// create the boot animation object
sp<BootAnimation> boot = new BootAnimation();
IPCThreadState::self()->joinThreadPool();
-
}
return 0;
}
diff --git a/cmds/bu/src/com/android/commands/bu/Backup.java b/cmds/bu/src/com/android/commands/bu/Backup.java
index ffc0f875c79c..345895b794a3 100644
--- a/cmds/bu/src/com/android/commands/bu/Backup.java
+++ b/cmds/bu/src/com/android/commands/bu/Backup.java
@@ -53,15 +53,15 @@ public final class Backup {
String arg = nextArg();
if (arg.equals("backup")) {
- doFullBackup(OsConstants.STDOUT_FILENO);
+ doBackup(OsConstants.STDOUT_FILENO);
} else if (arg.equals("restore")) {
- doFullRestore(OsConstants.STDIN_FILENO);
+ doRestore(OsConstants.STDIN_FILENO);
} else {
- Log.e(TAG, "Invalid operation '" + arg + "'");
+ showUsage();
}
}
- private void doFullBackup(int socketFd) {
+ private void doBackup(int socketFd) {
ArrayList<String> packages = new ArrayList<String>();
boolean saveApks = false;
boolean saveObbs = false;
@@ -70,6 +70,7 @@ public final class Backup {
boolean doWidgets = false;
boolean allIncludesSystem = true;
boolean doCompress = true;
+ boolean doKeyValue = false;
String arg;
while ((arg = nextArg()) != null) {
@@ -100,6 +101,10 @@ public final class Backup {
doCompress = true;
} else if ("-nocompress".equals(arg)) {
doCompress = false;
+ } else if ("-keyvalue".equals(arg)) {
+ doKeyValue = true;
+ } else if ("-nokeyvalue".equals(arg)) {
+ doKeyValue = false;
} else {
Log.w(TAG, "Unknown backup flag " + arg);
continue;
@@ -123,8 +128,8 @@ public final class Backup {
try {
fd = ParcelFileDescriptor.adoptFd(socketFd);
String[] packArray = new String[packages.size()];
- mBackupManager.fullBackup(fd, saveApks, saveObbs, saveShared, doWidgets,
- doEverything, allIncludesSystem, doCompress, packages.toArray(packArray));
+ mBackupManager.adbBackup(fd, saveApks, saveObbs, saveShared, doWidgets, doEverything,
+ allIncludesSystem, doCompress, doKeyValue, packages.toArray(packArray));
} catch (RemoteException e) {
Log.e(TAG, "Unable to invoke backup manager for backup");
} finally {
@@ -136,12 +141,12 @@ public final class Backup {
}
}
- private void doFullRestore(int socketFd) {
+ private void doRestore(int socketFd) {
// No arguments to restore
ParcelFileDescriptor fd = null;
try {
fd = ParcelFileDescriptor.adoptFd(socketFd);
- mBackupManager.fullRestore(fd);
+ mBackupManager.adbRestore(fd);
} catch (RemoteException e) {
Log.e(TAG, "Unable to invoke backup manager for restore");
} finally {
@@ -153,6 +158,21 @@ public final class Backup {
}
}
+ private static void showUsage() {
+ System.err.println(" backup [-f FILE] [-apk|-noapk] [-obb|-noobb] [-shared|-noshared] [-all]");
+ System.err.println(" [-system|-nosystem] [-keyvalue|-nokeyvalue] [PACKAGE...]");
+ System.err.println(" write an archive of the device's data to FILE [default=backup.adb]");
+ System.err.println(" package list optional if -all/-shared are supplied");
+ System.err.println(" -apk/-noapk: do/don't back up .apk files (default -noapk)");
+ System.err.println(" -obb/-noobb: do/don't back up .obb files (default -noobb)");
+ System.err.println(" -shared|-noshared: do/don't back up shared storage (default -noshared)");
+ System.err.println(" -all: back up all installed applications");
+ System.err.println(" -system|-nosystem: include system apps in -all (default -system)");
+ System.err.println(" -keyvalue|-nokeyvalue: include apps that perform key/value backups.");
+ System.err.println(" (default -nokeyvalue)");
+ System.err.println(" restore FILE restore device contents from FILE");
+ }
+
private String nextArg() {
if (mNextArg >= mArgs.length) {
return null;
diff --git a/cmds/content/src/com/android/commands/content/Content.java b/cmds/content/src/com/android/commands/content/Content.java
index d43b8c564e96..3687f10f9974 100644
--- a/cmds/content/src/com/android/commands/content/Content.java
+++ b/cmds/content/src/com/android/commands/content/Content.java
@@ -16,9 +16,10 @@
package com.android.commands.content;
-import android.app.ActivityManagerNative;
+import android.app.ActivityManager;
+import android.app.ContentProviderHolder;
import android.app.IActivityManager;
-import android.app.IActivityManager.ContentProviderHolder;
+import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.IContentProvider;
import android.database.Cursor;
@@ -72,59 +73,64 @@ import libcore.io.IoUtils;
public class Content {
private static final String USAGE =
- "usage: adb shell content [subcommand] [options]\n"
- + "\n"
- + "usage: adb shell content insert --uri <URI> [--user <USER_ID>]"
- + " --bind <BINDING> [--bind <BINDING>...]\n"
- + " <URI> a content provider URI.\n"
- + " <BINDING> binds a typed value to a column and is formatted:\n"
- + " <COLUMN_NAME>:<TYPE>:<COLUMN_VALUE> where:\n"
- + " <TYPE> specifies data type such as:\n"
- + " b - boolean, s - string, i - integer, l - long, f - float, d - double\n"
- + " Note: Omit the value for passing an empty string, e.g column:s:\n"
- + " Example:\n"
- + " # Add \"new_setting\" secure setting with value \"new_value\".\n"
- + " adb shell content insert --uri content://settings/secure --bind name:s:new_setting"
- + " --bind value:s:new_value\n"
- + "\n"
- + "usage: adb shell content update --uri <URI> [--user <USER_ID>] [--where <WHERE>]\n"
- + " <WHERE> is a SQL style where clause in quotes (You have to escape single quotes"
- + " - see example below).\n"
- + " Example:\n"
- + " # Change \"new_setting\" secure setting to \"newer_value\".\n"
- + " adb shell content update --uri content://settings/secure --bind"
- + " value:s:newer_value --where \"name=\'new_setting\'\"\n"
- + "\n"
- + "usage: adb shell content delete --uri <URI> [--user <USER_ID>] --bind <BINDING>"
- + " [--bind <BINDING>...] [--where <WHERE>]\n"
- + " Example:\n"
- + " # Remove \"new_setting\" secure setting.\n"
- + " adb shell content delete --uri content://settings/secure "
- + "--where \"name=\'new_setting\'\"\n"
- + "\n"
- + "usage: adb shell content query --uri <URI> [--user <USER_ID>]"
- + " [--projection <PROJECTION>] [--where <WHERE>] [--sort <SORT_ORDER>]\n"
- + " <PROJECTION> is a list of colon separated column names and is formatted:\n"
- + " <COLUMN_NAME>[:<COLUMN_NAME>...]\n"
- + " <SORT_ORDER> is the order in which rows in the result should be sorted.\n"
- + " Example:\n"
- + " # Select \"name\" and \"value\" columns from secure settings where \"name\" is "
- + "equal to \"new_setting\" and sort the result by name in ascending order.\n"
- + " adb shell content query --uri content://settings/secure --projection name:value"
- + " --where \"name=\'new_setting\'\" --sort \"name ASC\"\n"
- + "\n"
- + "usage: adb shell content call --uri <URI> --method <METHOD> [--arg <ARG>]\n"
- + " [--extra <BINDING> ...]\n"
- + " <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";
+ "usage: adb shell content [subcommand] [options]\n"
+ + "\n"
+ + "usage: adb shell content insert --uri <URI> [--user <USER_ID>]"
+ + " --bind <BINDING> [--bind <BINDING>...]\n"
+ + " <URI> a content provider URI.\n"
+ + " <BINDING> binds a typed value to a column and is formatted:\n"
+ + " <COLUMN_NAME>:<TYPE>:<COLUMN_VALUE> where:\n"
+ + " <TYPE> specifies data type such as:\n"
+ + " b - boolean, s - string, i - integer, l - long, f - float, d - double\n"
+ + " Note: Omit the value for passing an empty string, e.g column:s:\n"
+ + " Example:\n"
+ + " # Add \"new_setting\" secure setting with value \"new_value\".\n"
+ + " adb shell content insert --uri content://settings/secure --bind name:s:new_setting"
+ + " --bind value:s:new_value\n"
+ + "\n"
+ + "usage: adb shell content update --uri <URI> [--user <USER_ID>] [--where <WHERE>]\n"
+ + " <WHERE> is a SQL style where clause in quotes (You have to escape single quotes"
+ + " - see example below).\n"
+ + " Example:\n"
+ + " # Change \"new_setting\" secure setting to \"newer_value\".\n"
+ + " adb shell content update --uri content://settings/secure --bind"
+ + " value:s:newer_value --where \"name=\'new_setting\'\"\n"
+ + "\n"
+ + "usage: adb shell content delete --uri <URI> [--user <USER_ID>] --bind <BINDING>"
+ + " [--bind <BINDING>...] [--where <WHERE>]\n"
+ + " Example:\n"
+ + " # Remove \"new_setting\" secure setting.\n"
+ + " adb shell content delete --uri content://settings/secure "
+ + "--where \"name=\'new_setting\'\"\n"
+ + "\n"
+ + "usage: adb shell content query --uri <URI> [--user <USER_ID>]"
+ + " [--projection <PROJECTION>] [--where <WHERE>] [--sort <SORT_ORDER>]\n"
+ + " <PROJECTION> is a list of colon separated column names and is formatted:\n"
+ + " <COLUMN_NAME>[:<COLUMN_NAME>...]\n"
+ + " <SORT_ORDER> is the order in which rows in the result should be sorted.\n"
+ + " Example:\n"
+ + " # Select \"name\" and \"value\" columns from secure settings where \"name\" is "
+ + "equal to \"new_setting\" and sort the result by name in ascending order.\n"
+ + " adb shell content query --uri content://settings/secure --projection name:value"
+ + " --where \"name=\'new_setting\'\" --sort \"name ASC\"\n"
+ + "\n"
+ + "usage: adb shell content call --uri <URI> --method <METHOD> [--arg <ARG>]\n"
+ + " [--extra <BINDING> ...]\n"
+ + " <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"
+ + "usage: adb shell content gettype --uri <URI> [--user <USER_ID>]\n"
+ + " Example:\n"
+ + " # Show the mime-type of the URI\n"
+ + " adb shell content gettype --uri content://media/internal/audio/media/\n"
+ + "\n";
private static class Parser {
private static final String ARGUMENT_INSERT = "insert";
@@ -133,6 +139,7 @@ public class Content {
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_GET_TYPE = "gettype";
private static final String ARGUMENT_WHERE = "--where";
private static final String ARGUMENT_BIND = "--bind";
private static final String ARGUMENT_URI = "--uri";
@@ -172,6 +179,8 @@ public class Content {
return parseCallCommand();
} else if (ARGUMENT_READ.equals(operation)) {
return parseReadCommand();
+ } else if (ARGUMENT_GET_TYPE.equals(operation)) {
+ return parseGetTypeCommand();
} else {
throw new IllegalArgumentException("Unsupported operation: " + operation);
}
@@ -291,6 +300,26 @@ public class Content {
return new CallCommand(uri, userId, method, arg, values);
}
+ private GetTypeCommand parseGetTypeCommand() {
+ Uri uri = null;
+ int userId = UserHandle.USER_SYSTEM;
+
+ 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 GetTypeCommand(uri, userId);
+ }
+
private ReadCommand parseReadCommand() {
Uri uri = null;
int userId = UserHandle.USER_SYSTEM;
@@ -405,7 +434,7 @@ public class Content {
public final void execute() {
String providerName = mUri.getAuthority();
try {
- IActivityManager activityManager = ActivityManagerNative.getDefault();
+ IActivityManager activityManager = ActivityManager.getService();
IContentProvider provider = null;
IBinder token = new Binder();
try {
@@ -511,6 +540,18 @@ public class Content {
}
}
+ private static class GetTypeCommand extends Command {
+ public GetTypeCommand(Uri uri, int userId) {
+ super(uri, userId);
+ }
+
+ @Override
+ public void onExecute(IContentProvider provider) throws Exception {
+ String type = provider.getType(mUri);
+ System.out.println("Result: " + type);
+ }
+ }
+
private static class ReadCommand extends Command {
public ReadCommand(Uri uri, int userId) {
super(uri, userId);
@@ -549,8 +590,8 @@ public class Content {
@Override
public void onExecute(IContentProvider provider) throws Exception {
- Cursor cursor = provider.query(resolveCallingPackage(), mUri, mProjection, mWhere,
- null, mSortOrder, null);
+ Cursor cursor = provider.query(resolveCallingPackage(), mUri, mProjection,
+ ContentResolver.createSqlQueryBundle(mWhere, null, mSortOrder), null);
if (cursor == null) {
System.out.println("No result found.");
return;
diff --git a/cmds/dpm/src/com/android/commands/dpm/Dpm.java b/cmds/dpm/src/com/android/commands/dpm/Dpm.java
index 31c742153f24..3ac70d668198 100644
--- a/cmds/dpm/src/com/android/commands/dpm/Dpm.java
+++ b/cmds/dpm/src/com/android/commands/dpm/Dpm.java
@@ -16,7 +16,7 @@
package com.android.commands.dpm;
-import android.app.ActivityManagerNative;
+import android.app.ActivityManager;
import android.app.IActivityManager;
import android.app.admin.DevicePolicyManager;
import android.app.admin.IDevicePolicyManager;
@@ -117,7 +117,7 @@ public final class Dpm extends BaseCommand {
mUserId = parseInt(arg);
}
if (mUserId == UserHandle.USER_CURRENT) {
- IActivityManager activityManager = ActivityManagerNative.getDefault();
+ IActivityManager activityManager = ActivityManager.getService();
try {
mUserId = activityManager.getCurrentUser().id;
} catch (RemoteException e) {
diff --git a/cmds/idmap/Android.mk b/cmds/idmap/Android.mk
index 50ccb07a3826..aeb8a0c001ec 100644
--- a/cmds/idmap/Android.mk
+++ b/cmds/idmap/Android.mk
@@ -17,7 +17,7 @@ include $(CLEAR_VARS)
LOCAL_SRC_FILES := idmap.cpp create.cpp scan.cpp inspect.cpp
-LOCAL_SHARED_LIBRARIES := liblog libutils libandroidfw
+LOCAL_SHARED_LIBRARIES := liblog libutils libandroidfw libcutils
LOCAL_MODULE := idmap
diff --git a/cmds/idmap/create.cpp b/cmds/idmap/create.cpp
index c13d318f7449..524db14f7aab 100644
--- a/cmds/idmap/create.cpp
+++ b/cmds/idmap/create.cpp
@@ -221,3 +221,9 @@ int idmap_create_fd(const char *target_apk_path, const char *overlay_apk_path, i
return create_and_write_idmap(target_apk_path, overlay_apk_path, fd, true) == 0 ?
EXIT_SUCCESS : EXIT_FAILURE;
}
+
+int idmap_verify_fd(const char *target_apk_path, const char *overlay_apk_path, int fd)
+{
+ return !is_idmap_stale_fd(target_apk_path, overlay_apk_path, fd) ?
+ EXIT_SUCCESS : EXIT_FAILURE;
+}
diff --git a/cmds/idmap/idmap.cpp b/cmds/idmap/idmap.cpp
index 3ab191553625..8f86ed8f7d32 100644
--- a/cmds/idmap/idmap.cpp
+++ b/cmds/idmap/idmap.cpp
@@ -16,6 +16,7 @@ SYNOPSIS \n\
idmap --scan target-package-name-to-look-for path-to-target-apk dir-to-hold-idmaps \\\
dir-to-scan [additional-dir-to-scan [additional-dir-to-scan [...]]]\n\
idmap --inspect idmap \n\
+ idmap --verify target overlay fd \n\
\n\
DESCRIPTION \n\
Idmap files play an integral part in the runtime resource overlay framework. An idmap \n\
@@ -49,14 +50,17 @@ OPTIONS \n\
--path: create idmap for target package 'target' (path to apk) and overlay package \n\
'overlay' (path to apk); write results to 'idmap' (path). \n\
\n\
- --scan: non-recursively search directory 'dir-to-scan' (path) for overlay packages with \n\
- target package 'target-package-name-to-look-for' (package name) present at\n\
+ --scan: non-recursively search directory 'dir-to-scan' (path) for static overlay packages \n\
+ with target package 'target-package-name-to-look-for' (package name) present at\n\
'path-to-target-apk' (path to apk). For each overlay package found, create an\n\
idmap file in 'dir-to-hold-idmaps' (path). \n\
\n\
--inspect: decode the binary format of 'idmap' (path) and display the contents in a \n\
debug-friendly format. \n\
\n\
+ --verify: verify if idmap corresponding to file descriptor 'fd' (integer) is made from \n\
+ target package 'target' (path to apk) and overlay package 'overlay'. \n\
+\n\
EXAMPLES \n\
Create an idmap file: \n\
\n\
@@ -167,6 +171,29 @@ NOTES \n\
return idmap_create_path(target_apk_path, overlay_apk_path, idmap_path);
}
+ int maybe_verify_fd(const char *target_apk_path, const char *overlay_apk_path,
+ const char *idmap_str)
+ {
+ char *endptr;
+ int idmap_fd = strtol(idmap_str, &endptr, 10);
+ if (*endptr != '\0') {
+ fprintf(stderr, "error: failed to parse file descriptor argument %s\n", idmap_str);
+ return -1;
+ }
+
+ if (!verify_file_readable(target_apk_path)) {
+ ALOGD("error: failed to read apk %s: %s\n", target_apk_path, strerror(errno));
+ return -1;
+ }
+
+ if (!verify_file_readable(overlay_apk_path)) {
+ ALOGD("error: failed to read apk %s: %s\n", overlay_apk_path, strerror(errno));
+ return -1;
+ }
+
+ return idmap_verify_fd(target_apk_path, overlay_apk_path, idmap_fd);
+ }
+
int maybe_scan(const char *target_package_name, const char *target_apk_path,
const char *idmap_dir, const android::Vector<const char *> *overlay_dirs)
{
@@ -235,6 +262,10 @@ int main(int argc, char **argv)
return maybe_create_path(argv[2], argv[3], argv[4]);
}
+ if (argc == 5 && !strcmp(argv[1], "--verify")) {
+ return maybe_verify_fd(argv[2], argv[3], argv[4]);
+ }
+
if (argc >= 6 && !strcmp(argv[1], "--scan")) {
android::Vector<const char *> v;
for (int i = 5; i < argc; i++) {
diff --git a/cmds/idmap/idmap.h b/cmds/idmap/idmap.h
index 8d4210bcb443..5962108c9f7e 100644
--- a/cmds/idmap/idmap.h
+++ b/cmds/idmap/idmap.h
@@ -25,6 +25,8 @@ int idmap_create_path(const char *target_apk_path, const char *overlay_apk_path,
int idmap_create_fd(const char *target_apk_path, const char *overlay_apk_path, int fd);
+int idmap_verify_fd(const char *target_apk_path, const char *overlay_apk_path, int fd);
+
// Regarding target_package_name: the idmap_scan implementation should
// be able to extract this from the manifest in target_apk_path,
// simplifying the external API.
diff --git a/cmds/idmap/inspect.cpp b/cmds/idmap/inspect.cpp
index f6afc8594309..154cb25a02a1 100644
--- a/cmds/idmap/inspect.cpp
+++ b/cmds/idmap/inspect.cpp
@@ -2,6 +2,7 @@
#include <androidfw/AssetManager.h>
#include <androidfw/ResourceTypes.h>
+#include <utils/ByteOrder.h>
#include <utils/String8.h>
#include <fcntl.h>
diff --git a/cmds/idmap/scan.cpp b/cmds/idmap/scan.cpp
index ab6adfb9475f..d69dd79555a1 100644
--- a/cmds/idmap/scan.cpp
+++ b/cmds/idmap/scan.cpp
@@ -9,6 +9,8 @@
#include <androidfw/ResourceTypes.h>
#include <androidfw/StreamingZipInflater.h>
#include <androidfw/ZipFileRO.h>
+#include <cutils/jstring.h>
+#include <cutils/properties.h>
#include <private/android_filesystem_config.h> // for AID_SYSTEM
#include <utils/SortedVector.h>
#include <utils/String16.h>
@@ -81,11 +83,26 @@ namespace {
return String8(tmp);
}
- int parse_overlay_tag(const ResXMLTree& parser, const char *target_package_name)
+ bool check_property(String16 property, String16 value) {
+ const char *prop;
+ const char *val;
+
+ prop = strndup16to8(property.string(), property.size());
+ char propBuf[PROPERTY_VALUE_MAX];
+ property_get(prop, propBuf, NULL);
+ val = strndup16to8(value.string(), value.size());
+
+ return (strcmp(propBuf, val) == 0);
+ }
+
+ int parse_overlay_tag(const ResXMLTree& parser, const char *target_package_name,
+ bool* is_static_overlay)
{
const size_t N = parser.getAttributeCount();
String16 target;
int priority = -1;
+ String16 propName = String16();
+ String16 propValue = String16();
for (size_t i = 0; i < N; ++i) {
size_t len;
String16 key(parser.getAttributeName(i, &len));
@@ -102,8 +119,33 @@ namespace {
return -1;
}
}
+ } else if (key == String16("isStatic")) {
+ Res_value v;
+ if (parser.getAttributeValue(i, &v) == sizeof(Res_value)) {
+ *is_static_overlay = (v.data != 0);
+ }
+ } else if (key == String16("requiredSystemPropertyName")) {
+ const char16_t *p = parser.getAttributeStringValue(i, &len);
+ if (p != NULL) {
+ propName = String16(p, len);
+ }
+ } else if (key == String16("requiredSystemPropertyValue")) {
+ const char16_t *p = parser.getAttributeStringValue(i, &len);
+ if (p != NULL) {
+ propValue = String16(p, len);
+ }
+ }
+ }
+
+ // Note that conditional property enablement/exclusion only applies if
+ // the attribute is present. In its absence, all overlays are presumed enabled.
+ if (propName.size() > 0 && propValue.size() > 0) {
+ // if property set & equal to value, then include overlay - otherwise skip
+ if (!check_property(propName, propValue)) {
+ return NO_OVERLAY_TAG;
}
}
+
if (target == String16(target_package_name)) {
return priority;
}
@@ -120,17 +162,23 @@ namespace {
}
ResXMLParser::event_code_t type;
+ bool is_static_overlay = false;
+ int priority = NO_OVERLAY_TAG;
do {
type = parser.next();
if (type == ResXMLParser::START_TAG) {
size_t len;
String16 tag(parser.getElementName(&len));
if (tag == String16("overlay")) {
- return parse_overlay_tag(parser, target_package_name);
+ priority = parse_overlay_tag(parser, target_package_name, &is_static_overlay);
+ break;
}
}
} while (type != ResXMLParser::BAD_DOCUMENT && type != ResXMLParser::END_DOCUMENT);
+ if (is_static_overlay) {
+ return priority;
+ }
return NO_OVERLAY_TAG;
}
@@ -237,3 +285,4 @@ int idmap_scan(const char *target_package_name, const char *target_apk_path,
return EXIT_SUCCESS;
}
+
diff --git a/cmds/incident/Android.mk b/cmds/incident/Android.mk
new file mode 100644
index 000000000000..e1c9b93a8e6d
--- /dev/null
+++ b/cmds/incident/Android.mk
@@ -0,0 +1,48 @@
+# Copyright (C) 2016 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.
+
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+ main.cpp
+
+LOCAL_MODULE := incident
+
+LOCAL_SHARED_LIBRARIES := \
+ libbase \
+ libbinder \
+ libcutils \
+ liblog \
+ libutils \
+ libincident
+
+LOCAL_CFLAGS += \
+ -Wall -Werror -Wno-missing-field-initializers -Wno-unused-variable -Wunused-parameter
+
+LOCAL_MODULE_CLASS := EXECUTABLES
+gen_src_dir := $(local-generated-sources-dir)
+
+gen := $(gen_src_dir)/incident_sections.cpp
+$(gen): $(HOST_OUT_EXECUTABLES)/incident-section-gen
+$(gen): PRIVATE_CUSTOM_TOOL = \
+ $(HOST_OUT_EXECUTABLES)/incident-section-gen > $@
+$(gen): $(HOST_OUT_EXECUTABLES)/incident-section-gen
+ $(transform-generated-source)
+LOCAL_GENERATED_SOURCES += $(gen)
+
+gen_src_dir:=
+gen:=
+
+include $(BUILD_EXECUTABLE)
diff --git a/cmds/incident/incident_sections.h b/cmds/incident/incident_sections.h
new file mode 100644
index 000000000000..1972088fc82c
--- /dev/null
+++ b/cmds/incident/incident_sections.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2016 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 INCIDENT_SECTIONS_H
+#define INCIDENT_SECTIONS_H
+
+struct IncidentSection
+{
+ int id;
+ char const* name;
+};
+
+extern IncidentSection const INCIDENT_SECTIONS[];
+extern const int INCIDENT_SECTION_COUNT;
+
+#endif // INCIDENT_SECTIONS_H
diff --git a/cmds/incident/main.cpp b/cmds/incident/main.cpp
new file mode 100644
index 000000000000..91b7c22b2038
--- /dev/null
+++ b/cmds/incident/main.cpp
@@ -0,0 +1,236 @@
+/*
+ * Copyright (C) 2016 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_TAG "incident"
+
+#include "incident_sections.h"
+
+#include <android/os/BnIncidentReportStatusListener.h>
+#include <android/os/IIncidentManager.h>
+#include <android/os/IncidentReportArgs.h>
+#include <binder/IPCThreadState.h>
+#include <binder/IServiceManager.h>
+#include <utils/Looper.h>
+
+#include <fcntl.h>
+#include <getopt.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+using namespace android;
+using namespace android::base;
+using namespace android::binder;
+using namespace android::os;
+
+// ================================================================================
+class StatusListener : public BnIncidentReportStatusListener {
+public:
+ StatusListener();
+ virtual ~StatusListener();
+
+ virtual Status onReportStarted();
+ virtual Status onReportSectionStatus(int32_t section, int32_t status);
+ virtual Status onReportServiceStatus(const String16& service, int32_t status);
+ virtual Status onReportFinished();
+ virtual Status onReportFailed();
+};
+
+StatusListener::StatusListener()
+{
+}
+
+StatusListener::~StatusListener()
+{
+}
+
+Status
+StatusListener::onReportStarted()
+{
+ return Status::ok();
+}
+
+Status
+StatusListener::onReportSectionStatus(int32_t section, int32_t status)
+{
+ fprintf(stderr, "section %d status %d\n", section, status);
+ return Status::ok();
+}
+
+Status
+StatusListener::onReportServiceStatus(const String16& service, int32_t status)
+{
+ fprintf(stderr, "service '%s' status %d\n", String8(service).string(), status);
+ return Status::ok();
+}
+
+Status
+StatusListener::onReportFinished()
+{
+ fprintf(stderr, "done\n");
+ exit(0);
+ return Status::ok();
+}
+
+Status
+StatusListener::onReportFailed()
+{
+ fprintf(stderr, "failed\n");
+ exit(1);
+ return Status::ok();
+}
+
+// ================================================================================
+static IncidentSection const*
+find_section(const char* name)
+{
+ size_t low = 0;
+ size_t high = INCIDENT_SECTION_COUNT - 1;
+
+ while (low <= high) {
+ size_t mid = (low + high) >> 1;
+ IncidentSection const* section = INCIDENT_SECTIONS + mid;
+
+ int cmp = strcmp(section->name, name);
+ if (cmp < 0) {
+ low = mid + 1;
+ } else if (cmp > 0) {
+ high = mid - 1;
+ } else {
+ return section;
+ }
+ }
+ return NULL;
+}
+
+// ================================================================================
+static void
+usage(FILE* out)
+{
+ fprintf(out, "usage: incident OPTIONS [SECTION...]\n");
+ fprintf(out, "\n");
+ fprintf(out, "Takes an incident report.\n");
+ fprintf(out, "\n");
+ fprintf(out, "OPTIONS\n");
+ fprintf(out, " -b (default) print the report to stdout (in proto format)\n");
+ fprintf(out, " -d send the report into dropbox\n");
+ fprintf(out, "\n");
+ fprintf(out, " SECTION the field numbers of the incident report fields to include\n");
+ fprintf(out, "\n");
+}
+
+int
+main(int argc, char** argv)
+{
+ Status status;
+ IncidentReportArgs args;
+ enum { DEST_DROPBOX, DEST_STDOUT } destination = DEST_STDOUT;
+
+ // Parse the args
+ int opt;
+ while ((opt = getopt(argc, argv, "bhd")) != -1) {
+ switch (opt) {
+ case 'b':
+ destination = DEST_STDOUT;
+ break;
+ case 'h':
+ usage(stdout);
+ return 0;
+ case 'd':
+ destination = DEST_DROPBOX;
+ break;
+ default:
+ usage(stderr);
+ return 1;
+ }
+ }
+
+ if (optind == argc) {
+ args.setAll(true);
+ } else {
+ for (int i=optind; i<argc; i++) {
+ const char* arg = argv[i];
+ char* end;
+ if (arg[0] != '\0') {
+ int section = strtol(arg, &end, 0);
+ if (*end == '\0') {
+ args.addSection(section);
+ } else {
+ IncidentSection const* ic = find_section(arg);
+ if (ic == NULL) {
+ fprintf(stderr, "Invalid section: %s\n", arg);
+ return 1;
+ }
+ args.addSection(ic->id);
+ }
+ }
+ }
+ }
+
+
+
+ // Start the thread pool.
+ sp<ProcessState> ps(ProcessState::self());
+ ps->startThreadPool();
+ ps->giveThreadPoolName();
+
+ // Look up the service
+ sp<IIncidentManager> service = interface_cast<IIncidentManager>(
+ defaultServiceManager()->getService(android::String16("incident")));
+ if (service == NULL) {
+ fprintf(stderr, "Couldn't look up the incident service\n");
+ return 1;
+ }
+
+ // Construct the stream
+ int fds[2];
+ pipe(fds);
+
+ unique_fd readEnd(fds[0]);
+ unique_fd writeEnd(fds[1]);
+
+ if (destination == DEST_STDOUT) {
+ // Call into the service
+ sp<StatusListener> listener(new StatusListener());
+ status = service->reportIncidentToStream(args, listener, writeEnd);
+
+ if (!status.isOk()) {
+ fprintf(stderr, "reportIncident returned \"%s\"\n", status.toString8().string());
+ }
+
+ // Wait for the result and print out the data they send.
+ //IPCThreadState::self()->joinThreadPool();
+
+ while (true) {
+ int amt = splice(fds[0], NULL, STDOUT_FILENO, NULL, 4096, 0);
+ fprintf(stderr, "spliced %d bytes\n", amt);
+ if (amt < 0) {
+ return errno;
+ } else if (amt == 0) {
+ return 0;
+ }
+ }
+ } else {
+ status = service->reportIncident(args);
+ if (!status.isOk()) {
+ fprintf(stderr, "reportIncident returned \"%s\"\n", status.toString8().string());
+ return 1;
+ } else {
+ return 0;
+ }
+ }
+
+}
diff --git a/cmds/incidentd/Android.mk b/cmds/incidentd/Android.mk
new file mode 100644
index 000000000000..bacf672e81b5
--- /dev/null
+++ b/cmds/incidentd/Android.mk
@@ -0,0 +1,56 @@
+# Copyright (C) 2016 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.
+
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := incidentd
+
+LOCAL_SRC_FILES := \
+ src/FdBuffer.cpp \
+ src/IncidentService.cpp \
+ src/Reporter.cpp \
+ src/Section.cpp \
+ src/main.cpp \
+ src/protobuf.cpp \
+ src/report_directory.cpp \
+ src/section_list.cpp
+
+LOCAL_CFLAGS += \
+ -Wall -Werror -Wno-missing-field-initializers -Wno-unused-variable -Wunused-parameter
+
+ifeq (debug,)
+ LOCAL_CFLAGS += \
+ -g -O0
+else
+ # optimize for size (protobuf glop can get big)
+ LOCAL_CFLAGS += \
+ -Os
+endif
+
+LOCAL_SHARED_LIBRARIES := \
+ libbase \
+ libbinder \
+ libcutils \
+ libincident \
+ liblog \
+ libselinux \
+ libservices \
+ libutils
+
+ifeq (BUILD_WITH_INCIDENTD_RC,true)
+LOCAL_INIT_RC := incidentd.rc
+endif
+
+include $(BUILD_EXECUTABLE)
diff --git a/cmds/incidentd/incidentd.rc b/cmds/incidentd/incidentd.rc
new file mode 100644
index 000000000000..d11e3cf70567
--- /dev/null
+++ b/cmds/incidentd/incidentd.rc
@@ -0,0 +1,16 @@
+# Copyright (C) 2016 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.
+
+#service incidentd /system/bin/incidentd
+# class main
diff --git a/cmds/incidentd/src/FdBuffer.cpp b/cmds/incidentd/src/FdBuffer.cpp
new file mode 100644
index 000000000000..527d7eef3a96
--- /dev/null
+++ b/cmds/incidentd/src/FdBuffer.cpp
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2016 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_TAG "incidentd"
+
+#include "FdBuffer.h"
+
+#include <cutils/log.h>
+#include <utils/SystemClock.h>
+
+#include <fcntl.h>
+#include <poll.h>
+#include <unistd.h>
+
+const ssize_t BUFFER_SIZE = 16 * 1024;
+const ssize_t MAX_BUFFER_COUNT = 256; // 4 MB max
+
+
+FdBuffer::FdBuffer()
+ :mBuffers(),
+ mStartTime(-1),
+ mFinishTime(-1),
+ mCurrentWritten(-1),
+ mTimedOut(false),
+ mTruncated(false)
+{
+}
+
+FdBuffer::~FdBuffer()
+{
+ const int N = mBuffers.size();
+ for (int i=0; i<N; i++) {
+ uint8_t* buf = mBuffers[i];
+ free(buf);
+ }
+}
+
+status_t
+FdBuffer::read(int fd, int64_t timeout)
+{
+ struct pollfd pfds = {
+ .fd = fd,
+ .events = POLLIN
+ };
+ mStartTime = uptimeMillis();
+
+ fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) | O_NONBLOCK);
+
+ uint8_t* buf = NULL;
+ while (true) {
+ if (mCurrentWritten >= BUFFER_SIZE || mCurrentWritten < 0) {
+ if (mBuffers.size() == MAX_BUFFER_COUNT) {
+ mTruncated = true;
+ break;
+ }
+ buf = (uint8_t*)malloc(BUFFER_SIZE);
+ if (buf == NULL) {
+ return NO_MEMORY;
+ }
+ mBuffers.push_back(buf);
+ mCurrentWritten = 0;
+ }
+
+ int64_t remainingTime = (mStartTime + timeout) - uptimeMillis();
+ if (remainingTime <= 0) {
+ mTimedOut = true;
+ break;
+ }
+
+ int count = poll(&pfds, 1, remainingTime);
+ if (count == 0) {
+ mTimedOut = true;
+ break;
+ } else if (count < 0) {
+ return -errno;
+ } else {
+ if ((pfds.revents & POLLERR) != 0) {
+ return errno != 0 ? -errno : UNKNOWN_ERROR;
+ } else {
+ ssize_t amt = ::read(fd, buf + mCurrentWritten, BUFFER_SIZE - mCurrentWritten);
+ if (amt < 0) {
+ if (errno == EAGAIN || errno == EWOULDBLOCK) {
+ continue;
+ } else {
+ return -errno;
+ }
+ } else if (amt == 0) {
+ break;
+ }
+ mCurrentWritten += amt;
+ }
+ }
+ }
+
+ mFinishTime = uptimeMillis();
+ return NO_ERROR;
+}
+
+size_t
+FdBuffer::size()
+{
+ return ((mBuffers.size() - 1) * BUFFER_SIZE) + mCurrentWritten;
+}
+
+status_t
+FdBuffer::write(ReportRequestSet* reporter)
+{
+ const int N = mBuffers.size() - 1;
+ for (int i=0; i<N; i++) {
+ reporter->write(mBuffers[i], BUFFER_SIZE);
+ }
+ reporter->write(mBuffers[N], mCurrentWritten);
+ return NO_ERROR;
+}
+
+
diff --git a/cmds/incidentd/src/FdBuffer.h b/cmds/incidentd/src/FdBuffer.h
new file mode 100644
index 000000000000..e12374f21558
--- /dev/null
+++ b/cmds/incidentd/src/FdBuffer.h
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2016 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 FD_BUFFER_H
+#define FD_BUFFER_H
+
+#include "Reporter.h"
+
+#include <utils/Errors.h>
+
+#include <set>
+#include <vector>
+
+using namespace android;
+using namespace std;
+
+/**
+ * Reads a file into a buffer, and then writes that data to an FdSet.
+ */
+class FdBuffer
+{
+public:
+ FdBuffer();
+ ~FdBuffer();
+
+ /**
+ * Read the data until the timeout is hit or we hit eof.
+ * Returns NO_ERROR if there were no errors or if we timed out.
+ * Will mark the file O_NONBLOCK.
+ */
+ status_t read(int fd, int64_t timeoutMs);
+
+ /**
+ * Whether we timed out.
+ */
+ bool timedOut() { return mTimedOut; }
+
+ /**
+ * If more than 4 MB is read, we truncate the data and return success.
+ * Downstream tools must handle truncated incident reports as best as possible
+ * anyway because they could be cut off for a lot of reasons and it's best
+ * to get as much useful information out of the system as possible. If this
+ * happens, truncated() will return true so it can be marked. If the data is
+ * exactly 4 MB, truncated is still set. Sorry.
+ */
+ bool truncated() { return mTruncated; }
+
+ /**
+ * How much data was read.
+ */
+ size_t size();
+
+ /**
+ * Write the data that we recorded to the fd given.
+ */
+ status_t write(ReportRequestSet* requests);
+
+ /**
+ * How long the read took in milliseconds.
+ */
+ int64_t durationMs() { return mFinishTime - mStartTime; }
+
+private:
+ vector<uint8_t*> mBuffers;
+ int64_t mStartTime;
+ int64_t mFinishTime;
+ ssize_t mCurrentWritten;
+ bool mTimedOut;
+ bool mTruncated;
+};
+
+
+#endif // FD_BUFFER_H
diff --git a/cmds/incidentd/src/IncidentService.cpp b/cmds/incidentd/src/IncidentService.cpp
new file mode 100644
index 000000000000..7c6789e6e5ba
--- /dev/null
+++ b/cmds/incidentd/src/IncidentService.cpp
@@ -0,0 +1,244 @@
+/*
+ * Copyright (C) 2016 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_TAG "incidentd"
+
+#include "IncidentService.h"
+
+#include "Reporter.h"
+
+#include <binder/IPCThreadState.h>
+#include <binder/IServiceManager.h>
+#include <cutils/log.h>
+#include <private/android_filesystem_config.h>
+#include <utils/Looper.h>
+
+#include <unistd.h>
+
+using namespace android;
+
+enum {
+ WHAT_RUN_REPORT = 1,
+ WHAT_SEND_BACKLOG_TO_DROPBOX = 2
+};
+
+//#define DEFAULT_BACKLOG_DELAY_NS (1000000000LL * 60 * 5)
+#define DEFAULT_BACKLOG_DELAY_NS (1000000000LL)
+
+// ================================================================================
+String16 const DUMP_PERMISSION("android.permission.DUMP");
+String16 const USAGE_STATS_PERMISSION("android.permission.PACKAGE_USAGE_STATS");
+
+static Status
+checkIncidentPermissions()
+{
+ if (!checkCallingPermission(DUMP_PERMISSION)) {
+ ALOGW("Calling pid %d and uid %d does not have permission: android.permission.DUMP",
+ IPCThreadState::self()->getCallingPid(), IPCThreadState::self()->getCallingUid());
+ return Status::fromExceptionCode(Status::EX_SECURITY,
+ "Calling process does not have permission: android.permission.DUMP");
+ }
+ if (!checkCallingPermission(USAGE_STATS_PERMISSION)) {
+ ALOGW("Calling pid %d and uid %d does not have permission: android.permission.USAGE_STATS",
+ IPCThreadState::self()->getCallingPid(), IPCThreadState::self()->getCallingUid());
+ return Status::fromExceptionCode(Status::EX_SECURITY,
+ "Calling process does not have permission: android.permission.USAGE_STATS");
+ }
+ return Status::ok();
+}
+
+
+// ================================================================================
+ReportRequestQueue::ReportRequestQueue()
+{
+}
+
+ReportRequestQueue::~ReportRequestQueue()
+{
+}
+
+void
+ReportRequestQueue::addRequest(const sp<ReportRequest>& request)
+{
+ unique_lock<mutex> lock(mLock);
+ mQueue.push_back(request);
+}
+
+sp<ReportRequest>
+ReportRequestQueue::getNextRequest()
+{
+ unique_lock<mutex> lock(mLock);
+ if (mQueue.empty()) {
+ return NULL;
+ } else {
+ sp<ReportRequest> front(mQueue.front());
+ mQueue.pop_front();
+ return front;
+ }
+}
+
+
+// ================================================================================
+ReportHandler::ReportHandler(const sp<Looper>& handlerLooper, const sp<ReportRequestQueue>& queue)
+ :mBacklogDelay(DEFAULT_BACKLOG_DELAY_NS),
+ mHandlerLooper(handlerLooper),
+ mQueue(queue)
+{
+}
+
+ReportHandler::~ReportHandler()
+{
+}
+
+void
+ReportHandler::handleMessage(const Message& message)
+{
+ switch (message.what) {
+ case WHAT_RUN_REPORT:
+ run_report();
+ break;
+ case WHAT_SEND_BACKLOG_TO_DROPBOX:
+ send_backlog_to_dropbox();
+ break;
+ }
+}
+
+void
+ReportHandler::scheduleRunReport(const sp<ReportRequest>& request)
+{
+ mQueue->addRequest(request);
+ mHandlerLooper->removeMessages(this, WHAT_RUN_REPORT);
+ mHandlerLooper->sendMessage(this, Message(WHAT_RUN_REPORT));
+}
+
+void
+ReportHandler::scheduleSendBacklogToDropbox()
+{
+ unique_lock<mutex> lock(mLock);
+ mBacklogDelay = DEFAULT_BACKLOG_DELAY_NS;
+ schedule_send_backlog_to_dropbox_locked();
+}
+
+void
+ReportHandler::schedule_send_backlog_to_dropbox_locked()
+{
+ mHandlerLooper->removeMessages(this, WHAT_SEND_BACKLOG_TO_DROPBOX);
+ mHandlerLooper->sendMessageDelayed(mBacklogDelay, this,
+ Message(WHAT_SEND_BACKLOG_TO_DROPBOX));
+}
+
+void
+ReportHandler::run_report()
+{
+ sp<Reporter> reporter = new Reporter();
+
+ // Merge all of the requests into one that has all of the
+ // requested fields.
+ while (true) {
+ sp<ReportRequest> request = mQueue->getNextRequest();
+ if (request == NULL) {
+ break;
+ }
+ reporter->batch.add(request);
+ reporter->args.merge(request->args);
+ }
+
+ // Take the report, which might take a while. More requests might queue
+ // up while we're doing this, and we'll handle them in their next batch.
+ // TODO: We should further rate-limit the reports to no more than N per time-period.
+ Reporter::run_report_status_t reportStatus = reporter->runReport();
+ if (reportStatus == Reporter::REPORT_NEEDS_DROPBOX) {
+ unique_lock<mutex> lock(mLock);
+ schedule_send_backlog_to_dropbox_locked();
+ }
+}
+
+void
+ReportHandler::send_backlog_to_dropbox()
+{
+ if (Reporter::upload_backlog() == Reporter::REPORT_NEEDS_DROPBOX) {
+ // There was a failure. Exponential backoff.
+ unique_lock<mutex> lock(mLock);
+ mBacklogDelay *= 2;
+ ALOGI("Error sending to dropbox. Trying again in %lld minutes",
+ (mBacklogDelay / (1000000000LL * 60)));
+ schedule_send_backlog_to_dropbox_locked();
+ } else {
+ mBacklogDelay = DEFAULT_BACKLOG_DELAY_NS;
+ }
+}
+
+// ================================================================================
+IncidentService::IncidentService(const sp<Looper>& handlerLooper)
+ :mQueue(new ReportRequestQueue())
+{
+ mHandler = new ReportHandler(handlerLooper, mQueue);
+}
+
+IncidentService::~IncidentService()
+{
+}
+
+Status
+IncidentService::reportIncident(const IncidentReportArgs& args)
+{
+ ALOGI("reportIncident");
+
+ Status status = checkIncidentPermissions();
+ if (!status.isOk()) {
+ return status;
+ }
+
+ mHandler->scheduleRunReport(new ReportRequest(args, NULL, -1));
+
+ return Status::ok();
+}
+
+Status
+IncidentService::reportIncidentToStream(const IncidentReportArgs& args,
+ const sp<IIncidentReportStatusListener>& listener, const unique_fd& stream)
+{
+ ALOGI("reportIncidentToStream");
+
+ Status status = checkIncidentPermissions();
+ if (!status.isOk()) {
+ return status;
+ }
+
+ int fd = dup(stream.get());
+ if (fd < 0) {
+ return Status::fromStatusT(-errno);
+ }
+
+ mHandler->scheduleRunReport(new ReportRequest(args, listener, fd));
+
+ return Status::ok();
+}
+
+Status
+IncidentService::systemRunning()
+{
+ if (IPCThreadState::self()->getCallingUid() != AID_SYSTEM) {
+ return Status::fromExceptionCode(Status::EX_SECURITY,
+ "Only system uid can call systemRunning");
+ }
+
+ // When system_server is up and running, schedule the dropbox task to run.
+ mHandler->scheduleSendBacklogToDropbox();
+
+ return Status::ok();
+}
+
diff --git a/cmds/incidentd/src/IncidentService.h b/cmds/incidentd/src/IncidentService.h
new file mode 100644
index 000000000000..d6f33dfb1a86
--- /dev/null
+++ b/cmds/incidentd/src/IncidentService.h
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2016 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 INCIDENT_SERVICE_H
+#define INCIDENT_SERVICE_H
+
+#include "Reporter.h"
+
+#include <android/os/BnIncidentManager.h>
+#include <utils/Looper.h>
+
+#include <deque>
+#include <mutex>
+
+using namespace android;
+using namespace android::base;
+using namespace android::binder;
+using namespace android::os;
+using namespace std;
+
+// ================================================================================
+class ReportRequestQueue : public virtual RefBase
+{
+public:
+ ReportRequestQueue();
+ virtual ~ReportRequestQueue();
+
+ void addRequest(const sp<ReportRequest>& request);
+ sp<ReportRequest> getNextRequest();
+
+private:
+ mutex mLock;
+ deque<sp<ReportRequest> > mQueue;
+};
+
+
+// ================================================================================
+class ReportHandler : public MessageHandler
+{
+public:
+ ReportHandler(const sp<Looper>& handlerLooper, const sp<ReportRequestQueue>& queue);
+ virtual ~ReportHandler();
+
+ virtual void handleMessage(const Message& message);
+
+ /**
+ * Adds a ReportRequest to the queue.
+ */
+ void scheduleRunReport(const sp<ReportRequest>& request);
+
+ /**
+ * Resets mBacklogDelay to the default and schedules sending
+ * the messages to dropbox.
+ */
+ void scheduleSendBacklogToDropbox();
+
+private:
+ mutex mLock;
+ nsecs_t mBacklogDelay;
+ sp<Looper> mHandlerLooper;
+ sp<ReportRequestQueue> mQueue;
+
+ /**
+ * Runs all of the reports that have been queued.
+ */
+ void run_report();
+
+ /**
+ * Schedules a dropbox task mBacklogDelay nanoseconds from now.
+ */
+ void schedule_send_backlog_to_dropbox_locked();
+
+ /**
+ * Sends the backlog to the dropbox service.
+ */
+ void send_backlog_to_dropbox();
+};
+
+
+// ================================================================================
+class IncidentService : public BnIncidentManager {
+public:
+ IncidentService(const sp<Looper>& handlerLooper);
+ virtual ~IncidentService();
+
+ virtual Status reportIncident(const IncidentReportArgs& args);
+
+ virtual Status reportIncidentToStream(const IncidentReportArgs& args,
+ const sp<IIncidentReportStatusListener>& listener, const unique_fd& stream);
+
+ virtual Status systemRunning();
+
+private:
+ sp<ReportRequestQueue> mQueue;
+ sp<ReportHandler> mHandler;
+};
+
+
+#endif // INCIDENT_SERVICE_H
diff --git a/cmds/incidentd/src/Reporter.cpp b/cmds/incidentd/src/Reporter.cpp
new file mode 100644
index 000000000000..1ecb291c84a1
--- /dev/null
+++ b/cmds/incidentd/src/Reporter.cpp
@@ -0,0 +1,352 @@
+/*
+ * Copyright (C) 2016 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_TAG "incidentd"
+
+#include "Reporter.h"
+#include "protobuf.h"
+
+#include "report_directory.h"
+#include "section_list.h"
+
+#include <private/android_filesystem_config.h>
+#include <android/os/DropBoxManager.h>
+#include <utils/SystemClock.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include <errno.h>
+
+/**
+ * The directory where the incident reports are stored.
+ */
+static const String8 INCIDENT_DIRECTORY("/data/incidents");
+
+static status_t
+write_all(int fd, uint8_t const* buf, size_t size)
+{
+ while (size > 0) {
+ ssize_t amt = ::write(fd, buf, size);
+ if (amt < 0) {
+ return -errno;
+ }
+ size -= amt;
+ buf += amt;
+ }
+ return NO_ERROR;
+}
+
+// ================================================================================
+ReportRequest::ReportRequest(const IncidentReportArgs& a,
+ const sp<IIncidentReportStatusListener> &l, int f)
+ :args(a),
+ listener(l),
+ fd(f),
+ err(NO_ERROR)
+{
+}
+
+ReportRequest::~ReportRequest()
+{
+}
+
+// ================================================================================
+ReportRequestSet::ReportRequestSet()
+ :mRequests(),
+ mWritableCount(0),
+ mMainFd(-1)
+{
+}
+
+ReportRequestSet::~ReportRequestSet()
+{
+}
+
+void
+ReportRequestSet::add(const sp<ReportRequest>& request)
+{
+ mRequests.push_back(request);
+ mWritableCount++;
+}
+
+void
+ReportRequestSet::setMainFd(int fd)
+{
+ mMainFd = fd;
+ mWritableCount++;
+}
+
+status_t
+ReportRequestSet::write(uint8_t const* buf, size_t size)
+{
+ status_t err = EBADF;
+
+ // The streaming ones
+ int const N = mRequests.size();
+ for (int i=N-1; i>=0; i--) {
+ sp<ReportRequest> request = mRequests[i];
+ if (request->fd >= 0 && request->err == NO_ERROR) {
+ err = write_all(request->fd, buf, size);
+ if (err != NO_ERROR) {
+ request->err = err;
+ mWritableCount--;
+ }
+ }
+ }
+
+ // The dropbox file
+ if (mMainFd >= 0) {
+ err = write_all(mMainFd, buf, size);
+ if (err != NO_ERROR) {
+ mMainFd = -1;
+ mWritableCount--;
+ }
+ }
+
+ // Return an error only when there are no FDs to write.
+ return mWritableCount > 0 ? NO_ERROR : err;
+}
+
+
+// ================================================================================
+Reporter::Reporter()
+ :args(),
+ batch()
+{
+ char buf[100];
+
+ // TODO: Make the max size smaller for user builds.
+ mMaxSize = 100 * 1024 * 1024;
+ mMaxCount = 100;
+
+ // There can't be two at the same time because it's on one thread.
+ mStartTime = time(NULL);
+ strftime(buf, sizeof(buf), "/incident-%Y%m%d-%H%M%S", localtime(&mStartTime));
+ mFilename = INCIDENT_DIRECTORY + buf;
+}
+
+Reporter::~Reporter()
+{
+}
+
+Reporter::run_report_status_t
+Reporter::runReport()
+{
+
+ status_t err = NO_ERROR;
+ bool needMainFd = false;
+ int mainFd = -1;
+
+ // See if we need the main file
+ for (ReportRequestSet::iterator it=batch.begin(); it!=batch.end(); it++) {
+ if ((*it)->fd < 0 && mainFd < 0) {
+ needMainFd = true;
+ break;
+ }
+ }
+ if (needMainFd) {
+ // Create the directory
+ err = create_directory(INCIDENT_DIRECTORY);
+ if (err != NO_ERROR) {
+ goto done;
+ }
+
+ // If there are too many files in the directory (for whatever reason),
+ // delete the oldest ones until it's under the limit. Doing this first
+ // does mean that we can go over, so the max size is not a hard limit.
+ clean_directory(INCIDENT_DIRECTORY, mMaxSize, mMaxCount);
+
+ // Open the file.
+ err = create_file(&mainFd);
+ if (err != NO_ERROR) {
+ goto done;
+ }
+
+ // Add to the set
+ batch.setMainFd(mainFd);
+ }
+
+ // Tell everyone that we're starting.
+ for (ReportRequestSet::iterator it=batch.begin(); it!=batch.end(); it++) {
+ if ((*it)->listener != NULL) {
+ (*it)->listener->onReportStarted();
+ }
+ }
+
+ // Write the incident headers
+ for (ReportRequestSet::iterator it=batch.begin(); it!=batch.end(); it++) {
+ const sp<ReportRequest> request = (*it);
+ const vector<vector<int8_t>>& headers = request->args.headers();
+
+ for (vector<vector<int8_t>>::const_iterator buf=headers.begin(); buf!=headers.end();
+ buf++) {
+ int fd = request->fd >= 0 ? request->fd : mainFd;
+
+ uint8_t buffer[20];
+ uint8_t* p = write_length_delimited_tag_header(buffer, FIELD_ID_INCIDENT_HEADER,
+ buf->size());
+ write_all(fd, buffer, p-buffer);
+
+ write_all(fd, (uint8_t const*)buf->data(), buf->size());
+ // If there was an error now, there will be an error later and we will remove
+ // it from the list then.
+ }
+ }
+
+ // For each of the report fields, see if we need it, and if so, execute the command
+ // and report to those that care that we're doing it.
+ for (const Section** section=SECTION_LIST; *section; section++) {
+ const int id = (*section)->id;
+ ALOGD("Taking incident report section %d '%s'", id, (*section)->name.string());
+
+ if (this->args.containsSection(id)) {
+ // Notify listener of starting
+ for (ReportRequestSet::iterator it=batch.begin(); it!=batch.end(); it++) {
+ if ((*it)->listener != NULL && (*it)->args.containsSection(id)) {
+ (*it)->listener->onReportSectionStatus(id,
+ IIncidentReportStatusListener::STATUS_STARTING);
+ }
+ }
+
+ // Execute - go get the data and write it into the file descriptors.
+ err = (*section)->Execute(&batch);
+ if (err != NO_ERROR) {
+ ALOGW("Incident section %s (%d) failed. Stopping report.",
+ (*section)->name.string(), id);
+ goto done;
+ }
+
+ // Notify listener of starting
+ for (ReportRequestSet::iterator it=batch.begin(); it!=batch.end(); it++) {
+ if ((*it)->listener != NULL && (*it)->args.containsSection(id)) {
+ (*it)->listener->onReportSectionStatus(id,
+ IIncidentReportStatusListener::STATUS_FINISHED);
+ }
+ }
+ }
+ }
+
+done:
+ // Close the file.
+ if (mainFd >= 0) {
+ close(mainFd);
+ }
+
+ // Tell everyone that we're done.
+ for (ReportRequestSet::iterator it=batch.begin(); it!=batch.end(); it++) {
+ if ((*it)->listener != NULL) {
+ if (err == NO_ERROR) {
+ (*it)->listener->onReportFinished();
+ } else {
+ (*it)->listener->onReportFailed();
+ }
+ }
+ }
+
+ // Put the report into dropbox.
+ if (needMainFd && err == NO_ERROR) {
+ sp<DropBoxManager> dropbox = new DropBoxManager();
+ Status status = dropbox->addFile(String16("incident"), mFilename, 0);
+ ALOGD("Incident report done. dropbox status=%s\n", status.toString8().string());
+ if (!status.isOk()) {
+ return REPORT_NEEDS_DROPBOX;
+ }
+
+ // If the status was ok, delete the file. If not, leave it around until the next
+ // boot or the next checkin. If the directory gets too big older files will
+ // be rotated out.
+ unlink(mFilename.c_str());
+ }
+
+ return REPORT_FINISHED;
+}
+
+/**
+ * Create our output file and set the access permissions to -rw-rw----
+ */
+status_t
+Reporter::create_file(int* fd)
+{
+ const char* filename = mFilename.c_str();
+
+ *fd = open(filename, O_CREAT | O_TRUNC | O_RDWR, 0660);
+ if (*fd < 0) {
+ ALOGE("Couldn't open incident file: %s (%s)", filename, strerror(errno));
+ return -errno;
+ }
+
+ // Override umask. Not super critical. If it fails go on with life.
+ chmod(filename, 0660);
+
+ if (chown(filename, AID_SYSTEM, AID_SYSTEM)) {
+ ALOGE("Unable to change ownership of incident file %s: %s\n", filename, strerror(errno));
+ status_t err = -errno;
+ unlink(mFilename.c_str());
+ return err;
+ }
+
+ return NO_ERROR;
+}
+
+// ================================================================================
+Reporter::run_report_status_t
+Reporter::upload_backlog()
+{
+ DIR* dir;
+ struct dirent* entry;
+ struct stat st;
+
+ if ((dir = opendir(INCIDENT_DIRECTORY.string())) == NULL) {
+ ALOGE("Couldn't open incident directory: %s", INCIDENT_DIRECTORY.string());
+ return REPORT_NEEDS_DROPBOX;
+ }
+
+ String8 dirbase(INCIDENT_DIRECTORY + "/");
+ sp<DropBoxManager> dropbox = new DropBoxManager();
+
+ // Enumerate, count and add up size
+ while ((entry = readdir(dir)) != NULL) {
+ if (entry->d_name[0] == '.') {
+ continue;
+ }
+ String8 filename = dirbase + entry->d_name;
+ if (stat(filename.string(), &st) != 0) {
+ ALOGE("Unable to stat file %s", filename.string());
+ continue;
+ }
+ if (!S_ISREG(st.st_mode)) {
+ continue;
+ }
+
+ Status status = dropbox->addFile(String16("incident"), filename.string(), 0);
+ ALOGD("Incident report done. dropbox status=%s\n", status.toString8().string());
+ if (!status.isOk()) {
+ return REPORT_NEEDS_DROPBOX;
+ }
+
+ // If the status was ok, delete the file. If not, leave it around until the next
+ // boot or the next checkin. If the directory gets too big older files will
+ // be rotated out.
+ unlink(filename.string());
+ }
+
+ closedir(dir);
+
+ return REPORT_FINISHED;
+}
+
diff --git a/cmds/incidentd/src/Reporter.h b/cmds/incidentd/src/Reporter.h
new file mode 100644
index 000000000000..5b86561520f8
--- /dev/null
+++ b/cmds/incidentd/src/Reporter.h
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2016 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 REPORTER_H
+#define REPORTER_H
+
+#include <android/os/IIncidentReportStatusListener.h>
+#include <android/os/IncidentReportArgs.h>
+
+#include <string>
+#include <vector>
+
+#include <time.h>
+
+using namespace android;
+using namespace android::os;
+using namespace std;
+
+// ================================================================================
+struct ReportRequest : public virtual RefBase
+{
+ IncidentReportArgs args;
+ sp<IIncidentReportStatusListener> listener;
+ int fd;
+ status_t err;
+
+ ReportRequest(const IncidentReportArgs& args,
+ const sp<IIncidentReportStatusListener> &listener, int fd);
+ virtual ~ReportRequest();
+};
+
+// ================================================================================
+class ReportRequestSet
+{
+public:
+ ReportRequestSet();
+ ~ReportRequestSet();
+
+ void add(const sp<ReportRequest>& request);
+ void setMainFd(int fd);
+
+ // Write to all of the fds for the requests. If a write fails, it stops
+ // writing to that fd and returns NO_ERROR. When we are out of fds to write
+ // to it returns an error.
+ status_t write(uint8_t const* buf, size_t size);
+
+ typedef vector<sp<ReportRequest>>::iterator iterator;
+
+ iterator begin() { return mRequests.begin(); }
+ iterator end() { return mRequests.end(); }
+
+private:
+ vector<sp<ReportRequest>> mRequests;
+ int mWritableCount;
+ int mMainFd;
+};
+
+// ================================================================================
+class Reporter : public virtual RefBase
+{
+public:
+ enum run_report_status_t {
+ REPORT_FINISHED = 0,
+ REPORT_NEEDS_DROPBOX = 1
+ };
+
+ IncidentReportArgs args;
+ ReportRequestSet batch;
+
+ Reporter();
+ virtual ~Reporter();
+
+ // Run the report as described in the batch and args parameters.
+ run_report_status_t runReport();
+
+ static run_report_status_t upload_backlog();
+
+private:
+ string mFilename;
+ off_t mMaxSize;
+ size_t mMaxCount;
+ time_t mStartTime;
+
+ status_t create_file(int* fd);
+};
+
+
+#endif // REPORTER_H
diff --git a/cmds/incidentd/src/Section.cpp b/cmds/incidentd/src/Section.cpp
new file mode 100644
index 000000000000..fac299ed0dcd
--- /dev/null
+++ b/cmds/incidentd/src/Section.cpp
@@ -0,0 +1,292 @@
+/*
+ * Copyright (C) 2016 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_TAG "incidentd"
+
+#include "Section.h"
+#include "protobuf.h"
+
+#include <binder/IServiceManager.h>
+#include <mutex>
+
+using namespace std;
+
+const int64_t REMOTE_CALL_TIMEOUT_MS = 10 * 1000; // 10 seconds
+
+// ================================================================================
+Section::Section(int i)
+ :id(i)
+{
+}
+
+Section::~Section()
+{
+}
+
+status_t
+Section::WriteHeader(ReportRequestSet* requests, size_t size) const
+{
+ ssize_t amt;
+ uint8_t buf[20];
+ uint8_t* p = write_length_delimited_tag_header(buf, this->id, size);
+ return requests->write(buf, p-buf);
+}
+
+// ================================================================================
+struct WorkerThreadData : public virtual RefBase
+{
+ const WorkerThreadSection* section;
+ int fds[2];
+
+ // Lock protects these fields
+ mutex lock;
+ bool workerDone;
+ status_t workerError;
+
+ WorkerThreadData(const WorkerThreadSection* section);
+ virtual ~WorkerThreadData();
+
+ int readFd() { return fds[0]; }
+ int writeFd() { return fds[1]; }
+};
+
+WorkerThreadData::WorkerThreadData(const WorkerThreadSection* sec)
+ :section(sec),
+ workerDone(false),
+ workerError(NO_ERROR)
+{
+ fds[0] = -1;
+ fds[1] = -1;
+}
+
+WorkerThreadData::~WorkerThreadData()
+{
+}
+
+// ================================================================================
+WorkerThreadSection::WorkerThreadSection(int id)
+ :Section(id)
+{
+}
+
+WorkerThreadSection::~WorkerThreadSection()
+{
+}
+
+static void*
+worker_thread_func(void* cookie)
+{
+ WorkerThreadData* data = (WorkerThreadData*)cookie;
+ status_t err = data->section->BlockingCall(data->writeFd());
+
+ {
+ unique_lock<mutex> lock(data->lock);
+ data->workerDone = true;
+ data->workerError = err;
+ }
+
+ close(data->writeFd());
+ data->decStrong(data->section);
+ // data might be gone now. don't use it after this point in this thread.
+ return NULL;
+}
+
+status_t
+WorkerThreadSection::Execute(ReportRequestSet* requests) const
+{
+ status_t err = NO_ERROR;
+ pthread_t thread;
+ pthread_attr_t attr;
+ bool timedOut = false;
+ FdBuffer buffer;
+
+ // Data shared between this thread and the worker thread.
+ sp<WorkerThreadData> data = new WorkerThreadData(this);
+
+ // Create the pipe
+ err = pipe(data->fds);
+ if (err != 0) {
+ return -errno;
+ }
+
+ // The worker thread needs a reference and we can't let the count go to zero
+ // if that thread is slow to start.
+ data->incStrong(this);
+
+ // Create the thread
+ err = pthread_attr_init(&attr);
+ if (err != 0) {
+ return -err;
+ }
+ // TODO: Do we need to tweak thread priority?
+ err = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+ if (err != 0) {
+ pthread_attr_destroy(&attr);
+ return -err;
+ }
+ err = pthread_create(&thread, &attr, worker_thread_func, (void*)data.get());
+ if (err != 0) {
+ pthread_attr_destroy(&attr);
+ return -err;
+ }
+ pthread_attr_destroy(&attr);
+
+ // Loop reading until either the timeout or the worker side is done (i.e. eof).
+ err = buffer.read(data->readFd(), REMOTE_CALL_TIMEOUT_MS);
+ if (err != NO_ERROR) {
+ // TODO: Log this error into the incident report.
+ ALOGW("WorkerThreadSection '%s' reader failed with error '%s'", this->name.string(),
+ strerror(-err));
+ }
+
+ // Done with the read fd. The worker thread closes the write one so
+ // we never race and get here first.
+ close(data->readFd());
+
+ // If the worker side is finished, then return its error (which may overwrite
+ // our possible error -- but it's more interesting anyway). If not, then we timed out.
+ {
+ unique_lock<mutex> lock(data->lock);
+ if (!data->workerDone) {
+ // We timed out
+ timedOut = true;
+ } else {
+ if (data->workerError != NO_ERROR) {
+ err = data->workerError;
+ // TODO: Log this error into the incident report.
+ ALOGW("WorkerThreadSection '%s' worker failed with error '%s'", this->name.string(),
+ strerror(-err));
+ }
+ }
+ }
+
+ if (timedOut || buffer.timedOut()) {
+ ALOGW("WorkerThreadSection '%s' timed out", this->name.string());
+ return NO_ERROR;
+ }
+
+ if (buffer.truncated()) {
+ // TODO: Log this into the incident report.
+ }
+
+ // TODO: There was an error with the command or buffering. Report that. For now
+ // just exit with a log messasge.
+ if (err != NO_ERROR) {
+ ALOGW("WorkerThreadSection '%s' failed with error '%s'", this->name.string(),
+ strerror(-err));
+ return NO_ERROR;
+ }
+
+ // Write the data that was collected
+ ALOGD("section '%s' wrote %zd bytes in %d ms", name.string(), buffer.size(),
+ (int)buffer.durationMs());
+ WriteHeader(requests, buffer.size());
+ err = buffer.write(requests);
+ if (err != NO_ERROR) {
+ ALOGW("WorkerThreadSection '%s' failed writing: '%s'", this->name.string(), strerror(-err));
+ return err;
+ }
+
+ return NO_ERROR;
+}
+
+// ================================================================================
+CommandSection::CommandSection(int id, const char* first, ...)
+ :Section(id)
+{
+ va_list args;
+ int count = 0;
+
+ va_start(args, first);
+ while (va_arg(args, const char*) != NULL) {
+ count++;
+ }
+ va_end(args);
+
+ mCommand = (const char**)malloc(sizeof(const char*) * count);
+
+ mCommand[0] = first;
+ name = first;
+ name += " ";
+ va_start(args, first);
+ for (int i=0; i<count; i++) {
+ const char* arg = va_arg(args, const char*);
+ mCommand[i+1] = arg;
+ if (arg != NULL) {
+ name += va_arg(args, const char*);
+ name += " ";
+ }
+ }
+ va_end(args);
+}
+
+CommandSection::~CommandSection()
+{
+}
+
+status_t
+CommandSection::Execute(ReportRequestSet* /*requests*/) const
+{
+ return NO_ERROR;
+}
+
+// ================================================================================
+DumpsysSection::DumpsysSection(int id, const char* service, ...)
+ :WorkerThreadSection(id),
+ mService(service)
+{
+ name = "dumpsys ";
+ name += service;
+
+ va_list args;
+ va_start(args, service);
+ while (true) {
+ const char* arg = va_arg(args, const char*);
+ if (arg == NULL) {
+ break;
+ }
+ mArgs.add(String16(arg));
+ name += " ";
+ name += arg;
+ }
+ va_end(args);
+}
+
+DumpsysSection::~DumpsysSection()
+{
+}
+
+status_t
+DumpsysSection::BlockingCall(int pipeWriteFd) const
+{
+ // checkService won't wait for the service to show up like getService will.
+ sp<IBinder> service = defaultServiceManager()->checkService(mService);
+
+ if (service == NULL) {
+ // Returning an error interrupts the entire incident report, so just
+ // log the failure.
+ // TODO: have a meta record inside the report that would log this
+ // failure inside the report, because the fact that we can't find
+ // the service is good data in and of itself. This is running in
+ // another thread so lock that carefully...
+ ALOGW("DumpsysSection: Can't lookup service: %s", String8(mService).string());
+ return NO_ERROR;
+ }
+
+ service->dump(pipeWriteFd, mArgs);
+
+ return NO_ERROR;
+}
diff --git a/cmds/incidentd/src/Section.h b/cmds/incidentd/src/Section.h
new file mode 100644
index 000000000000..35740e9771d5
--- /dev/null
+++ b/cmds/incidentd/src/Section.h
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2016 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 SECTIONS_H
+#define SECTIONS_H
+
+#include "FdBuffer.h"
+
+#include <utils/String8.h>
+#include <utils/String16.h>
+#include <utils/Vector.h>
+
+using namespace android;
+
+/**
+ * Base class for sections
+ */
+class Section
+{
+public:
+ int id;
+ String8 name;
+
+ Section(int id);
+ virtual ~Section();
+
+ virtual status_t Execute(ReportRequestSet* requests) const = 0;
+
+ status_t WriteHeader(ReportRequestSet* requests, size_t size) const;
+};
+
+/**
+ * Section that reads in a file.
+ */
+class FileSection : public Section
+{
+public:
+ FileSection(int id, const char* filename);
+ virtual ~FileSection();
+
+ virtual status_t Execute(ReportRequestSet* requests) const;
+
+private:
+ const char* mFilename;
+};
+
+/**
+ * Base class for sections that call a command that might need a timeout.
+ */
+class WorkerThreadSection : public Section
+{
+public:
+ WorkerThreadSection(int id);
+ virtual ~WorkerThreadSection();
+
+ virtual status_t Execute(ReportRequestSet* requests) const;
+
+ virtual status_t BlockingCall(int pipeWriteFd) const = 0;
+};
+
+/**
+ * Section that forks and execs a command, and puts stdout as the section.
+ */
+class CommandSection : public Section
+{
+public:
+ CommandSection(int id, const char* first, ...);
+ virtual ~CommandSection();
+
+ virtual status_t Execute(ReportRequestSet* requests) const;
+
+private:
+ const char** mCommand;
+};
+
+/**
+ * Section that calls dumpsys on a system service.
+ */
+class DumpsysSection : public WorkerThreadSection
+{
+public:
+ DumpsysSection(int id, const char* service, ...);
+ virtual ~DumpsysSection();
+
+ virtual status_t BlockingCall(int pipeWriteFd) const;
+
+private:
+ String16 mService;
+ Vector<String16> mArgs;
+};
+
+#endif // SECTIONS_H
+
diff --git a/cmds/incidentd/src/main.cpp b/cmds/incidentd/src/main.cpp
new file mode 100644
index 000000000000..3a7511d43048
--- /dev/null
+++ b/cmds/incidentd/src/main.cpp
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2016 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_TAG "incidentd"
+
+#include "IncidentService.h"
+
+#include <binder/IInterface.h>
+#include <binder/IPCThreadState.h>
+#include <binder/IServiceManager.h>
+#include <binder/ProcessState.h>
+#include <binder/Status.h>
+#include <cutils/log.h>
+#include <utils/Looper.h>
+#include <utils/StrongPointer.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+using namespace android;
+
+// ================================================================================
+int
+main(int /*argc*/, char** /*argv*/)
+{
+ // Set up the looper
+ sp<Looper> looper(Looper::prepare(0 /* opts */));
+
+ // Set up the binder
+ sp<ProcessState> ps(ProcessState::self());
+ ps->setThreadPoolMaxThreadCount(1); // everything is oneway, let it queue and save ram
+ ps->startThreadPool();
+ ps->giveThreadPoolName();
+ IPCThreadState::self()->disableBackgroundScheduling(true);
+
+ // Create the service
+ android::sp<IncidentService> service = new IncidentService(looper);
+ if (defaultServiceManager()->addService(String16("incident"), service) != 0) {
+ ALOGE("Failed to add service");
+ return -1;
+ }
+
+ // Loop forever -- the reports run on this thread in a handler, and the
+ // binder calls remain responsive in their pool of one thread.
+ while (true) {
+ looper->pollAll(-1 /* timeoutMillis */);
+ }
+ ALOGW("incidentd escaped from its loop.");
+
+ return 1;
+}
diff --git a/cmds/incidentd/src/protobuf.cpp b/cmds/incidentd/src/protobuf.cpp
new file mode 100644
index 000000000000..cb864fd3b619
--- /dev/null
+++ b/cmds/incidentd/src/protobuf.cpp
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+#include "protobuf.h"
+
+uint8_t*
+write_raw_varint(uint8_t* buf, uint32_t val)
+{
+ uint8_t* p = buf;
+ while (true) {
+ if ((val & ~0x7F) == 0) {
+ *p++ = (uint8_t)val;
+ return p;
+ } else {
+ *p++ = (uint8_t)((val & 0x7F) | 0x80);
+ val >>= 7;
+ }
+ }
+}
+
+uint8_t*
+write_length_delimited_tag_header(uint8_t* buf, uint32_t fieldId, size_t size)
+{
+ buf = write_raw_varint(buf, (fieldId << 3) | 2);
+ buf = write_raw_varint(buf, size);
+ return buf;
+}
+
diff --git a/cmds/incidentd/src/protobuf.h b/cmds/incidentd/src/protobuf.h
new file mode 100644
index 000000000000..a24399832b00
--- /dev/null
+++ b/cmds/incidentd/src/protobuf.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2016 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 PROTOBUF_H
+#define PROTOBUF_H
+
+#include <stdint.h>
+
+/**
+ * Write a varint into the buffer. Return the next position to write at.
+ * There must be 10 bytes in the buffer. The same as EncodedBuffer.writeRawVarint32
+ */
+uint8_t* write_raw_varint(uint8_t* buf, uint32_t val);
+
+/**
+ * Write a protobuf WIRE_TYPE_LENGTH_DELIMITED header. Return the next position to write at.
+ * There must be 20 bytes in the buffer.
+ */
+uint8_t* write_length_delimited_tag_header(uint8_t* buf, uint32_t fieldId, size_t size);
+
+enum {
+ // IncidentProto.header
+ FIELD_ID_INCIDENT_HEADER = 1
+};
+
+#endif // PROTOBUF_H
+
diff --git a/cmds/incidentd/src/report_directory.cpp b/cmds/incidentd/src/report_directory.cpp
new file mode 100644
index 000000000000..f60b8ac46cca
--- /dev/null
+++ b/cmds/incidentd/src/report_directory.cpp
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2016 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_TAG "incidentd"
+
+#include "report_directory.h"
+
+#include <cutils/log.h>
+#include <private/android_filesystem_config.h>
+#include <utils/String8.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <dirent.h>
+#include <libgen.h>
+#include <unistd.h>
+
+#include <vector>
+
+using namespace android;
+using namespace std;
+
+status_t
+create_directory(const char* directory)
+{
+ struct stat st;
+ status_t err = NO_ERROR;
+ char* dir = strdup(directory);
+
+ // Skip first slash
+ char* d = dir + 1;
+
+ // Create directories, assigning them to the system user
+ bool last = false;
+ while (!last) {
+ d = strchr(d, '/');
+ if (d != NULL) {
+ *d = '\0';
+ } else {
+ last = true;
+ }
+ if (stat(dir, &st) == 0) {
+ if (!S_ISDIR(st.st_mode)) {
+ err = ALREADY_EXISTS;
+ goto done;
+ }
+ } else {
+ if (mkdir(dir, 0770)) {
+ ALOGE("No incident reports today. "
+ "Unable to create incident report dir %s: %s", dir,
+ strerror(errno));
+ err = -errno;
+ goto done;
+ }
+ if (chmod(dir, 0770)) {
+ ALOGE("No incident reports today. "
+ "Unable to set permissions for incident report dir %s: %s", dir,
+ strerror(errno));
+ err = -errno;
+ goto done;
+ }
+ if (chown(dir, AID_SYSTEM, AID_SYSTEM)) {
+ ALOGE("No incident reports today. Unable to change ownership of dir %s: %s\n",
+ dir, strerror(errno));
+ err = -errno;
+ goto done;
+ }
+ }
+ if (!last) {
+ *d++ = '/';
+ }
+ }
+
+ // Ensure that the final directory is owned by the system with 0770. If it isn't
+ // we won't write into it.
+ if (stat(directory, &st) != 0) {
+ ALOGE("No incident reports today. Can't stat: %s", directory);
+ err = -errno;
+ goto done;
+ }
+ if ((st.st_mode & 0777) != 0770) {
+ ALOGE("No incident reports today. Mode is %0o on report directory %s",
+ st.st_mode, directory);
+ err = BAD_VALUE;
+ goto done;
+ }
+ if (st.st_uid != AID_SYSTEM || st.st_gid != AID_SYSTEM) {
+ ALOGE("No incident reports today. Owner is %d and group is %d on report directory %s",
+ st.st_uid, st.st_gid, directory);
+ err = BAD_VALUE;
+ goto done;
+ }
+
+done:
+ free(dir);
+ return err;
+}
+
+static bool
+stat_mtime_cmp(const pair<String8,struct stat>& a, const pair<String8,struct stat>& b)
+{
+ return a.second.st_mtime < b.second.st_mtime;
+}
+
+void
+clean_directory(const char* directory, off_t maxSize, size_t maxCount)
+{
+ DIR* dir;
+ struct dirent* entry;
+ struct stat st;
+
+ vector<pair<String8,struct stat>> files;
+
+ if ((dir = opendir(directory)) == NULL) {
+ ALOGE("Couldn't open incident directory: %s", directory);
+ return;
+ }
+
+ String8 dirbase(String8(directory) + "/");
+
+ off_t totalSize = 0;
+ size_t totalCount = 0;
+
+ // Enumerate, count and add up size
+ while ((entry = readdir(dir)) != NULL) {
+ if (entry->d_name[0] == '.') {
+ continue;
+ }
+ String8 filename = dirbase + entry->d_name;
+ if (stat(filename.string(), &st) != 0) {
+ ALOGE("Unable to stat file %s", filename.string());
+ continue;
+ }
+ if (!S_ISREG(st.st_mode)) {
+ continue;
+ }
+ files.push_back(pair<String8,struct stat>(filename, st));
+
+ totalSize += st.st_size;
+ totalCount++;
+ }
+
+ closedir(dir);
+
+ // Count or size is less than max, then we're done.
+ if (totalSize < maxSize && totalCount < maxCount) {
+ return;
+ }
+
+ // Oldest files first.
+ sort(files.begin(), files.end(), stat_mtime_cmp);
+
+ // Remove files until we're under our limits.
+ for (vector<pair<String8,struct stat>>::iterator it = files.begin();
+ it != files.end() && totalSize >= maxSize && totalCount >= maxCount; it++) {
+ remove(it->first.string());
+ totalSize -= it->second.st_size;
+ totalCount--;
+ }
+}
diff --git a/cmds/incidentd/src/report_directory.h b/cmds/incidentd/src/report_directory.h
new file mode 100644
index 000000000000..bed4f869cfe4
--- /dev/null
+++ b/cmds/incidentd/src/report_directory.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2016 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 DIRECTORY_CLEANER_H
+#define DIRECTORY_CLEANER_H
+
+#include <utils/Errors.h>
+
+#include <sys/types.h>
+
+using namespace android;
+
+status_t create_directory(const char* directory);
+void clean_directory(const char* directory, off_t maxSize, size_t maxCount);
+
+#endif // DIRECTORY_CLEANER_H
diff --git a/cmds/incidentd/src/section_list.cpp b/cmds/incidentd/src/section_list.cpp
new file mode 100644
index 000000000000..b6112ed0f7df
--- /dev/null
+++ b/cmds/incidentd/src/section_list.cpp
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+#include "section_list.h"
+
+//using namespace android::util;
+
+/**
+ * This is the mapping of section IDs to the commands that are run to get those commands.
+ */
+const Section* SECTION_LIST[] = {
+ new DumpsysSection(3000,
+ "fingerprint", "--proto", "--incident", NULL),
+ NULL
+};
+
diff --git a/cmds/incidentd/src/section_list.h b/cmds/incidentd/src/section_list.h
new file mode 100644
index 000000000000..c97751937d6d
--- /dev/null
+++ b/cmds/incidentd/src/section_list.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2016 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 SECTION_LIST_H
+#define SECTION_LIST_H
+
+#include "Section.h"
+
+/**
+ * This is the mapping of section IDs to the commands that are run to get those commands.
+ */
+extern const Section* SECTION_LIST[];
+
+#endif // SECTION_LIST_H
+
diff --git a/cmds/input/src/com/android/commands/input/Input.java b/cmds/input/src/com/android/commands/input/Input.java
index 754d3f510bbd..9ee11f8571e2 100644
--- a/cmds/input/src/com/android/commands/input/Input.java
+++ b/cmds/input/src/com/android/commands/input/Input.java
@@ -23,6 +23,7 @@ import android.view.InputDevice;
import android.view.KeyCharacterMap;
import android.view.KeyEvent;
import android.view.MotionEvent;
+import android.view.ViewConfiguration;
import java.util.HashMap;
import java.util.Map;
@@ -118,6 +119,19 @@ public class Input {
duration);
return;
}
+ } else if (command.equals("draganddrop")) {
+ int duration = -1;
+ inputSource = getSource(inputSource, InputDevice.SOURCE_TOUCHSCREEN);
+ switch (length) {
+ case 6:
+ duration = Integer.parseInt(args[index+5]);
+ case 5:
+ sendDragAndDrop(inputSource,
+ Float.parseFloat(args[index+1]), Float.parseFloat(args[index+2]),
+ Float.parseFloat(args[index+3]), Float.parseFloat(args[index+4]),
+ duration);
+ return;
+ }
} else if (command.equals("press")) {
inputSource = getSource(inputSource, InputDevice.SOURCE_TRACKBALL);
if (length == 1) {
@@ -216,6 +230,31 @@ public class Input {
injectMotionEvent(inputSource, MotionEvent.ACTION_UP, now, x2, y2, 0.0f);
}
+ private void sendDragAndDrop(int inputSource, float x1, float y1, float x2, float y2,
+ int dragDuration) {
+ if (dragDuration < 0) {
+ dragDuration = 300;
+ }
+ long now = SystemClock.uptimeMillis();
+ injectMotionEvent(inputSource, MotionEvent.ACTION_DOWN, now, x1, y1, 1.0f);
+ try {
+ Thread.sleep(ViewConfiguration.getLongPressTimeout());
+ } catch (InterruptedException e) {
+ throw new RuntimeException(e);
+ }
+ now = SystemClock.uptimeMillis();
+ long startTime = now;
+ long endTime = startTime + dragDuration;
+ while (now < endTime) {
+ long elapsedTime = now - startTime;
+ float alpha = (float) elapsedTime / dragDuration;
+ injectMotionEvent(inputSource, MotionEvent.ACTION_MOVE, now, lerp(x1, x2, alpha),
+ lerp(y1, y2, alpha), 1.0f);
+ now = SystemClock.uptimeMillis();
+ }
+ injectMotionEvent(inputSource, MotionEvent.ACTION_UP, now, x2, y2, 0.0f);
+ }
+
/**
* Sends a simple zero-pressure move event.
*
@@ -294,6 +333,8 @@ public class Input {
System.err.println(" tap <x> <y> (Default: touchscreen)");
System.err.println(" swipe <x1> <y1> <x2> <y2> [duration(ms)]"
+ " (Default: touchscreen)");
+ System.err.println(" draganddrop <x1> <y1> <x2> <y2> [duration(ms)]"
+ + " (Default: touchscreen)");
System.err.println(" press (Default: trackball)");
System.err.println(" roll <dx> <dy> (Default: trackball)");
}
diff --git a/cmds/locksettings/Android.mk b/cmds/locksettings/Android.mk
new file mode 100644
index 000000000000..76766c7c6955
--- /dev/null
+++ b/cmds/locksettings/Android.mk
@@ -0,0 +1,30 @@
+# Copyright (C) 2016 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.
+
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
+LOCAL_MODULE := locksettings
+LOCAL_MODULE_TAGS := optional
+include $(BUILD_JAVA_LIBRARY)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := locksettings
+LOCAL_SRC_FILES := locksettings
+LOCAL_MODULE_CLASS := EXECUTABLES
+LOCAL_MODULE_TAGS := optional
+include $(BUILD_PREBUILT)
+
+
diff --git a/cmds/locksettings/locksettings b/cmds/locksettings/locksettings
new file mode 100755
index 000000000000..c963b238726b
--- /dev/null
+++ b/cmds/locksettings/locksettings
@@ -0,0 +1,5 @@
+# Script to start "locksettings" on the device
+#
+base=/system
+export CLASSPATH=$base/framework/locksettings.jar
+exec app_process $base/bin com.android.commands.locksettings.LockSettingsCmd "$@"
diff --git a/cmds/locksettings/src/com/android/commands/locksettings/LockSettingsCmd.java b/cmds/locksettings/src/com/android/commands/locksettings/LockSettingsCmd.java
new file mode 100644
index 000000000000..1e426d62e4e0
--- /dev/null
+++ b/cmds/locksettings/src/com/android/commands/locksettings/LockSettingsCmd.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2016 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.locksettings;
+
+import android.os.ResultReceiver;
+import android.os.ServiceManager;
+import android.os.ShellCallback;
+
+import com.android.internal.os.BaseCommand;
+import com.android.internal.widget.ILockSettings;
+
+import java.io.FileDescriptor;
+import java.io.PrintStream;
+
+public final class LockSettingsCmd extends BaseCommand {
+
+ private static final String USAGE =
+ "usage: locksettings set-pattern [--old OLD_CREDENTIAL] NEW_PATTERN\n" +
+ " locksettings set-pin [--old OLD_CREDENTIAL] NEW_PIN\n" +
+ " locksettings set-password [--old OLD_CREDENTIAL] NEW_PASSWORD\n" +
+ " locksettings clear [--old OLD_CREDENTIAL]\n" +
+ "\n" +
+ "locksettings set-pattern: sets a pattern\n" +
+ " A pattern is specified by a non-separated list of numbers that index the cell\n" +
+ " on the pattern in a 1-based manner in left to right and top to bottom order,\n" +
+ " i.e. the top-left cell is indexed with 1, whereas the bottom-right cell\n" +
+ " is indexed with 9. Example: 1234\n" +
+ "\n" +
+ "locksettings set-pin: sets a PIN\n" +
+ "\n" +
+ "locksettings set-password: sets a password\n" +
+ "\n" +
+ "locksettings clear: clears the unlock credential\n";
+
+ public static void main(String[] args) {
+ (new LockSettingsCmd()).run(args);
+ }
+
+ @Override
+ public void onShowUsage(PrintStream out) {
+ out.println(USAGE);
+ }
+
+ @Override
+ public void onRun() throws Exception {
+ ILockSettings lockSettings = ILockSettings.Stub.asInterface(
+ ServiceManager.getService("lock_settings"));
+ lockSettings.asBinder().shellCommand(FileDescriptor.in, FileDescriptor.out,
+ FileDescriptor.err, getRawArgs(), new ShellCallback(), new ResultReceiver(null) {});
+ }
+}
diff --git a/cmds/media/src/com/android/commands/media/Media.java b/cmds/media/src/com/android/commands/media/Media.java
index d7f23cb44098..9df229cbf490 100644
--- a/cmds/media/src/com/android/commands/media/Media.java
+++ b/cmds/media/src/com/android/commands/media/Media.java
@@ -57,22 +57,26 @@ public class Media extends BaseCommand {
(new Media()).run(args);
}
+ @Override
public void onShowUsage(PrintStream out) {
out.println(
"usage: media [subcommand] [options]\n" +
" media dispatch KEY\n" +
" media list-sessions\n" +
" media monitor <tag>\n" +
+ " media volume [options]\n" +
"\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, 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"
+ " Use the tag from list-sessions.\n" +
+ "media volume: " + VolumeCtrl.USAGE
);
}
+ @Override
public void onRun() throws Exception {
mSessionService = ISessionManager.Stub.asInterface(ServiceManager.checkService(
Context.MEDIA_SESSION_SERVICE));
@@ -90,6 +94,8 @@ public class Media extends BaseCommand {
runListSessions();
} else if (op.equals("monitor")) {
runMonitor();
+ } else if (op.equals("volume")) {
+ runVolume();
} else {
showError("Error: unknown command '" + op + "'");
return;
@@ -310,4 +316,10 @@ public class Media extends BaseCommand {
System.out.println("***Error listing sessions***");
}
}
+
+ //=================================
+ // "volume" command for stream volume control
+ private void runVolume() throws Exception {
+ VolumeCtrl.run(this);
+ }
}
diff --git a/cmds/media/src/com/android/commands/media/VolumeCtrl.java b/cmds/media/src/com/android/commands/media/VolumeCtrl.java
new file mode 100755
index 000000000000..1629c6f178f0
--- /dev/null
+++ b/cmds/media/src/com/android/commands/media/VolumeCtrl.java
@@ -0,0 +1,185 @@
+/*
+**
+** Copyright 2016, 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.media;
+
+import android.content.Context;
+import android.media.AudioManager;
+import android.media.AudioSystem;
+import android.media.IAudioService;
+import android.os.ServiceManager;
+import android.util.AndroidException;
+
+import com.android.internal.os.BaseCommand;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.PrintStream;
+import java.lang.ArrayIndexOutOfBoundsException;
+
+/**
+ * Command line tool to exercise AudioService.setStreamVolume()
+ * and AudioService.adjustStreamVolume()
+ */
+public class VolumeCtrl {
+
+ private final static String TAG = "VolumeCtrl";
+
+ // --stream affects --set, --adj or --get options.
+ // --show affects --set and --adj options.
+ // --get can be used with --set, --adj or by itself.
+ public final static String USAGE = new String(
+ "the options are as follows: \n" +
+ "\t\t--stream STREAM selects the stream to control, see AudioManager.STREAM_*\n" +
+ "\t\t controls AudioManager.STREAM_MUSIC if no stream is specified\n"+
+ "\t\t--set INDEX sets the volume index value\n" +
+ "\t\t--adj DIRECTION adjusts the volume, use raise|same|lower for the direction\n" +
+ "\t\t--get outputs the current volume\n" +
+ "\t\t--show shows the UI during the volume change\n" +
+ "\texamples:\n" +
+ "\t\tadb shell media volume --show --stream 3 --set 11\n" +
+ "\t\tadb shell media volume --stream 0 --adj lower\n" +
+ "\t\tadb shell media volume --stream 3 --get\n"
+ );
+
+ private final static int VOLUME_CONTROL_MODE_SET = 1;
+ private final static int VOLUME_CONTROL_MODE_ADJUST = 2;
+
+ private final static String ADJUST_LOWER = "lower";
+ private final static String ADJUST_SAME = "same";
+ private final static String ADJUST_RAISE = "raise";
+
+ public static void run(BaseCommand cmd) throws Exception {
+ //----------------------------------------
+ // Default parameters
+ int stream = AudioManager.STREAM_MUSIC;
+ int volIndex = 5;
+ int mode = 0;
+ int adjDir = AudioManager.ADJUST_RAISE;
+ boolean showUi = false;
+ boolean doGet = false;
+
+ //----------------------------------------
+ // read options
+ String option;
+ String adjustment = null;
+ while ((option = cmd.nextOption()) != null) {
+ switch (option) {
+ case "--show":
+ showUi = true;
+ break;
+ case "--get":
+ doGet = true;
+ log(LOG_V, "will get volume");
+ break;
+ case "--stream":
+ stream = Integer.decode(cmd.nextArgRequired()).intValue();
+ log(LOG_V, "will control stream=" + stream + " (" + streamName(stream) + ")");
+ break;
+ case "--set":
+ volIndex = Integer.decode(cmd.nextArgRequired()).intValue();
+ mode = VOLUME_CONTROL_MODE_SET;
+ log(LOG_V, "will set volume to index=" + volIndex);
+ break;
+ case "--adj":
+ mode = VOLUME_CONTROL_MODE_ADJUST;
+ adjustment = cmd.nextArgRequired();
+ log(LOG_V, "will adjust volume");
+ break;
+ default:
+ throw new IllegalArgumentException("Unknown argument " + option);
+ }
+ }
+
+ //------------------------------
+ // Read options: validation
+ if (mode == VOLUME_CONTROL_MODE_ADJUST) {
+ if (adjustment == null) {
+ cmd.showError("Error: no valid volume adjustment (null)");
+ return;
+ }
+ switch (adjustment) {
+ case ADJUST_RAISE: adjDir = AudioManager.ADJUST_RAISE; break;
+ case ADJUST_SAME: adjDir = AudioManager.ADJUST_SAME; break;
+ case ADJUST_LOWER: adjDir = AudioManager.ADJUST_LOWER; break;
+ default:
+ cmd.showError("Error: no valid volume adjustment, was " + adjustment
+ + ", expected " + ADJUST_LOWER + "|" + ADJUST_SAME + "|"
+ + ADJUST_RAISE);
+ return;
+ }
+ }
+
+ //----------------------------------------
+ // Test initialization
+ log(LOG_V, "Connecting to AudioService");
+ IAudioService audioService = IAudioService.Stub.asInterface(ServiceManager.checkService(
+ Context.AUDIO_SERVICE));
+ if (audioService == null) {
+ System.err.println(BaseCommand.NO_SYSTEM_ERROR_CODE);
+ throw new AndroidException(
+ "Can't connect to audio service; is the system running?");
+ }
+
+ if (mode == VOLUME_CONTROL_MODE_SET) {
+ if ((volIndex > audioService.getStreamMaxVolume(stream))
+ || (volIndex < audioService.getStreamMinVolume(stream))) {
+ cmd.showError(String.format("Error: invalid volume index %d for stream %d "
+ + "(should be in [%d..%d])", volIndex, stream,
+ audioService.getStreamMinVolume(stream),
+ audioService.getStreamMaxVolume(stream)));
+ return;
+ }
+ }
+
+ //----------------------------------------
+ // Non-interactive test
+ final int flag = showUi? AudioManager.FLAG_SHOW_UI : 0;
+ final String pack = cmd.getClass().getPackage().getName();
+ if (mode == VOLUME_CONTROL_MODE_SET) {
+ audioService.setStreamVolume(stream, volIndex, flag, pack/*callingPackage*/);
+ } else if (mode == VOLUME_CONTROL_MODE_ADJUST) {
+ audioService.adjustStreamVolume(stream, adjDir, flag, pack);
+ }
+ if (doGet) {
+ log(LOG_V, "volume is " + audioService.getStreamVolume(stream) +
+ " in range [" + audioService.getStreamMinVolume(stream) +
+ ".." + audioService.getStreamMaxVolume(stream) + "]");
+ }
+ }
+
+ //--------------------------------------------
+ // Utilities
+
+ static final String LOG_V = "[v]";
+ static final String LOG_W = "[w]";
+ static final String LOG_OK = "[ok]";
+
+ static void log(String code, String msg) {
+ System.out.println(code + " " + msg);
+ }
+
+ static String streamName(int stream) {
+ try {
+ return AudioSystem.STREAM_NAMES[stream];
+ } catch (ArrayIndexOutOfBoundsException e) {
+ return "invalid stream";
+ }
+ }
+
+}
diff --git a/cmds/pm/src/com/android/commands/pm/Pm.java b/cmds/pm/src/com/android/commands/pm/Pm.java
index 1b4eda804312..d71573f7ca50 100644
--- a/cmds/pm/src/com/android/commands/pm/Pm.java
+++ b/cmds/pm/src/com/android/commands/pm/Pm.java
@@ -24,7 +24,6 @@ import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATIO
import android.accounts.IAccountManager;
import android.app.ActivityManager;
-import android.app.ActivityManagerNative;
import android.app.PackageInstallObserver;
import android.content.ComponentName;
import android.content.Context;
@@ -52,16 +51,23 @@ import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
+import android.os.IBinder;
import android.os.IUserManager;
+import android.os.ParcelFileDescriptor;
+import android.os.Process;
import android.os.RemoteException;
import android.os.ResultReceiver;
+import android.os.SELinux;
import android.os.ServiceManager;
+import android.os.ShellCallback;
import android.os.SystemClock;
import android.os.UserHandle;
import android.os.UserManager;
+import android.os.storage.StorageManager;
import android.text.TextUtils;
import android.text.format.DateUtils;
import android.util.Log;
+import android.util.Pair;
import com.android.internal.content.PackageHelper;
import com.android.internal.util.ArrayUtils;
@@ -72,6 +78,7 @@ import libcore.io.IoUtils;
import java.io.File;
import java.io.FileDescriptor;
import java.io.FileInputStream;
+import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
@@ -80,6 +87,7 @@ import java.util.concurrent.TimeUnit;
public final class Pm {
private static final String TAG = "Pm";
+ private static final String STDIN_PATH = "-";
IPackageManager mPm;
IPackageInstaller mInstaller;
@@ -288,13 +296,45 @@ public final class Pm {
}
}
+ static final class MyShellCallback extends ShellCallback {
+ @Override public ParcelFileDescriptor onOpenOutputFile(String path, String seLinuxContext) {
+ File file = new File(path);
+ final ParcelFileDescriptor fd;
+ try {
+ fd = ParcelFileDescriptor.open(file,
+ ParcelFileDescriptor.MODE_CREATE |
+ ParcelFileDescriptor.MODE_TRUNCATE |
+ ParcelFileDescriptor.MODE_WRITE_ONLY);
+ } catch (FileNotFoundException e) {
+ String msg = "Unable to open file " + path + ": " + e;
+ System.err.println(msg);
+ throw new IllegalArgumentException(msg);
+ }
+ if (seLinuxContext != null) {
+ final String tcon = SELinux.getFileContext(file.getAbsolutePath());
+ if (!SELinux.checkSELinuxAccess(seLinuxContext, tcon, "file", "write")) {
+ try {
+ fd.close();
+ } catch (IOException e) {
+ }
+ String msg = "System server has no access to file context " + tcon;
+ System.err.println(msg + " (from path " + file.getAbsolutePath()
+ + ", context " + seLinuxContext + ")");
+ throw new IllegalArgumentException(msg);
+ }
+ }
+ return fd;
+ }
+ }
+
private int runShellCommand(String serviceName, String[] args) {
final HandlerThread handlerThread = new HandlerThread("results");
handlerThread.start();
try {
ServiceManager.getService(serviceName).shellCommand(
FileDescriptor.in, FileDescriptor.out, FileDescriptor.err,
- args, new ResultReceiver(new Handler(handlerThread.getLooper())));
+ args, new MyShellCallback(),
+ new ResultReceiver(new Handler(handlerThread.getLooper())));
return 0;
} catch (RemoteException e) {
e.printStackTrace();
@@ -309,7 +349,7 @@ public final class Pm {
private IIntentSender.Stub mLocalSender = new IIntentSender.Stub() {
@Override
- public void send(int code, Intent intent, String resolvedType,
+ public void send(int code, Intent intent, String resolvedType, IBinder whitelistToken,
IIntentReceiver finishedReceiver, String requiredPermission, Bundle options) {
try {
mResult.offer(intent, 5, TimeUnit.SECONDS);
@@ -365,27 +405,26 @@ public final class Pm {
* The use of "adb install" or "cmd package install" over "pm install" is highly encouraged.
*/
private int runInstall() throws RemoteException {
+ long startedTime = SystemClock.elapsedRealtime();
final InstallParams params = makeInstallParams();
final String inPath = nextArg();
- boolean installExternal =
- (params.sessionParams.installFlags & PackageManager.INSTALL_EXTERNAL) != 0;
- if (params.sessionParams.sizeBytes < 0 && inPath != null) {
+ if (params.sessionParams.sizeBytes == -1 && !STDIN_PATH.equals(inPath)) {
File file = new File(inPath);
if (file.isFile()) {
- if (installExternal) {
- try {
- ApkLite baseApk = PackageParser.parseApkLite(file, 0);
- PackageLite pkgLite = new PackageLite(null, baseApk, null, null, null);
- params.sessionParams.setSize(
- PackageHelper.calculateInstalledSize(pkgLite, false,
- params.sessionParams.abiOverride));
- } catch (PackageParserException | IOException e) {
- System.err.println("Error: Failed to parse APK file : " + e);
- return 1;
- }
- } else {
- params.sessionParams.setSize(file.length());
+ try {
+ ApkLite baseApk = PackageParser.parseApkLite(file, 0);
+ PackageLite pkgLite = new PackageLite(null, baseApk, null, null, null, null,
+ null, null);
+ params.sessionParams.setSize(
+ PackageHelper.calculateInstalledSize(pkgLite, false,
+ params.sessionParams.abiOverride));
+ } catch (PackageParserException | IOException e) {
+ System.err.println("Error: Failed to parse APK file: " + e);
+ return 1;
}
+ } else {
+ System.err.println("Error: Can't open non-file: " + inPath);
+ return 1;
}
}
@@ -393,7 +432,7 @@ public final class Pm {
params.installerPackageName, params.userId);
try {
- if (inPath == null && params.sessionParams.sizeBytes == 0) {
+ if (inPath == null && params.sessionParams.sizeBytes == -1) {
System.err.println("Error: must either specify a package size or an APK file");
return 1;
}
@@ -401,10 +440,12 @@ public final class Pm {
false /*logSuccess*/) != PackageInstaller.STATUS_SUCCESS) {
return 1;
}
- if (doCommitSession(sessionId, false /*logSuccess*/)
- != PackageInstaller.STATUS_SUCCESS) {
+ Pair<String, Integer> status = doCommitSession(sessionId, false /*logSuccess*/);
+ if (status.second != PackageInstaller.STATUS_SUCCESS) {
return 1;
}
+ Log.i(TAG, "Package " + status.first + " installed in " + (SystemClock.elapsedRealtime()
+ - startedTime) + " ms");
System.out.println("Success");
return 0;
} finally {
@@ -422,7 +463,7 @@ public final class Pm {
private int runInstallCommit() throws RemoteException {
final int sessionId = Integer.parseInt(nextArg());
- return doCommitSession(sessionId, true /*logSuccess*/);
+ return doCommitSession(sessionId, true /*logSuccess*/).second;
}
private int runInstallCreate() throws RemoteException {
@@ -509,14 +550,28 @@ public final class Pm {
throw new IllegalArgumentException("Missing inherit package name");
}
break;
+ case "--pkg":
+ sessionParams.appPackageName = nextOptionData();
+ if (sessionParams.appPackageName == null) {
+ throw new IllegalArgumentException("Missing package name");
+ }
+ break;
case "-S":
- sessionParams.setSize(Long.parseLong(nextOptionData()));
+ final long sizeBytes = Long.parseLong(nextOptionData());
+ if (sizeBytes <= 0) {
+ throw new IllegalArgumentException("Size must be positive");
+ }
+ sessionParams.setSize(sizeBytes);
break;
case "--abi":
sessionParams.abiOverride = checkAbiArgument(nextOptionData());
break;
case "--ephemeral":
- sessionParams.installFlags |= PackageManager.INSTALL_EPHEMERAL;
+ case "--instant":
+ sessionParams.setInstallAsInstantApp(true /*isInstantApp*/);
+ break;
+ case "--full":
+ sessionParams.setInstallAsInstantApp(false /*isInstantApp*/);
break;
case "--user":
params.userId = UserHandle.parseUserArg(nextOptionData());
@@ -555,7 +610,7 @@ public final class Pm {
private int doWriteSession(int sessionId, String inPath, long sizeBytes, String splitName,
boolean logSuccess) throws RemoteException {
- if ("-".equals(inPath)) {
+ if (STDIN_PATH.equals(inPath)) {
inPath = null;
} else if (inPath != null) {
final File file = new File(inPath);
@@ -608,7 +663,8 @@ public final class Pm {
}
}
- private int doCommitSession(int sessionId, boolean logSuccess) throws RemoteException {
+ private Pair<String, Integer> doCommitSession(int sessionId, boolean logSuccess)
+ throws RemoteException {
PackageInstaller.Session session = null;
try {
session = new PackageInstaller.Session(
@@ -628,7 +684,7 @@ public final class Pm {
System.err.println("Failure ["
+ result.getStringExtra(PackageInstaller.EXTRA_STATUS_MESSAGE) + "]");
}
- return status;
+ return new Pair<>(result.getStringExtra(PackageInstaller.EXTRA_PACKAGE_NAME), status);
} finally {
IoUtils.closeQuietly(session);
}
@@ -967,11 +1023,12 @@ public final class Pm {
// In non-split user mode, userId can only be SYSTEM
int parentUserId = userId >= 0 ? userId : UserHandle.USER_SYSTEM;
info = mUm.createRestrictedProfile(name, parentUserId);
- mAm.addSharedAccountsFromParentUser(parentUserId, userId);
+ mAm.addSharedAccountsFromParentUser(parentUserId, userId,
+ (Process.myUid() == Process.ROOT_UID) ? "root" : "com.android.shell");
} else if (userId < 0) {
info = mUm.createUser(name, flags);
} else {
- info = mUm.createProfileForUser(name, flags, userId);
+ info = mUm.createProfileForUser(name, flags, userId, null);
}
if (info != null) {
@@ -1153,7 +1210,7 @@ public final class Pm {
ClearDataObserver obs = new ClearDataObserver();
try {
- ActivityManagerNative.getDefault().clearApplicationUserData(pkg, obs, userId);
+ ActivityManager.getService().clearApplicationUserData(pkg, obs, userId);
synchronized (obs) {
while (!obs.finished) {
try {
@@ -1386,10 +1443,10 @@ public final class Pm {
System.err.println("Error: no size specified");
return showUsage();
}
- int len = size.length();
long multiplier = 1;
- if (len > 1) {
- char c = size.charAt(len-1);
+ int len = size.length();
+ char c = size.charAt(len - 1);
+ if (c < '0' || c > '9') {
if (c == 'K' || c == 'k') {
multiplier = 1024L;
} else if (c == 'M' || c == 'm') {
@@ -1415,7 +1472,8 @@ public final class Pm {
}
ClearDataObserver obs = new ClearDataObserver();
try {
- mPm.freeStorageAndNotify(volumeUuid, sizeVal, obs);
+ mPm.freeStorageAndNotify(volumeUuid, sizeVal,
+ StorageManager.FLAG_ALLOCATE_DEFY_RESERVED, obs);
synchronized (obs) {
while (!obs.finished) {
try {
@@ -1519,7 +1577,7 @@ public final class Pm {
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 uninstall [-k] [--user USER_ID] [--versionCode VERSION_CODE] PACKAGE");
System.err.println(" pm set-installer PACKAGE INSTALLER");
System.err.println(" pm move-package PACKAGE [internal|UUID]");
System.err.println(" pm move-primary-storage [internal|UUID]");
@@ -1529,6 +1587,7 @@ public final class Pm {
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 default-state [--user USER_ID] PACKAGE_OR_COMPONENT");
+ System.err.println(" pm set-user-restriction [--user USER_ID] RESTRICTION VALUE");
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 [--user USER_ID] PACKAGE PERMISSION");
diff --git a/cmds/screencap/screencap.cpp b/cmds/screencap/screencap.cpp
index 7bf073b4b1a1..5fedc9e74087 100644
--- a/cmds/screencap/screencap.cpp
+++ b/cmds/screencap/screencap.cpp
@@ -84,6 +84,11 @@ static status_t notifyMediaScanner(const char* fileName) {
int main(int argc, char** argv)
{
+ // setThreadPoolMaxThreadCount(0) actually tells the kernel it's
+ // not allowed to spawn any additional threads, but we still spawn
+ // a binder thread from userspace when we call startThreadPool().
+ // See b/36066697 for rationale
+ ProcessState::self()->setThreadPoolMaxThreadCount(0);
ProcessState::self()->startThreadPool();
const char* pname = argv[0];
@@ -162,7 +167,9 @@ int main(int argc, char** argv)
uint8_t displayOrientation = configs[activeConfig].orientation;
uint32_t captureOrientation = ORIENTATION_MAP[displayOrientation];
- status_t result = screenshot.update(display, Rect(), 0, 0, 0, -1U,
+ status_t result = screenshot.update(display, Rect(),
+ 0 /* reqWidth */, 0 /* reqHeight */,
+ INT32_MIN, INT32_MAX, /* all layers */
false, captureOrientation);
if (result == NO_ERROR) {
base = screenshot.getPixels();
@@ -175,13 +182,20 @@ int main(int argc, char** argv)
if (base != NULL) {
if (png) {
- const SkImageInfo info = SkImageInfo::Make(w, h, flinger2skia(f),
- kPremul_SkAlphaType);
- SkAutoTUnref<SkData> data(SkImageEncoder::EncodeData(info, base, s*bytesPerPixel(f),
- SkImageEncoder::kPNG_Type, SkImageEncoder::kDefaultQuality));
- if (data.get()) {
- write(fd, data->data(), data->size());
- }
+ const SkImageInfo info =
+ SkImageInfo::Make(w, h, flinger2skia(f), kPremul_SkAlphaType);
+ SkPixmap pixmap(info, base, s * bytesPerPixel(f));
+ struct FDWStream final : public SkWStream {
+ size_t fBytesWritten = 0;
+ int fFd;
+ FDWStream(int f) : fFd(f) {}
+ size_t bytesWritten() const override { return fBytesWritten; }
+ bool write(const void* buffer, size_t size) override {
+ fBytesWritten += size;
+ return size == 0 || ::write(fFd, buffer, size) > 0;
+ }
+ } fdStream(fd);
+ (void)SkEncodeImage(&fdStream, pixmap, SkEncodedImageFormat::kPNG, 100);
if (fn != NULL) {
notifyMediaScanner(fn);
}
diff --git a/cmds/settings/Android.mk b/cmds/settings/Android.mk
index 05deb99f7228..8a8d1bb95c66 100644
--- a/cmds/settings/Android.mk
+++ b/cmds/settings/Android.mk
@@ -3,11 +3,6 @@
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
-LOCAL_SRC_FILES := $(call all-subdir-java-files)
-LOCAL_MODULE := settings
-LOCAL_MODULE_TAGS := optional
-include $(BUILD_JAVA_LIBRARY)
-
include $(CLEAR_VARS)
LOCAL_MODULE := settings
LOCAL_SRC_FILES := settings
diff --git a/cmds/settings/settings b/cmds/settings/settings
index ef459ca7d4c4..d41ccc62811a 100755
--- a/cmds/settings/settings
+++ b/cmds/settings/settings
@@ -1,5 +1,2 @@
-# Script to start "settings" on the device
-#
-base=/system
-export CLASSPATH=$base/framework/settings.jar
-exec app_process $base/bin com.android.commands.settings.SettingsCmd "$@"
+#!/system/bin/sh
+cmd settings "$@"
diff --git a/cmds/settings/src/com/android/commands/settings/SettingsCmd.java b/cmds/settings/src/com/android/commands/settings/SettingsCmd.java
deleted file mode 100644
index e63a1f58f8fc..000000000000
--- a/cmds/settings/src/com/android/commands/settings/SettingsCmd.java
+++ /dev/null
@@ -1,320 +0,0 @@
-/*
- * Copyright (C) 2012 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.settings;
-
-import android.app.ActivityManagerNative;
-import android.app.IActivityManager;
-import android.app.IActivityManager.ContentProviderHolder;
-import android.content.IContentProvider;
-import android.database.Cursor;
-import android.net.Uri;
-import android.os.Binder;
-import android.os.Bundle;
-import android.os.IBinder;
-import android.os.Process;
-import android.os.RemoteException;
-import android.os.UserHandle;
-import android.provider.Settings;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
-public final class SettingsCmd {
-
- enum CommandVerb {
- UNSPECIFIED,
- GET,
- PUT,
- DELETE,
- LIST,
- }
-
- static String[] mArgs;
- int mNextArg;
- int mUser = -1; // unspecified
- CommandVerb mVerb = CommandVerb.UNSPECIFIED;
- String mTable = null;
- String mKey = null;
- String mValue = null;
-
- public static void main(String[] args) {
- if (args == null || args.length < 2) {
- printUsage();
- return;
- }
-
- mArgs = args;
- try {
- new SettingsCmd().run();
- } catch (Exception e) {
- System.err.println("Unable to run settings command");
- }
- }
-
- public void run() {
- boolean valid = false;
- String arg;
- try {
- while ((arg = nextArg()) != null) {
- if ("--user".equals(arg)) {
- if (mUser != -1) {
- // --user specified more than once; invalid
- break;
- }
- arg = nextArg();
- if ("current".equals(arg) || "cur".equals(arg)) {
- mUser = UserHandle.USER_CURRENT;
- } else {
- mUser = Integer.parseInt(arg);
- }
- } else if (mVerb == CommandVerb.UNSPECIFIED) {
- if ("get".equalsIgnoreCase(arg)) {
- mVerb = CommandVerb.GET;
- } else if ("put".equalsIgnoreCase(arg)) {
- mVerb = CommandVerb.PUT;
- } else if ("delete".equalsIgnoreCase(arg)) {
- mVerb = CommandVerb.DELETE;
- } else if ("list".equalsIgnoreCase(arg)) {
- mVerb = CommandVerb.LIST;
- } else {
- // invalid
- System.err.println("Invalid command: " + arg);
- break;
- }
- } else if (mTable == null) {
- if (!"system".equalsIgnoreCase(arg)
- && !"secure".equalsIgnoreCase(arg)
- && !"global".equalsIgnoreCase(arg)) {
- System.err.println("Invalid namespace '" + arg + "'");
- break; // invalid
- }
- mTable = arg.toLowerCase();
- if (mVerb == CommandVerb.LIST) {
- valid = true;
- break;
- }
- } else if (mVerb == CommandVerb.GET || mVerb == CommandVerb.DELETE) {
- mKey = arg;
- if (mNextArg >= mArgs.length) {
- valid = true;
- } else {
- System.err.println("Too many arguments");
- }
- break;
- } else if (mKey == null) {
- mKey = arg;
- // keep going; there's another PUT arg
- } else { // PUT, final arg
- mValue = arg;
- if (mNextArg >= mArgs.length) {
- valid = true;
- } else {
- System.err.println("Too many arguments");
- }
- break;
- }
- }
- } catch (Exception e) {
- valid = false;
- }
-
- if (valid) {
- try {
- IActivityManager activityManager = ActivityManagerNative.getDefault();
- if (mUser == UserHandle.USER_CURRENT) {
- mUser = activityManager.getCurrentUser().id;
- }
- if (mUser < 0) {
- mUser = UserHandle.USER_SYSTEM;
- }
- IContentProvider provider = null;
- IBinder token = new Binder();
- try {
- ContentProviderHolder holder = activityManager.getContentProviderExternal(
- "settings", UserHandle.USER_SYSTEM, token);
- if (holder == null) {
- throw new IllegalStateException("Could not find settings provider");
- }
- provider = holder.provider;
-
- switch (mVerb) {
- case GET:
- System.out.println(getForUser(provider, mUser, mTable, mKey));
- break;
- case PUT:
- putForUser(provider, mUser, mTable, mKey, mValue);
- break;
- case DELETE:
- System.out.println("Deleted "
- + deleteForUser(provider, mUser, mTable, mKey) + " rows");
- break;
- case LIST:
- for (String line : listForUser(provider, mUser, mTable)) {
- System.out.println(line);
- }
- break;
- default:
- System.err.println("Unspecified command");
- break;
- }
-
- } finally {
- if (provider != null) {
- activityManager.removeContentProviderExternal("settings", token);
- }
- }
- } catch (Exception e) {
- System.err.println("Error while accessing settings provider");
- e.printStackTrace();
- }
-
- } else {
- printUsage();
- }
- }
-
- private List<String> listForUser(IContentProvider provider, int userHandle, String table) {
- final Uri uri = "system".equals(table) ? Settings.System.CONTENT_URI
- : "secure".equals(table) ? Settings.Secure.CONTENT_URI
- : "global".equals(table) ? Settings.Global.CONTENT_URI
- : null;
- final ArrayList<String> lines = new ArrayList<String>();
- if (uri == null) {
- return lines;
- }
- try {
- final Cursor cursor = provider.query(resolveCallingPackage(), uri, null, null, null,
- null, null);
- try {
- while (cursor != null && cursor.moveToNext()) {
- lines.add(cursor.getString(1) + "=" + cursor.getString(2));
- }
- } finally {
- if (cursor != null) {
- cursor.close();
- }
- }
- Collections.sort(lines);
- } catch (RemoteException e) {
- System.err.println("List failed in " + table + " for user " + userHandle);
- }
- return lines;
- }
-
- private String nextArg() {
- if (mNextArg >= mArgs.length) {
- return null;
- }
- String arg = mArgs[mNextArg];
- mNextArg++;
- return arg;
- }
-
- String getForUser(IContentProvider provider, int userHandle,
- final String table, final String key) {
- final String callGetCommand;
- if ("system".equals(table)) callGetCommand = Settings.CALL_METHOD_GET_SYSTEM;
- else if ("secure".equals(table)) callGetCommand = Settings.CALL_METHOD_GET_SECURE;
- else if ("global".equals(table)) callGetCommand = Settings.CALL_METHOD_GET_GLOBAL;
- else {
- System.err.println("Invalid table; no put performed");
- throw new IllegalArgumentException("Invalid table " + table);
- }
-
- String result = null;
- try {
- Bundle arg = new Bundle();
- arg.putInt(Settings.CALL_METHOD_USER_KEY, userHandle);
- Bundle b = provider.call(resolveCallingPackage(), callGetCommand, key, arg);
- if (b != null) {
- result = b.getPairValue();
- }
- } catch (RemoteException e) {
- System.err.println("Can't read key " + key + " in " + table + " for user " + userHandle);
- }
- return result;
- }
-
- void putForUser(IContentProvider provider, int userHandle,
- final String table, final String key, final String value) {
- final String callPutCommand;
- if ("system".equals(table)) callPutCommand = Settings.CALL_METHOD_PUT_SYSTEM;
- else if ("secure".equals(table)) callPutCommand = Settings.CALL_METHOD_PUT_SECURE;
- else if ("global".equals(table)) callPutCommand = Settings.CALL_METHOD_PUT_GLOBAL;
- else {
- System.err.println("Invalid table; no put performed");
- return;
- }
-
- try {
- Bundle arg = new Bundle();
- arg.putString(Settings.NameValueTable.VALUE, value);
- arg.putInt(Settings.CALL_METHOD_USER_KEY, userHandle);
- provider.call(resolveCallingPackage(), callPutCommand, key, arg);
- } catch (RemoteException e) {
- System.err.println("Can't set key " + key + " in " + table + " for user " + userHandle);
- }
- }
-
- 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(resolveCallingPackage(), 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 <USER_ID> | current] get namespace key");
- System.err.println(" settings [--user <USER_ID> | current] put namespace key value");
- System.err.println(" settings [--user <USER_ID> | current] delete namespace key");
- System.err.println(" settings [--user <USER_ID> | current] list namespace");
- System.err.println("\n'namespace' is one of {system, secure, global}, case-insensitive");
- System.err.println("If '--user <USER_ID> | current' is not given, the operations are "
- + "performed on the system user.");
- }
-
- public static String resolveCallingPackage() {
- switch (android.os.Process.myUid()) {
- case Process.ROOT_UID: {
- return "root";
- }
-
- case Process.SHELL_UID: {
- return "com.android.shell";
- }
-
- default: {
- return null;
- }
- }
- }
-}
diff --git a/cmds/sm/src/com/android/commands/sm/Sm.java b/cmds/sm/src/com/android/commands/sm/Sm.java
index d527ad73b787..658d662de5e1 100644
--- a/cmds/sm/src/com/android/commands/sm/Sm.java
+++ b/cmds/sm/src/com/android/commands/sm/Sm.java
@@ -20,7 +20,7 @@ import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemProperties;
import android.os.storage.DiskInfo;
-import android.os.storage.IMountService;
+import android.os.storage.IStorageManager;
import android.os.storage.StorageManager;
import android.os.storage.VolumeInfo;
import android.util.Log;
@@ -28,7 +28,7 @@ import android.util.Log;
public final class Sm {
private static final String TAG = "Sm";
- IMountService mSm;
+ IStorageManager mSm;
private String[] mArgs;
private int mNextArg;
@@ -55,7 +55,7 @@ public final class Sm {
throw new IllegalArgumentException();
}
- mSm = IMountService.Stub.asInterface(ServiceManager.getService("mount"));
+ mSm = IStorageManager.Stub.asInterface(ServiceManager.getService("mount"));
if (mSm == null) {
throw new RemoteException("Failed to find running mount service");
}
@@ -92,6 +92,10 @@ public final class Sm {
runSetEmulateFbe();
} else if ("get-fbe-mode".equals(op)) {
runGetFbeMode();
+ } else if ("fstrim".equals(op)) {
+ runFstrim();
+ } else if ("set-virtual-disk".equals(op)) {
+ runSetVirtualDisk();
} else {
throw new IllegalArgumentException();
}
@@ -210,7 +214,7 @@ public final class Sm {
mSm.benchmark(volId);
}
- public void runForget() throws RemoteException{
+ public void runForget() throws RemoteException {
final String fsUuid = nextArg();
if ("all".equals(fsUuid)) {
mSm.forgetAllVolumes();
@@ -219,6 +223,16 @@ public final class Sm {
}
}
+ public void runFstrim() throws RemoteException {
+ mSm.fstrim(0);
+ }
+
+ public void runSetVirtualDisk() throws RemoteException {
+ final boolean virtualDisk = Boolean.parseBoolean(nextArg());
+ mSm.setDebugFlags(virtualDisk ? StorageManager.DEBUG_VIRTUAL_DISK : 0,
+ StorageManager.DEBUG_VIRTUAL_DISK);
+ }
+
private String nextArg() {
if (mNextArg >= mArgs.length) {
return null;
@@ -234,12 +248,14 @@ public final class Sm {
System.err.println(" sm has-adoptable");
System.err.println(" sm get-primary-storage-uuid");
System.err.println(" sm set-force-adoptable [true|false]");
+ System.err.println(" sm set-virtual-disk [true|false]");
System.err.println("");
System.err.println(" sm partition DISK [public|private|mixed] [ratio]");
System.err.println(" sm mount VOLUME");
System.err.println(" sm unmount VOLUME");
System.err.println(" sm format VOLUME");
System.err.println(" sm benchmark VOLUME");
+ System.err.println(" sm fstrim");
System.err.println("");
System.err.println(" sm forget [UUID|all]");
System.err.println("");
diff --git a/cmds/svc/src/com/android/commands/svc/PowerCommand.java b/cmds/svc/src/com/android/commands/svc/PowerCommand.java
index 6ce29cb24bc6..920a52dad641 100644
--- a/cmds/svc/src/com/android/commands/svc/PowerCommand.java
+++ b/cmds/svc/src/com/android/commands/svc/PowerCommand.java
@@ -22,6 +22,7 @@ import android.os.IPowerManager;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemClock;
+import android.os.SystemProperties;
public class PowerCommand extends Svc.Command {
public PowerCommand() {
@@ -87,7 +88,7 @@ public class PowerCommand extends Svc.Command {
// no confirm, wait till device is rebooted
pm.reboot(false, mode, true);
} catch (RemoteException e) {
- System.err.println("Failed to reboot.");
+ maybeLogRemoteException("Failed to reboot.");
}
return;
} else if ("shutdown".equals(args[1])) {
@@ -95,7 +96,7 @@ public class PowerCommand extends Svc.Command {
// no confirm, wait till device is off
pm.shutdown(false, null, true);
} catch (RemoteException e) {
- System.err.println("Failed to shutdown.");
+ maybeLogRemoteException("Failed to shutdown.");
}
return;
}
@@ -103,4 +104,14 @@ public class PowerCommand extends Svc.Command {
}
System.err.println(longHelp());
}
+
+ // Check if remote exception is benign during shutdown. Pm can be killed
+ // before system server during shutdown, so remote exception can be ignored
+ // if it is already in shutdown flow.
+ private void maybeLogRemoteException(String msg) {
+ String powerProp = SystemProperties.get("sys.powerctl");
+ if (powerProp.isEmpty()) {
+ System.err.println(msg);
+ }
+ }
}
diff --git a/cmds/uiautomator/library/Android.mk b/cmds/uiautomator/library/Android.mk
index 536cbef41feb..4bf856f8cbfa 100644
--- a/cmds/uiautomator/library/Android.mk
+++ b/cmds/uiautomator/library/Android.mk
@@ -49,7 +49,7 @@ LOCAL_DROIDDOC_OPTIONS:= \
-api $(uiautomator_internal_api_file) \
-removedApi $(uiautomator_internal_removed_api_file)
-LOCAL_DROIDDOC_CUSTOM_TEMPLATE_DIR := build/tools/droiddoc/templates-sdk
+LOCAL_DROIDDOC_CUSTOM_TEMPLATE_DIR := external/doclava/res/assets/templates-sdk
LOCAL_UNINSTALLABLE_MODULE := true
LOCAL_MODULE := uiautomator-stubs
diff --git a/cmds/uiautomator/library/testrunner-src/com/android/uiautomator/core/ShellUiAutomatorBridge.java b/cmds/uiautomator/library/testrunner-src/com/android/uiautomator/core/ShellUiAutomatorBridge.java
index cb39e375d307..653851546d01 100644
--- a/cmds/uiautomator/library/testrunner-src/com/android/uiautomator/core/ShellUiAutomatorBridge.java
+++ b/cmds/uiautomator/library/testrunner-src/com/android/uiautomator/core/ShellUiAutomatorBridge.java
@@ -16,10 +16,11 @@
package com.android.uiautomator.core;
-import android.app.ActivityManagerNative;
+import android.app.ActivityManager;
+import android.app.ContentProviderHolder;
import android.app.IActivityManager;
-import android.app.IActivityManager.ContentProviderHolder;
import android.app.UiAutomation;
+import android.content.ContentResolver;
import android.content.Context;
import android.content.IContentProvider;
import android.database.Cursor;
@@ -56,7 +57,7 @@ public class ShellUiAutomatorBridge extends UiAutomatorBridge {
try {
IContentProvider provider = null;
Cursor cursor = null;
- IActivityManager activityManager = ActivityManagerNative.getDefault();
+ IActivityManager activityManager = ActivityManager.getService();
String providerName = Settings.Secure.CONTENT_URI.getAuthority();
IBinder token = new Binder();
try {
@@ -69,10 +70,12 @@ public class ShellUiAutomatorBridge extends UiAutomatorBridge {
cursor = provider.query(null, Settings.Secure.CONTENT_URI,
new String[] {
Settings.Secure.VALUE
- }, "name=?",
- new String[] {
- Settings.Secure.LONG_PRESS_TIMEOUT
- }, null, null);
+ },
+ ContentResolver.createSqlQueryBundle(
+ "name=?",
+ new String[] { Settings.Secure.LONG_PRESS_TIMEOUT },
+ null),
+ null);
if (cursor.moveToFirst()) {
longPressTimeout = cursor.getInt(0);
}
@@ -98,7 +101,7 @@ public class ShellUiAutomatorBridge extends UiAutomatorBridge {
IWindowManager.Stub.asInterface(ServiceManager.getService(Context.WINDOW_SERVICE));
int ret = -1;
try {
- ret = wm.getRotation();
+ ret = wm.getDefaultDisplayRotation();
} catch (RemoteException e) {
Log.e(LOG_TAG, "Error getting screen rotation", e);
throw new RuntimeException(e);
diff --git a/cmds/uiautomator/library/testrunner-src/com/android/uiautomator/core/UiAutomationShellWrapper.java b/cmds/uiautomator/library/testrunner-src/com/android/uiautomator/core/UiAutomationShellWrapper.java
index 19aa11a3b1aa..71561c3c7023 100644
--- a/cmds/uiautomator/library/testrunner-src/com/android/uiautomator/core/UiAutomationShellWrapper.java
+++ b/cmds/uiautomator/library/testrunner-src/com/android/uiautomator/core/UiAutomationShellWrapper.java
@@ -2,7 +2,6 @@ package com.android.uiautomator.core;
import android.accessibilityservice.AccessibilityServiceInfo;
import android.app.ActivityManager;
-import android.app.ActivityManagerNative;
import android.app.IActivityController;
import android.app.IActivityManager;
import android.app.UiAutomation;
@@ -44,7 +43,7 @@ public class UiAutomationShellWrapper {
* @see ActivityManager#isUserAMonkey()
*/
public void setRunAsMonkey(boolean isSet) {
- IActivityManager am = ActivityManagerNative.getDefault();
+ IActivityManager am = ActivityManager.getService();
if (am == null) {
throw new RuntimeException("Can't manage monkey status; is the system running?");
}
diff --git a/cmds/vr/Android.mk b/cmds/vr/Android.mk
new file mode 100644
index 000000000000..d0dc25a36dc8
--- /dev/null
+++ b/cmds/vr/Android.mk
@@ -0,0 +1,15 @@
+# Copyright 2017 The Android Open Source Project
+#
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
+LOCAL_MODULE := vr
+include $(BUILD_JAVA_LIBRARY)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := vr
+LOCAL_SRC_FILES := vr
+LOCAL_MODULE_CLASS := EXECUTABLES
+LOCAL_MODULE_TAGS := optional
+include $(BUILD_PREBUILT)
diff --git a/cmds/vr/MODULE_LICENSE_APACHE2 b/cmds/vr/MODULE_LICENSE_APACHE2
new file mode 100644
index 000000000000..e69de29bb2d1
--- /dev/null
+++ b/cmds/vr/MODULE_LICENSE_APACHE2
diff --git a/cmds/vr/NOTICE b/cmds/vr/NOTICE
new file mode 100644
index 000000000000..25f8ab95ce61
--- /dev/null
+++ b/cmds/vr/NOTICE
@@ -0,0 +1,190 @@
+
+ Copyright (c) 2017, 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/vr/src/com/android/commands/vr/Vr.java b/cmds/vr/src/com/android/commands/vr/Vr.java
new file mode 100644
index 000000000000..b765866faef9
--- /dev/null
+++ b/cmds/vr/src/com/android/commands/vr/Vr.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2017 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.vr;
+
+import android.app.Vr2dDisplayProperties;
+import android.content.Context;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+
+import android.service.vr.IVrManager;
+import com.android.internal.os.BaseCommand;
+
+import java.io.PrintStream;
+
+public final class Vr extends BaseCommand {
+
+ /**
+ * Command-line entry point.
+ *
+ * @param args The command-line arguments
+ */
+ public static void main(String[] args) {
+ (new Vr()).run(args);
+ }
+
+ private static final String COMMAND_SET_PERSISTENT_VR_MODE_ENABLED =
+ "set-persistent-vr-mode-enabled";
+ private static final String COMMAND_SET_VR2D_DISPLAY_PROPERTIES =
+ "set-display-props";
+
+ private IVrManager mVrService;
+
+ @Override
+ public void onShowUsage(PrintStream out) {
+ out.println(
+ "usage: vr [subcommand]\n" +
+ "usage: vr set-persistent-vr-mode-enabled [true|false]\n" +
+ "usage: vr set-display-props [width] [height] [dpi]\n"
+ );
+ }
+
+ @Override
+ public void onRun() throws Exception {
+ mVrService = IVrManager.Stub.asInterface(ServiceManager.getService(Context.VR_SERVICE));
+ if (mVrService == null) {
+ showError("Error: Could not access the Vr Manager. Is the system running?");
+ return;
+ }
+
+ String command = nextArgRequired();
+ switch (command) {
+ case COMMAND_SET_VR2D_DISPLAY_PROPERTIES:
+ runSetVr2dDisplayProperties();
+ break;
+ case COMMAND_SET_PERSISTENT_VR_MODE_ENABLED:
+ runSetPersistentVrModeEnabled();
+ break;
+ default:
+ throw new IllegalArgumentException ("unknown command '" + command + "'");
+ }
+ }
+
+ private void runSetVr2dDisplayProperties() throws RemoteException {
+ String widthStr = nextArgRequired();
+ int width = Integer.parseInt(widthStr);
+
+ String heightStr = nextArgRequired();
+ int height = Integer.parseInt(heightStr);
+
+ String dpiStr = nextArgRequired();
+ int dpi = Integer.parseInt(dpiStr);
+
+ Vr2dDisplayProperties vr2dDisplayProperties =
+ new Vr2dDisplayProperties(width, height, dpi);
+
+ try {
+ mVrService.setVr2dDisplayProperties(vr2dDisplayProperties);
+ } catch (RemoteException re) {
+ System.err.println("Error: Can't set persistent mode " + re);
+ }
+ }
+
+ private void runSetPersistentVrModeEnabled() throws RemoteException {
+ String enableStr = nextArg();
+ boolean enabled = Boolean.parseBoolean(enableStr);
+ try {
+ mVrService.setPersistentVrModeEnabled(enabled);
+ } catch (RemoteException re) {
+ System.err.println("Error: Can't set persistent mode " + re);
+ }
+ }
+}
diff --git a/cmds/vr/vr b/cmds/vr/vr
new file mode 100755
index 000000000000..a279007caed4
--- /dev/null
+++ b/cmds/vr/vr
@@ -0,0 +1,6 @@
+# Script to start "vr" on the device
+#
+base=/system
+export CLASSPATH=$base/framework/vr.jar
+exec app_process $base/bin com.android.commands.vr.Vr "$@"
+
diff --git a/cmds/wm/src/com/android/commands/wm/Wm.java b/cmds/wm/src/com/android/commands/wm/Wm.java
index 383cd01ddcd6..8defb331e289 100644
--- a/cmds/wm/src/com/android/commands/wm/Wm.java
+++ b/cmds/wm/src/com/android/commands/wm/Wm.java
@@ -21,16 +21,22 @@ package com.android.commands.wm;
import android.content.Context;
import android.graphics.Point;
import android.graphics.Rect;
+import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.UserHandle;
import android.util.AndroidException;
import android.util.DisplayMetrics;
+import android.system.Os;
import android.view.Display;
import android.view.IWindowManager;
import com.android.internal.os.BaseCommand;
+import java.io.FileDescriptor;
+import java.io.FileInputStream;
+import java.io.DataInputStream;
import java.io.PrintStream;
+import java.lang.Runtime;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -69,7 +75,9 @@ public class Wm extends BaseCommand {
"wm screen-capture: enable/disable screen capture.\n" +
"\n" +
"wm dismiss-keyguard: dismiss the keyguard, prompting the user for auth if " +
- "necessary.\n"
+ "necessary.\n" +
+ "\n" +
+ "wm surface-trace: log surface commands to stdout in a binary format.\n"
);
}
@@ -96,12 +104,29 @@ public class Wm extends BaseCommand {
runSetScreenCapture();
} else if (op.equals("dismiss-keyguard")) {
runDismissKeyguard();
+ } else if (op.equals("surface-trace")) {
+ runSurfaceTrace();
} else {
showError("Error: unknown command '" + op + "'");
return;
}
}
+ private void runSurfaceTrace() throws Exception {
+ ParcelFileDescriptor pfd = ParcelFileDescriptor.dup(FileDescriptor.out);
+ mWm.enableSurfaceTrace(pfd);
+
+ try {
+ // No one is going to wake us up, we are just waiting on SIGINT. Otherwise
+ // the WM can happily continue writing to our stdout.
+ synchronized (this) {
+ this.wait();
+ }
+ } finally {
+ mWm.disableSurfaceTrace();
+ }
+ }
+
private void runSetScreenCapture() throws Exception {
String userIdStr = nextArg();
String enableStr = nextArg();
@@ -249,7 +274,7 @@ public class Wm extends BaseCommand {
}
private void runDismissKeyguard() throws Exception {
- mWm.dismissKeyguard();
+ mWm.dismissKeyguard(null /* callback */);
}
private int parseDimension(String s) throws NumberFormatException {