summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Android.mk1
-rw-r--r--apct-tests/perftests/multiuser/Android.mk3
-rw-r--r--apct-tests/perftests/multiuser/src/android/multiuser/BenchmarkResults.java77
-rw-r--r--apct-tests/perftests/multiuser/src/android/multiuser/BenchmarkResultsReporter.java74
-rw-r--r--apct-tests/perftests/multiuser/src/android/multiuser/BenchmarkRunner.java103
-rw-r--r--apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTest.java83
-rw-r--r--api/current.txt31
-rw-r--r--api/system-current.txt35
-rw-r--r--api/test-current.txt31
-rw-r--r--cmds/vr/src/com/android/commands/vr/Vr.java24
-rw-r--r--core/java/android/app/Activity.java55
-rw-r--r--core/java/android/app/ActivityManager.java48
-rw-r--r--core/java/android/app/ActivityThread.java57
-rw-r--r--core/java/android/app/IActivityManager.aidl6
-rw-r--r--core/java/android/app/IApplicationThread.aidl2
-rw-r--r--core/java/android/app/KeyguardManager.java3
-rw-r--r--core/java/android/app/ProfilerInfo.java62
-rw-r--r--core/java/android/app/Vr2dDisplayProperties.java123
-rw-r--r--core/java/android/app/WallpaperManager.java22
-rw-r--r--core/java/android/app/timezone/RulesUpdaterContract.java6
-rw-r--r--core/java/android/bluetooth/BluetoothHeadset.java55
-rw-r--r--core/java/android/content/pm/ActivityInfo.java17
-rw-r--r--core/java/android/content/pm/PackageParser.java9
-rw-r--r--core/java/android/hardware/radio/RadioManager.java19
-rw-r--r--core/java/android/os/MemoryFile.java154
-rw-r--r--core/java/android/os/SharedMemory.aidl20
-rw-r--r--core/java/android/os/SharedMemory.java351
-rw-r--r--core/java/android/preference/Preference.java1
-rw-r--r--core/java/android/util/Log.java5
-rw-r--r--core/java/android/view/View.java2
-rw-r--r--core/java/android/view/ViewGroup.java10
-rw-r--r--core/java/android/view/WindowManager.java25
-rw-r--r--core/java/android/view/WindowManagerImpl.java10
-rw-r--r--core/java/android/view/autofill/AutofillManager.java3
-rw-r--r--core/java/android/webkit/WebView.java11
-rw-r--r--core/java/android/webkit/WebViewFactoryProvider.java6
-rw-r--r--core/java/com/android/internal/policy/PhoneWindow.java17
-rw-r--r--core/jni/Android.bp1
-rw-r--r--core/jni/AndroidRuntime.cpp2
-rw-r--r--core/jni/android_os_MemoryFile.cpp98
-rw-r--r--core/jni/android_os_SharedMemory.cpp105
-rw-r--r--core/res/res/values/attrs_manifest.xml33
-rw-r--r--core/res/res/values/config.xml14
-rw-r--r--core/res/res/values/public.xml2
-rw-r--r--core/tests/coretests/src/android/provider/SettingsBackupTest.java11
-rw-r--r--graphics/java/android/graphics/drawable/AdaptiveIconDrawable.java5
-rw-r--r--libs/androidfw/ResourceTypes.cpp7
-rw-r--r--location/java/android/location/GnssStatus.java13
-rw-r--r--location/java/android/location/LocationManager.java7
-rw-r--r--packages/SettingsLib/res/values/strings.xml9
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java6
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/wifi/AccessPointPreference.java13
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/wifi/TestAccessPointBuilder.java (renamed from packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/TestAccessPointBuilder.java)21
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java58
-rw-r--r--packages/SettingsLib/tests/integ/AndroidManifest.xml1
-rw-r--r--packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/AccessPointTest.java34
-rw-r--r--packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java3
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/AccessPointPreferenceTest.java61
-rw-r--r--packages/SystemUI/res/anim/ic_portrait_from_auto_rotate_animation_arrow_top.xml33
-rw-r--r--packages/SystemUI/res/anim/ic_portrait_to_auto_rotate_animation_arrow_top.xml33
-rw-r--r--packages/SystemUI/res/anim/ic_portrait_to_auto_rotate_animation_arrows.xml54
-rw-r--r--packages/SystemUI/res/anim/ic_portrait_to_rotate_arrows_0_animation.xml (renamed from packages/SystemUI/res/anim/ic_portrait_from_auto_rotate_animation_arrows.xml)31
-rw-r--r--packages/SystemUI/res/anim/ic_portrait_to_rotate_arrows_animation.xml26
-rw-r--r--packages/SystemUI/res/anim/ic_portrait_to_rotate_bottom_merged_animation.xml (renamed from packages/SystemUI/res/anim/ic_portrait_from_auto_rotate_animation_arrow_bottom.xml)15
-rw-r--r--packages/SystemUI/res/anim/ic_portrait_to_rotate_device_0_animation.xml (renamed from packages/SystemUI/res/anim/ic_portrait_from_auto_rotate_animation_device.xml)10
-rw-r--r--packages/SystemUI/res/anim/ic_portrait_to_rotate_device_merged_animation.xml (renamed from packages/SystemUI/res/anim/ic_portrait_from_auto_rotate_animation_device_1.xml)11
-rw-r--r--packages/SystemUI/res/anim/ic_rotate_to_portrait_arrows_0_animation.xml33
-rw-r--r--packages/SystemUI/res/anim/ic_rotate_to_portrait_arrows_animation.xml26
-rw-r--r--packages/SystemUI/res/anim/ic_rotate_to_portrait_bottom_merged_animation.xml (renamed from packages/SystemUI/res/anim/ic_portrait_to_auto_rotate_animation_arrow_bottom.xml)15
-rw-r--r--packages/SystemUI/res/anim/ic_rotate_to_portrait_device_0_animation.xml (renamed from packages/SystemUI/res/anim/ic_portrait_to_auto_rotate_animation_device.xml)17
-rw-r--r--packages/SystemUI/res/anim/ic_rotate_to_portrait_device_merged_animation.xml (renamed from packages/SystemUI/res/anim/ic_portrait_to_auto_rotate_animation_device_1.xml)7
-rw-r--r--packages/SystemUI/res/drawable/ic_portrait_from_auto_rotate.xml57
-rw-r--r--packages/SystemUI/res/drawable/ic_portrait_from_auto_rotate_animation.xml27
-rw-r--r--packages/SystemUI/res/drawable/ic_portrait_to_auto_rotate.xml61
-rw-r--r--packages/SystemUI/res/drawable/ic_portrait_to_auto_rotate_animation.xml27
-rw-r--r--packages/SystemUI/res/interpolator/ic_rotate_to_portrait_animation_interpolator_0.xml19
-rw-r--r--packages/SystemUI/res/values/strings.xml2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java13
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationHeaderUtil.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java1
-rw-r--r--proto/src/metrics_constants.proto2
-rw-r--r--services/core/Android.mk12
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerDebugConfig.java1
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java176
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerShellCommand.java20
-rw-r--r--services/core/java/com/android/server/am/ActivityRecord.java107
-rw-r--r--services/core/java/com/android/server/am/ActivityStack.java144
-rw-r--r--services/core/java/com/android/server/am/ActivityStackSupervisor.java450
-rw-r--r--services/core/java/com/android/server/am/KeyguardController.java8
-rw-r--r--services/core/java/com/android/server/content/ContentService.java28
-rw-r--r--services/core/java/com/android/server/content/SyncManager.java16
-rw-r--r--services/core/java/com/android/server/pm/UserManagerService.java10
-rw-r--r--services/core/java/com/android/server/policy/PhoneWindowManager.java11
-rw-r--r--services/core/java/com/android/server/vr/Vr2dDisplay.java61
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerService.java5
-rw-r--r--services/core/jni/Android.mk1
-rw-r--r--services/core/jni/com_android_server_GraphicsStatsService.cpp3
-rw-r--r--services/core/jni/com_android_server_UsbDescriptorParser.cpp62
-rw-r--r--services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java21
-rw-r--r--services/usb/java/com/android/server/usb/UsbAlsaManager.java37
-rw-r--r--services/usb/java/com/android/server/usb/UsbDeviceManager.java5
-rw-r--r--services/usb/java/com/android/server/usb/UsbHostManager.java10
-rw-r--r--services/usb/java/com/android/server/usb/descriptors/ByteStream.java189
-rw-r--r--services/usb/java/com/android/server/usb/descriptors/UsbACAudioControlEndpoint.java72
-rw-r--r--services/usb/java/com/android/server/usb/descriptors/UsbACAudioStreamEndpoint.java39
-rw-r--r--services/usb/java/com/android/server/usb/descriptors/UsbACEndpoint.java71
-rw-r--r--services/usb/java/com/android/server/usb/descriptors/UsbACFeatureUnit.java76
-rw-r--r--services/usb/java/com/android/server/usb/descriptors/UsbACHeader.java78
-rw-r--r--services/usb/java/com/android/server/usb/descriptors/UsbACInputTerminal.java64
-rw-r--r--services/usb/java/com/android/server/usb/descriptors/UsbACInterface.java190
-rw-r--r--services/usb/java/com/android/server/usb/descriptors/UsbACMidiEndpoint.java52
-rw-r--r--services/usb/java/com/android/server/usb/descriptors/UsbACMixerUnit.java100
-rw-r--r--services/usb/java/com/android/server/usb/descriptors/UsbACOutputTerminal.java49
-rw-r--r--services/usb/java/com/android/server/usb/descriptors/UsbACSelectorUnit.java65
-rw-r--r--services/usb/java/com/android/server/usb/descriptors/UsbACTerminal.java54
-rw-r--r--services/usb/java/com/android/server/usb/descriptors/UsbASFormat.java64
-rw-r--r--services/usb/java/com/android/server/usb/descriptors/UsbASFormatI.java77
-rw-r--r--services/usb/java/com/android/server/usb/descriptors/UsbASFormatII.java72
-rw-r--r--services/usb/java/com/android/server/usb/descriptors/UsbASGeneral.java58
-rw-r--r--services/usb/java/com/android/server/usb/descriptors/UsbBinaryParser.java72
-rw-r--r--services/usb/java/com/android/server/usb/descriptors/UsbConfigDescriptor.java75
-rw-r--r--services/usb/java/com/android/server/usb/descriptors/UsbDescriptor.java223
-rw-r--r--services/usb/java/com/android/server/usb/descriptors/UsbDescriptorParser.java376
-rw-r--r--services/usb/java/com/android/server/usb/descriptors/UsbDeviceDescriptor.java109
-rw-r--r--services/usb/java/com/android/server/usb/descriptors/UsbEndpointDescriptor.java117
-rw-r--r--services/usb/java/com/android/server/usb/descriptors/UsbHIDDescriptor.java70
-rw-r--r--services/usb/java/com/android/server/usb/descriptors/UsbInterfaceAssoc.java73
-rw-r--r--services/usb/java/com/android/server/usb/descriptors/UsbInterfaceDescriptor.java78
-rw-r--r--services/usb/java/com/android/server/usb/descriptors/UsbMSMidiHeader.java36
-rw-r--r--services/usb/java/com/android/server/usb/descriptors/UsbMSMidiInputJack.java36
-rw-r--r--services/usb/java/com/android/server/usb/descriptors/UsbMSMidiOutputJack.java36
-rw-r--r--services/usb/java/com/android/server/usb/descriptors/UsbTerminalTypes.java93
-rw-r--r--services/usb/java/com/android/server/usb/descriptors/UsbUnknown.java28
-rw-r--r--services/usb/java/com/android/server/usb/descriptors/report/HTMLReporter.java572
-rw-r--r--services/usb/java/com/android/server/usb/descriptors/report/Reporter.java40
-rw-r--r--services/usb/java/com/android/server/usb/descriptors/report/Reporting.java27
-rw-r--r--services/usb/java/com/android/server/usb/descriptors/report/UsbStrings.java312
-rw-r--r--telephony/java/android/telephony/MbmsDownloadManager.java121
-rw-r--r--telephony/java/android/telephony/MbmsStreamingManager.java31
-rw-r--r--telephony/java/android/telephony/mbms/DownloadRequest.java48
-rw-r--r--telephony/java/android/telephony/mbms/MbmsDownloadReceiver.java49
-rw-r--r--telephony/java/android/telephony/mbms/MbmsException.java2
-rw-r--r--telephony/java/android/telephony/mbms/ServiceInfo.java23
-rw-r--r--telephony/java/android/telephony/mbms/StreamingService.java11
-rwxr-xr-xtelephony/java/android/telephony/mbms/vendor/IMbmsDownloadService.aidl32
-rwxr-xr-xtelephony/java/android/telephony/mbms/vendor/IMbmsStreamingService.aidl24
-rw-r--r--telephony/java/android/telephony/mbms/vendor/MbmsDownloadServiceBase.java80
-rw-r--r--telephony/java/android/telephony/mbms/vendor/MbmsStreamingServiceBase.java41
-rw-r--r--tests/ShowWhenLockedApp/Android.mk10
-rw-r--r--tests/ShowWhenLockedApp/AndroidManifest.xml31
-rw-r--r--tests/ShowWhenLockedApp/src/com/android/showwhenlocked/ShowWhenLockedActivity.java164
-rw-r--r--tools/aapt2/ConfigDescription.cpp4
-rw-r--r--tools/aapt2/ConfigDescription.h90
-rw-r--r--tools/aapt2/cmd/Link.cpp86
-rw-r--r--tools/aapt2/integration-tests/AppOne/res/drawable/adaptive-icon.xml20
-rw-r--r--tools/aapt2/link/AutoVersioner.cpp33
-rw-r--r--tools/aapt2/link/AutoVersioner_test.cpp2
-rw-r--r--tools/aapt2/link/Linkers.h15
-rw-r--r--tools/aapt2/readme.md1
-rw-r--r--tools/aapt2/util/TypeTraits.h1
-rw-r--r--tools/aapt2/util/Util.h5
-rw-r--r--tools/aapt2/xml/XmlUtil.cpp60
-rw-r--r--tools/aapt2/xml/XmlUtil.h28
166 files changed, 6987 insertions, 1563 deletions
diff --git a/Android.mk b/Android.mk
index 1a66896b2545..7fda1dde463a 100644
--- a/Android.mk
+++ b/Android.mk
@@ -729,6 +729,7 @@ aidl_files := \
frameworks/base/core/java/android/os/DropBoxManager.aidl \
frameworks/base/core/java/android/os/Bundle.aidl \
frameworks/base/core/java/android/os/Debug.aidl \
+ frameworks/base/core/java/android/os/SharedMemory.aidl \
frameworks/base/core/java/android/os/StrictMode.aidl \
frameworks/base/core/java/android/accessibilityservice/AccessibilityServiceInfo.aidl \
frameworks/base/core/java/android/net/Network.aidl \
diff --git a/apct-tests/perftests/multiuser/Android.mk b/apct-tests/perftests/multiuser/Android.mk
index f67004358ee6..e3f7775383bd 100644
--- a/apct-tests/perftests/multiuser/Android.mk
+++ b/apct-tests/perftests/multiuser/Android.mk
@@ -20,8 +20,7 @@ LOCAL_MODULE_TAGS := tests
LOCAL_SRC_FILES := $(call all-java-files-under, src)
LOCAL_STATIC_JAVA_LIBRARIES := \
- android-support-test \
- apct-perftests-utils
+ android-support-test
LOCAL_PACKAGE_NAME := MultiUserPerfTests
diff --git a/apct-tests/perftests/multiuser/src/android/multiuser/BenchmarkResults.java b/apct-tests/perftests/multiuser/src/android/multiuser/BenchmarkResults.java
new file mode 100644
index 000000000000..0d764ce29c74
--- /dev/null
+++ b/apct-tests/perftests/multiuser/src/android/multiuser/BenchmarkResults.java
@@ -0,0 +1,77 @@
+/*
+ * 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 android.multiuser;
+
+import android.os.Bundle;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.concurrent.TimeUnit;
+
+public class BenchmarkResults {
+ private final ArrayList<Long> mResults = new ArrayList<>();
+
+ public void addDuration(long duration) {
+ mResults.add(TimeUnit.NANOSECONDS.toMillis(duration));
+ }
+
+ public Bundle getStats() {
+ final Bundle stats = new Bundle();
+ stats.putDouble("Mean (ms)", mean());
+ stats.putDouble("Median (ms)", median());
+ stats.putDouble("Sigma (ms)", standardDeviation());
+ return stats;
+ }
+
+ public ArrayList<Long> getAllDurations() {
+ return mResults;
+ }
+
+ private double mean() {
+ final int size = mResults.size();
+ long sum = 0;
+ for (int i = 0; i < size; ++i) {
+ sum += mResults.get(i);
+ }
+ return (double) sum / size;
+ }
+
+ private double median() {
+ final int size = mResults.size();
+ if (size == 0) {
+ return 0f;
+ }
+ Collections.sort(mResults);
+ final int idx = size / 2;
+ return size % 2 == 0
+ ? (double) (mResults.get(idx) + mResults.get(idx - 1)) / 2
+ : mResults.get(idx);
+ }
+
+ private double standardDeviation() {
+ final int size = mResults.size();
+ if (size == 0) {
+ return 0f;
+ }
+ final double mean = mean();
+ double sd = 0;
+ for (int i = 0; i < size; ++i) {
+ double diff = mResults.get(i) - mean;
+ sd += diff * diff;
+ }
+ return Math.sqrt(sd / size);
+ }
+}
diff --git a/apct-tests/perftests/multiuser/src/android/multiuser/BenchmarkResultsReporter.java b/apct-tests/perftests/multiuser/src/android/multiuser/BenchmarkResultsReporter.java
new file mode 100644
index 000000000000..7472865e9a5a
--- /dev/null
+++ b/apct-tests/perftests/multiuser/src/android/multiuser/BenchmarkResultsReporter.java
@@ -0,0 +1,74 @@
+/*
+ * 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 android.multiuser;
+
+import android.app.Activity;
+import android.app.Instrumentation;
+import android.os.Bundle;
+import android.support.test.InstrumentationRegistry;
+import android.util.Log;
+
+import org.junit.rules.TestRule;
+import org.junit.runner.Description;
+import org.junit.runners.model.Statement;
+
+import java.util.ArrayList;
+
+public class BenchmarkResultsReporter implements TestRule {
+ private final BenchmarkRunner mRunner;
+
+ public BenchmarkResultsReporter(BenchmarkRunner benchmarkRunner) {
+ mRunner = benchmarkRunner;
+ }
+
+ @Override
+ public Statement apply(final Statement base, final Description description) {
+ return new Statement() {
+ @Override
+ public void evaluate() throws Throwable {
+ base.evaluate();
+ final Bundle stats = mRunner.getStats();
+ final String summary = getSummaryString(description.getMethodName(), stats);
+ logSummary(description.getTestClass().getSimpleName(), summary, mRunner.getAllDurations());
+ stats.putString(Instrumentation.REPORT_KEY_STREAMRESULT, summary);
+ InstrumentationRegistry.getInstrumentation().sendStatus(
+ Activity.RESULT_OK, stats);
+ }
+ };
+ }
+
+ private void logSummary(String tag, String summary, ArrayList<Long> durations) {
+ final StringBuilder sb = new StringBuilder(summary);
+ final int size = durations.size();
+ for (int i = 0; i < size; ++i) {
+ sb.append("\n").append(i).append("->").append(durations.get(i));
+ }
+ Log.d(tag, sb.toString());
+ }
+
+ private String getSummaryString(String testName, Bundle stats) {
+ final StringBuilder sb = new StringBuilder();
+ sb.append("\n\n").append(getKey(testName));
+ for (String key : stats.keySet()) {
+ sb.append("\n").append(key).append(": ").append(stats.get(key));
+ }
+ return sb.toString();
+ }
+
+ private String getKey(String testName) {
+ return testName.replaceAll("Perf$", "");
+ }
+}
diff --git a/apct-tests/perftests/multiuser/src/android/multiuser/BenchmarkRunner.java b/apct-tests/perftests/multiuser/src/android/multiuser/BenchmarkRunner.java
new file mode 100644
index 000000000000..ccadc9a8f6a9
--- /dev/null
+++ b/apct-tests/perftests/multiuser/src/android/multiuser/BenchmarkRunner.java
@@ -0,0 +1,103 @@
+/*
+ * 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 android.multiuser;
+
+import android.os.Bundle;
+import android.os.SystemClock;
+
+import java.util.ArrayList;
+
+// Based on //platform/frameworks/base/apct-tests/perftests/utils/BenchmarkState.java
+public class BenchmarkRunner {
+
+ private static long COOL_OFF_PERIOD_MS = 2000;
+
+ private static final int NUM_ITERATIONS = 4;
+
+ private static final int NOT_STARTED = 0; // The benchmark has not started yet.
+ private static final int RUNNING = 1; // The benchmark is running.
+ private static final int PAUSED = 2; // The benchmark is paused
+ private static final int FINISHED = 3; // The benchmark has stopped.
+
+ private final BenchmarkResults mResults = new BenchmarkResults();
+ private int mState = NOT_STARTED; // Current benchmark state.
+ private int mIteration;
+
+ public long mStartTimeNs;
+ public long mPausedDurationNs;
+ public long mPausedTimeNs;
+
+ public boolean keepRunning() {
+ switch (mState) {
+ case NOT_STARTED:
+ mState = RUNNING;
+ prepareForNextRun();
+ return true;
+ case RUNNING:
+ mIteration++;
+ return startNextTestRun();
+ case PAUSED:
+ throw new IllegalStateException("Benchmarking is in paused state");
+ case FINISHED:
+ throw new IllegalStateException("Benchmarking is finished");
+ default:
+ throw new IllegalStateException("BenchmarkRunner is in unknown state");
+ }
+ }
+
+ private boolean startNextTestRun() {
+ mResults.addDuration(System.nanoTime() - mStartTimeNs - mPausedDurationNs);
+ if (mIteration == NUM_ITERATIONS) {
+ mState = FINISHED;
+ return false;
+ } else {
+ prepareForNextRun();
+ return true;
+ }
+ }
+
+ private void prepareForNextRun() {
+ // TODO: Once http://b/63115387 is fixed, look into using "am wait-for-broadcast-idle"
+ // command instead of waiting for a fixed amount of time.
+ SystemClock.sleep(COOL_OFF_PERIOD_MS);
+ mStartTimeNs = System.nanoTime();
+ mPausedDurationNs = 0;
+ }
+
+ public void pauseTiming() {
+ if (mState != RUNNING) {
+ throw new IllegalStateException("Unable to pause the runner: not running currently");
+ }
+ mPausedTimeNs = System.nanoTime();
+ mState = PAUSED;
+ }
+
+ public void resumeTiming() {
+ if (mState != PAUSED) {
+ throw new IllegalStateException("Unable to resume the runner: already running");
+ }
+ mPausedDurationNs += System.nanoTime() - mPausedTimeNs;
+ mState = RUNNING;
+ }
+
+ public Bundle getStats() {
+ return mResults.getStats();
+ }
+
+ public ArrayList<Long> getAllDurations() {
+ return mResults.getAllDurations();
+ }
+} \ No newline at end of file
diff --git a/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTest.java b/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTest.java
index e89157b52108..f114ef47007b 100644
--- a/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTest.java
+++ b/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTest.java
@@ -27,8 +27,6 @@ import android.content.pm.UserInfo;
import android.os.RemoteException;
import android.os.UserHandle;
import android.os.UserManager;
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
import android.support.test.InstrumentationRegistry;
import android.support.test.filters.LargeTest;
import android.support.test.runner.AndroidJUnit4;
@@ -47,26 +45,34 @@ import java.util.concurrent.TimeUnit;
* Perf tests for user life cycle events.
*
* Running the tests:
+ *
* make MultiUserPerfTests &&
* adb install -r \
* ${ANDROID_PRODUCT_OUT}/data/app/MultiUserPerfTests/MultiUserPerfTests.apk &&
* adb shell am instrument -e class android.multiuser.UserLifecycleTest \
* -w com.android.perftests.multiuser/android.support.test.runner.AndroidJUnitRunner
+ *
+ * or
+ *
+ * bit MultiUserPerfTests:android.multiuser.UserLifecycleTest
+ *
+ * Note: If you use bit for running the tests, benchmark results won't be printed on the host side.
+ * But in either case, results can be checked on the device side 'adb logcat -s UserLifecycleTest'
*/
@LargeTest
@RunWith(AndroidJUnit4.class)
public class UserLifecycleTest {
- private final int TIMEOUT_IN_SECOND = 10;
+ private final int TIMEOUT_IN_SECOND = 30;
private final int CHECK_USER_REMOVED_INTERVAL_MS = 200;
private UserManager mUm;
private ActivityManager mAm;
private IActivityManager mIam;
- private BenchmarkState mState;
private ArrayList<Integer> mUsersToRemove;
+ private final BenchmarkRunner mRunner = new BenchmarkRunner();
@Rule
- public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+ public BenchmarkResultsReporter mReporter = new BenchmarkResultsReporter(mRunner);
@Before
public void setUp() {
@@ -74,7 +80,6 @@ public class UserLifecycleTest {
mUm = UserManager.get(context);
mAm = context.getSystemService(ActivityManager.class);
mIam = ActivityManager.getService();
- mState = mPerfStatusReporter.getBenchmarkState();
mUsersToRemove = new ArrayList<>();
}
@@ -91,7 +96,7 @@ public class UserLifecycleTest {
@Test
public void createAndStartUserPerf() throws Exception {
- while (mState.keepRunning()) {
+ while (mRunner.keepRunning()) {
final UserInfo userInfo = mUm.createUser("TestUser", 0);
final CountDownLatch latch = new CountDownLatch(1);
@@ -99,91 +104,91 @@ public class UserLifecycleTest {
mIam.startUserInBackground(userInfo.id);
latch.await(TIMEOUT_IN_SECOND, TimeUnit.SECONDS);
- mState.pauseTiming();
+ mRunner.pauseTiming();
removeUser(userInfo.id);
- mState.resumeTiming();
+ mRunner.resumeTiming();
}
}
@Test
public void switchUserPerf() throws Exception {
- while (mState.keepRunning()) {
- mState.pauseTiming();
+ while (mRunner.keepRunning()) {
+ mRunner.pauseTiming();
final int startUser = mAm.getCurrentUser();
final UserInfo userInfo = mUm.createUser("TestUser", 0);
- mState.resumeTiming();
+ mRunner.resumeTiming();
switchUser(userInfo.id);
- mState.pauseTiming();
+ mRunner.pauseTiming();
switchUser(startUser);
removeUser(userInfo.id);
- mState.resumeTiming();
+ mRunner.resumeTiming();
}
}
@Test
public void stopUserPerf() throws Exception {
- while (mState.keepRunning()) {
- mState.pauseTiming();
+ while (mRunner.keepRunning()) {
+ mRunner.pauseTiming();
final UserInfo userInfo = mUm.createUser("TestUser", 0);
final CountDownLatch latch = new CountDownLatch(1);
registerBroadcastReceiver(Intent.ACTION_USER_STARTED, latch, userInfo.id);
mIam.startUserInBackground(userInfo.id);
latch.await(TIMEOUT_IN_SECOND, TimeUnit.SECONDS);
- mState.resumeTiming();
+ mRunner.resumeTiming();
stopUser(userInfo.id, false);
- mState.pauseTiming();
+ mRunner.pauseTiming();
removeUser(userInfo.id);
- mState.resumeTiming();
+ mRunner.resumeTiming();
}
}
@Test
public void lockedBootCompletedPerf() throws Exception {
- while (mState.keepRunning()) {
- mState.pauseTiming();
+ while (mRunner.keepRunning()) {
+ mRunner.pauseTiming();
final int startUser = mAm.getCurrentUser();
final UserInfo userInfo = mUm.createUser("TestUser", 0);
final CountDownLatch latch = new CountDownLatch(1);
registerUserSwitchObserver(null, latch, userInfo.id);
- mState.resumeTiming();
+ mRunner.resumeTiming();
mAm.switchUser(userInfo.id);
latch.await(TIMEOUT_IN_SECOND, TimeUnit.SECONDS);
- mState.pauseTiming();
+ mRunner.pauseTiming();
switchUser(startUser);
removeUser(userInfo.id);
- mState.resumeTiming();
+ mRunner.resumeTiming();
}
}
@Test
public void managedProfileUnlockPerf() throws Exception {
- while (mState.keepRunning()) {
- mState.pauseTiming();
+ while (mRunner.keepRunning()) {
+ mRunner.pauseTiming();
final UserInfo userInfo = mUm.createProfileForUser("TestUser",
UserInfo.FLAG_MANAGED_PROFILE, mAm.getCurrentUser());
final CountDownLatch latch = new CountDownLatch(1);
registerBroadcastReceiver(Intent.ACTION_USER_UNLOCKED, latch, userInfo.id);
- mState.resumeTiming();
+ mRunner.resumeTiming();
mIam.startUserInBackground(userInfo.id);
latch.await(TIMEOUT_IN_SECOND, TimeUnit.SECONDS);
- mState.pauseTiming();
+ mRunner.pauseTiming();
removeUser(userInfo.id);
- mState.resumeTiming();
+ mRunner.resumeTiming();
}
}
@Test
public void ephemeralUserStoppedPerf() throws Exception {
- while (mState.keepRunning()) {
- mState.pauseTiming();
+ while (mRunner.keepRunning()) {
+ mRunner.pauseTiming();
final int startUser = mAm.getCurrentUser();
final UserInfo userInfo = mUm.createUser("TestUser",
UserInfo.FLAG_EPHEMERAL | UserInfo.FLAG_DEMO);
@@ -200,35 +205,35 @@ public class UserLifecycleTest {
}, new IntentFilter(Intent.ACTION_USER_STOPPED));
final CountDownLatch switchLatch = new CountDownLatch(1);
registerUserSwitchObserver(switchLatch, null, startUser);
- mState.resumeTiming();
+ mRunner.resumeTiming();
mAm.switchUser(startUser);
latch.await(TIMEOUT_IN_SECOND, TimeUnit.SECONDS);
- mState.pauseTiming();
+ mRunner.pauseTiming();
switchLatch.await(TIMEOUT_IN_SECOND, TimeUnit.SECONDS);
removeUser(userInfo.id);
- mState.resumeTiming();
+ mRunner.resumeTiming();
}
}
@Test
public void managedProfileStoppedPerf() throws Exception {
- while (mState.keepRunning()) {
- mState.pauseTiming();
+ while (mRunner.keepRunning()) {
+ mRunner.pauseTiming();
final UserInfo userInfo = mUm.createProfileForUser("TestUser",
UserInfo.FLAG_MANAGED_PROFILE, mAm.getCurrentUser());
final CountDownLatch latch = new CountDownLatch(1);
registerBroadcastReceiver(Intent.ACTION_USER_UNLOCKED, latch, userInfo.id);
mIam.startUserInBackground(userInfo.id);
latch.await(TIMEOUT_IN_SECOND, TimeUnit.SECONDS);
- mState.resumeTiming();
+ mRunner.resumeTiming();
stopUser(userInfo.id, true);
- mState.pauseTiming();
+ mRunner.pauseTiming();
removeUser(userInfo.id);
- mState.resumeTiming();
+ mRunner.resumeTiming();
}
}
diff --git a/api/current.txt b/api/current.txt
index 17dbcbf392b9..0bda5706ff13 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -1180,6 +1180,7 @@ package android {
field public static final int showSilent = 16843259; // 0x10101fb
field public static final int showText = 16843949; // 0x10104ad
field public static final deprecated int showWeekNumber = 16843582; // 0x101033e
+ field public static final int showWhenLocked = 16844137; // 0x1010569
field public static final deprecated int shownWeekCount = 16843585; // 0x1010341
field public static final int shrinkColumns = 16843082; // 0x101014a
field public static final deprecated int singleLine = 16843101; // 0x101015d
@@ -1438,6 +1439,7 @@ package android {
field public static final int trimPathOffset = 16843786; // 0x101040a
field public static final int trimPathStart = 16843784; // 0x1010408
field public static final int tunerCount = 16844061; // 0x101051d
+ field public static final int turnScreenOn = 16844138; // 0x101056a
field public static final int type = 16843169; // 0x10101a1
field public static final int typeface = 16842902; // 0x1010096
field public static final int uiOptions = 16843672; // 0x1010398
@@ -3764,10 +3766,12 @@ package android.app {
method public final void setResult(int);
method public final void setResult(int, android.content.Intent);
method public final deprecated void setSecondaryProgress(int);
+ method public void setShowWhenLocked(boolean);
method public void setTaskDescription(android.app.ActivityManager.TaskDescription);
method public void setTitle(java.lang.CharSequence);
method public void setTitle(int);
method public deprecated void setTitleColor(int);
+ method public void setTurnScreenOn(boolean);
method public void setVisible(boolean);
method public final void setVolumeControlStream(int);
method public void setVrModeEnabled(boolean, android.content.ComponentName) throws android.content.pm.PackageManager.NameNotFoundException;
@@ -31023,12 +31027,12 @@ package android.os {
public class MemoryFile {
ctor public MemoryFile(java.lang.String, int) throws java.io.IOException;
- method public synchronized boolean allowPurging(boolean) throws java.io.IOException;
+ method public deprecated synchronized boolean allowPurging(boolean) throws java.io.IOException;
method public void close();
- method protected void finalize();
+ method public java.io.FileDescriptor getFileDescriptor() throws java.io.IOException;
method public java.io.InputStream getInputStream();
method public java.io.OutputStream getOutputStream();
- method public boolean isPurgingAllowed();
+ method public deprecated boolean isPurgingAllowed();
method public int length();
method public int readBytes(byte[], int, int, int) throws java.io.IOException;
method public void writeBytes(byte[], int, int, int) throws java.io.IOException;
@@ -31453,6 +31457,22 @@ package android.os {
field public static final android.os.Parcelable.Creator<android.os.ResultReceiver> CREATOR;
}
+ public final class SharedMemory implements java.io.Closeable android.os.Parcelable {
+ method public void close();
+ method public static android.os.SharedMemory create(java.lang.String, int) throws android.system.ErrnoException;
+ method public int describeContents();
+ method public int getFd();
+ method public java.io.FileDescriptor getFileDescriptor();
+ method public int getSize();
+ method public java.nio.ByteBuffer map(int, int, int) throws android.system.ErrnoException;
+ method public java.nio.ByteBuffer mapReadOnly() throws android.system.ErrnoException;
+ method public java.nio.ByteBuffer mapReadWrite() throws android.system.ErrnoException;
+ method public boolean setProtect(int);
+ method public static void unmap(java.nio.ByteBuffer);
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.os.SharedMemory> CREATOR;
+ }
+
public class StatFs {
ctor public StatFs(java.lang.String);
method public deprecated int getAvailableBlocks();
@@ -46953,12 +46973,12 @@ package android.view {
field public static final int FLAG_SCALED = 16384; // 0x4000
field public static final int FLAG_SECURE = 8192; // 0x2000
field public static final int FLAG_SHOW_WALLPAPER = 1048576; // 0x100000
- field public static final int FLAG_SHOW_WHEN_LOCKED = 524288; // 0x80000
+ field public static final deprecated int FLAG_SHOW_WHEN_LOCKED = 524288; // 0x80000
field public static final int FLAG_SPLIT_TOUCH = 8388608; // 0x800000
field public static final deprecated int FLAG_TOUCHABLE_WHEN_WAKING = 64; // 0x40
field public static final int FLAG_TRANSLUCENT_NAVIGATION = 134217728; // 0x8000000
field public static final int FLAG_TRANSLUCENT_STATUS = 67108864; // 0x4000000
- field public static final int FLAG_TURN_SCREEN_ON = 2097152; // 0x200000
+ field public static final deprecated int FLAG_TURN_SCREEN_ON = 2097152; // 0x200000
field public static final int FLAG_WATCH_OUTSIDE_TOUCH = 262144; // 0x40000
field public static final int FORMAT_CHANGED = 8; // 0x8
field public static final int LAST_APPLICATION_WINDOW = 99; // 0x63
@@ -49014,6 +49034,7 @@ package android.webkit {
method public void setNetworkAvailable(boolean);
method public deprecated void setPictureListener(android.webkit.WebView.PictureListener);
method public void setRendererPriorityPolicy(int, boolean);
+ method public static void setSafeBrowsingWhiteList(java.lang.String[]);
method public deprecated void setVerticalScrollbarOverlay(boolean);
method public void setWebChromeClient(android.webkit.WebChromeClient);
method public static void setWebContentsDebuggingEnabled(boolean);
diff --git a/api/system-current.txt b/api/system-current.txt
index 53e1b81d95d3..12d3d9697e70 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -1307,6 +1307,7 @@ package android {
field public static final int showSilent = 16843259; // 0x10101fb
field public static final int showText = 16843949; // 0x10104ad
field public static final deprecated int showWeekNumber = 16843582; // 0x101033e
+ field public static final int showWhenLocked = 16844137; // 0x1010569
field public static final deprecated int shownWeekCount = 16843585; // 0x1010341
field public static final int shrinkColumns = 16843082; // 0x101014a
field public static final deprecated int singleLine = 16843101; // 0x101015d
@@ -1565,6 +1566,7 @@ package android {
field public static final int trimPathOffset = 16843786; // 0x101040a
field public static final int trimPathStart = 16843784; // 0x1010408
field public static final int tunerCount = 16844061; // 0x101051d
+ field public static final int turnScreenOn = 16844138; // 0x101056a
field public static final int type = 16843169; // 0x10101a1
field public static final int typeface = 16842902; // 0x1010096
field public static final int uiOptions = 16843672; // 0x1010398
@@ -3900,10 +3902,12 @@ package android.app {
method public final void setResult(int);
method public final void setResult(int, android.content.Intent);
method public final deprecated void setSecondaryProgress(int);
+ method public void setShowWhenLocked(boolean);
method public void setTaskDescription(android.app.ActivityManager.TaskDescription);
method public void setTitle(java.lang.CharSequence);
method public void setTitle(int);
method public deprecated void setTitleColor(int);
+ method public void setTurnScreenOn(boolean);
method public void setVisible(boolean);
method public final void setVolumeControlStream(int);
method public void setVrModeEnabled(boolean, android.content.ComponentName) throws android.content.pm.PackageManager.NameNotFoundException;
@@ -17242,6 +17246,8 @@ package android.hardware.radio {
method public int getSpacing();
method public int getType();
method public int getUpperLimit();
+ method public boolean isAmBand();
+ method public boolean isFmBand();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.hardware.radio.RadioManager.BandDescriptor> CREATOR;
}
@@ -33773,12 +33779,12 @@ package android.os {
public class MemoryFile {
ctor public MemoryFile(java.lang.String, int) throws java.io.IOException;
- method public synchronized boolean allowPurging(boolean) throws java.io.IOException;
+ method public deprecated synchronized boolean allowPurging(boolean) throws java.io.IOException;
method public void close();
- method protected void finalize();
+ method public java.io.FileDescriptor getFileDescriptor() throws java.io.IOException;
method public java.io.InputStream getInputStream();
method public java.io.OutputStream getOutputStream();
- method public boolean isPurgingAllowed();
+ method public deprecated boolean isPurgingAllowed();
method public int length();
method public int readBytes(byte[], int, int, int) throws java.io.IOException;
method public void writeBytes(byte[], int, int, int) throws java.io.IOException;
@@ -34232,6 +34238,22 @@ package android.os {
field public static final android.os.Parcelable.Creator<android.os.ResultReceiver> CREATOR;
}
+ public final class SharedMemory implements java.io.Closeable android.os.Parcelable {
+ method public void close();
+ method public static android.os.SharedMemory create(java.lang.String, int) throws android.system.ErrnoException;
+ method public int describeContents();
+ method public int getFd();
+ method public java.io.FileDescriptor getFileDescriptor();
+ method public int getSize();
+ method public java.nio.ByteBuffer map(int, int, int) throws android.system.ErrnoException;
+ method public java.nio.ByteBuffer mapReadOnly() throws android.system.ErrnoException;
+ method public java.nio.ByteBuffer mapReadWrite() throws android.system.ErrnoException;
+ method public boolean setProtect(int);
+ method public static void unmap(java.nio.ByteBuffer);
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.os.SharedMemory> CREATOR;
+ }
+
public class StatFs {
ctor public StatFs(java.lang.String);
method public deprecated int getAvailableBlocks();
@@ -50436,6 +50458,7 @@ package android.view {
}
public abstract interface WindowManager implements android.view.ViewManager {
+ method public abstract android.graphics.Region getCurrentImeTouchRegion();
method public abstract android.view.Display getDefaultDisplay();
method public abstract void removeViewImmediate(android.view.View);
}
@@ -50504,12 +50527,12 @@ package android.view {
field public static final int FLAG_SCALED = 16384; // 0x4000
field public static final int FLAG_SECURE = 8192; // 0x2000
field public static final int FLAG_SHOW_WALLPAPER = 1048576; // 0x100000
- field public static final int FLAG_SHOW_WHEN_LOCKED = 524288; // 0x80000
+ field public static final deprecated int FLAG_SHOW_WHEN_LOCKED = 524288; // 0x80000
field public static final int FLAG_SPLIT_TOUCH = 8388608; // 0x800000
field public static final deprecated int FLAG_TOUCHABLE_WHEN_WAKING = 64; // 0x40
field public static final int FLAG_TRANSLUCENT_NAVIGATION = 134217728; // 0x8000000
field public static final int FLAG_TRANSLUCENT_STATUS = 67108864; // 0x4000000
- field public static final int FLAG_TURN_SCREEN_ON = 2097152; // 0x200000
+ field public static final deprecated int FLAG_TURN_SCREEN_ON = 2097152; // 0x200000
field public static final int FLAG_WATCH_OUTSIDE_TOUCH = 262144; // 0x40000
field public static final int FORMAT_CHANGED = 8; // 0x8
field public static final int LAST_APPLICATION_WINDOW = 99; // 0x63
@@ -52660,6 +52683,7 @@ package android.webkit {
method public void setNetworkAvailable(boolean);
method public deprecated void setPictureListener(android.webkit.WebView.PictureListener);
method public void setRendererPriorityPolicy(int, boolean);
+ method public static void setSafeBrowsingWhiteList(java.lang.String[]);
method public deprecated void setVerticalScrollbarOverlay(boolean);
method public void setWebChromeClient(android.webkit.WebChromeClient);
method public static void setWebContentsDebuggingEnabled(boolean);
@@ -52864,6 +52888,7 @@ package android.webkit {
method public abstract java.lang.String getDefaultUserAgent(android.content.Context);
method public abstract void initSafeBrowsing(android.content.Context, android.webkit.ValueCallback<java.lang.Boolean>);
method public abstract android.net.Uri[] parseFileChooserResult(int, android.content.Intent);
+ method public abstract void setSafeBrowsingWhiteList(java.lang.String[]);
method public abstract void setWebContentsDebuggingEnabled(boolean);
method public abstract void shutdownSafeBrowsing();
}
diff --git a/api/test-current.txt b/api/test-current.txt
index abfcaba3727e..6d401e1758fb 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -1180,6 +1180,7 @@ package android {
field public static final int showSilent = 16843259; // 0x10101fb
field public static final int showText = 16843949; // 0x10104ad
field public static final deprecated int showWeekNumber = 16843582; // 0x101033e
+ field public static final int showWhenLocked = 16844137; // 0x1010569
field public static final deprecated int shownWeekCount = 16843585; // 0x1010341
field public static final int shrinkColumns = 16843082; // 0x101014a
field public static final deprecated int singleLine = 16843101; // 0x101015d
@@ -1438,6 +1439,7 @@ package android {
field public static final int trimPathOffset = 16843786; // 0x101040a
field public static final int trimPathStart = 16843784; // 0x1010408
field public static final int tunerCount = 16844061; // 0x101051d
+ field public static final int turnScreenOn = 16844138; // 0x101056a
field public static final int type = 16843169; // 0x10101a1
field public static final int typeface = 16842902; // 0x1010096
field public static final int uiOptions = 16843672; // 0x1010398
@@ -3766,10 +3768,12 @@ package android.app {
method public final void setResult(int);
method public final void setResult(int, android.content.Intent);
method public final deprecated void setSecondaryProgress(int);
+ method public void setShowWhenLocked(boolean);
method public void setTaskDescription(android.app.ActivityManager.TaskDescription);
method public void setTitle(java.lang.CharSequence);
method public void setTitle(int);
method public deprecated void setTitleColor(int);
+ method public void setTurnScreenOn(boolean);
method public void setVisible(boolean);
method public final void setVolumeControlStream(int);
method public void setVrModeEnabled(boolean, android.content.ComponentName) throws android.content.pm.PackageManager.NameNotFoundException;
@@ -31154,12 +31158,12 @@ package android.os {
public class MemoryFile {
ctor public MemoryFile(java.lang.String, int) throws java.io.IOException;
- method public synchronized boolean allowPurging(boolean) throws java.io.IOException;
+ method public deprecated synchronized boolean allowPurging(boolean) throws java.io.IOException;
method public void close();
- method protected void finalize();
+ method public java.io.FileDescriptor getFileDescriptor() throws java.io.IOException;
method public java.io.InputStream getInputStream();
method public java.io.OutputStream getOutputStream();
- method public boolean isPurgingAllowed();
+ method public deprecated boolean isPurgingAllowed();
method public int length();
method public int readBytes(byte[], int, int, int) throws java.io.IOException;
method public void writeBytes(byte[], int, int, int) throws java.io.IOException;
@@ -31585,6 +31589,22 @@ package android.os {
field public static final android.os.Parcelable.Creator<android.os.ResultReceiver> CREATOR;
}
+ public final class SharedMemory implements java.io.Closeable android.os.Parcelable {
+ method public void close();
+ method public static android.os.SharedMemory create(java.lang.String, int) throws android.system.ErrnoException;
+ method public int describeContents();
+ method public int getFd();
+ method public java.io.FileDescriptor getFileDescriptor();
+ method public int getSize();
+ method public java.nio.ByteBuffer map(int, int, int) throws android.system.ErrnoException;
+ method public java.nio.ByteBuffer mapReadOnly() throws android.system.ErrnoException;
+ method public java.nio.ByteBuffer mapReadWrite() throws android.system.ErrnoException;
+ method public boolean setProtect(int);
+ method public static void unmap(java.nio.ByteBuffer);
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.os.SharedMemory> CREATOR;
+ }
+
public class StatFs {
ctor public StatFs(java.lang.String);
method public deprecated int getAvailableBlocks();
@@ -47369,12 +47389,12 @@ package android.view {
field public static final int FLAG_SCALED = 16384; // 0x4000
field public static final int FLAG_SECURE = 8192; // 0x2000
field public static final int FLAG_SHOW_WALLPAPER = 1048576; // 0x100000
- field public static final int FLAG_SHOW_WHEN_LOCKED = 524288; // 0x80000
+ field public static final deprecated int FLAG_SHOW_WHEN_LOCKED = 524288; // 0x80000
field public static final int FLAG_SPLIT_TOUCH = 8388608; // 0x800000
field public static final deprecated int FLAG_TOUCHABLE_WHEN_WAKING = 64; // 0x40
field public static final int FLAG_TRANSLUCENT_NAVIGATION = 134217728; // 0x8000000
field public static final int FLAG_TRANSLUCENT_STATUS = 67108864; // 0x4000000
- field public static final int FLAG_TURN_SCREEN_ON = 2097152; // 0x200000
+ field public static final deprecated int FLAG_TURN_SCREEN_ON = 2097152; // 0x200000
field public static final int FLAG_WATCH_OUTSIDE_TOUCH = 262144; // 0x40000
field public static final int FORMAT_CHANGED = 8; // 0x8
field public static final int LAST_APPLICATION_WINDOW = 99; // 0x63
@@ -49435,6 +49455,7 @@ package android.webkit {
method public void setNetworkAvailable(boolean);
method public deprecated void setPictureListener(android.webkit.WebView.PictureListener);
method public void setRendererPriorityPolicy(int, boolean);
+ method public static void setSafeBrowsingWhiteList(java.lang.String[]);
method public deprecated void setVerticalScrollbarOverlay(boolean);
method public void setWebChromeClient(android.webkit.WebChromeClient);
method public static void setWebContentsDebuggingEnabled(boolean);
diff --git a/cmds/vr/src/com/android/commands/vr/Vr.java b/cmds/vr/src/com/android/commands/vr/Vr.java
index b765866faef9..8fb1b7b80618 100644
--- a/cmds/vr/src/com/android/commands/vr/Vr.java
+++ b/cmds/vr/src/com/android/commands/vr/Vr.java
@@ -41,6 +41,7 @@ public final class Vr extends BaseCommand {
"set-persistent-vr-mode-enabled";
private static final String COMMAND_SET_VR2D_DISPLAY_PROPERTIES =
"set-display-props";
+ private static final String COMMAND_ENABLE_VD = "enable-virtual-display";
private IVrManager mVrService;
@@ -49,7 +50,8 @@ public final class Vr extends BaseCommand {
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"
+ "usage: vr set-display-props [width] [height] [dpi]\n" +
+ "usage: vr enable-virtual-display [true|false]\n"
);
}
@@ -69,6 +71,9 @@ public final class Vr extends BaseCommand {
case COMMAND_SET_PERSISTENT_VR_MODE_ENABLED:
runSetPersistentVrModeEnabled();
break;
+ case COMMAND_ENABLE_VD:
+ runEnableVd();
+ break;
default:
throw new IllegalArgumentException ("unknown command '" + command + "'");
}
@@ -94,6 +99,23 @@ public final class Vr extends BaseCommand {
}
}
+ private void runEnableVd() throws RemoteException {
+ Vr2dDisplayProperties.Builder builder = new Vr2dDisplayProperties.Builder();
+
+ String value = nextArgRequired();
+ if ("true".equals(value)) {
+ builder.setEnabled(true);
+ } else if ("false".equals(value)) {
+ builder.setEnabled(false);
+ } // Don't do anything if not exactly true/false
+
+ try {
+ mVrService.setVr2dDisplayProperties(builder.build());
+ } catch (RemoteException re) {
+ System.err.println("Error: Can't enable (" + value +") virtual display" + re);
+ }
+ }
+
private void runSetPersistentVrModeEnabled() throws RemoteException {
String enableStr = nextArg();
boolean enabled = Boolean.parseBoolean(enableStr);
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 0ff3215e1271..adb3152d4ae7 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -801,10 +801,6 @@ public class Activity extends ContextThemeWrapper
final Handler mHandler = new Handler();
final FragmentController mFragments = FragmentController.createController(new HostCallbacks());
- // Most recent call to requestVisibleBehind().
- @Deprecated
- boolean mVisibleBehind;
-
private static final class ManagedCursor {
ManagedCursor(Cursor cursor) {
mCursor = cursor;
@@ -6459,10 +6455,6 @@ public class Activity extends ContextThemeWrapper
@Deprecated
@SystemApi
public boolean isBackgroundVisibleBehind() {
- try {
- return ActivityManager.getService().isBackgroundVisibleBehind(mToken);
- } catch (RemoteException e) {
- }
return false;
}
@@ -7556,6 +7548,53 @@ public class Activity extends ContextThemeWrapper
}
}
+ /**
+ * Specifies whether an {@link Activity} should be shown on top of the the lock screen whenever
+ * the lockscreen is up and the activity is resumed. Normally an activity will be transitioned
+ * to the stopped state if it is started while the lockscreen is up, but with this flag set the
+ * activity will remain in the resumed state visible on-top of the lock screen. This value can
+ * be set as a manifest attribute using {@link android.R.attr#showWhenLocked}.
+ *
+ * @param showWhenLocked {@code true} to show the {@link Activity} on top of the lock screen;
+ * {@code false} otherwise.
+ * @see #setTurnScreenOn(boolean)
+ * @see android.R.attr#turnScreenOn
+ * @see android.R.attr#showWhenLocked
+ */
+ public void setShowWhenLocked(boolean showWhenLocked) {
+ try {
+ ActivityManager.getService().setShowWhenLocked(mToken, showWhenLocked);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to call setShowWhenLocked", e);
+ }
+ }
+
+ /**
+ * Specifies whether the screen should be turned on when the {@link Activity} is resumed.
+ * Normally an activity will be transitioned to the stopped state if it is started while the
+ * screen if off, but with this flag set the activity will cause the screen to turn on if the
+ * activity will be visible and resumed due to the screen coming on. The screen will not be
+ * turned on if the activity won't be visible after the screen is turned on. This flag is
+ * normally used in conjunction with the {@link android.R.attr#showWhenLocked} flag to make sure
+ * the activity is visible after the screen is turned on when the lockscreen is up. In addition,
+ * if this flag is set and the activity calls {@link
+ * KeyguardManager#requestDismissKeyguard(Activity, KeyguardManager.KeyguardDismissCallback)}
+ * the screen will turn on.
+ *
+ * @param turnScreenOn {@code true} to turn on the screen; {@code false} otherwise.
+ *
+ * @see #setShowWhenLocked(boolean)
+ * @see android.R.attr#turnScreenOn
+ * @see android.R.attr#showWhenLocked
+ */
+ public void setTurnScreenOn(boolean turnScreenOn) {
+ try {
+ ActivityManager.getService().setTurnScreenOn(mToken, turnScreenOn);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to call setTurnScreenOn", e);
+ }
+ }
+
class HostCallbacks extends FragmentHostCallback<Activity> {
public HostCallbacks() {
super(Activity.this /*activity*/);
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 06dbe8218450..20a4ba159ef9 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -24,42 +24,35 @@ import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.annotation.TestApi;
-import android.content.pm.ActivityInfo;
-import android.content.res.Configuration;
-import android.graphics.Canvas;
-import android.graphics.GraphicBuffer;
-import android.graphics.Matrix;
-import android.graphics.Point;
-import android.os.BatteryStats;
-import android.os.Build;
-import android.os.Build.VERSION_CODES;
-import android.os.IBinder;
-import android.os.ParcelFileDescriptor;
-
-import com.android.internal.app.procstats.ProcessStats;
-import com.android.internal.os.RoSystemProperties;
-import com.android.internal.os.TransferPipe;
-import com.android.internal.util.FastPrintWriter;
-import com.android.server.LocalServices;
-
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.UriPermission;
+import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.ConfigurationInfo;
import android.content.pm.IPackageDataObserver;
import android.content.pm.PackageManager;
import android.content.pm.ParceledListSlice;
import android.content.pm.UserInfo;
+import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Bitmap;
+import android.graphics.Canvas;
import android.graphics.Color;
+import android.graphics.GraphicBuffer;
+import android.graphics.Matrix;
+import android.graphics.Point;
import android.graphics.Rect;
+import android.os.BatteryStats;
+import android.os.Build;
+import android.os.Build.VERSION_CODES;
import android.os.Bundle;
import android.os.Debug;
import android.os.Handler;
+import android.os.IBinder;
import android.os.Parcel;
+import android.os.ParcelFileDescriptor;
import android.os.Parcelable;
import android.os.Process;
import android.os.RemoteException;
@@ -72,6 +65,12 @@ import android.util.DisplayMetrics;
import android.util.Singleton;
import android.util.Size;
+import com.android.internal.app.procstats.ProcessStats;
+import com.android.internal.os.RoSystemProperties;
+import com.android.internal.os.TransferPipe;
+import com.android.internal.util.FastPrintWriter;
+import com.android.server.LocalServices;
+
import org.xmlpull.v1.XmlSerializer;
import java.io.FileDescriptor;
@@ -904,19 +903,6 @@ public class ActivityManager {
}
/**
- * Returns true if activities contained in this stack can request visible behind by
- * calling {@link Activity#requestVisibleBehind}.
- *
- * @deprecated This method's functionality is no longer supported as of
- * {@link android.os.Build.VERSION_CODES#O} and will be removed in a future release.
- */
- @Deprecated
- public static boolean activitiesCanRequestVisibleBehind(int stackId) {
- return stackId == FULLSCREEN_WORKSPACE_STACK_ID ||
- stackId == ASSISTANT_STACK_ID;
- }
-
- /**
* Returns true if this stack may be scaled without resizing, and windows within may need
* to be configured as such.
*/
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 4a01fe68f0fd..074cdefc52b0 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -445,7 +445,6 @@ public final class ActivityThread {
sb.append(", startedActivity=").append(activity.mStartedActivity);
sb.append(", temporaryPause=").append(activity.mTemporaryPause);
sb.append(", changingConfigurations=").append(activity.mChangingConfigurations);
- sb.append(", visibleBehind=").append(activity.mVisibleBehind);
sb.append("}");
}
sb.append("}");
@@ -1384,16 +1383,6 @@ public final class ActivityThread {
}
@Override
- public void scheduleCancelVisibleBehind(IBinder token) {
- sendMessage(H.CANCEL_VISIBLE_BEHIND, token);
- }
-
- @Override
- public void scheduleBackgroundVisibleBehindChanged(IBinder token, boolean visible) {
- sendMessage(H.BACKGROUND_VISIBLE_BEHIND_CHANGED, token, visible ? 1 : 0);
- }
-
- @Override
public void scheduleEnterAnimationComplete(IBinder token) {
sendMessage(H.ENTER_ANIMATION_COMPLETE, token);
}
@@ -1509,8 +1498,6 @@ public final class ActivityThread {
public static final int TRANSLUCENT_CONVERSION_COMPLETE = 144;
public static final int INSTALL_PROVIDER = 145;
public static final int ON_NEW_ACTIVITY_OPTIONS = 146;
- public static final int CANCEL_VISIBLE_BEHIND = 147;
- public static final int BACKGROUND_VISIBLE_BEHIND_CHANGED = 148;
public static final int ENTER_ANIMATION_COMPLETE = 149;
public static final int START_BINDER_TRACKING = 150;
public static final int STOP_BINDER_TRACKING_AND_DUMP = 151;
@@ -1571,8 +1558,6 @@ public final class ActivityThread {
case TRANSLUCENT_CONVERSION_COMPLETE: return "TRANSLUCENT_CONVERSION_COMPLETE";
case INSTALL_PROVIDER: return "INSTALL_PROVIDER";
case ON_NEW_ACTIVITY_OPTIONS: return "ON_NEW_ACTIVITY_OPTIONS";
- case CANCEL_VISIBLE_BEHIND: return "CANCEL_VISIBLE_BEHIND";
- case BACKGROUND_VISIBLE_BEHIND_CHANGED: return "BACKGROUND_VISIBLE_BEHIND_CHANGED";
case ENTER_ANIMATION_COMPLETE: return "ENTER_ANIMATION_COMPLETE";
case MULTI_WINDOW_MODE_CHANGED: return "MULTI_WINDOW_MODE_CHANGED";
case PICTURE_IN_PICTURE_MODE_CHANGED: return "PICTURE_IN_PICTURE_MODE_CHANGED";
@@ -1815,12 +1800,6 @@ public final class ActivityThread {
Pair<IBinder, ActivityOptions> pair = (Pair<IBinder, ActivityOptions>) msg.obj;
onNewActivityOptions(pair.first, pair.second);
break;
- case CANCEL_VISIBLE_BEHIND:
- handleCancelVisibleBehind((IBinder) msg.obj);
- break;
- case BACKGROUND_VISIBLE_BEHIND_CHANGED:
- handleOnBackgroundVisibleBehindChanged((IBinder) msg.obj, msg.arg1 > 0);
- break;
case ENTER_ANIMATION_COMPLETE:
handleEnterAnimationComplete((IBinder) msg.obj);
break;
@@ -3070,36 +3049,6 @@ public final class ActivityThread {
}
}
- public void handleCancelVisibleBehind(IBinder token) {
- ActivityClientRecord r = mActivities.get(token);
- if (r != null) {
- mSomeActivitiesChanged = true;
- final Activity activity = r.activity;
- if (activity.mVisibleBehind) {
- activity.mCalled = false;
- activity.onVisibleBehindCanceled();
- // Tick, tick, tick. The activity has 500 msec to return or it will be destroyed.
- if (!activity.mCalled) {
- throw new SuperNotCalledException("Activity " + activity.getLocalClassName() +
- " did not call through to super.onVisibleBehindCanceled()");
- }
- activity.mVisibleBehind = false;
- }
- }
- try {
- ActivityManager.getService().backgroundResourcesReleased(token);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
-
- public void handleOnBackgroundVisibleBehindChanged(IBinder token, boolean visible) {
- ActivityClientRecord r = mActivities.get(token);
- if (r != null) {
- r.activity.onBackgroundVisibleBehindChanged(visible);
- }
- }
-
public void handleInstallProvider(ProviderInfo info) {
final StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskWrites();
try {
@@ -5189,11 +5138,7 @@ public final class ActivityThread {
Slog.w(TAG, "Profiling failed on path " + profilerInfo.profileFile
+ " -- can the process access this path?");
} finally {
- try {
- profilerInfo.profileFd.close();
- } catch (IOException e) {
- Slog.w(TAG, "Failure closing profile fd", e);
- }
+ profilerInfo.closeFd();
}
} else {
switch (profileType) {
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index 3ac026d70a6f..b27e224251d3 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -412,9 +412,6 @@ interface IActivityManager {
void stopSystemLockTaskMode();
void finishVoiceTask(in IVoiceInteractionSession session);
boolean isTopOfTask(in IBinder token);
- boolean requestVisibleBehind(in IBinder token, boolean visible);
- boolean isBackgroundVisibleBehind(in IBinder token);
- void backgroundResourcesReleased(in IBinder token);
void notifyLaunchTaskBehindComplete(in IBinder token);
int startActivityFromRecents(int taskId, in Bundle options);
void notifyEnterAnimationComplete(in IBinder token);
@@ -636,4 +633,7 @@ interface IActivityManager {
// side. If so, make sure they are using the correct transaction ids and arguments.
// If a transaction which will also be used on the native side is being inserted, add it
// alongside with other transactions of this kind at the top of this file.
+
+ void setShowWhenLocked(in IBinder token, boolean showWhenLocked);
+ void setTurnScreenOn(in IBinder token, boolean turnScreenOn);
}
diff --git a/core/java/android/app/IApplicationThread.aidl b/core/java/android/app/IApplicationThread.aidl
index b3521c06ae96..bd8c5c92a502 100644
--- a/core/java/android/app/IApplicationThread.aidl
+++ b/core/java/android/app/IApplicationThread.aidl
@@ -140,8 +140,6 @@ oneway interface IApplicationThread {
void setProcessState(int state);
void scheduleInstallProvider(in ProviderInfo provider);
void updateTimePrefs(int timeFormatPreference);
- void scheduleCancelVisibleBehind(IBinder token);
- void scheduleBackgroundVisibleBehindChanged(IBinder token, boolean enabled);
void scheduleEnterAnimationComplete(IBinder token);
void notifyCleartextNetwork(in byte[] firstPacket);
void startBinderTracking();
diff --git a/core/java/android/app/KeyguardManager.java b/core/java/android/app/KeyguardManager.java
index 16b21f15353e..c0381d69d681 100644
--- a/core/java/android/app/KeyguardManager.java
+++ b/core/java/android/app/KeyguardManager.java
@@ -461,6 +461,9 @@ public class KeyguardManager {
* <p>
* If the Keyguard is secure and the device is not in a trusted state, this will bring up the
* UI so the user can enter their credentials.
+ * <p>
+ * If the value set for the {@link Activity} attr {@link android.R.attr#turnScreenOn} is true,
+ * the screen will turn on when the keyguard is dismissed.
*
* @param activity The activity requesting the dismissal. The activity must be either visible
* by using {@link LayoutParams#FLAG_SHOW_WHEN_LOCKED} or must be in a state in
diff --git a/core/java/android/app/ProfilerInfo.java b/core/java/android/app/ProfilerInfo.java
index f3fe6778375b..fad4798e3a3e 100644
--- a/core/java/android/app/ProfilerInfo.java
+++ b/core/java/android/app/ProfilerInfo.java
@@ -17,8 +17,11 @@
package android.app;
import android.os.Parcel;
-import android.os.Parcelable;
import android.os.ParcelFileDescriptor;
+import android.os.Parcelable;
+import android.util.Slog;
+
+import java.io.IOException;
/**
* System private API for passing profiler settings.
@@ -27,6 +30,8 @@ import android.os.ParcelFileDescriptor;
*/
public class ProfilerInfo implements Parcelable {
+ private static final String TAG = "ProfilerInfo";
+
/* Name of profile output file. */
public final String profileFile;
@@ -39,18 +44,50 @@ public class ProfilerInfo implements Parcelable {
/* Automatically stop the profiler when the app goes idle. */
public final boolean autoStopProfiler;
- /* Indicates whether to stream the profiling info to the out file continuously. */
+ /*
+ * Indicates whether to stream the profiling info to the out file continuously.
+ */
public final boolean streamingOutput;
+ /**
+ * Denotes an agent (and its parameters) to attach for profiling.
+ */
+ public final String agent;
+
public ProfilerInfo(String filename, ParcelFileDescriptor fd, int interval, boolean autoStop,
- boolean streaming) {
+ boolean streaming, String agent) {
profileFile = filename;
profileFd = fd;
samplingInterval = interval;
autoStopProfiler = autoStop;
streamingOutput = streaming;
+ this.agent = agent;
+ }
+
+ public ProfilerInfo(ProfilerInfo in) {
+ profileFile = in.profileFile;
+ profileFd = in.profileFd;
+ samplingInterval = in.samplingInterval;
+ autoStopProfiler = in.autoStopProfiler;
+ streamingOutput = in.streamingOutput;
+ agent = in.agent;
}
+ /**
+ * Close profileFd, if it is open. The field will be null after a call to this function.
+ */
+ public void closeFd() {
+ if (profileFd != null) {
+ try {
+ profileFd.close();
+ } catch (IOException e) {
+ Slog.w(TAG, "Failure closing profile fd", e);
+ }
+ profileFd = null;
+ }
+ }
+
+ @Override
public int describeContents() {
if (profileFd != null) {
return profileFd.describeContents();
@@ -59,6 +96,7 @@ public class ProfilerInfo implements Parcelable {
}
}
+ @Override
public void writeToParcel(Parcel out, int flags) {
out.writeString(profileFile);
if (profileFd != null) {
@@ -70,18 +108,21 @@ public class ProfilerInfo implements Parcelable {
out.writeInt(samplingInterval);
out.writeInt(autoStopProfiler ? 1 : 0);
out.writeInt(streamingOutput ? 1 : 0);
+ out.writeString(agent);
}
public static final Parcelable.Creator<ProfilerInfo> CREATOR =
new Parcelable.Creator<ProfilerInfo>() {
- public ProfilerInfo createFromParcel(Parcel in) {
- return new ProfilerInfo(in);
- }
+ @Override
+ public ProfilerInfo createFromParcel(Parcel in) {
+ return new ProfilerInfo(in);
+ }
- public ProfilerInfo[] newArray(int size) {
- return new ProfilerInfo[size];
- }
- };
+ @Override
+ public ProfilerInfo[] newArray(int size) {
+ return new ProfilerInfo[size];
+ }
+ };
private ProfilerInfo(Parcel in) {
profileFile = in.readString();
@@ -89,5 +130,6 @@ public class ProfilerInfo implements Parcelable {
samplingInterval = in.readInt();
autoStopProfiler = in.readInt() != 0;
streamingOutput = in.readInt() != 0;
+ agent = in.readString();
}
}
diff --git a/core/java/android/app/Vr2dDisplayProperties.java b/core/java/android/app/Vr2dDisplayProperties.java
index a608bb08836f..0eb2af361ae9 100644
--- a/core/java/android/app/Vr2dDisplayProperties.java
+++ b/core/java/android/app/Vr2dDisplayProperties.java
@@ -16,7 +16,6 @@
package android.app;
-import android.content.ComponentName;
import android.os.Parcel;
import android.os.Parcelable;
@@ -27,19 +26,31 @@ import java.io.PrintWriter;
*
* @hide
*/
-public class Vr2dDisplayProperties implements Parcelable {
+public final class Vr2dDisplayProperties implements Parcelable {
- /**
- * The actual width, height and dpi.
- */
+ public static final int FLAG_VIRTUAL_DISPLAY_ENABLED = 1;
+
+ /**
+ * The actual width, height and dpi.
+ */
private final int mWidth;
private final int mHeight;
private final int mDpi;
+ // Flags describing the virtual display behavior.
+ private final int mAddedFlags;
+ private final int mRemovedFlags;
+
public Vr2dDisplayProperties(int width, int height, int dpi) {
+ this(width, height, dpi, 0, 0);
+ }
+
+ private Vr2dDisplayProperties(int width, int height, int dpi, int flags, int removedFlags) {
mWidth = width;
mHeight = height;
mDpi = dpi;
+ mAddedFlags = flags;
+ mRemovedFlags = removedFlags;
}
@Override
@@ -52,11 +63,13 @@ public class Vr2dDisplayProperties implements Parcelable {
@Override
public String toString() {
- return "Vr2dDisplayProperties{" +
- "mWidth=" + mWidth +
- ", mHeight=" + mHeight +
- ", mDpi=" + mDpi +
- "}";
+ return "Vr2dDisplayProperties{"
+ + "mWidth=" + mWidth
+ + ", mHeight=" + mHeight
+ + ", mDpi=" + mDpi
+ + ", flags=" + toReadableFlags(mAddedFlags)
+ + ", removed_flags=" + toReadableFlags(mRemovedFlags)
+ + "}";
}
@Override
@@ -66,6 +79,8 @@ public class Vr2dDisplayProperties implements Parcelable {
Vr2dDisplayProperties that = (Vr2dDisplayProperties) o;
+ if (getFlags() != that.getFlags()) return false;
+ if (getRemovedFlags() != that.getRemovedFlags()) return false;
if (getWidth() != that.getWidth()) return false;
if (getHeight() != that.getHeight()) return false;
return getDpi() == that.getDpi();
@@ -81,6 +96,8 @@ public class Vr2dDisplayProperties implements Parcelable {
dest.writeInt(mWidth);
dest.writeInt(mHeight);
dest.writeInt(mDpi);
+ dest.writeInt(mAddedFlags);
+ dest.writeInt(mRemovedFlags);
}
public static final Parcelable.Creator<Vr2dDisplayProperties> CREATOR
@@ -100,13 +117,12 @@ public class Vr2dDisplayProperties implements Parcelable {
mWidth = source.readInt();
mHeight = source.readInt();
mDpi = source.readInt();
+ mAddedFlags = source.readInt();
+ mRemovedFlags = source.readInt();
}
public void dump(PrintWriter pw, String prefix) {
- pw.println(prefix + "Vr2dDisplayProperties:");
- pw.println(prefix + " width=" + mWidth);
- pw.println(prefix + " height=" + mHeight);
- pw.println(prefix + " dpi=" + mDpi);
+ pw.println(prefix + toString());
}
public int getWidth() {
@@ -120,4 +136,83 @@ public class Vr2dDisplayProperties implements Parcelable {
public int getDpi() {
return mDpi;
}
+
+ public int getFlags() {
+ return mAddedFlags;
+ }
+
+ public int getRemovedFlags() {
+ return mRemovedFlags;
+ }
+
+ private static String toReadableFlags(int flags) {
+ String retval = "{";
+ if ((flags & FLAG_VIRTUAL_DISPLAY_ENABLED) == FLAG_VIRTUAL_DISPLAY_ENABLED) {
+ retval += "enabled";
+ }
+ return retval + "}";
+ }
+
+ /**
+ * Convenience class for creating Vr2dDisplayProperties.
+ */
+ public static class Builder {
+ private int mAddedFlags = 0;
+ private int mRemovedFlags = 0;
+
+ // Negative values are translated as an "ignore" to VrManagerService.
+ private int mWidth = -1;
+ private int mHeight = -1;
+ private int mDpi = -1;
+
+ public Builder() {
+ }
+
+ /**
+ * Sets the dimensions to use for the virtual display.
+ */
+ public Builder setDimensions(int width, int height, int dpi) {
+ mWidth = width;
+ mHeight = height;
+ mDpi = dpi;
+ return this;
+ }
+
+ /**
+ * Toggles the virtual display functionality for 2D activities in VR.
+ */
+ public Builder setEnabled(boolean enabled) {
+ if (enabled) {
+ addFlags(FLAG_VIRTUAL_DISPLAY_ENABLED);
+ } else {
+ removeFlags(FLAG_VIRTUAL_DISPLAY_ENABLED);
+ }
+ return this;
+ }
+
+ /**
+ * Adds property flags.
+ */
+ public Builder addFlags(int flags) {
+ mAddedFlags |= flags;
+ mRemovedFlags &= ~flags;
+ return this;
+ }
+
+ /**
+ * Removes property flags.
+ */
+ public Builder removeFlags(int flags) {
+ mRemovedFlags |= flags;
+ mAddedFlags &= ~flags;
+ return this;
+ }
+
+ /**
+ * Builds the Vr2dDisplayProperty instance.
+ */
+ public Vr2dDisplayProperties build() {
+ return new Vr2dDisplayProperties(mWidth, mHeight, mDpi, mAddedFlags, mRemovedFlags);
+ }
+ }
}
diff --git a/core/java/android/app/WallpaperManager.java b/core/java/android/app/WallpaperManager.java
index dfcbfc4f8e09..0b8b689ce4e6 100644
--- a/core/java/android/app/WallpaperManager.java
+++ b/core/java/android/app/WallpaperManager.java
@@ -22,9 +22,9 @@ import android.annotation.Nullable;
import android.annotation.RawRes;
import android.annotation.RequiresPermission;
import android.annotation.SdkConstant;
+import android.annotation.SdkConstant.SdkConstantType;
import android.annotation.SystemApi;
import android.annotation.SystemService;
-import android.annotation.SdkConstant.SdkConstantType;
import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Context;
@@ -74,7 +74,6 @@ import java.io.InputStream;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
-import java.util.Iterator;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
@@ -369,18 +368,17 @@ public class WallpaperManager {
}
WallpaperColors getWallpaperColors(int which) {
- synchronized (this) {
- if (which != FLAG_LOCK && which != FLAG_SYSTEM)
- throw new IllegalArgumentException(
- "which should be either FLAG_LOCK or FLAG_SYSTEM");
+ if (which != FLAG_LOCK && which != FLAG_SYSTEM) {
+ throw new IllegalArgumentException(
+ "Must request colors for exactly one kind of wallpaper");
+ }
- try {
- return mService.getWallpaperColors(which);
- } catch (RemoteException e) {
- // Can't get colors, connection lost.
- }
- return null;
+ try {
+ return mService.getWallpaperColors(which);
+ } catch (RemoteException e) {
+ // Can't get colors, connection lost.
}
+ return null;
}
public Bitmap peekWallpaperBitmap(Context context, boolean returnDefault,
diff --git a/core/java/android/app/timezone/RulesUpdaterContract.java b/core/java/android/app/timezone/RulesUpdaterContract.java
index 4e7781866aae..07b2f33ef5e0 100644
--- a/core/java/android/app/timezone/RulesUpdaterContract.java
+++ b/core/java/android/app/timezone/RulesUpdaterContract.java
@@ -19,6 +19,7 @@ package android.app.timezone;
import android.content.Context;
import android.content.Intent;
import android.os.ParcelFileDescriptor;
+import android.os.UserHandle;
/**
* Constants related to the contract between the Android system and the privileged time zone updater
@@ -82,6 +83,9 @@ public final class RulesUpdaterContract {
byte[] checkTokenBytes) {
Intent intent = createUpdaterIntent(updaterAppPackageName);
intent.putExtra(EXTRA_CHECK_TOKEN, checkTokenBytes);
- context.sendBroadcast(intent, RulesUpdaterContract.UPDATE_TIME_ZONE_RULES_PERMISSION);
+ context.sendBroadcastAsUser(
+ intent,
+ UserHandle.of(UserHandle.myUserId()),
+ RulesUpdaterContract.UPDATE_TIME_ZONE_RULES_PERMISSION);
}
}
diff --git a/core/java/android/bluetooth/BluetoothHeadset.java b/core/java/android/bluetooth/BluetoothHeadset.java
index 5b8d81d81efb..c84643fc46b3 100644
--- a/core/java/android/bluetooth/BluetoothHeadset.java
+++ b/core/java/android/bluetooth/BluetoothHeadset.java
@@ -200,6 +200,37 @@ public final class BluetoothHeadset implements BluetoothProfile {
public static final String VENDOR_RESULT_CODE_COMMAND_ANDROID = "+ANDROID";
/**
+ * A vendor-specific AT command
+ * @hide
+ */
+ public static final String VENDOR_SPECIFIC_HEADSET_EVENT_XAPL = "+XAPL";
+
+ /**
+ * A vendor-specific AT command
+ * @hide
+ */
+ public static final String VENDOR_SPECIFIC_HEADSET_EVENT_IPHONEACCEV = "+IPHONEACCEV";
+
+ /**
+ * Battery level indicator associated with
+ * {@link #VENDOR_SPECIFIC_HEADSET_EVENT_IPHONEACCEV}
+ * @hide
+ */
+ public static final int VENDOR_SPECIFIC_HEADSET_EVENT_IPHONEACCEV_BATTERY_LEVEL = 1;
+
+ /**
+ * A vendor-specific AT command
+ * @hide
+ */
+ public static final String VENDOR_SPECIFIC_HEADSET_EVENT_XEVENT = "+XEVENT";
+
+ /**
+ * Battery level indicator associated with {@link #VENDOR_SPECIFIC_HEADSET_EVENT_XEVENT}
+ * @hide
+ */
+ public static final String VENDOR_SPECIFIC_HEADSET_EVENT_XEVENT_BATTERY_LEVEL = "BATTERY";
+
+ /**
* Headset state when SCO audio is not connected.
* This state can be one of
* {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} of
@@ -229,31 +260,31 @@ public final class BluetoothHeadset implements BluetoothProfile {
* <ul>
* <li> {@link #EXTRA_HF_INDICATORS_IND_ID} - The Assigned number of headset Indicator which
* is supported by the headset ( as indicated by AT+BIND command in the SLC
- * sequence).or whose value is changed (indicated by AT+BIEV command) </li>
- * <li> {@link #EXTRA_HF_INDICATORS_IND_VALUE}- The updated value of headset indicator. </li>
- * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. </li>
+ * sequence) or whose value is changed (indicated by AT+BIEV command) </li>
+ * <li> {@link #EXTRA_HF_INDICATORS_IND_VALUE} - Updated value of headset indicator. </li>
+ * <li> {@link BluetoothDevice#EXTRA_DEVICE} - Remote device. </li>
* </ul>
- * <p>{@link #EXTRA_HF_INDICATORS_IND_ID} is defined by Bluetooth SIG and each of the indicators are
- * given an assigned number. Below shows the assigned number of Indicator added so far
- * - Enhanced Safety - 1
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission to
- * receive.
+ * <p>{@link #EXTRA_HF_INDICATORS_IND_ID} is defined by Bluetooth SIG and each of the indicators
+ * are given an assigned number. Below shows the assigned number of Indicator added so far
+ * - Enhanced Safety - 1, Valid Values: 0 - Disabled, 1 - Enabled
+ * - Battery Level - 2, Valid Values: 0~100 - Remaining level of Battery
+ * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission to receive.
* @hide
*/
public static final String ACTION_HF_INDICATORS_VALUE_CHANGED =
"android.bluetooth.headset.action.HF_INDICATORS_VALUE_CHANGED";
/**
- * A String extra field in {@link #ACTION_HF_INDICATORS_VALUE_CHANGED}
- * intents that contains the UUID of the headset indicator (as defined by Bluetooth SIG)
- * that is being sent.
+ * A int extra field in {@link #ACTION_HF_INDICATORS_VALUE_CHANGED}
+ * intents that contains the assigned number of the headset indicator as defined by
+ * Bluetooth SIG that is being sent. Value range is 0-65535 as defined in HFP 1.7
* @hide
*/
public static final String EXTRA_HF_INDICATORS_IND_ID =
"android.bluetooth.headset.extra.HF_INDICATORS_IND_ID";
/**
- * A int extra field in {@link #ACTION_HF_INDICATORS_VALUE_CHANGED}
+ * A int extra field in {@link #ACTION_HF_INDICATORS_VALUE_CHANGED}
* intents that contains the value of the Headset indicator that is being sent.
* @hide
*/
diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java
index 9a014766c206..b75c39c0d350 100644
--- a/core/java/android/content/pm/ActivityInfo.java
+++ b/core/java/android/content/pm/ActivityInfo.java
@@ -441,6 +441,22 @@ public class ActivityInfo extends ComponentInfo
* @hide
*/
public static final int FLAG_SUPPORTS_PICTURE_IN_PICTURE = 0x400000;
+
+ /**
+ * Bit in {@link #flags} indicating if the activity should be shown when locked.
+ * See {@link android.R.attr#showWhenLocked}
+ * @hide
+ */
+ public static final int FLAG_SHOW_WHEN_LOCKED = 0x800000;
+
+ /**
+ * Bit in {@link #flags} indicating if the screen should turn on when starting the activity.
+ * See {@link android.R.attr#turnScreenOn}
+ * @hide
+ */
+ public static final int FLAG_TURN_SCREEN_ON = 0x1000000;
+
+
/**
* @hide Bit in {@link #flags}: If set, this component will only be seen
* by the system user. Only works with broadcast receivers. Set from the
@@ -462,6 +478,7 @@ public class ActivityInfo extends ComponentInfo
* thrown. Set from the {@link android.R.attr#allowEmbedded} attribute.
*/
public static final int FLAG_ALLOW_EMBEDDED = 0x80000000;
+
/**
* Options that have been set in the activity declaration in the
* manifest.
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 1b88ce7c3a04..e4f2fc1c5035 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -4292,6 +4292,15 @@ public class PackageParser {
a.info.colorMode = sa.getInt(R.styleable.AndroidManifestActivity_colorMode,
ActivityInfo.COLOR_MODE_DEFAULT);
+
+ if (sa.getBoolean(R.styleable.AndroidManifestActivity_showWhenLocked, false)) {
+ a.info.flags |= ActivityInfo.FLAG_SHOW_WHEN_LOCKED;
+ }
+
+ if (sa.getBoolean(R.styleable.AndroidManifestActivity_turnScreenOn, false)) {
+ a.info.flags |= ActivityInfo.FLAG_TURN_SCREEN_ON;
+ }
+
} else {
a.info.launchMode = ActivityInfo.LAUNCH_MULTIPLE;
a.info.configChanges = 0;
diff --git a/core/java/android/hardware/radio/RadioManager.java b/core/java/android/hardware/radio/RadioManager.java
index b6eaa5c7cd10..f697b89f6fdd 100644
--- a/core/java/android/hardware/radio/RadioManager.java
+++ b/core/java/android/hardware/radio/RadioManager.java
@@ -431,6 +431,25 @@ public class RadioManager {
public int getType() {
return mType;
}
+
+ /**
+ * Checks if the band is either AM or AM_HD.
+ *
+ * @return {@code true}, if band is AM or AM_HD.
+ */
+ public boolean isAmBand() {
+ return mType == BAND_AM || mType == BAND_AM_HD;
+ }
+
+ /**
+ * Checks if the band is either FM or FM_HD.
+ *
+ * @return {@code true}, if band is FM or FM_HD.
+ */
+ public boolean isFmBand() {
+ return mType == BAND_FM || mType == BAND_FM_HD;
+ }
+
/** Lower band limit expressed in units according to band type.
* Currently all defined band types express channels as frequency in kHz
* @return the lower band limit.
diff --git a/core/java/android/os/MemoryFile.java b/core/java/android/os/MemoryFile.java
index 6cec55a4a09e..9294c449e4d6 100644
--- a/core/java/android/os/MemoryFile.java
+++ b/core/java/android/os/MemoryFile.java
@@ -16,68 +16,50 @@
package android.os;
-import android.util.Log;
+import android.system.ErrnoException;
import java.io.FileDescriptor;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
+import java.nio.ByteBuffer;
/**
- * MemoryFile is a wrapper for the Linux ashmem driver.
- * MemoryFiles are backed by shared memory, which can be optionally
- * set to be purgeable.
+ * MemoryFile is a wrapper for {@link SharedMemory} which can optionally be set to purgeable.
+ *
+ * Applications should generally prefer to use {@link SharedMemory} which offers more flexible
+ * access & control over the shared memory region than MemoryFile does.
+ *
* Purgeable files may have their contents reclaimed by the kernel
* in low memory conditions (only if allowPurging is set to true).
* After a file is purged, attempts to read or write the file will
* cause an IOException to be thrown.
*/
-public class MemoryFile
-{
+public class MemoryFile {
private static String TAG = "MemoryFile";
- // mmap(2) protection flags from <sys/mman.h>
- private static final int PROT_READ = 0x1;
- private static final int PROT_WRITE = 0x2;
-
- private static native FileDescriptor native_open(String name, int length) throws IOException;
- // returns memory address for ashmem region
- private static native long native_mmap(FileDescriptor fd, int length, int mode)
- throws IOException;
- private static native void native_munmap(long addr, int length) throws IOException;
- private static native void native_close(FileDescriptor fd);
- private static native int native_read(FileDescriptor fd, long address, byte[] buffer,
- int srcOffset, int destOffset, int count, boolean isUnpinned) throws IOException;
- private static native void native_write(FileDescriptor fd, long address, byte[] buffer,
- int srcOffset, int destOffset, int count, boolean isUnpinned) throws IOException;
- private static native void native_pin(FileDescriptor fd, boolean pin) throws IOException;
+ // Returns 'true' if purged, 'false' otherwise
+ private static native boolean native_pin(FileDescriptor fd, boolean pin) throws IOException;
private static native int native_get_size(FileDescriptor fd) throws IOException;
- private FileDescriptor mFD; // ashmem file descriptor
- private long mAddress; // address of ashmem memory
- private int mLength; // total length of our ashmem region
+ private SharedMemory mSharedMemory;
+ private ByteBuffer mMapping;
private boolean mAllowPurging = false; // true if our ashmem region is unpinned
/**
* Allocates a new ashmem region. The region is initially not purgable.
*
* @param name optional name for the file (can be null).
- * @param length of the memory file in bytes, must be non-negative.
+ * @param length of the memory file in bytes, must be positive.
* @throws IOException if the memory file could not be created.
*/
public MemoryFile(String name, int length) throws IOException {
- mLength = length;
- if (length >= 0) {
- mFD = native_open(name, length);
- } else {
- throw new IOException("Invalid length: " + length);
- }
-
- if (length > 0) {
- mAddress = native_mmap(mFD, length, PROT_READ | PROT_WRITE);
- } else {
- mAddress = 0;
+ try {
+ mSharedMemory = SharedMemory.create(name, length);
+ mMapping = mSharedMemory.mapReadWrite();
+ } catch (ErrnoException ex) {
+ ex.rethrowAsIOException();
}
}
@@ -87,9 +69,7 @@ public class MemoryFile
*/
public void close() {
deactivate();
- if (!isClosed()) {
- native_close(mFD);
- }
+ mSharedMemory.close();
}
/**
@@ -100,35 +80,30 @@ public class MemoryFile
* @hide
*/
void deactivate() {
- if (!isDeactivated()) {
- try {
- native_munmap(mAddress, mLength);
- mAddress = 0;
- } catch (IOException ex) {
- Log.e(TAG, ex.toString());
- }
+ if (mMapping != null) {
+ SharedMemory.unmap(mMapping);
+ mMapping = null;
}
}
- /**
- * Checks whether the memory file has been deactivated.
- */
- private boolean isDeactivated() {
- return mAddress == 0;
+ private void checkActive() throws IOException {
+ if (mMapping == null) {
+ throw new IOException("MemoryFile has been deactivated");
+ }
}
- /**
- * Checks whether the memory file has been closed.
- */
- private boolean isClosed() {
- return !mFD.valid();
+ private void beginAccess() throws IOException {
+ checkActive();
+ if (mAllowPurging) {
+ if (native_pin(mSharedMemory.getFileDescriptor(), true)) {
+ throw new IOException("MemoryFile has been purged");
+ }
+ }
}
- @Override
- protected void finalize() {
- if (!isClosed()) {
- Log.e(TAG, "MemoryFile.finalize() called while ashmem still open");
- close();
+ private void endAccess() throws IOException {
+ if (mAllowPurging) {
+ native_pin(mSharedMemory.getFileDescriptor(), false);
}
}
@@ -138,14 +113,19 @@ public class MemoryFile
* @return file length.
*/
public int length() {
- return mLength;
+ return mSharedMemory.getSize();
}
/**
* Is memory file purging enabled?
*
* @return true if the file may be purged.
+ *
+ * @deprecated Purgable is considered generally fragile and hard to use safely. Applications
+ * are recommend to instead use {@link android.content.ComponentCallbacks2#onTrimMemory(int)}
+ * to react to memory events and release shared memory regions as appropriate.
*/
+ @Deprecated
public boolean isPurgingAllowed() {
return mAllowPurging;
}
@@ -156,11 +136,16 @@ public class MemoryFile
* @param allowPurging true if the operating system can purge the contents
* of the file in low memory situations
* @return previous value of allowPurging
+ *
+ * @deprecated Purgable is considered generally fragile and hard to use safely. Applications
+ * are recommend to instead use {@link android.content.ComponentCallbacks2#onTrimMemory(int)}
+ * to react to memory events and release shared memory regions as appropriate.
*/
+ @Deprecated
synchronized public boolean allowPurging(boolean allowPurging) throws IOException {
boolean oldValue = mAllowPurging;
if (oldValue != allowPurging) {
- native_pin(mFD, !allowPurging);
+ native_pin(mSharedMemory.getFileDescriptor(), !allowPurging);
mAllowPurging = allowPurging;
}
return oldValue;
@@ -197,16 +182,14 @@ public class MemoryFile
*/
public int readBytes(byte[] buffer, int srcOffset, int destOffset, int count)
throws IOException {
- if (isDeactivated()) {
- throw new IOException("Can't read from deactivated memory file.");
+ beginAccess();
+ try {
+ mMapping.position(srcOffset);
+ mMapping.get(buffer, destOffset, count);
+ } finally {
+ endAccess();
}
- if (destOffset < 0 || destOffset > buffer.length || count < 0
- || count > buffer.length - destOffset
- || srcOffset < 0 || srcOffset > mLength
- || count > mLength - srcOffset) {
- throw new IndexOutOfBoundsException();
- }
- return native_read(mFD, mAddress, buffer, srcOffset, destOffset, count, mAllowPurging);
+ return count;
}
/**
@@ -221,16 +204,13 @@ public class MemoryFile
*/
public void writeBytes(byte[] buffer, int srcOffset, int destOffset, int count)
throws IOException {
- if (isDeactivated()) {
- throw new IOException("Can't write to deactivated memory file.");
- }
- if (srcOffset < 0 || srcOffset > buffer.length || count < 0
- || count > buffer.length - srcOffset
- || destOffset < 0 || destOffset > mLength
- || count > mLength - destOffset) {
- throw new IndexOutOfBoundsException();
+ beginAccess();
+ try {
+ mMapping.position(destOffset);
+ mMapping.put(buffer, srcOffset, count);
+ } finally {
+ endAccess();
}
- native_write(mFD, mAddress, buffer, srcOffset, destOffset, count, mAllowPurging);
}
/**
@@ -239,11 +219,9 @@ public class MemoryFile
* The returned file descriptor is not duplicated.
*
* @throws IOException If the memory file has been closed.
- *
- * @hide
*/
public FileDescriptor getFileDescriptor() throws IOException {
- return mFD;
+ return mSharedMemory.getFileDescriptor();
}
/**
@@ -266,10 +244,10 @@ public class MemoryFile
@Override
public int available() throws IOException {
- if (mOffset >= mLength) {
+ if (mOffset >= mSharedMemory.getSize()) {
return 0;
}
- return mLength - mOffset;
+ return mSharedMemory.getSize() - mOffset;
}
@Override
@@ -319,8 +297,8 @@ public class MemoryFile
@Override
public long skip(long n) throws IOException {
- if (mOffset + n > mLength) {
- n = mLength - mOffset;
+ if (mOffset + n > mSharedMemory.getSize()) {
+ n = mSharedMemory.getSize() - mOffset;
}
mOffset += n;
return n;
diff --git a/core/java/android/os/SharedMemory.aidl b/core/java/android/os/SharedMemory.aidl
new file mode 100644
index 000000000000..b7c695e5f15c
--- /dev/null
+++ b/core/java/android/os/SharedMemory.aidl
@@ -0,0 +1,20 @@
+/* //device/java/android/android/os/SharedMemory.aidl
+**
+** Copyright 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 android.os;
+
+parcelable SharedMemory; \ No newline at end of file
diff --git a/core/java/android/os/SharedMemory.java b/core/java/android/os/SharedMemory.java
new file mode 100644
index 000000000000..712bbaa101aa
--- /dev/null
+++ b/core/java/android/os/SharedMemory.java
@@ -0,0 +1,351 @@
+/*
+ * 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 android.os;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.system.ErrnoException;
+import android.system.Os;
+import android.system.OsConstants;
+
+import dalvik.system.VMRuntime;
+
+import java.io.Closeable;
+import java.io.FileDescriptor;
+import java.nio.ByteBuffer;
+import java.nio.DirectByteBuffer;
+
+import sun.misc.Cleaner;
+
+/**
+ * SharedMemory enables the creation, mapping, and protection control over anonymous shared memory.
+ */
+public final class SharedMemory implements Parcelable, Closeable {
+
+ private final FileDescriptor mFileDescriptor;
+ private final int mSize;
+ private final MemoryRegistration mMemoryRegistration;
+ private Cleaner mCleaner;
+
+ private SharedMemory(FileDescriptor fd) {
+ // This constructor is only used internally so it should be impossible to hit any of the
+ // exceptions unless something goes horribly wrong.
+ if (fd == null) {
+ throw new IllegalArgumentException(
+ "Unable to create SharedMemory from a null FileDescriptor");
+ }
+ if (!fd.valid()) {
+ throw new IllegalArgumentException(
+ "Unable to create SharedMemory from closed FileDescriptor");
+ }
+ mFileDescriptor = fd;
+ mSize = nGetSize(mFileDescriptor);
+ if (mSize <= 0) {
+ throw new IllegalArgumentException("FileDescriptor is not a valid ashmem fd");
+ }
+
+ mMemoryRegistration = new MemoryRegistration(mSize);
+ mCleaner = Cleaner.create(this, new Closer(mFileDescriptor, mMemoryRegistration));
+ }
+
+ /**
+ * Creates an anonymous SharedMemory instance with the provided debug name and size. The name
+ * is only used for debugging purposes and can help identify what the shared memory is used
+ * for when inspecting memory maps for the processes that have mapped this SharedMemory
+ * instance.
+ *
+ * @param name The debug name to use for this SharedMemory instance. This can be null, however
+ * a debug name is recommended to help identify memory usage when using tools
+ * such as lsof or examining /proc/[pid]/maps
+ * @param size The size of the shared memory to create. Must be greater than 0.
+ * @return A SharedMemory instance of the requested size
+ * @throws ErrnoException if the requested allocation fails.
+ */
+ public static @NonNull SharedMemory create(@Nullable String name, int size)
+ throws ErrnoException {
+ if (size <= 0) {
+ throw new IllegalArgumentException("Size must be greater than zero");
+ }
+ return new SharedMemory(nCreate(name, size));
+ }
+
+ private void checkOpen() {
+ if (!mFileDescriptor.valid()) {
+ throw new IllegalStateException("SharedMemory is closed");
+ }
+ }
+
+ private static final int PROT_MASK = OsConstants.PROT_READ | OsConstants.PROT_WRITE
+ | OsConstants.PROT_EXEC | OsConstants.PROT_NONE;
+
+ private static void validateProt(int prot) {
+ if ((prot & ~PROT_MASK) != 0) {
+ throw new IllegalArgumentException("Invalid prot value");
+ }
+ }
+
+ /**
+ * Sets the protection on the shared memory to the combination specified in prot, which
+ * is either a bitwise-or'd combination of {@link android.system.OsConstants#PROT_READ},
+ * {@link android.system.OsConstants#PROT_WRITE}, {@link android.system.OsConstants#PROT_EXEC}
+ * from {@link android.system.OsConstants}, or {@link android.system.OsConstants#PROT_NONE},
+ * to remove all further access.
+ *
+ * Note that protection can only ever be removed, not added. By default shared memory
+ * is created with protection set to PROT_READ | PROT_WRITE | PROT_EXEC. The protection
+ * passed here also only applies to any mappings created after calling this method. Existing
+ * mmaps of the shared memory retain whatever protection they had when they were created.
+ *
+ * A common usage of this is to share a read-only copy of the data with something else. To do
+ * that first create the read/write mapping with PROT_READ | PROT_WRITE,
+ * then call setProtect(PROT_READ) to remove write capability, then send the SharedMemory
+ * to another process. That process will only be able to mmap with PROT_READ.
+ *
+ * @param prot Any bitwise-or'ed combination of
+ * {@link android.system.OsConstants#PROT_READ},
+ * {@link android.system.OsConstants#PROT_WRITE}, and
+ * {@link android.system.OsConstants#PROT_EXEC}; or
+ * {@link android.system.OsConstants#PROT_NONE}
+ * @return Whether or not the requested protection was applied. Returns true on success,
+ * false if the requested protection was broader than the existing protection.
+ */
+ public boolean setProtect(int prot) {
+ checkOpen();
+ validateProt(prot);
+ int errno = nSetProt(mFileDescriptor, prot);
+ return errno == 0;
+ }
+
+ /**
+ * Returns the backing {@link FileDescriptor} for this SharedMemory object. The SharedMemory
+ * instance retains ownership of the FileDescriptor.
+ *
+ * This FileDescriptor is interoperable with the ASharedMemory NDK APIs.
+ *
+ * @return Returns the FileDescriptor associated with this object.
+ */
+ public @NonNull FileDescriptor getFileDescriptor() {
+ return mFileDescriptor;
+ }
+
+ /**
+ * Returns the backing native fd int for this SharedMemory object. The SharedMemory
+ * instance retains ownership of the fd.
+ *
+ * This fd is interoperable with the ASharedMemory NDK APIs.
+ *
+ * @return Returns the native fd associated with this object, or -1 if it is already closed.
+ */
+ public int getFd() {
+ return mFileDescriptor.getInt$();
+ }
+
+ /**
+ * @return The size of the SharedMemory region.
+ */
+ public int getSize() {
+ checkOpen();
+ return mSize;
+ }
+
+ /**
+ * Creates a read/write mapping of the entire shared memory region. This requires the the
+ * protection level of the shared memory is at least PROT_READ|PROT_WRITE or the map will fail.
+ *
+ * Use {@link #map(int, int, int)} to have more control over the mapping if desired.
+ * This is equivalent to map(OsConstants.PROT_READ | OsConstants.PROT_WRITE, 0, getSize())
+ *
+ * @return A ByteBuffer mapping
+ * @throws ErrnoException if the mmap call failed.
+ */
+ public @NonNull ByteBuffer mapReadWrite() throws ErrnoException {
+ return map(OsConstants.PROT_READ | OsConstants.PROT_WRITE, 0, mSize);
+ }
+
+ /**
+ * Creates a read-only mapping of the entire shared memory region. This requires the the
+ * protection level of the shared memory is at least PROT_READ or the map will fail.
+ *
+ * Use {@link #map(int, int, int)} to have more control over the mapping if desired.
+ * This is equivalent to map(OsConstants.PROT_READ, 0, getSize())
+ *
+ * @return A ByteBuffer mapping
+ * @throws ErrnoException if the mmap call failed.
+ */
+ public @NonNull ByteBuffer mapReadOnly() throws ErrnoException {
+ return map(OsConstants.PROT_READ, 0, mSize);
+ }
+
+ /**
+ * Creates an mmap of the SharedMemory with the specified prot, offset, and length.
+ *
+ * @param prot A bitwise-or'd combination of PROT_READ, PROT_WRITE, PROT_EXEC, or PROT_NONE.
+ * @param offset The offset into the shared memory to begin mapping
+ * @param length The length of the region to map
+ * @return A ByteBuffer mapping.
+ * @throws ErrnoException if the mmap call failed.
+ */
+ public @NonNull ByteBuffer map(int prot, int offset, int length) throws ErrnoException {
+ checkOpen();
+ validateProt(prot);
+ if (offset < 0) {
+ throw new IllegalArgumentException("Offset must be > 0");
+ }
+ if (length <= 0) {
+ throw new IllegalArgumentException("Length must be > 0");
+ }
+ if (offset + length > mSize) {
+ throw new IllegalArgumentException("offset + length must not exceed getSize()");
+ }
+ long address = Os.mmap(0, length, prot, OsConstants.MAP_SHARED, mFileDescriptor, offset);
+ boolean readOnly = (prot & OsConstants.PROT_WRITE) == 0;
+ Runnable unmapper = new Unmapper(address, length, mMemoryRegistration.acquire());
+ return new DirectByteBuffer(length, address, mFileDescriptor, unmapper, readOnly);
+ }
+
+ /**
+ * Unmaps a buffer previously returned by {@link #map(int, int, int)}
+ * @param buffer The buffer to unmap
+ */
+ public static void unmap(@NonNull ByteBuffer buffer) {
+ if (buffer instanceof DirectByteBuffer) {
+ Cleaner cleaner = ((DirectByteBuffer) buffer).cleaner();
+ if (cleaner != null) {
+ cleaner.clean();
+ }
+ } else {
+ throw new IllegalArgumentException(
+ "ByteBuffer wasn't created by #map(int, int, int); can't unmap");
+ }
+ }
+
+ /**
+ * Close the backing {@link FileDescriptor} of this SharedMemory instance. Note that all
+ * open mappings of the shared memory will remain valid and may continue to be used. The
+ * shared memory will not be freed until all file descriptor handles are closed and all
+ * memory mappings are unmapped.
+ */
+ @Override
+ public void close() {
+ if (mCleaner != null) {
+ mCleaner.clean();
+ mCleaner = null;
+ }
+ }
+
+ @Override
+ public int describeContents() {
+ return CONTENTS_FILE_DESCRIPTOR;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ checkOpen();
+ dest.writeFileDescriptor(mFileDescriptor);
+ }
+
+ public static final Parcelable.Creator<SharedMemory> CREATOR =
+ new Parcelable.Creator<SharedMemory>() {
+ @Override
+ public SharedMemory createFromParcel(Parcel source) {
+ FileDescriptor descriptor = source.readRawFileDescriptor();
+ return new SharedMemory(descriptor);
+ }
+
+ @Override
+ public SharedMemory[] newArray(int size) {
+ return new SharedMemory[size];
+ }
+ };
+
+ /**
+ * Cleaner that closes the FD
+ */
+ private static final class Closer implements Runnable {
+ private FileDescriptor mFd;
+ private MemoryRegistration mMemoryReference;
+
+ private Closer(FileDescriptor fd, MemoryRegistration memoryReference) {
+ mFd = fd;
+ mMemoryReference = memoryReference;
+ }
+
+ @Override
+ public void run() {
+ try {
+ Os.close(mFd);
+ } catch (ErrnoException e) { /* swallow error */ }
+ mMemoryReference.release();
+ mMemoryReference = null;
+ }
+ }
+
+ /**
+ * Cleaner that munmap regions
+ */
+ private static final class Unmapper implements Runnable {
+ private long mAddress;
+ private int mSize;
+ private MemoryRegistration mMemoryReference;
+
+ private Unmapper(long address, int size, MemoryRegistration memoryReference) {
+ mAddress = address;
+ mSize = size;
+ mMemoryReference = memoryReference;
+ }
+
+ @Override
+ public void run() {
+ try {
+ Os.munmap(mAddress, mSize);
+ } catch (ErrnoException e) { /* swallow exception */ }
+ mMemoryReference.release();
+ mMemoryReference = null;
+ }
+ }
+
+ /**
+ * Helper class that ensures that the native allocation pressure against the VM heap stays
+ * active until the FD is closed as well as all mappings from that FD are closed.
+ */
+ private static final class MemoryRegistration {
+ private int mSize;
+ private int mReferenceCount;
+
+ private MemoryRegistration(int size) {
+ mSize = size;
+ mReferenceCount = 1;
+ VMRuntime.getRuntime().registerNativeAllocation(mSize);
+ }
+
+ public synchronized MemoryRegistration acquire() {
+ mReferenceCount++;
+ return this;
+ }
+
+ public synchronized void release() {
+ mReferenceCount--;
+ if (mReferenceCount == 0) {
+ VMRuntime.getRuntime().registerNativeFree(mSize);
+ }
+ }
+ }
+
+ private static native FileDescriptor nCreate(String name, int size) throws ErrnoException;
+ private static native int nGetSize(FileDescriptor fd);
+ private static native int nSetProt(FileDescriptor fd, int prot);
+}
diff --git a/core/java/android/preference/Preference.java b/core/java/android/preference/Preference.java
index 2179bd45e303..4306bc47f2b5 100644
--- a/core/java/android/preference/Preference.java
+++ b/core/java/android/preference/Preference.java
@@ -934,6 +934,7 @@ public class Preference implements Comparable<Preference> {
* @param singleLineTitle set {@code true} if the title should be constrained to one line
*/
public void setSingleLineTitle(boolean singleLineTitle) {
+ mHasSingleLineTitleAttr = true;
mSingleLineTitle = singleLineTitle;
notifyChanged();
}
diff --git a/core/java/android/util/Log.java b/core/java/android/util/Log.java
index 951aa8df9ea3..8691136dfedc 100644
--- a/core/java/android/util/Log.java
+++ b/core/java/android/util/Log.java
@@ -30,8 +30,9 @@ import java.net.UnknownHostException;
/**
* API for sending log output.
*
- * <p>Generally, use the Log.v() Log.d() Log.i() Log.w() and Log.e()
- * methods.
+ * <p>Generally, you should use the {@link #v Log.v()}, {@link #d Log.d()},
+ * {@link #i Log.i()}, {@link #w Log.w()}, and {@link #e Log.e()} methods to write logs.
+ * You can then <a href="{@docRoot}studio/debug/am-logcat.html">view the logs in logcat</a>.
*
* <p>The order in terms of verbosity, from least to most is
* ERROR, WARN, INFO, DEBUG, VERBOSE. Verbose should never be compiled
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 371277622740..63fe300a7927 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -724,6 +724,8 @@ import java.util.function.Predicate;
* @attr ref android.R.styleable#View_nextFocusUp
* @attr ref android.R.styleable#View_onClick
* @attr ref android.R.styleable#View_padding
+ * @attr ref android.R.styleable#View_paddingHorizontal
+ * @attr ref android.R.styleable#View_paddingVertical
* @attr ref android.R.styleable#View_paddingBottom
* @attr ref android.R.styleable#View_paddingLeft
* @attr ref android.R.styleable#View_paddingRight
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index cb92a4ca3ad4..ecdfa3fc3336 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -7609,6 +7609,16 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
* See
* {@link android.R.styleable#ViewGroup_MarginLayout ViewGroup Margin Layout Attributes}
* for a list of all child view attributes that this class supports.
+ *
+ * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_margin
+ * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginHorizontal
+ * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginVertical
+ * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginLeft
+ * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginTop
+ * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginRight
+ * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginBottom
+ * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginStart
+ * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginEnd
*/
public static class MarginLayoutParams extends ViewGroup.LayoutParams {
/**
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index 4c0a1902d6a4..3fd459958ed4 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -18,6 +18,7 @@ package android.view;
import android.annotation.IntDef;
import android.annotation.NonNull;
+import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.annotation.TestApi;
@@ -27,6 +28,7 @@ import android.content.Context;
import android.content.pm.ActivityInfo;
import android.graphics.PixelFormat;
import android.graphics.Rect;
+import android.graphics.Region;
import android.os.IBinder;
import android.os.Parcel;
import android.os.Parcelable;
@@ -169,6 +171,18 @@ public interface WindowManager extends ViewManager {
*/
public void requestAppKeyboardShortcuts(final KeyboardShortcutsReceiver receiver, int deviceId);
+ /**
+ * Return the touch region for the current IME window, or an empty region if there is none.
+ *
+ * @return The region of the IME that is accepting touch inputs, or null if there is no IME, no
+ * region or there was an error.
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.RESTRICTED_VR_ACCESS)
+ public Region getCurrentImeTouchRegion();
+
public static class LayoutParams extends ViewGroup.LayoutParams implements Parcelable {
/**
* X position for this window. With the default gravity it is ignored.
@@ -941,7 +955,11 @@ public interface WindowManager extends ViewManager {
* {@link #FLAG_DISMISS_KEYGUARD} to automatically fully dismisss
* non-secure keyguards. This flag only applies to the top-most
* full-screen window.
+ * @deprecated Use {@link android.R.attr#showWhenLocked} or
+ * {@link android.app.Activity#setShowWhenLocked(boolean)} instead to prevent an
+ * unintentional double life-cycle event.
*/
+ @Deprecated
public static final int FLAG_SHOW_WHEN_LOCKED = 0x00080000;
/** Window flag: ask that the system wallpaper be shown behind
@@ -966,7 +984,12 @@ public interface WindowManager extends ViewManager {
/** Window flag: when set as a window is being added or made
* visible, once the window has been shown then the system will
* poke the power manager's user activity (as if the user had woken
- * up the device) to turn the screen on. */
+ * up the device) to turn the screen on.
+ * @deprecated Use {@link android.R.attr#turnScreenOn} or
+ * {@link android.app.Activity#setTurnScreenOn(boolean)} instead to prevent an
+ * unintentional double life-cycle event.
+ */
+ @Deprecated
public static final int FLAG_TURN_SCREEN_ON = 0x00200000;
/** Window flag: when set the window will cause the keyguard to
diff --git a/core/java/android/view/WindowManagerImpl.java b/core/java/android/view/WindowManagerImpl.java
index c1b8f04a6865..a8722f101ef4 100644
--- a/core/java/android/view/WindowManagerImpl.java
+++ b/core/java/android/view/WindowManagerImpl.java
@@ -18,6 +18,7 @@ package android.view;
import android.annotation.NonNull;
import android.content.Context;
+import android.graphics.Region;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
@@ -145,4 +146,13 @@ public final class WindowManagerImpl implements WindowManager {
public Display getDefaultDisplay() {
return mContext.getDisplay();
}
+
+ @Override
+ public Region getCurrentImeTouchRegion() {
+ try {
+ return WindowManagerGlobal.getWindowManagerService().getCurrentImeTouchRegion();
+ } catch (RemoteException e) {
+ }
+ return null;
+ }
}
diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java
index 5b04f41c7ee2..e1e8317d8ccb 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -741,7 +741,8 @@ public final class AutofillManager {
}
/**
- * Returns {@code true} if Autofill is supported for this user.
+ * Returns {@code true} if autofill is supported by the current device and
+ * is supported for this user.
*
* <p>Autofill is typically supported, but it could be unsupported in cases like:
* <ol>
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index ec2b302d35ca..5fed925dd956 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -1638,6 +1638,17 @@ public class WebView extends AbsoluteLayout
}
/**
+ * Sets the list of domains that are exempt from SafeBrowsing checks. The list is
+ * global for all the WebViews.
+ * TODO: Add documentation for the format of the urls.
+ *
+ * @param urls the list of URLs
+ */
+ public static void setSafeBrowsingWhiteList(@Nullable String[] urls) {
+ getFactory().getStatics().setSafeBrowsingWhiteList(urls);
+ }
+
+ /**
* Gets the WebBackForwardList for this WebView. This contains the
* back/forward list for use in querying each item in the history stack.
* This is a copy of the private WebBackForwardList so it contains only a
diff --git a/core/java/android/webkit/WebViewFactoryProvider.java b/core/java/android/webkit/WebViewFactoryProvider.java
index 5cf48b585b16..7c938ae5bb70 100644
--- a/core/java/android/webkit/WebViewFactoryProvider.java
+++ b/core/java/android/webkit/WebViewFactoryProvider.java
@@ -86,6 +86,12 @@ public interface WebViewFactoryProvider {
* {@link android.webkit.WebView#shutdownSafeBrowsing()}
*/
void shutdownSafeBrowsing();
+
+ /**
+ * Implement the API method
+ * {@link android.webkit.WebView#setSafeBrowsingWhiteList(String[])}
+ */
+ void setSafeBrowsingWhiteList(String[] urls);
}
Statics getStatics();
diff --git a/core/java/com/android/internal/policy/PhoneWindow.java b/core/java/com/android/internal/policy/PhoneWindow.java
index 8fe9100d2011..544afd993b37 100644
--- a/core/java/com/android/internal/policy/PhoneWindow.java
+++ b/core/java/com/android/internal/policy/PhoneWindow.java
@@ -3103,12 +3103,29 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
}
/**
+ * Check if Setup or Post-Setup update is completed on TV
+ * @return true if completed
+ */
+ private boolean isTvUserSetupComplete() {
+ boolean isTvSetupComplete = Settings.Secure.getInt(getContext().getContentResolver(),
+ Settings.Secure.USER_SETUP_COMPLETE, 0) != 0;
+ isTvSetupComplete &= Settings.Secure.getInt(getContext().getContentResolver(),
+ Settings.Secure.TV_USER_SETUP_COMPLETE, 0) != 0;
+ return isTvSetupComplete;
+ }
+
+ /**
* Helper method for adding launch-search to most applications. Opens the
* search window using default settings.
*
* @return true if search window opened
*/
private boolean launchDefaultSearch(KeyEvent event) {
+ if (getContext().getPackageManager().hasSystemFeature(PackageManager.FEATURE_LEANBACK)
+ && !isTvUserSetupComplete()) {
+ // If we are in Setup or Post-Setup update mode on TV, consume the search key
+ return false;
+ }
boolean result;
final Callback cb = getCallback();
if (cb == null || isDestroyed()) {
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index 51851690c08d..c4533c36d0f2 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -90,6 +90,7 @@ cc_library_shared {
"android_os_Parcel.cpp",
"android_os_SELinux.cpp",
"android_os_seccomp.cpp",
+ "android_os_SharedMemory.cpp",
"android_os_SystemClock.cpp",
"android_os_SystemProperties.cpp",
"android_os_Trace.cpp",
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 92bc16055931..9d9828ead705 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -167,6 +167,7 @@ extern int register_android_os_Trace(JNIEnv* env);
extern int register_android_os_FileObserver(JNIEnv *env);
extern int register_android_os_UEventObserver(JNIEnv* env);
extern int register_android_os_MemoryFile(JNIEnv* env);
+extern int register_android_os_SharedMemory(JNIEnv* env);
extern int register_android_net_LocalSocketImpl(JNIEnv* env);
extern int register_android_net_NetworkUtils(JNIEnv* env);
extern int register_android_net_TrafficStats(JNIEnv* env);
@@ -1392,6 +1393,7 @@ static const RegJNIRec gRegJNI[] = {
REG_JNI(register_android_net_NetworkUtils),
REG_JNI(register_android_net_TrafficStats),
REG_JNI(register_android_os_MemoryFile),
+ REG_JNI(register_android_os_SharedMemory),
REG_JNI(register_com_android_internal_os_PathClassLoaderFactory),
REG_JNI(register_com_android_internal_os_Zygote),
REG_JNI(register_com_android_internal_util_VirtualRefBasePtr),
diff --git a/core/jni/android_os_MemoryFile.cpp b/core/jni/android_os_MemoryFile.cpp
index c198a733103d..fdc9f9dc7464 100644
--- a/core/jni/android_os_MemoryFile.cpp
+++ b/core/jni/android_os_MemoryFile.cpp
@@ -26,95 +26,14 @@
namespace android {
-static jobject android_os_MemoryFile_open(JNIEnv* env, jobject clazz, jstring name, jint length)
-{
- const char* namestr = (name ? env->GetStringUTFChars(name, NULL) : NULL);
-
- int result = ashmem_create_region(namestr, length);
-
- if (name)
- env->ReleaseStringUTFChars(name, namestr);
-
- if (result < 0) {
- jniThrowException(env, "java/io/IOException", "ashmem_create_region failed");
- return NULL;
- }
-
- return jniCreateFileDescriptor(env, result);
-}
-
-static jlong android_os_MemoryFile_mmap(JNIEnv* env, jobject clazz, jobject fileDescriptor,
- jint length, jint prot)
-{
- int fd = jniGetFDFromFileDescriptor(env, fileDescriptor);
- void* result = mmap(NULL, length, prot, MAP_SHARED, fd, 0);
- if (result == MAP_FAILED) {
- jniThrowException(env, "java/io/IOException", "mmap failed");
- }
- return reinterpret_cast<jlong>(result);
-}
-
-static void android_os_MemoryFile_munmap(JNIEnv* env, jobject clazz, jlong addr, jint length)
-{
- int result = munmap(reinterpret_cast<void *>(addr), length);
- if (result < 0)
- jniThrowException(env, "java/io/IOException", "munmap failed");
-}
-
-static void android_os_MemoryFile_close(JNIEnv* env, jobject clazz, jobject fileDescriptor)
-{
- int fd = jniGetFDFromFileDescriptor(env, fileDescriptor);
- if (fd >= 0) {
- jniSetFileDescriptorOfFD(env, fileDescriptor, -1);
- close(fd);
- }
-}
-
-static jint android_os_MemoryFile_read(JNIEnv* env, jobject clazz,
- jobject fileDescriptor, jlong address, jbyteArray buffer, jint srcOffset, jint destOffset,
- jint count, jboolean unpinned)
-{
- int fd = jniGetFDFromFileDescriptor(env, fileDescriptor);
- if (unpinned && ashmem_pin_region(fd, 0, 0) == ASHMEM_WAS_PURGED) {
- ashmem_unpin_region(fd, 0, 0);
- jniThrowException(env, "java/io/IOException", "ashmem region was purged");
- return -1;
- }
-
- env->SetByteArrayRegion(buffer, destOffset, count, (const jbyte *)address + srcOffset);
-
- if (unpinned) {
- ashmem_unpin_region(fd, 0, 0);
- }
- return count;
-}
-
-static jint android_os_MemoryFile_write(JNIEnv* env, jobject clazz,
- jobject fileDescriptor, jlong address, jbyteArray buffer, jint srcOffset, jint destOffset,
- jint count, jboolean unpinned)
-{
- int fd = jniGetFDFromFileDescriptor(env, fileDescriptor);
- if (unpinned && ashmem_pin_region(fd, 0, 0) == ASHMEM_WAS_PURGED) {
- ashmem_unpin_region(fd, 0, 0);
- jniThrowException(env, "java/io/IOException", "ashmem region was purged");
- return -1;
- }
-
- env->GetByteArrayRegion(buffer, srcOffset, count, (jbyte *)address + destOffset);
-
- if (unpinned) {
- ashmem_unpin_region(fd, 0, 0);
- }
- return count;
-}
-
-static void android_os_MemoryFile_pin(JNIEnv* env, jobject clazz, jobject fileDescriptor, jboolean pin)
-{
+static jboolean android_os_MemoryFile_pin(JNIEnv* env, jobject clazz, jobject fileDescriptor,
+ jboolean pin) {
int fd = jniGetFDFromFileDescriptor(env, fileDescriptor);
int result = (pin ? ashmem_pin_region(fd, 0, 0) : ashmem_unpin_region(fd, 0, 0));
if (result < 0) {
jniThrowException(env, "java/io/IOException", NULL);
}
+ return result == ASHMEM_WAS_PURGED;
}
static jint android_os_MemoryFile_get_size(JNIEnv* env, jobject clazz,
@@ -138,19 +57,12 @@ static jint android_os_MemoryFile_get_size(JNIEnv* env, jobject clazz,
}
static const JNINativeMethod methods[] = {
- {"native_open", "(Ljava/lang/String;I)Ljava/io/FileDescriptor;", (void*)android_os_MemoryFile_open},
- {"native_mmap", "(Ljava/io/FileDescriptor;II)J", (void*)android_os_MemoryFile_mmap},
- {"native_munmap", "(JI)V", (void*)android_os_MemoryFile_munmap},
- {"native_close", "(Ljava/io/FileDescriptor;)V", (void*)android_os_MemoryFile_close},
- {"native_read", "(Ljava/io/FileDescriptor;J[BIIIZ)I", (void*)android_os_MemoryFile_read},
- {"native_write", "(Ljava/io/FileDescriptor;J[BIIIZ)V", (void*)android_os_MemoryFile_write},
- {"native_pin", "(Ljava/io/FileDescriptor;Z)V", (void*)android_os_MemoryFile_pin},
+ {"native_pin", "(Ljava/io/FileDescriptor;Z)Z", (void*)android_os_MemoryFile_pin},
{"native_get_size", "(Ljava/io/FileDescriptor;)I",
(void*)android_os_MemoryFile_get_size}
};
-int register_android_os_MemoryFile(JNIEnv* env)
-{
+int register_android_os_MemoryFile(JNIEnv* env) {
return RegisterMethodsOrDie(env, "android/os/MemoryFile", methods, NELEM(methods));
}
diff --git a/core/jni/android_os_SharedMemory.cpp b/core/jni/android_os_SharedMemory.cpp
new file mode 100644
index 000000000000..24d08112275e
--- /dev/null
+++ b/core/jni/android_os_SharedMemory.cpp
@@ -0,0 +1,105 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "SharedMemory"
+
+#include "core_jni_helpers.h"
+
+#include <cutils/ashmem.h>
+#include <utils/Log.h>
+#include "JNIHelp.h"
+#include "JniConstants.h"
+#include "ScopedLocalRef.h"
+
+#include <algorithm>
+#include <errno.h>
+#include <limits>
+#include <unistd.h>
+
+namespace {
+
+static void throwErrnoException(JNIEnv* env, const char* functionName, int error) {
+ static jmethodID ctor = env->GetMethodID(JniConstants::errnoExceptionClass,
+ "<init>", "(Ljava/lang/String;I)V");
+
+ ScopedLocalRef<jstring> detailMessage(env, env->NewStringUTF(functionName));
+ if (detailMessage.get() == NULL) {
+ // Not really much we can do here. We're probably dead in the water,
+ // but let's try to stumble on...
+ env->ExceptionClear();
+ }
+
+ jobject exception = env->NewObject(JniConstants::errnoExceptionClass, ctor,
+ detailMessage.get(), error);
+ env->Throw(reinterpret_cast<jthrowable>(exception));
+}
+
+static jobject SharedMemory_create(JNIEnv* env, jobject, jstring jname, jint size) {
+
+ // Name is optional so we can't use ScopedUtfChars for this as it throws NPE on null
+ const char* name = jname ? env->GetStringUTFChars(jname, nullptr) : nullptr;
+
+ int fd = ashmem_create_region(name, size);
+
+ // Capture the error, if there is one, before calling ReleaseStringUTFChars
+ int err = fd < 0 ? errno : 0;
+
+ if (name) {
+ env->ReleaseStringUTFChars(jname, name);
+ }
+
+ if (fd < 0) {
+ throwErrnoException(env, "SharedMemory_create", err);
+ return nullptr;
+ }
+
+ return jniCreateFileDescriptor(env, fd);
+}
+
+static jint SharedMemory_getSize(JNIEnv* env, jobject, jobject fileDescriptor) {
+ int fd = jniGetFDFromFileDescriptor(env, fileDescriptor);
+ if (!ashmem_valid(fd)) {
+ return -1;
+ }
+ size_t size = ashmem_get_size_region(fd);
+ return static_cast<jint>(std::min(size, static_cast<size_t>(std::numeric_limits<jint>::max())));
+}
+
+static jint SharedMemory_setProt(JNIEnv* env, jobject, jobject fileDescriptor, jint prot) {
+ int fd = jniGetFDFromFileDescriptor(env, fileDescriptor);
+ int err = 0;
+ if (ashmem_set_prot_region(fd, prot)) {
+ err = errno;
+ }
+ return err;
+}
+
+static const JNINativeMethod methods[] = {
+ {"nCreate", "(Ljava/lang/String;I)Ljava/io/FileDescriptor;", (void*)SharedMemory_create},
+ {"nGetSize", "(Ljava/io/FileDescriptor;)I", (void*)SharedMemory_getSize},
+ {"nSetProt", "(Ljava/io/FileDescriptor;I)I", (void*)SharedMemory_setProt},
+};
+
+} // anonymous namespace
+
+namespace android {
+
+int register_android_os_SharedMemory(JNIEnv* env)
+{
+ return RegisterMethodsOrDie(env, "android/os/SharedMemory", methods, NELEM(methods));
+}
+
+} // namespace android
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index ced22cc37f5a..01b8e1558fb3 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -561,6 +561,35 @@
to be set for all windows of this activity -->
<attr name="showForAllUsers" format="boolean" />
+ <!-- Specifies whether an {@link Activity} should be shown on top of the the lock screen
+ whenever the lockscreen is up and the activity is resumed. Normally an activity will be
+ transitioned to the stopped state if it is started while the lockscreen is up, but with
+ this flag set the activity will remain in the resumed state visible on-top of the lock
+ screen.
+
+ <p>This should be used instead of {@link android.view.LayoutParams#FLAG_SHOW_WHEN_LOCKED}
+ flag set for Windows. When using the Window flag during activity startup, there may not be
+ time to add it before the system stops your activity for being behind the lock-screen.
+ This leads to a double life-cycle as it is then restarted.</p> -->
+ <attr name="showWhenLocked" format="boolean" />
+
+ <!-- Specifies whether the screen should be turned on when the {@link Activity} is resumed.
+ Normally an activity will be transitioned to the stopped state if it is started while the
+ screen if off, but with this flag set the activity will cause the screen to turn on if the
+ activity will be visible and resumed due to the screen coming on. The screen will not be
+ turned on if the activity won't be visible after the screen is turned on. This flag is
+ normally used in conjunction with the {@link android.R.attr#showWhenLocked} flag to make
+ sure the activity is visible after the screen is turned on when the lockscreen is up. In
+ addition, if this flag is set and the activity calls
+ {@link KeyguardManager#requestDismissKeyguard(Activity, KeyguardManager.KeyguardDismissCallback)}
+ the screen will turn on.
+
+ <p>This should be used instead of {@link android.view.LayoutParams.FLAG_TURN_SCREEN_ON}
+ flag set for Windows. When using the Window flag during activity startup, there may not be
+ time to add it before the system stops your activity because the screen has not yet turned
+ on. This leads to a double life-cycle as it is then restarted.</p> -->
+ <attr name="turnScreenOn" format="boolean" />
+
<!-- Specify the authorities under which this content provider can be
found. Multiple authorities may be supplied by separating them
with a semicolon. Authority names should use a Java-style naming
@@ -2093,6 +2122,10 @@
<attr name="maxAspectRatio" />
<attr name="lockTaskMode" />
<attr name="showForAllUsers" />
+
+ <attr name="showWhenLocked" />
+ <attr name="turnScreenOn" />
+
<attr name="directBootAware" />
<!-- @hide This activity is always focusable regardless of if it is in a task/stack whose
activities are normally not focusable.
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 3033f8c6543a..31c6039c8f78 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -407,12 +407,21 @@
<!-- Array of ConnectivityManager.TYPE_xxxx values allowable for tethering -->
<!-- Common options are [1, 4] for TYPE_WIFI and TYPE_MOBILE_DUN or
<!== [0,1,5,7] for TYPE_MOBILE, TYPE_WIFI, TYPE_MOBILE_HIPRI and TYPE_BLUETOOTH -->
+ <!-- This list is also modified by code within the framework, including:
+
+ - TYPE_ETHERNET (9) is prepended to this list, and
+ - the output of TelephonyManager.getTetherApnRequired()
+ determines whether both TYPE_MOBILE (0) and TYPE_HIPRI (5)
+ or TYPE_MOBILE_DUN (4) are appended (if not already present).
+
+ For other changes applied to this list, now and in the future, see
+ com.android.server.connectivity.tethering.TetheringConfiguration.
+ -->
<integer-array translatable="false" name="config_tether_upstream_types">
<item>0</item>
<item>1</item>
<item>5</item>
<item>7</item>
- <item>9</item>
</integer-array>
<!-- If the DUN connection for this CDMA device supports more than just DUN -->
@@ -2975,4 +2984,7 @@
<!-- Show area update info settings in CellBroadcastReceiver and information in SIM status in Settings app -->
<bool name="config_showAreaUpdateInfoSettings">false</bool>
+
+ <!-- Enable the RingtonePickerActivity in 'com.android.providers.media'. -->
+ <bool name="config_defaultRingtonePickerEnabled">true</bool>
</resources>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 48e667a06aa0..2a4881d6b167 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2843,6 +2843,8 @@
<eat-comment />
<public-group type="attr" first-id="0x01010569">
+ <public name="showWhenLocked"/>
+ <public name="turnScreenOn"/>
</public-group>
<public-group type="style" first-id="0x010302e0">
diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
index 1ebd42925b98..b96bda1596b6 100644
--- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
@@ -26,6 +26,7 @@ import static java.lang.reflect.Modifier.isFinal;
import static java.lang.reflect.Modifier.isPublic;
import static java.lang.reflect.Modifier.isStatic;
+import android.platform.test.annotations.Presubmit;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
@@ -37,6 +38,7 @@ import java.util.HashSet;
import java.util.Set;
/** Tests that ensure appropriate settings are backed up. */
+@Presubmit
@RunWith(AndroidJUnit4.class)
@SmallTest
public class SettingsBackupTest {
@@ -98,6 +100,7 @@ public class SettingsBackupTest {
Settings.Global.ALLOW_USER_SWITCHING_WHEN_SYSTEM_USER_LOCKED,
Settings.Global.ALWAYS_FINISH_ACTIVITIES,
Settings.Global.ANIMATOR_DURATION_SCALE,
+ Settings.Global.ANOMALY_DETECTION_CONSTANTS,
Settings.Global.APN_DB_UPDATE_CONTENT_URL,
Settings.Global.APN_DB_UPDATE_METADATA_URL,
Settings.Global.APP_IDLE_CONSTANTS,
@@ -117,7 +120,6 @@ public class SettingsBackupTest {
Settings.Global.BLUETOOTH_INTEROPERABILITY_LIST,
Settings.Global.BLUETOOTH_MAP_CLIENT_PRIORITY_PREFIX,
Settings.Global.BLUETOOTH_MAP_PRIORITY_PREFIX,
- Settings.Global.BLUETOOTH_ON, // Candidate for backup?
Settings.Global.BLUETOOTH_PAN_PRIORITY_PREFIX,
Settings.Global.BLUETOOTH_PBAP_CLIENT_PRIORITY_PREFIX,
Settings.Global.BLUETOOTH_SAP_PRIORITY_PREFIX,
@@ -166,6 +168,7 @@ public class SettingsBackupTest {
Settings.Global.DEVICE_IDLE_CONSTANTS,
Settings.Global.DEVICE_IDLE_CONSTANTS_WATCH,
Settings.Global.BATTERY_SAVER_CONSTANTS,
+ Settings.Global.DEFAULT_SM_DP_PLUS,
Settings.Global.DEVICE_NAME,
Settings.Global.DEVICE_POLICY_CONSTANTS,
Settings.Global.DEVICE_PROVISIONED,
@@ -190,11 +193,14 @@ public class SettingsBackupTest {
Settings.Global.ENABLE_ACCESSIBILITY_GLOBAL_GESTURE_ENABLED,
Settings.Global.ENABLE_CACHE_QUOTA_CALCULATION,
Settings.Global.ENABLE_CELLULAR_ON_BOOT,
+ Settings.Global.ENABLE_DELETION_HELPER_NO_THRESHOLD_TOGGLE,
Settings.Global.ENABLE_DISKSTATS_LOGGING,
Settings.Global.ENABLE_EPHEMERAL_FEATURE,
Settings.Global.ENHANCED_4G_MODE_ENABLED,
Settings.Global.EPHEMERAL_COOKIE_MAX_SIZE_BYTES,
Settings.Global.ERROR_LOGCAT_PREFIX,
+ Settings.Global.EUICC_PROVISIONED,
+ Settings.Global.EUICC_WIPING_TIMEOUT_MILLIS,
Settings.Global.FANCY_IME_ANIMATIONS,
Settings.Global.FORCE_ALLOW_ON_EXTERNAL,
Settings.Global.FSTRIM_MANDATORY_INTERVAL,
@@ -211,6 +217,7 @@ public class SettingsBackupTest {
Settings.Global.HTTP_PROXY,
Settings.Global.INET_CONDITION_DEBOUNCE_DOWN_DELAY,
Settings.Global.INET_CONDITION_DEBOUNCE_UP_DELAY,
+ Settings.Global.INSTANT_APP_DEXOPT_ENABLED,
Settings.Global.INTENT_FIREWALL_UPDATE_CONTENT_URL,
Settings.Global.INTENT_FIREWALL_UPDATE_METADATA_URL,
Settings.Global.JOB_SCHEDULER_CONSTANTS,
@@ -330,6 +337,7 @@ public class SettingsBackupTest {
Settings.Global.TCP_DEFAULT_INIT_RWND,
Settings.Global.TETHER_DUN_APN,
Settings.Global.TETHER_DUN_REQUIRED,
+ Settings.Global.TETHER_OFFLOAD_DISABLED,
Settings.Global.TETHER_SUPPORTED,
Settings.Global.THEATER_MODE_ON,
Settings.Global.TRANSITION_ANIMATION_SCALE,
@@ -488,6 +496,7 @@ public class SettingsBackupTest {
Settings.Secure.TRUST_AGENTS_INITIALIZED,
Settings.Secure.TV_INPUT_CUSTOM_LABELS,
Settings.Secure.TV_INPUT_HIDDEN_INPUTS,
+ Settings.Secure.TV_USER_SETUP_COMPLETE,
Settings.Secure.UI_NIGHT_MODE, // candidate?
Settings.Secure.UNKNOWN_SOURCES_DEFAULT_REVERSED,
Settings.Secure.UNSAFE_VOLUME_MUSIC_ACTIVE_MS,
diff --git a/graphics/java/android/graphics/drawable/AdaptiveIconDrawable.java b/graphics/java/android/graphics/drawable/AdaptiveIconDrawable.java
index 8616d58a0319..1d0cfa5ff082 100644
--- a/graphics/java/android/graphics/drawable/AdaptiveIconDrawable.java
+++ b/graphics/java/android/graphics/drawable/AdaptiveIconDrawable.java
@@ -289,6 +289,9 @@ public class AdaptiveIconDrawable extends Drawable implements Drawable.Callback
}
private void updateLayerBounds(Rect bounds) {
+ if (bounds.isEmpty()) {
+ return;
+ }
try {
suspendChildInvalidation();
updateLayerBoundsInternal(bounds);
@@ -1109,4 +1112,4 @@ public class AdaptiveIconDrawable extends Drawable implements Drawable.Callback
mCheckedStateful = false;
}
}
-} \ No newline at end of file
+}
diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp
index 0782269d7de1..99ccc98a1b31 100644
--- a/libs/androidfw/ResourceTypes.cpp
+++ b/libs/androidfw/ResourceTypes.cpp
@@ -1901,8 +1901,6 @@ int ResTable_config::compare(const ResTable_config& o) const {
if (diff != 0) return diff;
diff = (int32_t)(screenSize - o.screenSize);
if (diff != 0) return diff;
- diff = (int32_t)(version - o.version);
- if (diff != 0) return diff;
diff = (int32_t)(screenLayout - o.screenLayout);
if (diff != 0) return diff;
diff = (int32_t)(screenLayout2 - o.screenLayout2);
@@ -1914,6 +1912,11 @@ int ResTable_config::compare(const ResTable_config& o) const {
diff = (int32_t)(smallestScreenWidthDp - o.smallestScreenWidthDp);
if (diff != 0) return diff;
diff = (int32_t)(screenSizeDp - o.screenSizeDp);
+ if (diff != 0) return diff;
+
+ // Version MUST be last to ensure that a sorted list of configurations will always have the
+ // versions beside each other.
+ diff = (int32_t)(version - o.version);
return (int)diff;
}
diff --git a/location/java/android/location/GnssStatus.java b/location/java/android/location/GnssStatus.java
index 912551f5e4a9..b2903c4689e0 100644
--- a/location/java/android/location/GnssStatus.java
+++ b/location/java/android/location/GnssStatus.java
@@ -97,13 +97,12 @@ public final class GnssStatus {
CONSTELLATION_QZSS, CONSTELLATION_BEIDOU, CONSTELLATION_GALILEO})
public @interface ConstellationType {}
- /* These package private values are modified by the LocationManager class */
- /* package */ int[] mSvidWithFlags;
- /* package */ float[] mCn0DbHz;
- /* package */ float[] mElevations;
- /* package */ float[] mAzimuths;
- /* package */ int mSvCount;
- /* package */ float[] mCarrierFrequencies;
+ final int[] mSvidWithFlags;
+ final float[] mCn0DbHz;
+ final float[] mElevations;
+ final float[] mAzimuths;
+ final int mSvCount;
+ final float[] mCarrierFrequencies;
GnssStatus(int svCount, int[] svidWithFlags, float[] cn0s, float[] elevations,
float[] azimuths, float[] carrierFrequencies) {
diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java
index 26ac2a23d8c5..968f596e0cc5 100644
--- a/location/java/android/location/LocationManager.java
+++ b/location/java/android/location/LocationManager.java
@@ -74,7 +74,8 @@ public class LocationManager {
new HashMap<>();
private final HashMap<OnNmeaMessageListener, GnssStatusListenerTransport> mGnssNmeaListeners =
new HashMap<>();
- private GnssStatus mGnssStatus;
+ // volatile + GnssStatus final-fields pattern to avoid a partially published object
+ private volatile GnssStatus mGnssStatus;
private int mTimeToFirstFix;
/**
@@ -1563,7 +1564,7 @@ public class LocationManager {
float[] cn0s, float[] elevations, float[] azimuths, float[] carrierFreqs) {
if (mGnssCallback != null) {
mGnssStatus = new GnssStatus(svCount, prnWithFlags, cn0s, elevations, azimuths,
- carrierFreqs);
+ carrierFreqs);
Message msg = Message.obtain();
msg.what = GpsStatus.GPS_EVENT_SATELLITE_STATUS;
@@ -1949,7 +1950,7 @@ public class LocationManager {
status = new GpsStatus();
}
// When mGnssStatus is null, that means that this method is called outside
- // onGpsStatusChanged(). Return an empty status to maintain backwards compatibility.
+ // onGpsStatusChanged(). Return an empty status to maintain backwards compatibility.
if (mGnssStatus != null) {
status.setStatus(mGnssStatus, mTimeToFirstFix);
}
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index fd994b572981..a4302880dfaf 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -130,6 +130,15 @@
<!-- Bluetooth settings. Message when connected to a device, except for phone/media audio. [CHAR LIMIT=40] -->
<string name="bluetooth_connected_no_headset_no_a2dp">Connected (no phone or media)</string>
+ <!-- Bluetooth settings. Message when connected to a device, showing remote device battery level. [CHAR LIMIT=NONE] -->
+ <string name="bluetooth_connected_battery_level">Connected, battery <xliff:g id="battery_level_as_percentage">%1$s</xliff:g></string>
+ <!-- Bluetooth settings. Message when connected to a device, except for phone audio, showing remote device battery level. [CHAR LIMIT=NONE] -->
+ <string name="bluetooth_connected_no_headset_battery_level">Connected (no phone), battery <xliff:g id="battery_level_as_percentage">%1$s</xliff:g></string>
+ <!-- Bluetooth settings. Message when connected to a device, except for media audio, showing remote device battery level. [CHAR LIMIT=NONE] -->
+ <string name="bluetooth_connected_no_a2dp_battery_level">Connected (no media), battery <xliff:g id="battery_level_as_percentage">%1$s</xliff:g></string>
+ <!-- Bluetooth settings. Message when connected to a device, except for phone/media audio, showing remote device battery level. [CHAR LIMIT=NONE] -->
+ <string name="bluetooth_connected_no_headset_no_a2dp_battery_level">Connected (no phone or media), battery <xliff:g id="battery_level_as_percentage">%1$s</xliff:g></string>
+
<!-- Bluetooth settings. The user-visible string that is used whenever referring to the A2DP profile. -->
<string name="bluetooth_profile_a2dp">Media audio</string>
<!-- Bluetooth settings. The user-visible string that is used whenever referring to the headset or handsfree profile. -->
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
index fc6a1611e697..4a5445163650 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
@@ -1058,6 +1058,12 @@ public class AccessPoint implements Comparable<AccessPoint> {
final int oldLevel = getLevel();
if (info != null && isInfoForThisAccessPoint(config, info)) {
updated = (mInfo == null);
+ if (mConfig != config) {
+ // We do not set updated = true as we do not want to increase the amount of sorting
+ // and copying performed in WifiTracker at this time. If issues involving refresh
+ // are still seen, we will investigate further.
+ update(config); // Notifies the AccessPointListener of the change
+ }
if (mRssi != info.getRssi()) {
mRssi = info.getRssi();
updated = true;
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPointPreference.java b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPointPreference.java
index e5977f313091..92995a8e850d 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPointPreference.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPointPreference.java
@@ -70,6 +70,19 @@ public class AccessPointPreference extends Preference {
R.string.accessibility_wifi_signal_full
};
+ public static String generatePreferenceKey(AccessPoint accessPoint) {
+ StringBuilder builder = new StringBuilder();
+
+ if (TextUtils.isEmpty(accessPoint.getBssid())) {
+ builder.append(accessPoint.getSsidStr());
+ } else {
+ builder.append(accessPoint.getBssid());
+ }
+
+ builder.append(',').append(accessPoint.getSecurity());
+ return builder.toString();
+ }
+
// Used for dummy pref.
public AccessPointPreference(Context context, AttributeSet attrs) {
super(context, attrs);
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/TestAccessPointBuilder.java b/packages/SettingsLib/src/com/android/settingslib/wifi/TestAccessPointBuilder.java
index 2213ae647e98..4307cb0f1b7b 100644
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/TestAccessPointBuilder.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/TestAccessPointBuilder.java
@@ -26,9 +26,9 @@ import android.os.Bundle;
/**
* Build and return a valid AccessPoint.
*
-* Only intended for testing the AccessPoint class;
-* AccessPoints were designed to only be populated
-* by the mechanisms of scan results and wifi configurations.
+* Only intended for testing the AccessPoint class or creating Access points to be used in testing
+* applications. AccessPoints were designed to only be populated by the mechanisms of scan results
+* and wifi configurations.
*/
public class TestAccessPointBuilder {
// match the private values in WifiManager
@@ -36,12 +36,14 @@ public class TestAccessPointBuilder {
private static final int MAX_RSSI = -55;
// set some sensible defaults
+ private String mBssid = null;
private int mRssi = AccessPoint.UNREACHABLE_RSSI;
private int mNetworkId = WifiConfiguration.INVALID_NETWORK_ID;
private String ssid = "TestSsid";
private NetworkInfo mNetworkInfo = null;
private String mFqdn = null;
private String mProviderFriendlyName = null;
+ private int mSecurity = AccessPoint.SECURITY_NONE;
private WifiConfiguration mWifiConfig;
private WifiInfo mWifiInfo;
@@ -56,6 +58,7 @@ public class TestAccessPointBuilder {
WifiConfiguration wifiConfig = new WifiConfiguration();
wifiConfig.networkId = mNetworkId;
+ wifiConfig.BSSID = mBssid;
bundle.putString(AccessPoint.KEY_SSID, ssid);
bundle.putParcelable(AccessPoint.KEY_CONFIG, wifiConfig);
@@ -67,6 +70,8 @@ public class TestAccessPointBuilder {
if (mProviderFriendlyName != null) {
bundle.putString(AccessPoint.KEY_PROVIDER_FRIENDLY_NAME, mProviderFriendlyName);
}
+ bundle.putInt(AccessPoint.KEY_SECURITY, mSecurity);
+
AccessPoint ap = new AccessPoint(mContext, bundle);
ap.setRssi(mRssi);
return ap;
@@ -141,6 +146,11 @@ public class TestAccessPointBuilder {
return this;
}
+ public TestAccessPointBuilder setSecurity(int security) {
+ mSecurity = security;
+ return this;
+ }
+
public TestAccessPointBuilder setSsid(String newSsid) {
ssid = newSsid;
return this;
@@ -171,4 +181,9 @@ public class TestAccessPointBuilder {
mNetworkId = networkId;
return this;
}
+
+ public TestAccessPointBuilder setBssid(String bssid) {
+ mBssid = bssid;
+ return this;
+ }
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
index 85a453d24e6f..596eaefcd054 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
@@ -16,6 +16,7 @@
package com.android.settingslib.wifi;
import android.annotation.MainThread;
+import android.annotation.Nullable;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@@ -36,7 +37,6 @@ import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
import android.net.wifi.WifiNetworkScoreCache;
import android.net.wifi.WifiNetworkScoreCache.CacheListener;
-import android.os.ConditionVariable;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
@@ -247,7 +247,10 @@ public class WifiTracker {
mWorkHandler.removeMessages(WorkHandler.MSG_UPDATE_ACCESS_POINTS);
mLastInfo = mWifiManager.getConnectionInfo();
mLastNetworkInfo = mConnectivityManager.getNetworkInfo(mWifiManager.getCurrentNetwork());
- updateAccessPointsLocked();
+
+ final List<ScanResult> newScanResults = mWifiManager.getScanResults();
+ List<WifiConfiguration> configs = mWifiManager.getConfiguredNetworks();
+ updateAccessPointsLocked(newScanResults, configs);
if (DBG) {
Log.d(TAG, "force update - internal access point list:\n" + mInternalAccessPoints);
@@ -377,7 +380,7 @@ public class WifiTracker {
mNetworkScoreManager.unregisterNetworkScoreCache(NetworkKey.TYPE_WIFI, mScoreCache);
mScoreCache.clearScores();
- // Synchronize on mLock to avoid concurrent modification during updateAccessPointsLocked
+ // Synchronize on mLock to avoid concurrent modification during updateAccessPoints
synchronized (mLock) {
mRequestedScores.clear();
}
@@ -427,9 +430,8 @@ public class WifiTracker {
mScanId = 0;
}
- private Collection<ScanResult> fetchScanResults() {
+ private Collection<ScanResult> updateScanResultCache(final List<ScanResult> newResults) {
mScanId++;
- final List<ScanResult> newResults = mWifiManager.getScanResults();
for (ScanResult newResult : newResults) {
if (newResult.SSID == null || newResult.SSID.isEmpty()) {
continue;
@@ -457,8 +459,8 @@ public class WifiTracker {
return mScanResultCache.values();
}
- private WifiConfiguration getWifiConfigurationForNetworkId(int networkId) {
- final List<WifiConfiguration> configs = mWifiManager.getConfiguredNetworks();
+ private WifiConfiguration getWifiConfigurationForNetworkId(
+ int networkId, final List<WifiConfiguration> configs) {
if (configs != null) {
for (WifiConfiguration config : configs) {
if (mLastInfo != null && networkId == config.networkId &&
@@ -470,20 +472,37 @@ public class WifiTracker {
return null;
}
- /** Safely modify {@link #mInternalAccessPoints} by acquiring {@link #mLock} first. */
- private void updateAccessPointsLocked() {
+ /**
+ * Safely modify {@link #mInternalAccessPoints} by acquiring {@link #mLock} first.
+ *
+ * <p>Will not perform the update if {@link #mStaleScanResults} is true
+ */
+ private void updateAccessPoints() {
+ List<WifiConfiguration> configs = mWifiManager.getConfiguredNetworks();
+ final List<ScanResult> newScanResults = mWifiManager.getScanResults();
+
synchronized (mLock) {
- updateAccessPoints();
+ if(!mStaleScanResults) {
+ updateAccessPointsLocked(newScanResults, configs);
+ }
}
}
/**
* Update the internal list of access points.
*
- * <p>Should never be called directly, use {@link #updateAccessPointsLocked()} instead.
+ * <p>Do not called directly (except for forceUpdate), use {@link #updateAccessPoints()} which
+ * respects {@link #mStaleScanResults}.
*/
@GuardedBy("mLock")
- private void updateAccessPoints() {
+ private void updateAccessPointsLocked(final List<ScanResult> newScanResults,
+ List<WifiConfiguration> configs) {
+ WifiConfiguration connectionConfig = null;
+ if (mLastInfo != null) {
+ connectionConfig = getWifiConfigurationForNetworkId(
+ mLastInfo.getNetworkId(), mWifiManager.getConfiguredNetworks());
+ }
+
// Swap the current access points into a cached list.
List<AccessPoint> cachedAccessPoints = new ArrayList<>(mInternalAccessPoints);
ArrayList<AccessPoint> accessPoints = new ArrayList<>();
@@ -496,14 +515,9 @@ public class WifiTracker {
/* Lookup table to more quickly update AccessPoints by only considering objects with the
* correct SSID. Maps SSID -> List of AccessPoints with the given SSID. */
Multimap<String, AccessPoint> apMap = new Multimap<String, AccessPoint>();
- WifiConfiguration connectionConfig = null;
- if (mLastInfo != null) {
- connectionConfig = getWifiConfigurationForNetworkId(mLastInfo.getNetworkId());
- }
- final Collection<ScanResult> results = fetchScanResults();
+ final Collection<ScanResult> results = updateScanResultCache(newScanResults);
- final List<WifiConfiguration> configs = mWifiManager.getConfiguredNetworks();
if (configs != null) {
for (WifiConfiguration config : configs) {
if (config.selfAdded && config.numAssociation == 0) {
@@ -671,7 +685,8 @@ public class WifiTracker {
WifiConfiguration connectionConfig = null;
mLastInfo = mWifiManager.getConnectionInfo();
if (mLastInfo != null) {
- connectionConfig = getWifiConfigurationForNetworkId(mLastInfo.getNetworkId());
+ connectionConfig = getWifiConfigurationForNetworkId(mLastInfo.getNetworkId(),
+ mWifiManager.getConfiguredNetworks());
}
boolean updated = false;
@@ -868,15 +883,12 @@ public class WifiTracker {
}
}
- @GuardedBy("mLock")
private void processMessage(Message msg) {
if (!mRegistered) return;
switch (msg.what) {
case MSG_UPDATE_ACCESS_POINTS:
- if (!mStaleScanResults) {
- updateAccessPointsLocked();
- }
+ updateAccessPoints();
break;
case MSG_UPDATE_NETWORK_INFO:
updateNetworkInfo((NetworkInfo) msg.obj);
diff --git a/packages/SettingsLib/tests/integ/AndroidManifest.xml b/packages/SettingsLib/tests/integ/AndroidManifest.xml
index 0d5ff2ca05a5..e8e0b41ffd41 100644
--- a/packages/SettingsLib/tests/integ/AndroidManifest.xml
+++ b/packages/SettingsLib/tests/integ/AndroidManifest.xml
@@ -21,6 +21,7 @@
<uses-permission android:name="android.permission.MANAGE_USERS" />
<uses-permission android:name="android.permission.MANAGE_NETWORK_POLICY"/>
<uses-permission android:name="android.permission.SET_TIME_ZONE" />
+ <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/AccessPointTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/AccessPointTest.java
index 57ad093422a4..5c64cff78a88 100644
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/AccessPointTest.java
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/AccessPointTest.java
@@ -20,6 +20,8 @@ import static com.google.common.truth.Truth.assertWithMessage;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.content.Context;
@@ -622,4 +624,36 @@ public class AccessPointTest {
NetworkInfo newInfo = new NetworkInfo(networkInfo); // same values
assertThat(ap.update(config, wifiInfo, newInfo)).isFalse();
}
+
+ @Test
+ public void testUpdateWithConfigChangeOnly_returnsFalseButInvokesListener() {
+ int networkId = 123;
+ int rssi = -55;
+ WifiConfiguration config = new WifiConfiguration();
+ config.networkId = networkId;
+ config.numNoInternetAccessReports = 1;
+
+ WifiInfo wifiInfo = new WifiInfo();
+ wifiInfo.setNetworkId(networkId);
+ wifiInfo.setRssi(rssi);
+
+ NetworkInfo networkInfo =
+ new NetworkInfo(ConnectivityManager.TYPE_WIFI, 0 /* subtype */, "WIFI", "");
+ networkInfo.setDetailedState(NetworkInfo.DetailedState.CONNECTED, "", "");
+
+ AccessPoint ap = new TestAccessPointBuilder(mContext)
+ .setNetworkInfo(networkInfo)
+ .setNetworkId(networkId)
+ .setRssi(rssi)
+ .setWifiInfo(wifiInfo)
+ .build();
+
+ AccessPoint.AccessPointListener mockListener = mock(AccessPoint.AccessPointListener.class);
+ ap.setListener(mockListener);
+ WifiConfiguration newConfig = new WifiConfiguration(config);
+ config.validatedInternetAccess = true;
+
+ assertThat(ap.update(newConfig, wifiInfo, networkInfo)).isFalse();
+ verify(mockListener).onAccessPointChanged(ap);
+ }
}
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java
index 073da7eea25f..071f9216ea56 100644
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java
@@ -59,6 +59,7 @@ import android.provider.Settings;
import android.support.test.InstrumentationRegistry;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
+import android.support.test.filters.FlakyTest;
import org.junit.After;
import org.junit.Before;
@@ -492,6 +493,7 @@ public class WifiTrackerTest {
waitForHandlersToProcessCurrentlyEnqueuedMessages(tracker);
}
+ @FlakyTest
@Test
public void scoreCacheUpdateScoresShouldChangeSortOrder() throws InterruptedException {
WifiTracker tracker = createTrackerWithImmediateBroadcastsAndInjectInitialScanResults();
@@ -530,6 +532,7 @@ public class WifiTrackerTest {
assertEquals(aps.get(1).getSsidStr(), SSID_2);
}
+ @FlakyTest
@Test
public void scoreCacheUpdateScoresShouldInsertSpeedIntoAccessPoint()
throws InterruptedException {
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/AccessPointPreferenceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/AccessPointPreferenceTest.java
new file mode 100644
index 000000000000..335653bc2f18
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/AccessPointPreferenceTest.java
@@ -0,0 +1,61 @@
+/*
+ * 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.settingslib.wifi;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.Context;
+
+import com.android.settingslib.SettingLibRobolectricTestRunner;
+import com.android.settingslib.TestConfig;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+
+@RunWith(SettingLibRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
+public class AccessPointPreferenceTest {
+
+ private Context mContext = RuntimeEnvironment.application;
+
+ @Test
+ public void generatePreferenceKey_shouldReturnSsidPlusSecurity() {
+ String ssid = "ssid";
+ int security = AccessPoint.SECURITY_WEP;
+ String expectedKey = ssid + ',' + security;
+
+ TestAccessPointBuilder builder = new TestAccessPointBuilder(mContext);
+ builder.setSsid(ssid).setSecurity(security);
+
+ assertThat(AccessPointPreference.generatePreferenceKey(builder.build()))
+ .isEqualTo(expectedKey);
+ }
+
+ @Test
+ public void generatePreferenceKey_shouldReturnBssidPlusSecurity() {
+ String bssid = "bssid";
+ int security = AccessPoint.SECURITY_WEP;
+ String expectedKey = bssid + ',' + security;
+
+ TestAccessPointBuilder builder = new TestAccessPointBuilder(mContext);
+ builder.setBssid(bssid).setSecurity(security);
+
+ assertThat(AccessPointPreference.generatePreferenceKey(builder.build()))
+ .isEqualTo(expectedKey);
+ }
+}
diff --git a/packages/SystemUI/res/anim/ic_portrait_from_auto_rotate_animation_arrow_top.xml b/packages/SystemUI/res/anim/ic_portrait_from_auto_rotate_animation_arrow_top.xml
deleted file mode 100644
index 4e20d8190bbc..000000000000
--- a/packages/SystemUI/res/anim/ic_portrait_from_auto_rotate_animation_arrow_top.xml
+++ /dev/null
@@ -1,33 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2014 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<set xmlns:android="http://schemas.android.com/apk/res/android" >
- <set
- android:ordering="sequentially" >
- <objectAnimator
- android:duration="400"
- android:propertyName="fillAlpha"
- android:valueFrom="1"
- android:valueTo="1"
- android:interpolator="@android:interpolator/linear" />
- <objectAnimator
- android:duration="83"
- android:propertyName="fillAlpha"
- android:valueFrom="1"
- android:valueTo="0"
- android:interpolator="@android:interpolator/linear" />
- </set>
-</set>
diff --git a/packages/SystemUI/res/anim/ic_portrait_to_auto_rotate_animation_arrow_top.xml b/packages/SystemUI/res/anim/ic_portrait_to_auto_rotate_animation_arrow_top.xml
deleted file mode 100644
index 682dcf35e076..000000000000
--- a/packages/SystemUI/res/anim/ic_portrait_to_auto_rotate_animation_arrow_top.xml
+++ /dev/null
@@ -1,33 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2014 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<set xmlns:android="http://schemas.android.com/apk/res/android" >
- <set
- android:ordering="sequentially" >
- <objectAnimator
- android:duration="50"
- android:propertyName="fillAlpha"
- android:valueFrom="0"
- android:valueTo="0"
- android:interpolator="@android:interpolator/linear" />
- <objectAnimator
- android:duration="83"
- android:propertyName="fillAlpha"
- android:valueFrom="0"
- android:valueTo="1"
- android:interpolator="@android:interpolator/linear" />
- </set>
-</set>
diff --git a/packages/SystemUI/res/anim/ic_portrait_to_auto_rotate_animation_arrows.xml b/packages/SystemUI/res/anim/ic_portrait_to_auto_rotate_animation_arrows.xml
deleted file mode 100644
index 9fa8ec0d65d4..000000000000
--- a/packages/SystemUI/res/anim/ic_portrait_to_auto_rotate_animation_arrows.xml
+++ /dev/null
@@ -1,54 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2014 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<set xmlns:android="http://schemas.android.com/apk/res/android" >
- <set
- android:ordering="sequentially" >
- <objectAnimator
- android:duration="133"
- android:propertyName="scaleX"
- android:valueFrom="0.9"
- android:valueTo="0.9"
- android:interpolator="@android:interpolator/linear" />
- <objectAnimator
- android:duration="333"
- android:propertyName="scaleX"
- android:valueFrom="0.9"
- android:valueTo="1"
- android:interpolator="@android:interpolator/linear" />
- </set>
- <set
- android:ordering="sequentially" >
- <objectAnimator
- android:duration="133"
- android:propertyName="scaleY"
- android:valueFrom="0.9"
- android:valueTo="0.9"
- android:interpolator="@android:interpolator/linear" />
- <objectAnimator
- android:duration="333"
- android:propertyName="scaleY"
- android:valueFrom="0.9"
- android:valueTo="1"
- android:interpolator="@android:interpolator/linear" />
- </set>
- <objectAnimator
- android:duration="617"
- android:propertyName="rotation"
- android:valueFrom="-221"
- android:valueTo="0"
- android:interpolator="@android:interpolator/fast_out_slow_in" />
-</set>
diff --git a/packages/SystemUI/res/anim/ic_portrait_from_auto_rotate_animation_arrows.xml b/packages/SystemUI/res/anim/ic_portrait_to_rotate_arrows_0_animation.xml
index 61bdfeaa27a0..ad2a5fad5268 100644
--- a/packages/SystemUI/res/anim/ic_portrait_from_auto_rotate_animation_arrows.xml
+++ b/packages/SystemUI/res/anim/ic_portrait_to_rotate_arrows_0_animation.xml
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
- Copyright (C) 2014 The Android Open Source Project
+ 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.
@@ -14,41 +14,40 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<set xmlns:android="http://schemas.android.com/apk/res/android" >
+<set
+ xmlns:android="http://schemas.android.com/apk/res/android" >
<set
android:ordering="sequentially" >
<objectAnimator
- android:duration="117"
+ android:duration="116"
android:propertyName="scaleX"
- android:valueFrom="1"
- android:valueTo="1"
+ android:valueFrom="1.0"
+ android:valueTo="1.0"
+ android:valueType="floatType"
android:interpolator="@android:interpolator/linear" />
<objectAnimator
android:duration="333"
android:propertyName="scaleX"
- android:valueFrom="1"
+ android:valueFrom="1.0"
android:valueTo="0.9"
+ android:valueType="floatType"
android:interpolator="@android:interpolator/linear" />
</set>
<set
android:ordering="sequentially" >
<objectAnimator
- android:duration="117"
+ android:duration="116"
android:propertyName="scaleY"
- android:valueFrom="1"
- android:valueTo="1"
+ android:valueFrom="1.0"
+ android:valueTo="1.0"
+ android:valueType="floatType"
android:interpolator="@android:interpolator/linear" />
<objectAnimator
android:duration="333"
android:propertyName="scaleY"
- android:valueFrom="1"
+ android:valueFrom="1.0"
android:valueTo="0.9"
+ android:valueType="floatType"
android:interpolator="@android:interpolator/linear" />
</set>
- <objectAnimator
- android:duration="617"
- android:propertyName="rotation"
- android:valueFrom="0"
- android:valueTo="-221"
- android:interpolator="@android:interpolator/fast_out_slow_in" />
</set>
diff --git a/packages/SystemUI/res/anim/ic_portrait_to_rotate_arrows_animation.xml b/packages/SystemUI/res/anim/ic_portrait_to_rotate_arrows_animation.xml
new file mode 100644
index 000000000000..cdb7890dc170
--- /dev/null
+++ b/packages/SystemUI/res/anim/ic_portrait_to_rotate_arrows_animation.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ 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.
+-->
+<set
+ xmlns:android="http://schemas.android.com/apk/res/android" >
+ <objectAnimator
+ android:duration="616"
+ android:propertyName="rotation"
+ android:valueFrom="0.0"
+ android:valueTo="-221.0"
+ android:valueType="floatType"
+ android:interpolator="@android:interpolator/fast_out_slow_in" />
+</set>
diff --git a/packages/SystemUI/res/anim/ic_portrait_from_auto_rotate_animation_arrow_bottom.xml b/packages/SystemUI/res/anim/ic_portrait_to_rotate_bottom_merged_animation.xml
index 4e20d8190bbc..46100b407831 100644
--- a/packages/SystemUI/res/anim/ic_portrait_from_auto_rotate_animation_arrow_bottom.xml
+++ b/packages/SystemUI/res/anim/ic_portrait_to_rotate_bottom_merged_animation.xml
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
- Copyright (C) 2014 The Android Open Source Project
+ 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.
@@ -14,20 +14,23 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<set xmlns:android="http://schemas.android.com/apk/res/android" >
+<set
+ xmlns:android="http://schemas.android.com/apk/res/android" >
<set
android:ordering="sequentially" >
<objectAnimator
android:duration="400"
android:propertyName="fillAlpha"
- android:valueFrom="1"
- android:valueTo="1"
+ android:valueFrom="1.0"
+ android:valueTo="1.0"
+ android:valueType="floatType"
android:interpolator="@android:interpolator/linear" />
<objectAnimator
android:duration="83"
android:propertyName="fillAlpha"
- android:valueFrom="1"
- android:valueTo="0"
+ android:valueFrom="1.0"
+ android:valueTo="0.0"
+ android:valueType="floatType"
android:interpolator="@android:interpolator/linear" />
</set>
</set>
diff --git a/packages/SystemUI/res/anim/ic_portrait_from_auto_rotate_animation_device.xml b/packages/SystemUI/res/anim/ic_portrait_to_rotate_device_0_animation.xml
index 6a0a20bd1242..8f6d24d0d61d 100644
--- a/packages/SystemUI/res/anim/ic_portrait_from_auto_rotate_animation_device.xml
+++ b/packages/SystemUI/res/anim/ic_portrait_to_rotate_device_0_animation.xml
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
- Copyright (C) 2014 The Android Open Source Project
+ 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.
@@ -14,11 +14,13 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<set xmlns:android="http://schemas.android.com/apk/res/android" >
+<set
+ xmlns:android="http://schemas.android.com/apk/res/android" >
<objectAnimator
android:duration="400"
android:propertyName="rotation"
- android:valueFrom="0"
- android:valueTo="-135"
+ android:valueFrom="0.0"
+ android:valueTo="-135.0"
+ android:valueType="floatType"
android:interpolator="@android:interpolator/fast_out_slow_in" />
</set>
diff --git a/packages/SystemUI/res/anim/ic_portrait_from_auto_rotate_animation_device_1.xml b/packages/SystemUI/res/anim/ic_portrait_to_rotate_device_merged_animation.xml
index 92b19add4bcb..300ed53052a8 100644
--- a/packages/SystemUI/res/anim/ic_portrait_from_auto_rotate_animation_device_1.xml
+++ b/packages/SystemUI/res/anim/ic_portrait_to_rotate_device_merged_animation.xml
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
- Copyright (C) 2014 The Android Open Source Project
+ 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.
@@ -14,18 +14,19 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<set xmlns:android="http://schemas.android.com/apk/res/android" >
+<set
+ xmlns:android="http://schemas.android.com/apk/res/android" >
<set
android:ordering="sequentially" >
<objectAnimator
- android:duration="167"
+ android:duration="66"
android:propertyName="pathData"
android:valueFrom="M -3.5,-20.5 c -1.19999694824,-1.19999694824 -3.10000610352,-1.19999694824 -4.19999694824,0.0 c 0.0,0.0 -12.8000030518,12.6999969482 -12.8000030518,12.6999969482 c -1.19999694824,1.19999694824 -1.19999694824,3.10000610352 0.0,4.19999694824 c 0.0,0.0 24.0,24.0000152588 24.0,24.0000152588 c 1.19999694824,1.19999694824 3.10000610352,1.19999694824 4.19999694824,0.0 c 0.0,0.0 12.6999969482,-12.700012207 12.6999969482,-12.700012207 c 1.20001220703,-1.19999694824 1.20001220703,-3.09999084473 0.0,-4.19999694824 c 0.0,0.0 -23.8999938965,-24.0 -23.8999938965,-24.0 Z M 2.84999084473,15.5500183105 c 0.0,0.0 -18.6000061035,-18.5000457764 -18.6000061035,-18.5000457764 c 0.0,0.0 12.5999908447,-12.8000030518 12.5999908447,-12.8000030518 c 0.0,0.0 18.6000213623,18.5000457764 18.6000213623,18.5000457764 c 0.0,0.0 -12.6000061035,12.8000030518 -12.6000061035,12.8000030518 Z"
android:valueTo="M -3.5,-20.5 c -1.19999694824,-1.19999694824 -3.10000610352,-1.19999694824 -4.19999694824,0.0 c 0.0,0.0 -12.8000030518,12.6999969482 -12.8000030518,12.6999969482 c -1.19999694824,1.19999694824 -1.19999694824,3.10000610352 0.0,4.19999694824 c 0.0,0.0 24.0,24.0000152588 24.0,24.0000152588 c 1.19999694824,1.19999694824 3.10000610352,1.19999694824 4.19999694824,0.0 c 0.0,0.0 12.6999969482,-12.700012207 12.6999969482,-12.700012207 c 1.20001220703,-1.19999694824 1.20001220703,-3.09999084473 0.0,-4.19999694824 c 0.0,0.0 -23.8999938965,-24.0 -23.8999938965,-24.0 Z M 2.84999084473,15.5500183105 c 0.0,0.0 -18.6000061035,-18.5000457764 -18.6000061035,-18.5000457764 c 0.0,0.0 12.5999908447,-12.8000030518 12.5999908447,-12.8000030518 c 0.0,0.0 18.6000213623,18.5000457764 18.6000213623,18.5000457764 c 0.0,0.0 -12.6000061035,12.8000030518 -12.6000061035,12.8000030518 Z"
android:valueType="pathType"
- android:interpolator="@android:interpolator/fast_out_slow_in" />
+ android:interpolator="@android:interpolator/linear" />
<objectAnimator
- android:duration="217"
+ android:duration="216"
android:propertyName="pathData"
android:valueFrom="M -3.5,-20.5 c -1.19999694824,-1.19999694824 -3.10000610352,-1.19999694824 -4.19999694824,0.0 c 0.0,0.0 -12.8000030518,12.6999969482 -12.8000030518,12.6999969482 c -1.19999694824,1.19999694824 -1.19999694824,3.10000610352 0.0,4.19999694824 c 0.0,0.0 24.0,24.0000152588 24.0,24.0000152588 c 1.19999694824,1.19999694824 3.10000610352,1.19999694824 4.19999694824,0.0 c 0.0,0.0 12.6999969482,-12.700012207 12.6999969482,-12.700012207 c 1.20001220703,-1.19999694824 1.20001220703,-3.09999084473 0.0,-4.19999694824 c 0.0,0.0 -23.8999938965,-24.0 -23.8999938965,-24.0 Z M 2.84999084473,15.5500183105 c 0.0,0.0 -18.6000061035,-18.5000457764 -18.6000061035,-18.5000457764 c 0.0,0.0 12.5999908447,-12.8000030518 12.5999908447,-12.8000030518 c 0.0,0.0 18.6000213623,18.5000457764 18.6000213623,18.5000457764 c 0.0,0.0 -12.6000061035,12.8000030518 -12.6000061035,12.8000030518 Z"
android:valueTo="M -3.34053039551,-22.9980926514 c -1.3207244873,-1.3207244873 -3.46876525879,-1.26383972168 -4.74829101563,0.125762939453 c 0.0,0.0 -14.8512420654,14.7411804199 -14.8512420654,14.7411804199 c -1.39259338379,1.392578125 -1.44947814941,3.54061889648 -0.125762939453,4.74827575684 c 0.0,0.0 26.4143981934,26.4144134521 26.4143981934,26.4144134521 c 1.3207244873,1.3207244873 3.46876525879,1.26382446289 4.74829101562,-0.125762939453 c 0.0,0.0 14.7381896973,-14.7381896973 14.7381896973,-14.7381896973 c 1.392578125,-1.39259338379 1.44947814941,-3.54061889648 0.125762939453,-4.74829101562 c 0.0,0.0 -26.3013458252,-26.417388916 -26.3013458252,-26.417388916 Z M 2.87156677246,16.9857940674 c 0.0,0.0 -19.7573547363,-19.7573699951 -19.7573547363,-19.7573699951 c 0.0,0.0 14.0142059326,-14.2142181396 14.0142059326,-14.2142181396 c 0.0,0.0 19.7573699951,19.7573699951 19.7573699951,19.7573699951 c 0.0,0.0 -14.0142211914,14.2142181396 -14.0142211914,14.2142181396 Z"
diff --git a/packages/SystemUI/res/anim/ic_rotate_to_portrait_arrows_0_animation.xml b/packages/SystemUI/res/anim/ic_rotate_to_portrait_arrows_0_animation.xml
new file mode 100644
index 000000000000..ce267704dac5
--- /dev/null
+++ b/packages/SystemUI/res/anim/ic_rotate_to_portrait_arrows_0_animation.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ 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.
+-->
+<set
+ xmlns:android="http://schemas.android.com/apk/res/android" >
+ <objectAnimator
+ android:duration="466"
+ android:propertyName="scaleX"
+ android:valueFrom="0.9"
+ android:valueTo="1.0"
+ android:valueType="floatType"
+ android:interpolator="@interpolator/ic_rotate_to_portrait_animation_interpolator_0" />
+ <objectAnimator
+ android:duration="466"
+ android:propertyName="scaleY"
+ android:valueFrom="0.9"
+ android:valueTo="1.0"
+ android:valueType="floatType"
+ android:interpolator="@interpolator/ic_rotate_to_portrait_animation_interpolator_0" />
+</set>
diff --git a/packages/SystemUI/res/anim/ic_rotate_to_portrait_arrows_animation.xml b/packages/SystemUI/res/anim/ic_rotate_to_portrait_arrows_animation.xml
new file mode 100644
index 000000000000..6e8941d608cd
--- /dev/null
+++ b/packages/SystemUI/res/anim/ic_rotate_to_portrait_arrows_animation.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ 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.
+-->
+<set
+ xmlns:android="http://schemas.android.com/apk/res/android" >
+ <objectAnimator
+ android:duration="616"
+ android:propertyName="rotation"
+ android:valueFrom="-221.0"
+ android:valueTo="0.0"
+ android:valueType="floatType"
+ android:interpolator="@android:interpolator/fast_out_slow_in" />
+</set>
diff --git a/packages/SystemUI/res/anim/ic_portrait_to_auto_rotate_animation_arrow_bottom.xml b/packages/SystemUI/res/anim/ic_rotate_to_portrait_bottom_merged_animation.xml
index 682dcf35e076..3c3c131ef16b 100644
--- a/packages/SystemUI/res/anim/ic_portrait_to_auto_rotate_animation_arrow_bottom.xml
+++ b/packages/SystemUI/res/anim/ic_rotate_to_portrait_bottom_merged_animation.xml
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
- Copyright (C) 2014 The Android Open Source Project
+ 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.
@@ -14,20 +14,23 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<set xmlns:android="http://schemas.android.com/apk/res/android" >
+<set
+ xmlns:android="http://schemas.android.com/apk/res/android" >
<set
android:ordering="sequentially" >
<objectAnimator
android:duration="50"
android:propertyName="fillAlpha"
- android:valueFrom="0"
- android:valueTo="0"
+ android:valueFrom="0.0"
+ android:valueTo="0.0"
+ android:valueType="floatType"
android:interpolator="@android:interpolator/linear" />
<objectAnimator
android:duration="83"
android:propertyName="fillAlpha"
- android:valueFrom="0"
- android:valueTo="1"
+ android:valueFrom="0.0"
+ android:valueTo="1.0"
+ android:valueType="floatType"
android:interpolator="@android:interpolator/linear" />
</set>
</set>
diff --git a/packages/SystemUI/res/anim/ic_portrait_to_auto_rotate_animation_device.xml b/packages/SystemUI/res/anim/ic_rotate_to_portrait_device_0_animation.xml
index 3208eee2c2c6..fd8e4f881160 100644
--- a/packages/SystemUI/res/anim/ic_portrait_to_auto_rotate_animation_device.xml
+++ b/packages/SystemUI/res/anim/ic_rotate_to_portrait_device_0_animation.xml
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
- Copyright (C) 2014 The Android Open Source Project
+ 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.
@@ -14,20 +14,23 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<set xmlns:android="http://schemas.android.com/apk/res/android" >
+<set
+ xmlns:android="http://schemas.android.com/apk/res/android" >
<set
android:ordering="sequentially" >
<objectAnimator
android:duration="50"
android:propertyName="rotation"
- android:valueFrom="-135"
- android:valueTo="-135"
- android:interpolator="@android:interpolator/fast_out_slow_in" />
+ android:valueFrom="-135.0"
+ android:valueTo="-135.0"
+ android:valueType="floatType"
+ android:interpolator="@android:interpolator/linear" />
<objectAnimator
android:duration="400"
android:propertyName="rotation"
- android:valueFrom="-135"
- android:valueTo="0"
+ android:valueFrom="-135.0"
+ android:valueTo="0.0"
+ android:valueType="floatType"
android:interpolator="@android:interpolator/fast_out_slow_in" />
</set>
</set>
diff --git a/packages/SystemUI/res/anim/ic_portrait_to_auto_rotate_animation_device_1.xml b/packages/SystemUI/res/anim/ic_rotate_to_portrait_device_merged_animation.xml
index c1124af4e234..a77a536e46c0 100644
--- a/packages/SystemUI/res/anim/ic_portrait_to_auto_rotate_animation_device_1.xml
+++ b/packages/SystemUI/res/anim/ic_rotate_to_portrait_device_merged_animation.xml
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
- Copyright (C) 2014 The Android Open Source Project
+ 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.
@@ -14,7 +14,8 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<set xmlns:android="http://schemas.android.com/apk/res/android" >
+<set
+ xmlns:android="http://schemas.android.com/apk/res/android" >
<set
android:ordering="sequentially" >
<objectAnimator
@@ -23,7 +24,7 @@
android:valueFrom="M -3.34053039551,-22.9980926514 c -1.3207244873,-1.3207244873 -3.46876525879,-1.26383972168 -4.74829101563,0.125762939453 c 0.0,0.0 -14.8512420654,14.7411804199 -14.8512420654,14.7411804199 c -1.39259338379,1.392578125 -1.44947814941,3.54061889648 -0.125762939453,4.74827575684 c 0.0,0.0 26.4143981934,26.4144134521 26.4143981934,26.4144134521 c 1.3207244873,1.3207244873 3.46876525879,1.26382446289 4.74829101562,-0.125762939453 c 0.0,0.0 14.7381896973,-14.7381896973 14.7381896973,-14.7381896973 c 1.392578125,-1.39259338379 1.44947814941,-3.54061889648 0.125762939453,-4.74829101562 c 0.0,0.0 -26.3013458252,-26.417388916 -26.3013458252,-26.417388916 Z M 2.87156677246,16.9857940674 c 0.0,0.0 -19.7573547363,-19.7573699951 -19.7573547363,-19.7573699951 c 0.0,0.0 14.0142059326,-14.2142181396 14.0142059326,-14.2142181396 c 0.0,0.0 19.7573699951,19.7573699951 19.7573699951,19.7573699951 c 0.0,0.0 -14.0142211914,14.2142181396 -14.0142211914,14.2142181396 Z"
android:valueTo="M -3.34053039551,-22.9980926514 c -1.3207244873,-1.3207244873 -3.46876525879,-1.26383972168 -4.74829101563,0.125762939453 c 0.0,0.0 -14.8512420654,14.7411804199 -14.8512420654,14.7411804199 c -1.39259338379,1.392578125 -1.44947814941,3.54061889648 -0.125762939453,4.74827575684 c 0.0,0.0 26.4143981934,26.4144134521 26.4143981934,26.4144134521 c 1.3207244873,1.3207244873 3.46876525879,1.26382446289 4.74829101562,-0.125762939453 c 0.0,0.0 14.7381896973,-14.7381896973 14.7381896973,-14.7381896973 c 1.392578125,-1.39259338379 1.44947814941,-3.54061889648 0.125762939453,-4.74829101562 c 0.0,0.0 -26.3013458252,-26.417388916 -26.3013458252,-26.417388916 Z M 2.87156677246,16.9857940674 c 0.0,0.0 -19.7573547363,-19.7573699951 -19.7573547363,-19.7573699951 c 0.0,0.0 14.0142059326,-14.2142181396 14.0142059326,-14.2142181396 c 0.0,0.0 19.7573699951,19.7573699951 19.7573699951,19.7573699951 c 0.0,0.0 -14.0142211914,14.2142181396 -14.0142211914,14.2142181396 Z"
android:valueType="pathType"
- android:interpolator="@android:interpolator/fast_out_slow_in" />
+ android:interpolator="@android:interpolator/linear" />
<objectAnimator
android:duration="500"
android:propertyName="pathData"
diff --git a/packages/SystemUI/res/drawable/ic_portrait_from_auto_rotate.xml b/packages/SystemUI/res/drawable/ic_portrait_from_auto_rotate.xml
index 17185a7c36b0..bce494c975ed 100644
--- a/packages/SystemUI/res/drawable/ic_portrait_from_auto_rotate.xml
+++ b/packages/SystemUI/res/drawable/ic_portrait_from_auto_rotate.xml
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
- Copyright (C) 2014 The Android Open Source Project
+ 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.
@@ -14,50 +14,51 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
+<vector
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:name="ic_portrait_to_rotate"
android:height="48dp"
android:width="48dp"
android:viewportHeight="48"
android:viewportWidth="48"
android:tint="?android:attr/colorControlNormal" >
<group
- android:name="icon"
+ android:name="device"
android:translateX="24"
android:translateY="24" >
<group
- android:name="icon_pivot"
+ android:name="device_pivot"
android:translateX="-24.15"
android:translateY="-24.25" >
<group
- android:name="arrows"
- android:translateX="24.1"
- android:translateY="24.1" >
- <group
- android:name="arrows_pivot"
- android:translateX="-24.1"
- android:translateY="-24.1" >
- <path
- android:name="arrow_top"
- android:pathData="M 33.1499938965,5.25 c 6.5,3.10000610352 11.1999969482,9.40000915527 11.8999938965,17.0 c 0.0,0.0 3.00001525879,0.0 3.00001525879,0.0 c -1.00001525879,-12.3000030518 -11.3000030518,-22.0 -23.9000091553,-22.0 c -0.399993896484,0.0 -0.899993896484,0.0 -1.30000305176,0.100006103516 c 0.0,0.0 7.60000610352,7.59999084473 7.60000610352,7.59999084473 c 0.0,0.0 2.69999694824,-2.69999694824 2.69999694824,-2.69999694824 Z"
- android:fillColor="#FFFFFFFF"
- android:fillAlpha="1" />
- <path
- android:name="arrow_bottom"
- android:pathData="M 15.1499938965,43.25 c -6.5,-3.09999084473 -11.1999969482,-9.5 -11.8999938965,-17.0 c 0.0,0.0 -3.0,0.0 -3.0,0.0 c 1.0,12.3000030518 11.299987793,22.0 23.8999938965,22.0 c 0.399993896484,0.0 0.899993896484,0.0 1.30000305176,-0.0999908447266 c 0.0,0.0 -7.60000610352,-7.60000610352 -7.60000610352,-7.60000610352 c 0.0,0.0 -2.69999694824,2.69999694824 -2.69999694824,2.69999694824 Z"
- android:fillColor="#FFFFFFFF"
- android:fillAlpha="1" />
- </group>
- </group>
- <group
- android:name="device"
+ android:name="device_0"
android:translateX="24.14999"
android:translateY="24.25" >
<path
- android:name="device_1"
+ android:name="device_merged"
android:pathData="M -3.5,-20.5 c -1.19999694824,-1.19999694824 -3.10000610352,-1.19999694824 -4.19999694824,0.0 c 0.0,0.0 -12.8000030518,12.6999969482 -12.8000030518,12.6999969482 c -1.19999694824,1.19999694824 -1.19999694824,3.10000610352 0.0,4.19999694824 c 0.0,0.0 24.0,24.0000152588 24.0,24.0000152588 c 1.19999694824,1.19999694824 3.10000610352,1.19999694824 4.19999694824,0.0 c 0.0,0.0 12.6999969482,-12.700012207 12.6999969482,-12.700012207 c 1.20001220703,-1.19999694824 1.20001220703,-3.09999084473 0.0,-4.19999694824 c 0.0,0.0 -23.8999938965,-24.0 -23.8999938965,-24.0 Z M 2.84999084473,15.5500183105 c 0.0,0.0 -18.6000061035,-18.5000457764 -18.6000061035,-18.5000457764 c 0.0,0.0 12.5999908447,-12.8000030518 12.5999908447,-12.8000030518 c 0.0,0.0 18.6000213623,18.5000457764 18.6000213623,18.5000457764 c 0.0,0.0 -12.6000061035,12.8000030518 -12.6000061035,12.8000030518 Z"
- android:fillColor="#FFFFFFFF"
- android:fillAlpha="1" />
+ android:fillColor="#FFFFFFFF" />
+ </group>
+ </group>
+ </group>
+ <group
+ android:name="arrows"
+ android:translateX="24"
+ android:translateY="24" >
+ <group
+ android:name="arrows_pivot"
+ android:translateX="-24.0798"
+ android:translateY="-24.23" >
+ <group
+ android:name="arrows_0"
+ android:translateX="12.2505"
+ android:translateY="37.2145" >
+ <path
+ android:name="bottom_merged"
+ android:pathData="M 20.7395019531,-31.9844970703 c 6.23999023438,2.83999633789 10.6999969482,8.7200012207 11.8399963379,15.7799987793 c 0.119995117188,0.699996948242 0.740005493164,1.2200012207 1.46099853516,1.2200012207 c 0.919998168945,0.0 1.6190032959,-0.84001159668 1.47900390625,-1.74000549316 c -1.75900268555,-10.3800048828 -9.75900268555,-19.1199951172 -22.5800018311,-20.1600036621 c -0.919998168945,-0.0800018310547 -1.43899536133,1.04000854492 -0.800003051758,1.70001220703 c 0.0,0.0 5.12100219727,5.11999511719 5.12100219727,5.11999511719 c 0.378997802734,0.380004882812 0.97900390625,0.380004882812 1.37899780273,0.020004272461 c 0.0,0.0 2.10000610352,-1.94000244141 2.10000610352,-1.94000244141 Z M 2.73950195312,6.01550292969 c -6.26000976562,-2.83999633789 -10.7200012207,-8.76000976562 -11.8399963379,-15.8600006104 c -0.118011474609,-0.667007446289 -0.702011108398,-1.15100097656 -1.38000488281,-1.13999938965 c -0.860000610352,0.0 -1.52000427246,0.759994506836 -1.38000488281,1.61999511719 c 1.54000854492,10.4000091553 9.5,19.2200012207 22.4199981689,20.2799987793 c 0.920013427734,0.0800018310547 1.44100952148,-1.03999328613 0.800003051758,-1.69999694824 c 0.0,0.0 -5.11999511719,-5.11999511719 -5.11999511719,-5.11999511719 c -0.380004882812,-0.376007080078 -0.988998413086,-0.385009765625 -1.38000488281,-0.0200042724609 c 0.0,0.0 -2.11999511719,1.94000244141 -2.11999511719,1.94000244141 Z"
+ android:fillColor="#FFFFFFFF" />
</group>
</group>
</group>
</vector>
+
diff --git a/packages/SystemUI/res/drawable/ic_portrait_from_auto_rotate_animation.xml b/packages/SystemUI/res/drawable/ic_portrait_from_auto_rotate_animation.xml
index 565ef269743d..b8465f4ba6b6 100644
--- a/packages/SystemUI/res/drawable/ic_portrait_from_auto_rotate_animation.xml
+++ b/packages/SystemUI/res/drawable/ic_portrait_from_auto_rotate_animation.xml
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
- Copyright (C) 2014 The Android Open Source Project
+ 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.
@@ -14,21 +14,22 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:drawable="@drawable/ic_portrait_from_auto_rotate" >
+<animated-vector
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:drawable="@drawable/ic_portrait_to_auto_rotate" >
<target
- android:name="arrows"
- android:animation="@anim/ic_portrait_from_auto_rotate_animation_arrows" />
+ android:name="device_0"
+ android:animation="@anim/ic_portrait_to_rotate_device_0_animation" />
<target
- android:name="arrow_top"
- android:animation="@anim/ic_portrait_from_auto_rotate_animation_arrow_top" />
+ android:name="device_merged"
+ android:animation="@anim/ic_portrait_to_rotate_device_merged_animation" />
<target
- android:name="arrow_bottom"
- android:animation="@anim/ic_portrait_from_auto_rotate_animation_arrow_bottom" />
+ android:name="arrows"
+ android:animation="@anim/ic_portrait_to_rotate_arrows_animation" />
<target
- android:name="device"
- android:animation="@anim/ic_portrait_from_auto_rotate_animation_device" />
+ android:name="arrows_0"
+ android:animation="@anim/ic_portrait_to_rotate_arrows_0_animation" />
<target
- android:name="device_1"
- android:animation="@anim/ic_portrait_from_auto_rotate_animation_device_1" />
+ android:name="bottom_merged"
+ android:animation="@anim/ic_portrait_to_rotate_bottom_merged_animation" />
</animated-vector>
diff --git a/packages/SystemUI/res/drawable/ic_portrait_to_auto_rotate.xml b/packages/SystemUI/res/drawable/ic_portrait_to_auto_rotate.xml
index 88bf486be459..0ef15f0582f7 100644
--- a/packages/SystemUI/res/drawable/ic_portrait_to_auto_rotate.xml
+++ b/packages/SystemUI/res/drawable/ic_portrait_to_auto_rotate.xml
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
- Copyright (C) 2014 The Android Open Source Project
+ 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.
@@ -14,53 +14,54 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
+<vector
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:name="ic_rotate_to_portrait"
android:height="48dp"
android:width="48dp"
android:viewportHeight="48"
android:viewportWidth="48"
android:tint="?android:attr/colorControlNormal" >
<group
- android:name="icon"
+ android:name="device"
android:translateX="24"
android:translateY="24" >
<group
- android:name="icon_pivot"
+ android:name="device_pivot"
android:translateX="-24.15"
android:translateY="-24.25" >
<group
- android:name="arrows"
- android:translateX="24.1"
- android:translateY="24.1"
- android:scaleX="0.9"
- android:scaleY="0.9"
- android:rotation="-221" >
- <group
- android:name="arrows_pivot"
- android:translateX="-24.1"
- android:translateY="-24.1" >
- <path
- android:name="arrow_top"
- android:pathData="M 33.1499938965,5.25 c 6.5,3.10000610352 11.1999969482,9.40000915527 11.8999938965,17.0 c 0.0,0.0 3.00001525879,0.0 3.00001525879,0.0 c -1.00001525879,-12.3000030518 -11.3000030518,-22.0 -23.9000091553,-22.0 c -0.399993896484,0.0 -0.899993896484,0.0 -1.30000305176,0.100006103516 c 0.0,0.0 7.60000610352,7.59999084473 7.60000610352,7.59999084473 c 0.0,0.0 2.69999694824,-2.69999694824 2.69999694824,-2.69999694824 Z"
- android:fillColor="#FFFFFFFF"
- android:fillAlpha="0" />
- <path
- android:name="arrow_bottom"
- android:pathData="M 15.1499938965,43.25 c -6.5,-3.09999084473 -11.1999969482,-9.5 -11.8999938965,-17.0 c 0.0,0.0 -3.0,0.0 -3.0,0.0 c 1.0,12.3000030518 11.299987793,22.0 23.8999938965,22.0 c 0.399993896484,0.0 0.899993896484,0.0 1.30000305176,-0.0999908447266 c 0.0,0.0 -7.60000610352,-7.60000610352 -7.60000610352,-7.60000610352 c 0.0,0.0 -2.69999694824,2.69999694824 -2.69999694824,2.69999694824 Z"
- android:fillColor="#FFFFFFFF"
- android:fillAlpha="0" />
- </group>
- </group>
- <group
- android:name="device"
+ android:name="device_0"
android:translateX="24.14999"
android:translateY="24.25"
android:rotation="-135" >
<path
- android:name="device_1"
+ android:name="device_merged"
android:pathData="M -3.34053039551,-22.9980926514 c -1.3207244873,-1.3207244873 -3.46876525879,-1.26383972168 -4.74829101563,0.125762939453 c 0.0,0.0 -14.8512420654,14.7411804199 -14.8512420654,14.7411804199 c -1.39259338379,1.392578125 -1.44947814941,3.54061889648 -0.125762939453,4.74827575684 c 0.0,0.0 26.4143981934,26.4144134521 26.4143981934,26.4144134521 c 1.3207244873,1.3207244873 3.46876525879,1.26382446289 4.74829101562,-0.125762939453 c 0.0,0.0 14.7381896973,-14.7381896973 14.7381896973,-14.7381896973 c 1.392578125,-1.39259338379 1.44947814941,-3.54061889648 0.125762939453,-4.74829101562 c 0.0,0.0 -26.3013458252,-26.417388916 -26.3013458252,-26.417388916 Z M 2.87156677246,16.9857940674 c 0.0,0.0 -19.7573547363,-19.7573699951 -19.7573547363,-19.7573699951 c 0.0,0.0 14.0142059326,-14.2142181396 14.0142059326,-14.2142181396 c 0.0,0.0 19.7573699951,19.7573699951 19.7573699951,19.7573699951 c 0.0,0.0 -14.0142211914,14.2142181396 -14.0142211914,14.2142181396 Z"
+ android:fillColor="#FFFFFFFF" />
+ </group>
+ </group>
+ </group>
+ <group
+ android:name="arrows"
+ android:translateX="24"
+ android:translateY="24"
+ android:rotation="-221" >
+ <group
+ android:name="arrows_pivot"
+ android:translateX="-24.0798"
+ android:translateY="-24.23" >
+ <group
+ android:name="arrows_0"
+ android:translateX="12.2505"
+ android:translateY="37.2145"
+ android:scaleX="0.9"
+ android:scaleY="0.9" >
+ <path
+ android:name="bottom_merged"
+ android:pathData="M 20.7395019531,-31.9844970703 c 6.23999023438,2.83999633789 10.6999969482,8.7200012207 11.8399963379,15.7799987793 c 0.119995117188,0.699996948242 0.740005493164,1.2200012207 1.46099853516,1.2200012207 c 0.919998168945,0.0 1.6190032959,-0.84001159668 1.47900390625,-1.74000549316 c -1.75900268555,-10.3800048828 -9.75900268555,-19.1199951172 -22.5800018311,-20.1600036621 c -0.919998168945,-0.0800018310547 -1.43899536133,1.04000854492 -0.800003051758,1.70001220703 c 0.0,0.0 5.12100219727,5.11999511719 5.12100219727,5.11999511719 c 0.378997802734,0.380004882812 0.97900390625,0.380004882812 1.37899780273,0.020004272461 c 0.0,0.0 2.10000610352,-1.94000244141 2.10000610352,-1.94000244141 Z M 2.73950195312,6.01550292969 c -6.26000976562,-2.83999633789 -10.7200012207,-8.76000976562 -11.8399963379,-15.8600006104 c -0.118011474609,-0.667007446289 -0.702011108398,-1.15100097656 -1.38000488281,-1.13999938965 c -0.860000610352,0.0 -1.52000427246,0.759994506836 -1.38000488281,1.61999511719 c 1.54000854492,10.4000091553 9.5,19.2200012207 22.4199981689,20.2799987793 c 0.920013427734,0.0800018310547 1.44100952148,-1.03999328613 0.800003051758,-1.69999694824 c 0.0,0.0 -5.11999511719,-5.11999511719 -5.11999511719,-5.11999511719 c -0.380004882812,-0.376007080078 -0.988998413086,-0.385009765625 -1.38000488281,-0.0200042724609 c 0.0,0.0 -2.11999511719,1.94000244141 -2.11999511719,1.94000244141 Z"
android:fillColor="#FFFFFFFF"
- android:fillAlpha="1" />
+ android:fillAlpha="0" />
</group>
</group>
</group>
diff --git a/packages/SystemUI/res/drawable/ic_portrait_to_auto_rotate_animation.xml b/packages/SystemUI/res/drawable/ic_portrait_to_auto_rotate_animation.xml
index f75617f04bef..6d3fd372b222 100644
--- a/packages/SystemUI/res/drawable/ic_portrait_to_auto_rotate_animation.xml
+++ b/packages/SystemUI/res/drawable/ic_portrait_to_auto_rotate_animation.xml
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
- Copyright (C) 2014 The Android Open Source Project
+ 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.
@@ -14,21 +14,22 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:drawable="@drawable/ic_portrait_to_auto_rotate" >
+<animated-vector
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:drawable="@drawable/ic_portrait_from_auto_rotate" >
<target
- android:name="arrows"
- android:animation="@anim/ic_portrait_to_auto_rotate_animation_arrows" />
+ android:name="device_0"
+ android:animation="@anim/ic_rotate_to_portrait_device_0_animation" />
<target
- android:name="arrow_top"
- android:animation="@anim/ic_portrait_to_auto_rotate_animation_arrow_top" />
+ android:name="device_merged"
+ android:animation="@anim/ic_rotate_to_portrait_device_merged_animation" />
<target
- android:name="arrow_bottom"
- android:animation="@anim/ic_portrait_to_auto_rotate_animation_arrow_bottom" />
+ android:name="arrows"
+ android:animation="@anim/ic_rotate_to_portrait_arrows_animation" />
<target
- android:name="device"
- android:animation="@anim/ic_portrait_to_auto_rotate_animation_device" />
+ android:name="arrows_0"
+ android:animation="@anim/ic_rotate_to_portrait_arrows_0_animation" />
<target
- android:name="device_1"
- android:animation="@anim/ic_portrait_to_auto_rotate_animation_device_1" />
+ android:name="bottom_merged"
+ android:animation="@anim/ic_rotate_to_portrait_bottom_merged_animation" />
</animated-vector>
diff --git a/packages/SystemUI/res/interpolator/ic_rotate_to_portrait_animation_interpolator_0.xml b/packages/SystemUI/res/interpolator/ic_rotate_to_portrait_animation_interpolator_0.xml
new file mode 100644
index 000000000000..793e7fff11e0
--- /dev/null
+++ b/packages/SystemUI/res/interpolator/ic_rotate_to_portrait_animation_interpolator_0.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ 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.
+-->
+<pathInterpolator
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:pathData="M 0.0,0.0 c 0.4,0.0 0.6,1.0 1.0,1.0" />
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 20531d395fb5..837cb8fcb3ba 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -741,6 +741,8 @@
<string name="quick_settings_done">Done</string>
<!-- QuickSettings: Control panel: Label for connected device. [CHAR LIMIT=NONE] -->
<string name="quick_settings_connected">Connected</string>
+ <!-- QuickSettings: Control panel: Label for connected device, showing remote device battery level. [CHAR LIMIT=NONE] -->
+ <string name="quick_settings_connected_battery_level">Connected, battery <xliff:g id="battery_level_as_percentage">%1$s</xliff:g></string>
<!-- QuickSettings: Control panel: Label for connecting device. [CHAR LIMIT=NONE] -->
<string name="quick_settings_connecting">Connecting...</string>
<!-- QuickSettings: Tethering. [CHAR LIMIT=NONE] -->
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java
index 7d76b9b7c944..68fe9a83c707 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java
@@ -952,6 +952,10 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView
}
}
+ public boolean isDrawingAppearAnimation() {
+ return mDrawingAppearAnimation;
+ }
+
@Override
protected void dispatchDraw(Canvas canvas) {
if (mDrawingAppearAnimation) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
index 429d5ab6f1ab..8046250c9412 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
@@ -756,6 +756,19 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
return getShowingLayout().getVisibleNotificationHeader();
}
+
+ /**
+ * @return the contracted notification header. This can be different from
+ * {@link #getNotificationHeader()} and also {@link #getVisibleNotificationHeader()} and only
+ * returns the contracted version.
+ */
+ public NotificationHeaderView getContractedNotificationHeader() {
+ if (mIsSummaryWithChildren) {
+ return mChildrenContainer.getHeaderView();
+ }
+ return mPrivateLayout.getContractedNotificationHeader();
+ }
+
public void setOnExpandClickListener(OnExpandClickListener onExpandClickListener) {
mOnExpandClickListener = onExpandClickListener;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
index 75704b1612d7..9e059c89ffe1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
@@ -1308,6 +1308,14 @@ public class NotificationContentView extends FrameLayout {
return header;
}
+
+ public NotificationHeaderView getContractedNotificationHeader() {
+ if (mContractedChild != null) {
+ return mContractedWrapper.getNotificationHeader();
+ }
+ return null;
+ }
+
public NotificationHeaderView getVisibleNotificationHeader() {
NotificationViewWrapper wrapper = getVisibleWrapper(mVisibleType);
return wrapper == null ? null : wrapper.getNotificationHeader();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationHeaderUtil.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationHeaderUtil.java
index 7f95d48f36d4..43018174de44 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationHeaderUtil.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationHeaderUtil.java
@@ -267,9 +267,10 @@ public class NotificationHeaderUtil {
if (!mApply) {
return;
}
- NotificationHeaderView header = row.getNotificationHeader();
+ NotificationHeaderView header = row.getContractedNotificationHeader();
if (header == null) {
- mApply = false;
+ // No header found. We still consider this to be the same to avoid weird flickering
+ // when for example showing an undo notification
return;
}
Object childData = mExtractor == null ? null : mExtractor.extractData(row);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
index 7abceaf41c70..a60102854618 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
@@ -505,6 +505,11 @@ public class NotificationShelf extends ActivatableNotificationView implements
iconState.scaleX = newSize / icon.getHeight() / icon.getIconScale();
iconState.scaleY = iconState.scaleX;
iconState.hidden = transitionAmount == 0.0f && !iconState.isAnimating(icon);
+ boolean isAppearing = row.isDrawingAppearAnimation() && !row.isInShelf();
+ if (isAppearing) {
+ iconState.hidden = true;
+ iconState.iconAppearAmount = 0.0f;
+ }
iconState.alpha = alpha;
iconState.yTranslation = iconYTranslation;
if (stayingInShelf) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
index 18dc7e2fa35b..426444158b11 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -613,7 +613,6 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav
if (!updateCurrentView()) {
return;
}
- Log.d(TAG, "reorient", new Throwable());
mDeadZone = (DeadZone) mCurrentView.findViewById(R.id.deadzone);
if (getRootView() instanceof NavigationBarFrame) {
diff --git a/proto/src/metrics_constants.proto b/proto/src/metrics_constants.proto
index c6e3ed5f30ae..c1a1901f762e 100644
--- a/proto/src/metrics_constants.proto
+++ b/proto/src/metrics_constants.proto
@@ -4190,6 +4190,8 @@ message MetricsEvent {
// OS: O DR
FIELD_TIME_ELAPSED_BETWEEN_CHARGE_MS = 1030;
+ // ---- End O-DR1 Constants, all O-DR1 constants go above this line ----
+
// Add new aosp constants above this line.
// END OF AOSP CONSTANTS
}
diff --git a/services/core/Android.mk b/services/core/Android.mk
index 2e962d534e67..3ec78f4cd58a 100644
--- a/services/core/Android.mk
+++ b/services/core/Android.mk
@@ -46,4 +46,16 @@ LOCAL_JACK_FLAGS := \
-D jack.transformations.boost-locked-region-priority.request=com.android.server.am.ActivityManagerService\#boostPriorityForLockedSection,com.android.server.wm.WindowManagerService\#boostPriorityForLockedSection \
-D jack.transformations.boost-locked-region-priority.reset=com.android.server.am.ActivityManagerService\#resetPriorityAfterLockedSection,com.android.server.wm.WindowManagerService\#resetPriorityAfterLockedSection
+LOCAL_JAR_PROCESSOR := lockedregioncodeinjection
+# Use = instead of := to delay evaluation of ${in} and ${out}
+LOCAL_JAR_PROCESSOR_ARGS = \
+ --targets \
+ "Lcom/android/server/am/ActivityManagerService;,Lcom/android/server/wm/WindowHashMap;" \
+ --pre \
+ "com/android/server/am/ActivityManagerService.boostPriorityForLockedSection,com/android/server/wm/WindowManagerService.boostPriorityForLockedSection" \
+ --post \
+ "com/android/server/am/ActivityManagerService.resetPriorityAfterLockedSection,com/android/server/wm/WindowManagerService.resetPriorityAfterLockedSection" \
+ -o ${out} \
+ -i ${in}
+
include $(BUILD_STATIC_JAVA_LIBRARY)
diff --git a/services/core/java/com/android/server/am/ActivityManagerDebugConfig.java b/services/core/java/com/android/server/am/ActivityManagerDebugConfig.java
index f440100b7621..3a9bf1258d12 100644
--- a/services/core/java/com/android/server/am/ActivityManagerDebugConfig.java
+++ b/services/core/java/com/android/server/am/ActivityManagerDebugConfig.java
@@ -91,7 +91,6 @@ class ActivityManagerDebugConfig {
static final boolean DEBUG_URI_PERMISSION = DEBUG_ALL || false;
static final boolean DEBUG_USER_LEAVING = DEBUG_ALL || false;
static final boolean DEBUG_VISIBILITY = DEBUG_ALL || false;
- static final boolean DEBUG_VISIBLE_BEHIND = DEBUG_ALL_ACTIVITIES || false;
static final boolean DEBUG_USAGE_STATS = DEBUG_ALL || false;
static final boolean DEBUG_PERMISSIONS_REVIEW = DEBUG_ALL || false;
static final boolean DEBUG_WHITELISTS = DEBUG_ALL || false;
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 209c6be5dea8..3566c2962727 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -136,7 +136,6 @@ import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_UID_OBSERVE
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_URI_PERMISSION;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_USAGE_STATS;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_VISIBILITY;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_VISIBLE_BEHIND;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_WHITELISTS;
import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_BACKUP;
import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_BROADCAST;
@@ -1500,11 +1499,7 @@ public class ActivityManagerService extends IActivityManager.Stub
boolean mControllerIsAMonkey = false;
String mProfileApp = null;
ProcessRecord mProfileProc = null;
- String mProfileFile;
- ParcelFileDescriptor mProfileFd;
- int mSamplingInterval = 0;
- boolean mAutoStopProfiler = false;
- boolean mStreamingOutput = false;
+ ProfilerInfo mProfilerInfo = null;
int mProfileType = 0;
final ProcessMap<Pair<Long, String>> mMemWatchProcesses = new ProcessMap<>();
String mMemWatchDumpProcName;
@@ -6910,19 +6905,19 @@ public class ActivityManagerService extends IActivityManager.Stub
mWaitForDebugger = mOrigWaitForDebugger;
}
}
- String profileFile = app.instr != null ? app.instr.mProfileFile : null;
- ParcelFileDescriptor profileFd = null;
- int samplingInterval = 0;
- boolean profileAutoStop = false;
- boolean profileStreamingOutput = false;
+
+ ProfilerInfo profilerInfo = null;
+ String agent = null;
if (mProfileApp != null && mProfileApp.equals(processName)) {
mProfileProc = app;
- profileFile = mProfileFile;
- profileFd = mProfileFd;
- samplingInterval = mSamplingInterval;
- profileAutoStop = mAutoStopProfiler;
- profileStreamingOutput = mStreamingOutput;
+ profilerInfo = (mProfilerInfo != null && mProfilerInfo.profileFile != null) ?
+ new ProfilerInfo(mProfilerInfo) : null;
+ agent = profilerInfo.agent;
+ } else if (app.instr != null && app.instr.mProfileFile != null) {
+ profilerInfo = new ProfilerInfo(app.instr.mProfileFile, null, 0, false, false,
+ null);
}
+
boolean enableTrackAllocation = false;
if (mTrackAllocationApp != null && mTrackAllocationApp.equals(processName)) {
enableTrackAllocation = true;
@@ -6946,12 +6941,10 @@ public class ActivityManagerService extends IActivityManager.Stub
+ processName + " with config " + getGlobalConfiguration());
ApplicationInfo appInfo = app.instr != null ? app.instr.mTargetInfo : app.info;
app.compat = compatibilityInfoForPackageLocked(appInfo);
- if (profileFd != null) {
- profileFd = profileFd.dup();
+
+ if (profilerInfo != null && profilerInfo.profileFd != null) {
+ profilerInfo.profileFd = profilerInfo.profileFd.dup();
}
- ProfilerInfo profilerInfo = profileFile == null ? null
- : new ProfilerInfo(profileFile, profileFd, samplingInterval, profileAutoStop,
- profileStreamingOutput);
// We deprecated Build.SERIAL and it is not accessible to
// apps that target the v2 security sandbox. Since access to
@@ -6991,6 +6984,12 @@ public class ActivityManagerService extends IActivityManager.Stub
}
}
+ // If we were asked to attach an agent on startup, do so now, before we're binding
+ // application code.
+ if (agent != null) {
+ thread.attachAgent(agent);
+ }
+
checkTime(startTime, "attachApplicationLocked: immediately before bindApplication");
mStackSupervisor.mActivityMetricsLogger.notifyBindApplication(app);
if (app.instr != null) {
@@ -7126,11 +7125,7 @@ public class ActivityManagerService extends IActivityManager.Stub
mStackSupervisor.activityIdleInternalLocked(token, false /* fromTimeout */,
false /* processPausingActivities */, config);
if (stopProfiling) {
- if ((mProfileProc == r.app) && (mProfileFd != null)) {
- try {
- mProfileFd.close();
- } catch (IOException e) {
- }
+ if ((mProfileProc == r.app) && mProfilerInfo != null) {
clearProfilerLocked();
}
}
@@ -7404,21 +7399,6 @@ public class ActivityManagerService extends IActivityManager.Stub
}
@Override
- public final void backgroundResourcesReleased(IBinder token) {
- final long origId = Binder.clearCallingIdentity();
- try {
- synchronized (this) {
- ActivityStack stack = ActivityRecord.getStackLocked(token);
- if (stack != null) {
- stack.backgroundResourcesReleased();
- }
- }
- } finally {
- Binder.restoreCallingIdentity(origId);
- }
- }
-
- @Override
public final void notifyLaunchTaskBehindComplete(IBinder token) {
mStackSupervisor.scheduleLaunchTaskBehindComplete(token);
}
@@ -12754,18 +12734,16 @@ public class ActivityManagerService extends IActivityManager.Stub
}
}
mProfileApp = processName;
- mProfileFile = profilerInfo.profileFile;
- if (mProfileFd != null) {
- try {
- mProfileFd.close();
- } catch (IOException e) {
+
+ if (mProfilerInfo != null) {
+ if (mProfilerInfo.profileFd != null) {
+ try {
+ mProfilerInfo.profileFd.close();
+ } catch (IOException e) {
+ }
}
- mProfileFd = null;
}
- mProfileFd = profilerInfo.profileFd;
- mSamplingInterval = profilerInfo.samplingInterval;
- mAutoStopProfiler = profilerInfo.autoStopProfiler;
- mStreamingOutput = profilerInfo.streamingOutput;
+ mProfilerInfo = new ProfilerInfo(profilerInfo);
mProfileType = 0;
}
}
@@ -13303,7 +13281,6 @@ public class ActivityManagerService extends IActivityManager.Stub
}
final boolean translucentChanged = r.changeWindowTranslucency(true);
if (translucentChanged) {
- r.getStack().releaseBackgroundResources(r);
mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
}
mWindowManager.setAppFullscreen(token, true);
@@ -13343,38 +13320,6 @@ public class ActivityManagerService extends IActivityManager.Stub
}
@Override
- public boolean requestVisibleBehind(IBinder token, boolean visible) {
- final long origId = Binder.clearCallingIdentity();
- try {
- synchronized (this) {
- final ActivityRecord r = ActivityRecord.isInStackLocked(token);
- if (r != null) {
- return mStackSupervisor.requestVisibleBehindLocked(r, visible);
- }
- }
- return false;
- } finally {
- Binder.restoreCallingIdentity(origId);
- }
- }
-
- @Override
- public boolean isBackgroundVisibleBehind(IBinder token) {
- final long origId = Binder.clearCallingIdentity();
- try {
- synchronized (this) {
- final ActivityStack stack = ActivityRecord.getStackLocked(token);
- final boolean visible = stack == null ? false : stack.hasVisibleBehindActivity();
- if (DEBUG_VISIBLE_BEHIND) Slog.d(TAG_VISIBLE_BEHIND,
- "isBackgroundVisibleBehind: stack=" + stack + " visible=" + visible);
- return visible;
- }
- } finally {
- Binder.restoreCallingIdentity(origId);
- }
- }
-
- @Override
public Bundle getActivityOptions(IBinder token) {
final long origId = Binder.clearCallingIdentity();
try {
@@ -15928,18 +15873,22 @@ public class ActivityManagerService extends IActivityManager.Stub
pw.println(" mTrackAllocationApp=" + mTrackAllocationApp);
}
}
- if (mProfileApp != null || mProfileProc != null || mProfileFile != null
- || mProfileFd != null) {
+ if (mProfileApp != null || mProfileProc != null || (mProfilerInfo != null &&
+ (mProfilerInfo.profileFile != null || mProfilerInfo.profileFd != null))) {
if (dumpPackage == null || dumpPackage.equals(mProfileApp)) {
if (needSep) {
pw.println();
needSep = false;
}
pw.println(" mProfileApp=" + mProfileApp + " mProfileProc=" + mProfileProc);
- pw.println(" mProfileFile=" + mProfileFile + " mProfileFd=" + mProfileFd);
- pw.println(" mSamplingInterval=" + mSamplingInterval + " mAutoStopProfiler="
- + mAutoStopProfiler + " mStreamingOutput=" + mStreamingOutput);
- pw.println(" mProfileType=" + mProfileType);
+ if (mProfilerInfo != null) {
+ pw.println(" mProfileFile=" + mProfilerInfo.profileFile + " mProfileFd=" +
+ mProfilerInfo.profileFd);
+ pw.println(" mSamplingInterval=" + mProfilerInfo.samplingInterval +
+ " mAutoStopProfiler=" + mProfilerInfo.autoStopProfiler +
+ " mStreamingOutput=" + mProfilerInfo.streamingOutput);
+ pw.println(" mProfileType=" + mProfileType);
+ }
}
}
if (mNativeDebuggingApp != null) {
@@ -23258,19 +23207,15 @@ public class ActivityManagerService extends IActivityManager.Stub
}
private void clearProfilerLocked() {
- if (mProfileFd != null) {
+ if (mProfilerInfo !=null && mProfilerInfo.profileFd != null) {
try {
- mProfileFd.close();
+ mProfilerInfo.profileFd.close();
} catch (IOException e) {
}
}
mProfileApp = null;
mProfileProc = null;
- mProfileFile = null;
- mProfileType = 0;
- mAutoStopProfiler = false;
- mStreamingOutput = false;
- mSamplingInterval = 0;
+ mProfilerInfo = null;
}
public boolean profileControl(String process, int userId, boolean start,
@@ -23314,10 +23259,10 @@ public class ActivityManagerService extends IActivityManager.Stub
proc.thread.profilerControl(start, profilerInfo, profileType);
fd = null;
try {
- mProfileFd.close();
+ mProfilerInfo.profileFd.close();
} catch (IOException e) {
}
- mProfileFd = null;
+ mProfilerInfo.profileFd = null;
} else {
stopProfilerLocked(proc, profileType);
if (profilerInfo != null && profilerInfo.profileFd != null) {
@@ -24496,4 +24441,37 @@ public class ActivityManagerService extends IActivityManager.Stub
return mNmi != null;
}
}
+
+ @Override
+ public void setShowWhenLocked(IBinder token, boolean showWhenLocked)
+ throws RemoteException {
+ synchronized (this) {
+ final ActivityRecord r = ActivityRecord.isInStackLocked(token);
+ if (r == null) {
+ return;
+ }
+ final long origId = Binder.clearCallingIdentity();
+ try {
+ r.setShowWhenLocked(showWhenLocked);
+ } finally {
+ Binder.restoreCallingIdentity(origId);
+ }
+ }
+ }
+
+ @Override
+ public void setTurnScreenOn(IBinder token, boolean turnScreenOn) throws RemoteException {
+ synchronized (this) {
+ final ActivityRecord r = ActivityRecord.isInStackLocked(token);
+ if (r == null) {
+ return;
+ }
+ final long origId = Binder.clearCallingIdentity();
+ try {
+ r.setTurnScreenOn(turnScreenOn);
+ } finally {
+ Binder.restoreCallingIdentity(origId);
+ }
+ }
+ }
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index d7683047143a..f0a886dfcb08 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -114,6 +114,7 @@ final class ActivityManagerShellCommand extends ShellCommand {
private int mSamplingInterval;
private boolean mAutoStop;
private boolean mStreaming; // Streaming the profiling output to a file.
+ private String mAgent; // Agent to attach on startup.
private int mDisplayId;
private int mStackId;
private int mTaskId;
@@ -292,6 +293,8 @@ final class ActivityManagerShellCommand extends ShellCommand {
mSamplingInterval = Integer.parseInt(getNextArgRequired());
} else if (opt.equals("--streaming")) {
mStreaming = true;
+ } else if (opt.equals("--attach-agent")) {
+ mAgent = getNextArgRequired();
} else if (opt.equals("-R")) {
mRepeat = Integer.parseInt(getNextArgRequired());
} else if (opt.equals("-S")) {
@@ -368,13 +371,16 @@ final class ActivityManagerShellCommand extends ShellCommand {
ProfilerInfo profilerInfo = null;
- if (mProfileFile != null) {
- ParcelFileDescriptor fd = openOutputFileForSystem(mProfileFile);
- if (fd == null) {
- return 1;
+ if (mProfileFile != null || mAgent != null) {
+ ParcelFileDescriptor fd = null;
+ if (mProfileFile != null) {
+ fd = openOutputFileForSystem(mProfileFile);
+ if (fd == null) {
+ return 1;
+ }
}
profilerInfo = new ProfilerInfo(mProfileFile, fd, mSamplingInterval, mAutoStop,
- mStreaming);
+ mStreaming, mAgent);
}
pw.println("Starting: " + intent);
@@ -747,7 +753,8 @@ final class ActivityManagerShellCommand extends ShellCommand {
if (fd == null) {
return -1;
}
- profilerInfo = new ProfilerInfo(profileFile, fd, mSamplingInterval, false, mStreaming);
+ profilerInfo = new ProfilerInfo(profileFile, fd, mSamplingInterval, false, mStreaming,
+ null);
}
try {
@@ -2490,6 +2497,7 @@ final class ActivityManagerShellCommand extends ShellCommand {
pw.println(" --streaming: stream the profiling output to the specified file");
pw.println(" (use with --start-profiler)");
pw.println(" -P <FILE>: like above, but profiling stops when app goes idle");
+ pw.println(" --attach-agent <agent>: attach the given agent before binding");
pw.println(" -R: repeat the activity launch <COUNT> times. Prior to each repeat,");
pw.println(" the top activity will be finished.");
pw.println(" -S: force stop the target app before starting the activity");
diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java
index c7cac3be5bc9..4b2b6a77deb6 100644
--- a/services/core/java/com/android/server/am/ActivityRecord.java
+++ b/services/core/java/com/android/server/am/ActivityRecord.java
@@ -47,11 +47,13 @@ import static android.content.pm.ActivityInfo.CONFIG_SCREEN_SIZE;
import static android.content.pm.ActivityInfo.CONFIG_SMALLEST_SCREEN_SIZE;
import static android.content.pm.ActivityInfo.CONFIG_UI_MODE;
import static android.content.pm.ActivityInfo.FLAG_ALWAYS_FOCUSABLE;
+import static android.content.pm.ActivityInfo.FLAG_SHOW_WHEN_LOCKED;
import static android.content.pm.ActivityInfo.FLAG_EXCLUDE_FROM_RECENTS;
import static android.content.pm.ActivityInfo.FLAG_IMMERSIVE;
import static android.content.pm.ActivityInfo.FLAG_MULTIPROCESS;
import static android.content.pm.ActivityInfo.FLAG_SHOW_FOR_ALL_USERS;
import static android.content.pm.ActivityInfo.FLAG_STATE_NOT_NEEDED;
+import static android.content.pm.ActivityInfo.FLAG_TURN_SCREEN_ON;
import static android.content.pm.ActivityInfo.LAUNCH_MULTIPLE;
import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_INSTANCE;
import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_TASK;
@@ -344,6 +346,9 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo
// handle calculating override configuration from the bounds.
private final Rect mBounds = new Rect();
+ private boolean mShowWhenLocked;
+ private boolean mTurnScreenOn;
+
/**
* Temp configs used in {@link #ensureActivityConfigurationLocked(int, boolean)}
*/
@@ -904,6 +909,9 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo
requestedVrComponent = (aInfo.requestedVrComponent == null) ?
null : ComponentName.unflattenFromString(aInfo.requestedVrComponent);
+
+ mShowWhenLocked = (aInfo.flags & FLAG_SHOW_WHEN_LOCKED) != 0;
+ mTurnScreenOn = (aInfo.flags & FLAG_TURN_SCREEN_ON) != 0;
}
AppWindowContainerController getWindowContainerController() {
@@ -1258,13 +1266,6 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo
return (info.flags & FLAG_ALWAYS_FOCUSABLE) != 0;
}
- /**
- * @return true if the activity contains windows that have
- * {@link LayoutParams#FLAG_SHOW_WHEN_LOCKED} set
- */
- boolean hasShowWhenLockedWindows() {
- return service.mWindowManager.containsShowWhenLockedWindow(appToken);
- }
/**
* @return true if the activity contains windows that have
@@ -1275,20 +1276,16 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo
}
void makeFinishingLocked() {
- if (!finishing) {
- final ActivityStack stack = getStack();
- if (stack != null && this == stack.getVisibleBehindActivity()) {
- // A finishing activity should not remain as visible in the background
- mStackSupervisor.requestVisibleBehindLocked(this, false);
- }
- finishing = true;
- if (stopped) {
- clearOptionsLocked();
- }
+ if (finishing) {
+ return;
+ }
+ finishing = true;
+ if (stopped) {
+ clearOptionsLocked();
+ }
- if (service != null) {
- service.mTaskChangeNotificationController.notifyTaskStackChanged();
- }
+ if (service != null) {
+ service.mTaskChangeNotificationController.notifyTaskStackChanged();
}
}
@@ -1345,11 +1342,7 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo
intent, getUriPermissionsLocked(), userId);
final ReferrerIntent rintent = new ReferrerIntent(intent, referrer);
boolean unsent = true;
- final ActivityStack stack = getStack();
- final boolean isTopActivityInStack =
- stack != null && stack.topRunningActivityLocked() == this;
- final boolean isTopActivityWhileSleeping =
- service.isSleepingLocked() && isTopActivityInStack;
+ final boolean isTopActivityWhileSleeping = service.isSleepingLocked() && isTopRunningActivity();
// We want to immediately deliver the intent to the activity if:
// - It is currently resumed or paused. i.e. it is currently visible to the user and we want
@@ -1621,20 +1614,12 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo
*
* @see {@link ActivityStack#checkKeyguardVisibility}
*/
- boolean shouldBeVisibleIgnoringKeyguard(boolean behindTranslucentActivity,
- boolean stackVisibleBehind, ActivityRecord visibleBehind,
- boolean behindFullscreenActivity) {
+ boolean shouldBeVisibleIgnoringKeyguard(boolean behindFullscreenActivity) {
if (!okToShowLocked()) {
return false;
}
- // mLaunchingBehind: Activities launching behind are at the back of the task stack
- // but must be drawn initially for the animation as though they were visible.
- final boolean activityVisibleBehind =
- (behindTranslucentActivity || stackVisibleBehind) && visibleBehind == this;
-
- boolean isVisible =
- !behindFullscreenActivity || mLaunchTaskBehind || activityVisibleBehind;
+ boolean isVisible = !behindFullscreenActivity || mLaunchTaskBehind;
if (service.mSupportsLeanbackOnly && isVisible && isRecentsActivity()) {
// On devices that support leanback only (Android TV), Recents activity can only be
@@ -1746,11 +1731,14 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo
returningOptions = null;
- if (stack.getVisibleBehindActivity() == this) {
- // When resuming an activity, require it to call requestVisibleBehind() again.
- stack.setVisibleBehindActivity(null /* ActivityRecord */);
+ if (canTurnScreenOn()) {
+ mStackSupervisor.wakeUp("turnScreenOnFlag");
+ } else {
+ // If the screen is going to turn on because the caller explicitly requested it and
+ // the keyguard is not showing don't attempt to sleep. Otherwise the Activity will
+ // pause and then resume again later, which will result in a double life-cycle event.
+ mStackSupervisor.checkReadyForSleepLocked();
}
- mStackSupervisor.checkReadyForSleepLocked();
}
final void activityStoppedLocked(Bundle newIcicle, PersistableBundle newPersistentState,
@@ -1783,9 +1771,6 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo
mWindowContainerController.notifyAppStopped();
- if (stack.getVisibleBehindActivity() == this) {
- mStackSupervisor.requestVisibleBehindLocked(this, false /* visible */);
- }
if (finishing) {
clearOptionsLocked();
} else {
@@ -2817,6 +2802,44 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo
return info.applicationInfo.uid;
}
+ void setShowWhenLocked(boolean showWhenLocked) {
+ mShowWhenLocked = showWhenLocked;
+ }
+
+ /**
+ * @return true if the activity contains windows that have
+ * {@link LayoutParams#FLAG_SHOW_WHEN_LOCKED} set or if the activity has set
+ * {@link #mShowWhenLocked}.
+ */
+ boolean canShowWhenLocked() {
+ return mShowWhenLocked || service.mWindowManager.containsShowWhenLockedWindow(appToken);
+ }
+
+ void setTurnScreenOn(boolean turnScreenOn) {
+ mTurnScreenOn = turnScreenOn;
+ }
+
+ /**
+ * Determines whether this ActivityRecord can turn the screen on. It checks whether the flag
+ * {@link #mTurnScreenOn} is set and checks whether the ActivityRecord should be visible
+ * depending on Keyguard state
+ *
+ * @return true if the screen can be turned on, false otherwise.
+ */
+ boolean canTurnScreenOn() {
+ final ActivityStack stack = getStack();
+ return mTurnScreenOn && stack != null &&
+ stack.checkKeyguardVisibility(this, true /* shouldBeVisible */, true /* isTop */);
+ }
+
+ boolean getTurnScreenOnFlag() {
+ return mTurnScreenOn;
+ }
+
+ boolean isTopRunningActivity() {
+ return mStackSupervisor.topRunningActivityLocked() == this;
+ }
+
@Override
public String toString() {
if (stringName != null) {
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index 9cde98598987..77e438a7c610 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -230,9 +230,6 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
static final int STACK_INVISIBLE = 0;
// Stack is considered visible
static final int STACK_VISIBLE = 1;
- // Stack is considered visible, but only becuase it has activity that is visible behind other
- // activities and there is a specific combination of stacks.
- static final int STACK_VISIBLE_ACTIVITY_BEHIND = 2;
@VisibleForTesting
/* The various modes for the method {@link #removeTask}. */
@@ -365,8 +362,6 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
static final int STOP_TIMEOUT_MSG = ActivityManagerService.FIRST_ACTIVITY_STACK_MSG + 4;
static final int DESTROY_ACTIVITIES_MSG = ActivityManagerService.FIRST_ACTIVITY_STACK_MSG + 5;
static final int TRANSLUCENT_TIMEOUT_MSG = ActivityManagerService.FIRST_ACTIVITY_STACK_MSG + 6;
- static final int RELEASE_BACKGROUND_RESOURCES_TIMEOUT_MSG =
- ActivityManagerService.FIRST_ACTIVITY_STACK_MSG + 7;
private static class ScheduleDestroyArgs {
final ProcessRecord mOwner;
@@ -440,15 +435,6 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
notifyActivityDrawnLocked(null);
}
} break;
- case RELEASE_BACKGROUND_RESOURCES_TIMEOUT_MSG: {
- synchronized (mService) {
- final ActivityRecord r = getVisibleBehindActivity();
- Slog.e(TAG, "Timeout waiting for cancelVisibleBehind player=" + r);
- if (r != null) {
- mService.killAppAtUsersRequest(r.app, null);
- }
- }
- } break;
}
}
}
@@ -1195,15 +1181,6 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Sleep still waiting to pause " + mPausingActivity);
return true;
}
-
- if (hasVisibleBehindActivity()) {
- // Stop visible behind activity before going to sleep.
- final ActivityRecord r = getVisibleBehindActivity();
- mStackSupervisor.mStoppingActivities.add(r);
- if (DEBUG_STATES) Slog.v(TAG_STATES, "Sleep still waiting to stop visible behind " + r);
- return true;
- }
-
return false;
}
@@ -1422,8 +1399,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
// We can't clobber it, because the stop confirmation will not be handled.
// We don't need to schedule another stop, we only need to let it happen.
prev.state = STOPPING;
- } else if ((!prev.visible && !hasVisibleBehindActivity())
- || mService.isSleepingOrShuttingDownLocked()) {
+ } else if (!prev.visible || mService.isSleepingOrShuttingDownLocked()) {
// Clear out any deferred client hide we might currently have.
prev.setDeferHidingClient(false);
// If we were visible then resumeTopActivities will release resources before
@@ -1628,8 +1604,8 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
}
/**
- * Returns what the stack visibility should be: {@link #STACK_INVISIBLE}, {@link #STACK_VISIBLE}
- * or {@link #STACK_VISIBLE_ACTIVITY_BEHIND}.
+ * Returns what the stack visibility should be: {@link #STACK_INVISIBLE} or
+ * {@link #STACK_VISIBLE}.
*
* @param starting The currently starting activity or null if there is none.
*/
@@ -1654,14 +1630,6 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
final ActivityStack topStack = getTopStackOnDisplay();
final int topStackId = topStack.mStackId;
- if (StackId.isBackdropToTranslucentActivity(mStackId)
- && hasVisibleBehindActivity() && StackId.isHomeOrRecentsStack(topStackId)
- && (topStack.topActivity() == null || !topStack.topActivity().fullscreen)) {
- // The fullscreen or assistant stack should be visible if it has a visible behind
- // activity behind the home or recents stack that is translucent.
- return STACK_VISIBLE_ACTIVITY_BEHIND;
- }
-
if (mStackId == DOCKED_STACK_ID) {
// If the assistant stack is focused and translucent, then the docked stack is always
// visible
@@ -1780,12 +1748,10 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
boolean aboveTop = top != null;
final int stackVisibility = shouldBeVisible(starting);
final boolean stackInvisible = stackVisibility != STACK_VISIBLE;
- final boolean stackVisibleBehind = stackVisibility == STACK_VISIBLE_ACTIVITY_BEHIND;
boolean behindFullscreenActivity = stackInvisible;
boolean resumeNextActivity = mStackSupervisor.isFocusedStack(this)
&& (isInStackLocked(starting) == null);
boolean behindTranslucentActivity = false;
- final ActivityRecord visibleBehind = getVisibleBehindActivity();
for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
final TaskRecord task = mTaskHistory.get(taskNdx);
final ArrayList<ActivityRecord> activities = task.mActivities;
@@ -1810,7 +1776,6 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
// Check whether activity should be visible without Keyguard influence
final boolean visibleIgnoringKeyguard = r.shouldBeVisibleIgnoringKeyguard(
- behindTranslucentActivity, stackVisibleBehind, visibleBehind,
behindFullscreenActivity);
r.visibleIgnoringKeyguard = visibleIgnoringKeyguard;
@@ -1862,7 +1827,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
+ stackInvisible + " behindFullscreenActivity="
+ behindFullscreenActivity + " mLaunchTaskBehind="
+ r.mLaunchTaskBehind);
- makeInvisible(r, visibleBehind);
+ makeInvisible(r);
}
}
if (mStackId == FREEFORM_WORKSPACE_STACK_ID) {
@@ -1942,7 +1907,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
final boolean isInPinnedStack = r.getStack().getStackId() == PINNED_STACK_ID;
final boolean keyguardShowing = mStackSupervisor.mKeyguardController.isKeyguardShowing();
final boolean keyguardLocked = mStackSupervisor.mKeyguardController.isKeyguardLocked();
- final boolean showWhenLocked = r.hasShowWhenLockedWindows() && !isInPinnedStack;
+ final boolean showWhenLocked = r.canShowWhenLocked() && !isInPinnedStack;
final boolean dismissKeyguard = r.hasDismissKeyguardWindows();
if (shouldBeVisible) {
if (dismissKeyguard && mTopDismissingKeyguardActivity == null) {
@@ -2028,7 +1993,8 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
return false;
}
- private void makeInvisible(ActivityRecord r, ActivityRecord visibleBehind) {
+ // TODO: Should probably be moved into ActivityRecord.
+ private void makeInvisible(ActivityRecord r) {
if (!r.visible) {
if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY, "Already invisible: " + r);
return;
@@ -2064,14 +2030,8 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
case RESUMED:
case PAUSING:
case PAUSED:
- // This case created for transitioning activities from
- // translucent to opaque {@link Activity#convertToOpaque}.
- if (visibleBehind == r) {
- releaseBackgroundResources(r);
- } else {
- addToStopping(r, true /* scheduleIdle */,
- canEnterPictureInPicture /* idleDelayed */);
- }
+ addToStopping(r, true /* scheduleIdle */,
+ canEnterPictureInPicture /* idleDelayed */);
break;
default:
@@ -2216,12 +2176,18 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
} finally {
mStackSupervisor.inResumeTopActivity = false;
}
+
// When resuming the top activity, it may be necessary to pause the top activity (for
// example, returning to the lock screen. We suppress the normal pause logic in
// {@link #resumeTopActivityUncheckedLocked}, since the top activity is resumed at the end.
// We call the {@link ActivityStackSupervisor#checkReadyForSleepLocked} again here to ensure
- // any necessary pause logic occurs.
- mStackSupervisor.checkReadyForSleepLocked();
+ // any necessary pause logic occurs. In the case where the Activity will be shown regardless
+ // of the lock screen, the call to {@link ActivityStackSupervisor#checkReadyForSleepLocked}
+ // is skipped.
+ final ActivityRecord next = topRunningActivityLocked(true /* focusableOnly */);
+ if (next == null || !next.canTurnScreenOn()) {
+ mStackSupervisor.checkReadyForSleepLocked();
+ }
return result;
}
@@ -3991,10 +3957,6 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
// Get rid of any pending idle timeouts.
removeTimeoutsForActivityLocked(r);
- if (getVisibleBehindActivity() == r) {
- mStackSupervisor.requestVisibleBehindLocked(r, false);
- }
-
// Clean-up activities are no longer relaunching (e.g. app process died). Notify window
// manager so it can update its bookkeeping.
mWindowManager.notifyAppRelaunchesCleared(r.appToken);
@@ -4298,56 +4260,6 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
}
}
- void releaseBackgroundResources(ActivityRecord r) {
- if (hasVisibleBehindActivity() &&
- !mHandler.hasMessages(RELEASE_BACKGROUND_RESOURCES_TIMEOUT_MSG)) {
- if (r == topRunningActivityLocked()
- && shouldBeVisible(null) == STACK_VISIBLE) {
- // Don't release the top activity if it has requested to run behind the next
- // activity and the stack is currently visible.
- return;
- }
- if (DEBUG_STATES) Slog.d(TAG_STATES, "releaseBackgroundResources activtyDisplay=" +
- mActivityContainer.mActivityDisplay + " visibleBehind=" + r + " app=" + r.app +
- " thread=" + r.app.thread);
- if (r != null && r.app != null && r.app.thread != null) {
- try {
- r.app.thread.scheduleCancelVisibleBehind(r.appToken);
- } catch (RemoteException e) {
- }
- mHandler.sendEmptyMessageDelayed(RELEASE_BACKGROUND_RESOURCES_TIMEOUT_MSG, 500);
- } else {
- Slog.e(TAG, "releaseBackgroundResources: activity " + r + " no longer running");
- backgroundResourcesReleased();
- }
- }
- }
-
- final void backgroundResourcesReleased() {
- mHandler.removeMessages(RELEASE_BACKGROUND_RESOURCES_TIMEOUT_MSG);
- final ActivityRecord r = getVisibleBehindActivity();
- if (r != null) {
- mStackSupervisor.mStoppingActivities.add(r);
- setVisibleBehindActivity(null);
- mStackSupervisor.scheduleIdleTimeoutLocked(null);
- }
- mStackSupervisor.resumeFocusedStackTopActivityLocked();
- }
-
- boolean hasVisibleBehindActivity() {
- return isAttached() && mActivityContainer.mActivityDisplay.hasVisibleBehindActivity();
- }
-
- void setVisibleBehindActivity(ActivityRecord r) {
- if (isAttached()) {
- mActivityContainer.mActivityDisplay.setVisibleBehindActivity(r);
- }
- }
-
- ActivityRecord getVisibleBehindActivity() {
- return isAttached() ? mActivityContainer.mActivityDisplay.mVisibleBehindActivity : null;
- }
-
private void removeHistoryRecordsForAppLocked(ArrayList<ActivityRecord> list,
ProcessRecord app, String listName) {
int i = list.size();
@@ -4609,28 +4521,6 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
if (DEBUG_TRANSITION) Slog.v(TAG_TRANSITION, "Prepare to back transition: task=" + taskId);
- if (mStackId == HOME_STACK_ID && topTask().isHomeTask()) {
- // For the case where we are moving the home task back and there is an activity visible
- // behind it on the fullscreen or assistant stack, we want to move the focus to the
- // visible behind activity to maintain order with what the user is seeing.
- ActivityRecord visibleBehind = null;
- final ActivityStack fullscreenStack =
- mStackSupervisor.getStack(FULLSCREEN_WORKSPACE_STACK_ID);
- final ActivityStack assistantStack =
- mStackSupervisor.getStack(ASSISTANT_STACK_ID);
- if (fullscreenStack != null && fullscreenStack.hasVisibleBehindActivity()) {
- visibleBehind = fullscreenStack.getVisibleBehindActivity();
- } else if (assistantStack != null && assistantStack.hasVisibleBehindActivity()) {
- visibleBehind = assistantStack.getVisibleBehindActivity();
- }
- if (visibleBehind != null) {
- mStackSupervisor.moveFocusableActivityStackToFrontLocked(visibleBehind,
- "moveTaskToBack");
- mStackSupervisor.resumeFocusedStackTopActivityLocked();
- return true;
- }
- }
-
boolean prevIsHome = false;
// If true, we should resume the home activity next if the task we are moving to the
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index e0f2a751604a..90d9149a2f66 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -40,6 +40,7 @@ import static android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK;
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.os.Process.SYSTEM_UID;
+import static android.os.PowerManager.PARTIAL_WAKE_LOCK;
import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.Display.FLAG_PRIVATE;
@@ -60,7 +61,6 @@ import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_STACK;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_STATES;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_SWITCH;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_TASKS;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_VISIBLE_BEHIND;
import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_CONTAINERS;
import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_FOCUS;
import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_IDLE;
@@ -557,6 +557,9 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
final KeyguardController mKeyguardController;
+ private PowerManager mPowerManager;
+ private int mDeferResumeCount;
+
/**
* Description of a request to start a new activity, which has been held
* due to app switches being disabled.
@@ -605,9 +608,10 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
* initialized. So we initialize our wakelocks afterwards.
*/
void initPowerManagement() {
- PowerManager pm = (PowerManager)mService.mContext.getSystemService(Context.POWER_SERVICE);
- mGoingToSleep = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "ActivityManager-Sleep");
- mLaunchingActivity = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "*launch*");
+ mPowerManager = (PowerManager)mService.mContext.getSystemService(Context.POWER_SERVICE);
+ mGoingToSleep = mPowerManager
+ .newWakeLock(PARTIAL_WAKE_LOCK, "ActivityManager-Sleep");
+ mLaunchingActivity = mPowerManager.newWakeLock(PARTIAL_WAKE_LOCK, "*launch*");
mLaunchingActivity.setReferenceCounted(false);
}
@@ -1335,184 +1339,187 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
return false;
}
- r.startFreezingScreenLocked(app, 0);
- if (r.getStack().checkKeyguardVisibility(r, true /* shouldBeVisible */, true /* isTop */)) {
- // We only set the visibility to true if the activity is allowed to be visible based on
- // keyguard state. This avoids setting this into motion in window manager that is later
- // cancelled due to later calls to ensure visible activities that set visibility back to
- // false.
- r.setVisibility(true);
- }
+ final TaskRecord task = r.getTask();
+ final ActivityStack stack = task.getStack();
- // schedule launch ticks to collect information about slow apps.
- r.startLaunchTickingLocked();
+ beginDeferResume();
- // Have the window manager re-evaluate the orientation of the screen based on the new
- // activity order. Note that as a result of this, it can call back into the activity
- // manager with a new orientation. We don't care about that, because the activity is not
- // currently running so we are just restarting it anyway.
- if (checkConfig) {
- final int displayId = r.getDisplayId();
- final Configuration config = mWindowManager.updateOrientationFromAppTokens(
- getDisplayOverrideConfiguration(displayId),
- r.mayFreezeScreenLocked(app) ? r.appToken : null, displayId);
- // Deferring resume here because we're going to launch new activity shortly.
- // We don't want to perform a redundant launch of the same record while ensuring
- // configurations and trying to resume top activity of focused stack.
- mService.updateDisplayOverrideConfigurationLocked(config, r, true /* deferResume */,
- displayId);
- }
+ try {
+ r.startFreezingScreenLocked(app, 0);
- if (mKeyguardController.isKeyguardLocked()) {
- r.notifyUnknownVisibilityLaunched();
- }
- final int applicationInfoUid =
- (r.info.applicationInfo != null) ? r.info.applicationInfo.uid : -1;
- if ((r.userId != app.userId) || (r.appInfo.uid != applicationInfoUid)) {
- Slog.wtf(TAG,
- "User ID for activity changing for " + r
- + " appInfo.uid=" + r.appInfo.uid
- + " info.ai.uid=" + applicationInfoUid
- + " old=" + r.app + " new=" + app);
- }
+ // schedule launch ticks to collect information about slow apps.
+ r.startLaunchTickingLocked();
- r.app = app;
- app.waitingToKill = null;
- r.launchCount++;
- r.lastLaunchTime = SystemClock.uptimeMillis();
+ r.app = app;
- if (DEBUG_ALL) Slog.v(TAG, "Launching: " + r);
+ // Have the window manager re-evaluate the orientation of the screen based on the new
+ // activity order. Note that as a result of this, it can call back into the activity
+ // manager with a new orientation. We don't care about that, because the activity is
+ // not currently running so we are just restarting it anyway.
+ if (checkConfig) {
+ final int displayId = r.getDisplayId();
+ final Configuration config = mWindowManager.updateOrientationFromAppTokens(
+ getDisplayOverrideConfiguration(displayId),
+ r.mayFreezeScreenLocked(app) ? r.appToken : null, displayId);
+ // Deferring resume here because we're going to launch new activity shortly.
+ // We don't want to perform a redundant launch of the same record while ensuring
+ // configurations and trying to resume top activity of focused stack.
+ mService.updateDisplayOverrideConfigurationLocked(config, r, true /* deferResume */,
+ displayId);
+ }
- int idx = app.activities.indexOf(r);
- if (idx < 0) {
- app.activities.add(r);
- }
- mService.updateLruProcessLocked(app, true, null);
- mService.updateOomAdjLocked();
+ if (r.getStack().checkKeyguardVisibility(r, true /* shouldBeVisible */,
+ true /* isTop */)) {
+ // We only set the visibility to true if the activity is allowed to be visible
+ // based on
+ // keyguard state. This avoids setting this into motion in window manager that is
+ // later cancelled due to later calls to ensure visible activities that set
+ // visibility back to false.
+ r.setVisibility(true);
+ }
- final TaskRecord task = r.getTask();
- if (task.mLockTaskAuth == LOCK_TASK_AUTH_LAUNCHABLE ||
- task.mLockTaskAuth == LOCK_TASK_AUTH_LAUNCHABLE_PRIV) {
- setLockTaskModeLocked(task, LOCK_TASK_MODE_LOCKED, "mLockTaskAuth==LAUNCHABLE", false);
- }
+ if (mKeyguardController.isKeyguardLocked()) {
+ r.notifyUnknownVisibilityLaunched();
+ }
+ final int applicationInfoUid =
+ (r.info.applicationInfo != null) ? r.info.applicationInfo.uid : -1;
+ if ((r.userId != app.userId) || (r.appInfo.uid != applicationInfoUid)) {
+ Slog.wtf(TAG,
+ "User ID for activity changing for " + r
+ + " appInfo.uid=" + r.appInfo.uid
+ + " info.ai.uid=" + applicationInfoUid
+ + " old=" + r.app + " new=" + app);
+ }
- final ActivityStack stack = task.getStack();
- try {
- if (app.thread == null) {
- throw new RemoteException();
- }
- List<ResultInfo> results = null;
- List<ReferrerIntent> newIntents = null;
- if (andResume) {
- // We don't need to deliver new intents and/or set results if activity is going
- // to pause immediately after launch.
- results = r.results;
- newIntents = r.newIntents;
- }
- if (DEBUG_SWITCH) Slog.v(TAG_SWITCH,
- "Launching: " + r + " icicle=" + r.icicle + " with results=" + results
- + " newIntents=" + newIntents + " andResume=" + andResume);
- EventLog.writeEvent(EventLogTags.AM_RESTART_ACTIVITY, r.userId,
- System.identityHashCode(r), task.taskId, r.shortComponentName);
- if (r.isHomeActivity()) {
- // Home process is the root process of the task.
- mService.mHomeProcess = task.mActivities.get(0).app;
- }
- mService.notifyPackageUse(r.intent.getComponent().getPackageName(),
- PackageManager.NOTIFY_PACKAGE_USE_ACTIVITY);
- r.sleeping = false;
- r.forceNewConfig = false;
- mService.showUnsupportedZoomDialogIfNeededLocked(r);
- mService.showAskCompatModeDialogLocked(r);
- r.compat = mService.compatibilityInfoForPackageLocked(r.info.applicationInfo);
- ProfilerInfo profilerInfo = null;
- if (mService.mProfileApp != null && mService.mProfileApp.equals(app.processName)) {
- if (mService.mProfileProc == null || mService.mProfileProc == app) {
- mService.mProfileProc = app;
- final String profileFile = mService.mProfileFile;
- if (profileFile != null) {
- ParcelFileDescriptor profileFd = mService.mProfileFd;
- if (profileFd != null) {
- try {
- profileFd = profileFd.dup();
- } catch (IOException e) {
- if (profileFd != null) {
- try {
- profileFd.close();
- } catch (IOException o) {
- }
- profileFd = null;
+ app.waitingToKill = null;
+ r.launchCount++;
+ r.lastLaunchTime = SystemClock.uptimeMillis();
+
+ if (DEBUG_ALL) Slog.v(TAG, "Launching: " + r);
+
+ int idx = app.activities.indexOf(r);
+ if (idx < 0) {
+ app.activities.add(r);
+ }
+ mService.updateLruProcessLocked(app, true, null);
+ mService.updateOomAdjLocked();
+
+ if (task.mLockTaskAuth == LOCK_TASK_AUTH_LAUNCHABLE ||
+ task.mLockTaskAuth == LOCK_TASK_AUTH_LAUNCHABLE_PRIV) {
+ setLockTaskModeLocked(task, LOCK_TASK_MODE_LOCKED, "mLockTaskAuth==LAUNCHABLE",
+ false);
+ }
+
+ try {
+ if (app.thread == null) {
+ throw new RemoteException();
+ }
+ List<ResultInfo> results = null;
+ List<ReferrerIntent> newIntents = null;
+ if (andResume) {
+ // We don't need to deliver new intents and/or set results if activity is going
+ // to pause immediately after launch.
+ results = r.results;
+ newIntents = r.newIntents;
+ }
+ if (DEBUG_SWITCH) Slog.v(TAG_SWITCH,
+ "Launching: " + r + " icicle=" + r.icicle + " with results=" + results
+ + " newIntents=" + newIntents + " andResume=" + andResume);
+ EventLog.writeEvent(EventLogTags.AM_RESTART_ACTIVITY, r.userId,
+ System.identityHashCode(r), task.taskId, r.shortComponentName);
+ if (r.isHomeActivity()) {
+ // Home process is the root process of the task.
+ mService.mHomeProcess = task.mActivities.get(0).app;
+ }
+ mService.notifyPackageUse(r.intent.getComponent().getPackageName(),
+ PackageManager.NOTIFY_PACKAGE_USE_ACTIVITY);
+ r.sleeping = false;
+ r.forceNewConfig = false;
+ mService.showUnsupportedZoomDialogIfNeededLocked(r);
+ mService.showAskCompatModeDialogLocked(r);
+ r.compat = mService.compatibilityInfoForPackageLocked(r.info.applicationInfo);
+ ProfilerInfo profilerInfo = null;
+ if (mService.mProfileApp != null && mService.mProfileApp.equals(app.processName)) {
+ if (mService.mProfileProc == null || mService.mProfileProc == app) {
+ mService.mProfileProc = app;
+ ProfilerInfo profilerInfoSvc = mService.mProfilerInfo;
+ if (profilerInfoSvc != null && profilerInfoSvc.profileFile != null) {
+ if (profilerInfoSvc.profileFd != null) {
+ try {
+ profilerInfoSvc.profileFd = profilerInfoSvc.profileFd.dup();
+ } catch (IOException e) {
+ profilerInfoSvc.closeFd();
}
}
- }
- profilerInfo = new ProfilerInfo(profileFile, profileFd,
- mService.mSamplingInterval, mService.mAutoStopProfiler,
- mService.mStreamingOutput);
+ profilerInfo = new ProfilerInfo(profilerInfoSvc);
+ }
}
}
- }
- app.hasShownUi = true;
- app.pendingUiClean = true;
- app.forceProcessStateUpTo(mService.mTopProcessState);
- // Because we could be starting an Activity in the system process this may not go across
- // a Binder interface which would create a new Configuration. Consequently we have to
- // always create a new Configuration here.
-
- final MergedConfiguration mergedConfiguration = new MergedConfiguration(
- mService.getGlobalConfiguration(), r.getMergedOverrideConfiguration());
- r.setLastReportedConfiguration(mergedConfiguration);
-
- logIfTransactionTooLarge(r.intent, r.icicle);
- app.thread.scheduleLaunchActivity(new Intent(r.intent), r.appToken,
- System.identityHashCode(r), r.info,
- // TODO: Have this take the merged configuration instead of separate global and
- // override configs.
- mergedConfiguration.getGlobalConfiguration(),
- mergedConfiguration.getOverrideConfiguration(), r.compat,
- r.launchedFromPackage, task.voiceInteractor, app.repProcState, r.icicle,
- r.persistentState, results, newIntents, !andResume,
- mService.isNextTransitionForward(), profilerInfo);
-
- if ((app.info.privateFlags&ApplicationInfo.PRIVATE_FLAG_CANT_SAVE_STATE) != 0) {
- // This may be a heavy-weight process! Note that the package
- // manager will ensure that only activity can run in the main
- // process of the .apk, which is the only thing that will be
- // considered heavy-weight.
- if (app.processName.equals(app.info.packageName)) {
- if (mService.mHeavyWeightProcess != null
- && mService.mHeavyWeightProcess != app) {
- Slog.w(TAG, "Starting new heavy weight process " + app
- + " when already running "
- + mService.mHeavyWeightProcess);
+ app.hasShownUi = true;
+ app.pendingUiClean = true;
+ app.forceProcessStateUpTo(mService.mTopProcessState);
+ // Because we could be starting an Activity in the system process this may not go
+ // across a Binder interface which would create a new Configuration. Consequently
+ // we have to always create a new Configuration here.
+
+ final MergedConfiguration mergedConfiguration = new MergedConfiguration(
+ mService.getGlobalConfiguration(), r.getMergedOverrideConfiguration());
+ r.setLastReportedConfiguration(mergedConfiguration);
+
+ logIfTransactionTooLarge(r.intent, r.icicle);
+ app.thread.scheduleLaunchActivity(new Intent(r.intent), r.appToken,
+ System.identityHashCode(r), r.info,
+ // TODO: Have this take the merged configuration instead of separate global
+ // and override configs.
+ mergedConfiguration.getGlobalConfiguration(),
+ mergedConfiguration.getOverrideConfiguration(), r.compat,
+ r.launchedFromPackage, task.voiceInteractor, app.repProcState, r.icicle,
+ r.persistentState, results, newIntents, !andResume,
+ mService.isNextTransitionForward(), profilerInfo);
+
+ if ((app.info.privateFlags & ApplicationInfo.PRIVATE_FLAG_CANT_SAVE_STATE) != 0) {
+ // This may be a heavy-weight process! Note that the package
+ // manager will ensure that only activity can run in the main
+ // process of the .apk, which is the only thing that will be
+ // considered heavy-weight.
+ if (app.processName.equals(app.info.packageName)) {
+ if (mService.mHeavyWeightProcess != null
+ && mService.mHeavyWeightProcess != app) {
+ Slog.w(TAG, "Starting new heavy weight process " + app
+ + " when already running "
+ + mService.mHeavyWeightProcess);
+ }
+ mService.mHeavyWeightProcess = app;
+ Message msg = mService.mHandler.obtainMessage(
+ ActivityManagerService.POST_HEAVY_NOTIFICATION_MSG);
+ msg.obj = r;
+ mService.mHandler.sendMessage(msg);
}
- mService.mHeavyWeightProcess = app;
- Message msg = mService.mHandler.obtainMessage(
- ActivityManagerService.POST_HEAVY_NOTIFICATION_MSG);
- msg.obj = r;
- mService.mHandler.sendMessage(msg);
}
- }
- } catch (RemoteException e) {
- if (r.launchFailed) {
- // This is the second time we failed -- finish activity
- // and give up.
- Slog.e(TAG, "Second failure launching "
- + r.intent.getComponent().flattenToShortString()
- + ", giving up", e);
- mService.appDiedLocked(app);
- stack.requestFinishActivityLocked(r.appToken, Activity.RESULT_CANCELED, null,
- "2nd-crash", false);
- return false;
- }
+ } catch (RemoteException e) {
+ if (r.launchFailed) {
+ // This is the second time we failed -- finish activity
+ // and give up.
+ Slog.e(TAG, "Second failure launching "
+ + r.intent.getComponent().flattenToShortString()
+ + ", giving up", e);
+ mService.appDiedLocked(app);
+ stack.requestFinishActivityLocked(r.appToken, Activity.RESULT_CANCELED, null,
+ "2nd-crash", false);
+ return false;
+ }
- // This is the first time we failed -- restart process and
- // retry.
- r.launchFailed = true;
- app.activities.remove(r);
- throw e;
+ // This is the first time we failed -- restart process and
+ // retry.
+ r.launchFailed = true;
+ app.activities.remove(r);
+ throw e;
+ }
+ } finally {
+ endDeferResume();
}
r.launchFailed = false;
@@ -1520,7 +1527,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
Slog.w(TAG, "Activity " + r + " being launched, but already in LRU list");
}
- if (andResume) {
+ if (andResume && readyToResume()) {
// As part of the process of launching, ActivityThread also performs
// a resume.
stack.minimalResumeActivityLocked(r);
@@ -2074,9 +2081,15 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
boolean resumeFocusedStackTopActivityLocked(
ActivityStack targetStack, ActivityRecord target, ActivityOptions targetOptions) {
+
+ if (!readyToResume()) {
+ return false;
+ }
+
if (targetStack != null && isFocusedStack(targetStack)) {
return targetStack.resumeTopActivityUncheckedLocked(target, targetOptions);
}
+
final ActivityRecord r = mFocusedStack.topRunningActivityLocked();
if (r == null || r.state != RESUMED) {
mFocusedStack.resumeTopActivityUncheckedLocked(null, null);
@@ -2084,6 +2097,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
// Kick off any lingering app transitions form the MoveTaskToFront operation.
mFocusedStack.executeAppTransition(targetOptions);
}
+
return false;
}
@@ -2983,13 +2997,6 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
try {
final TaskRecord task = r.getTask();
-
- if (r == task.getStack().getVisibleBehindActivity()) {
- // An activity can't be pinned and visible behind at the same time. Go ahead and
- // release it from been visible behind before pinning.
- requestVisibleBehindLocked(r, false);
- }
-
// Resize the pinned stack to match the current size of the task the activity we are
// going to be moving is currently contained in. We do this to have the right starting
// animation bounds for the pinned stack to the desired bounds the caller wants.
@@ -3312,70 +3319,6 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
}
}
- boolean requestVisibleBehindLocked(ActivityRecord r, boolean visible) {
- final ActivityStack stack = r.getStack();
- if (stack == null) {
- if (DEBUG_VISIBLE_BEHIND) Slog.d(TAG_VISIBLE_BEHIND,
- "requestVisibleBehind: r=" + r + " visible=" + visible + " stack is null");
- return false;
- }
-
- if (visible && !StackId.activitiesCanRequestVisibleBehind(stack.mStackId)) {
- if (DEBUG_VISIBLE_BEHIND) Slog.d(TAG_VISIBLE_BEHIND, "requestVisibleBehind: r=" + r
- + " visible=" + visible + " stackId=" + stack.mStackId
- + " can't contain visible behind activities");
- return false;
- }
-
- final boolean isVisible = stack.hasVisibleBehindActivity();
- if (DEBUG_VISIBLE_BEHIND) Slog.d(TAG_VISIBLE_BEHIND,
- "requestVisibleBehind r=" + r + " visible=" + visible + " isVisible=" + isVisible);
-
- final ActivityRecord top = topRunningActivityLocked();
- if (top == null || top == r || (visible == isVisible)) {
- if (DEBUG_VISIBLE_BEHIND) Slog.d(TAG_VISIBLE_BEHIND, "requestVisibleBehind: quick return");
- stack.setVisibleBehindActivity(visible ? r : null);
- return true;
- }
-
- // A non-top activity is reporting a visibility change.
- if (visible && top.fullscreen) {
- // Let the caller know that it can't be seen.
- if (DEBUG_VISIBLE_BEHIND) Slog.d(TAG_VISIBLE_BEHIND,
- "requestVisibleBehind: returning top.fullscreen=" + top.fullscreen
- + " top.state=" + top.state + " top.app=" + top.app + " top.app.thread="
- + top.app.thread);
- return false;
- } else if (!visible && stack.getVisibleBehindActivity() != r) {
- // Only the activity set as currently visible behind should actively reset its
- // visible behind state.
- if (DEBUG_VISIBLE_BEHIND) Slog.d(TAG_VISIBLE_BEHIND,
- "requestVisibleBehind: returning visible=" + visible
- + " stack.getVisibleBehindActivity()=" + stack.getVisibleBehindActivity()
- + " r=" + r);
- return false;
- }
-
- stack.setVisibleBehindActivity(visible ? r : null);
- if (!visible) {
- // If there is a translucent home activity, we need to force it stop being translucent,
- // because we can't depend on the application to necessarily perform that operation.
- // Check out b/14469711 for details.
- final ActivityRecord next = stack.findNextTranslucentActivity(r);
- if (next != null && next.isHomeActivity()) {
- mService.convertFromTranslucent(next.appToken);
- }
- }
- if (top.app != null && top.app.thread != null) {
- // Notify the top app of the change.
- try {
- top.app.thread.scheduleBackgroundVisibleBehindChanged(top.appToken, visible);
- } catch (RemoteException e) {
- }
- }
- return true;
- }
-
// Called when WindowManager has finished animating the launchingBehind activity to the back.
private void handleLaunchTaskBehindCompleteLocked(ActivityRecord r) {
final TaskRecord task = r.getTask();
@@ -4433,6 +4376,31 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
mIsDockMinimized = minimized;
}
+ void wakeUp(String reason) {
+ mPowerManager.wakeUp(SystemClock.uptimeMillis(), "android.server.am:TURN_ON:" + reason);
+ }
+
+ /**
+ * Begin deferring resume to avoid duplicate resumes in one pass.
+ */
+ private void beginDeferResume() {
+ mDeferResumeCount++;
+ }
+
+ /**
+ * End deferring resume and determine if resume can be called.
+ */
+ private void endDeferResume() {
+ mDeferResumeCount--;
+ }
+
+ /**
+ * @return True if resume can be called.
+ */
+ private boolean readyToResume() {
+ return mDeferResumeCount == 0;
+ }
+
private final class ActivityStackSupervisorHandler extends Handler {
public ActivityStackSupervisorHandler(Looper looper) {
@@ -4963,8 +4931,6 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
* stacks, bottommost behind. Accessed directly by ActivityManager package classes */
final ArrayList<ActivityStack> mStacks = new ArrayList<>();
- ActivityRecord mVisibleBehindActivity;
-
/** Array of all UIDs that are present on the display. */
private IntArray mDisplayAccessUIDs = new IntArray();
@@ -4998,14 +4964,6 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
mStacks.remove(stack);
}
- void setVisibleBehindActivity(ActivityRecord r) {
- mVisibleBehindActivity = r;
- }
-
- boolean hasVisibleBehindActivity() {
- return mVisibleBehindActivity != null;
- }
-
@Override
public String toString() {
return "ActivityDisplay={" + mDisplayId + " numStacks=" + mStacks.size() + "}";
diff --git a/services/core/java/com/android/server/am/KeyguardController.java b/services/core/java/com/android/server/am/KeyguardController.java
index a46c85170ba2..372d80df928f 100644
--- a/services/core/java/com/android/server/am/KeyguardController.java
+++ b/services/core/java/com/android/server/am/KeyguardController.java
@@ -30,7 +30,6 @@ import static com.android.server.wm.AppTransition.TRANSIT_FLAG_KEYGUARD_GOING_AW
import static com.android.server.wm.AppTransition.TRANSIT_KEYGUARD_GOING_AWAY;
import static com.android.server.wm.AppTransition.TRANSIT_KEYGUARD_OCCLUDE;
import static com.android.server.wm.AppTransition.TRANSIT_KEYGUARD_UNOCCLUDE;
-import static com.android.server.wm.AppTransition.TRANSIT_NONE;
import static com.android.server.wm.AppTransition.TRANSIT_UNSET;
import android.os.IBinder;
@@ -144,6 +143,13 @@ class KeyguardController {
failCallback(callback);
return;
}
+
+ // If the client has requested to dismiss the keyguard and the Activity has the flag to
+ // turn the screen on, wakeup the screen if it's the top Activity.
+ if (activityRecord.getTurnScreenOnFlag() && activityRecord.isTopRunningActivity()) {
+ mStackSupervisor.wakeUp("dismissKeyguard");
+ }
+
mWindowManager.dismissKeyguard(callback);
}
diff --git a/services/core/java/com/android/server/content/ContentService.java b/services/core/java/com/android/server/content/ContentService.java
index 468cb2990b67..ad74ff88e80f 100644
--- a/services/core/java/com/android/server/content/ContentService.java
+++ b/services/core/java/com/android/server/content/ContentService.java
@@ -102,6 +102,22 @@ public final class ContentService extends IContentService.Stub {
}
}
+
+ @Override
+ public void onStartUser(int userHandle) {
+ mService.onStartUser(userHandle);
+ }
+
+ @Override
+ public void onUnlockUser(int userHandle) {
+ mService.onUnlockUser(userHandle);
+ }
+
+ @Override
+ public void onStopUser(int userHandle) {
+ mService.onStopUser(userHandle);
+ }
+
@Override
public void onCleanupUser(int userHandle) {
synchronized (mService.mCache) {
@@ -162,6 +178,18 @@ public final class ContentService extends IContentService.Stub {
}
}
+ void onStartUser(int userHandle) {
+ if (mSyncManager != null) mSyncManager.onStartUser(userHandle);
+ }
+
+ void onUnlockUser(int userHandle) {
+ if (mSyncManager != null) mSyncManager.onUnlockUser(userHandle);
+ }
+
+ void onStopUser(int userHandle) {
+ if (mSyncManager != null) mSyncManager.onStopUser(userHandle);
+ }
+
@Override
protected synchronized void dump(FileDescriptor fd, PrintWriter pw_, String[] args) {
if (!DumpUtils.checkDumpAndUsageStatsPermission(mContext, TAG, pw_)) return;
diff --git a/services/core/java/com/android/server/content/SyncManager.java b/services/core/java/com/android/server/content/SyncManager.java
index 35591420af50..c250005204ba 100644
--- a/services/core/java/com/android/server/content/SyncManager.java
+++ b/services/core/java/com/android/server/content/SyncManager.java
@@ -405,6 +405,7 @@ public class SyncManager {
public void onReceive(Context context, Intent intent) {
Log.w(TAG, "Writing sync state before shutdown...");
getSyncStorageEngine().writeAllState();
+ mLogger.log("Shutting down.");
}
};
@@ -674,8 +675,23 @@ public class SyncManager {
// before we started checking for account access because they already know
// the account (they run before) which is the genie is out of the bottle.
whiteListExistingSyncAdaptersIfNeeded();
+
+ mLogger.log("Sync manager initialized.");
+ }
+
+ public void onStartUser(int userHandle) {
+ mLogger.log("onStartUser: user=", userHandle);
+ }
+
+ public void onUnlockUser(int userHandle) {
+ mLogger.log("onUnlockUser: user=", userHandle);
+ }
+
+ public void onStopUser(int userHandle) {
+ mLogger.log("onStopUser: user=", userHandle);
}
+
private void whiteListExistingSyncAdaptersIfNeeded() {
if (!mSyncStorageEngine.shouldGrantSyncAdaptersAccountAccess()) {
return;
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 553e3c6691e9..2d645c0eea90 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -2680,11 +2680,6 @@ public class UserManagerService extends IUserManager.Stub {
addRemovingUserIdLocked(userHandle);
}
- try {
- mAppOpsService.removeUser(userHandle);
- } catch (RemoteException e) {
- Log.w(LOG_TAG, "Unable to notify AppOpsService of removing user", e);
- }
// Set this to a partially created user, so that the user will be purged
// on next startup, in case the runtime stops now before stopping and
// removing the user completely.
@@ -2694,6 +2689,11 @@ public class UserManagerService extends IUserManager.Stub {
userData.info.flags |= UserInfo.FLAG_DISABLED;
writeUserLP(userData);
}
+ try {
+ mAppOpsService.removeUser(userHandle);
+ } catch (RemoteException e) {
+ Log.w(LOG_TAG, "Unable to notify AppOpsService of removing user", e);
+ }
if (userData.info.profileGroupId != UserInfo.NO_PROFILE_GROUP_ID
&& userData.info.isManagedProfile()) {
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 89dbc2a1c1c4..5d2d4b6d6090 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -1677,8 +1677,17 @@ public class PhoneWindowManager implements WindowManagerPolicy {
}
boolean isUserSetupComplete() {
- return Settings.Secure.getIntForUser(mContext.getContentResolver(),
+ boolean isSetupComplete = Settings.Secure.getIntForUser(mContext.getContentResolver(),
Settings.Secure.USER_SETUP_COMPLETE, 0, UserHandle.USER_CURRENT) != 0;
+ if (mHasFeatureLeanback) {
+ isSetupComplete &= isTvUserSetupComplete();
+ }
+ return isSetupComplete;
+ }
+
+ private boolean isTvUserSetupComplete() {
+ return Settings.Secure.getIntForUser(mContext.getContentResolver(),
+ Settings.Secure.TV_USER_SETUP_COMPLETE, 0, UserHandle.USER_CURRENT) != 0;
}
private void handleShortPressOnHome() {
diff --git a/services/core/java/com/android/server/vr/Vr2dDisplay.java b/services/core/java/com/android/server/vr/Vr2dDisplay.java
index 8335243d590f..fa98f1744393 100644
--- a/services/core/java/com/android/server/vr/Vr2dDisplay.java
+++ b/services/core/java/com/android/server/vr/Vr2dDisplay.java
@@ -100,6 +100,7 @@ class Vr2dDisplay {
private Runnable mStopVDRunnable;
private boolean mIsVrModeOverrideEnabled;
private boolean mIsVrModeEnabled;
+ private boolean mIsVirtualDisplayAllowed = true;
public Vr2dDisplay(DisplayManager displayManager,
ActivityManagerInternal activityManagerInternal, IVrManager vrManager) {
@@ -124,10 +125,11 @@ class Vr2dDisplay {
*/
private void updateVirtualDisplay() {
if (DEBUG) {
- Log.i(TAG, "isVrMode: " + mIsVrModeEnabled + ", override: " + mIsVrModeOverrideEnabled);
+ Log.i(TAG, "isVrMode: " + mIsVrModeEnabled + ", override: " + mIsVrModeOverrideEnabled
+ + ", isAllowed: " + mIsVirtualDisplayAllowed);
}
- if (mIsVrModeEnabled || mIsVrModeOverrideEnabled) {
+ if (shouldRunVirtualDisplay()) {
// TODO: Consider not creating the display until ActivityManager needs one on
// which to display a 2D application.
startVirtualDisplay();
@@ -190,33 +192,43 @@ class Vr2dDisplay {
*
* <p>Requires {@link android.Manifest.permission#ACCESS_VR_MANAGER} permission.</p>
*
- * @param compatDisplayProperties Properties of the virtual display for 2D applications
+ * @param displayProperties Properties of the virtual display for 2D applications
* in VR mode.
*/
- public void setVirtualDisplayProperties(Vr2dDisplayProperties compatDisplayProperties) {
+ public void setVirtualDisplayProperties(Vr2dDisplayProperties displayProperties) {
synchronized(mVdLock) {
if (DEBUG) {
- Log.i(TAG, "VD setVirtualDisplayProperties: res = "
- + compatDisplayProperties.getWidth() + "X"
- + compatDisplayProperties.getHeight() + ", dpi = "
- + compatDisplayProperties.getDpi());
+ Log.i(TAG, "VD setVirtualDisplayProperties: " +
+ displayProperties.toString());
}
- if (compatDisplayProperties.getWidth() < MIN_VR_DISPLAY_WIDTH ||
- compatDisplayProperties.getHeight() < MIN_VR_DISPLAY_HEIGHT ||
- compatDisplayProperties.getDpi() < MIN_VR_DISPLAY_DPI) {
- throw new IllegalArgumentException (
- "Illegal argument: height, width, dpi cannot be negative. res = "
- + compatDisplayProperties.getWidth() + "X"
- + compatDisplayProperties.getHeight()
- + ", dpi = " + compatDisplayProperties.getDpi());
+ int width = displayProperties.getWidth();
+ int height = displayProperties.getHeight();
+ int dpi = displayProperties.getDpi();
+ boolean resized = false;
+
+ if (width < MIN_VR_DISPLAY_WIDTH || height < MIN_VR_DISPLAY_HEIGHT ||
+ dpi < MIN_VR_DISPLAY_DPI) {
+ Log.i(TAG, "Ignoring Width/Height/Dpi values of " + width + "," + height + ","
+ + dpi);
+ } else {
+ Log.i(TAG, "Setting width/height/dpi to " + width + "," + height + "," + dpi);
+ mVirtualDisplayWidth = width;
+ mVirtualDisplayHeight = height;
+ mVirtualDisplayDpi = dpi;
+ resized = true;
}
- mVirtualDisplayWidth = compatDisplayProperties.getWidth();
- mVirtualDisplayHeight = compatDisplayProperties.getHeight();
- mVirtualDisplayDpi = compatDisplayProperties.getDpi();
+ if ((displayProperties.getFlags() & Vr2dDisplayProperties.FLAG_VIRTUAL_DISPLAY_ENABLED)
+ == Vr2dDisplayProperties.FLAG_VIRTUAL_DISPLAY_ENABLED) {
+ mIsVirtualDisplayAllowed = true;
+ } else if ((displayProperties.getRemovedFlags() &
+ Vr2dDisplayProperties.FLAG_VIRTUAL_DISPLAY_ENABLED)
+ == Vr2dDisplayProperties.FLAG_VIRTUAL_DISPLAY_ENABLED) {
+ mIsVirtualDisplayAllowed = false;
+ }
- if (mVirtualDisplay != null) {
+ if (mVirtualDisplay != null && resized && mIsVirtualDisplayAllowed) {
mVirtualDisplay.resize(mVirtualDisplayWidth, mVirtualDisplayHeight,
mVirtualDisplayDpi);
ImageReader oldImageReader = mImageReader;
@@ -224,6 +236,9 @@ class Vr2dDisplay {
startImageReader();
oldImageReader.close();
}
+
+ // Start/Stop the virtual display in case the updates indicated that we should.
+ updateVirtualDisplay();
}
}
@@ -297,7 +312,7 @@ class Vr2dDisplay {
mStopVDRunnable = new Runnable() {
@Override
public void run() {
- if (mIsVrModeEnabled) {
+ if (shouldRunVirtualDisplay()) {
Log.i(TAG, "Virtual Display destruction stopped: VrMode is back on.");
} else {
Log.i(TAG, "Stopping Virtual Display");
@@ -366,4 +381,8 @@ class Vr2dDisplay {
mImageReader = null;
}
}
+
+ private boolean shouldRunVirtualDisplay() {
+ return mIsVirtualDisplayAllowed && (mIsVrModeEnabled || mIsVrModeOverrideEnabled);
+ }
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index a95a0cf9808c..ceddc4e23fe5 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -19,12 +19,14 @@ package com.android.server.wm;
import static android.Manifest.permission.MANAGE_APP_TOKENS;
import static android.Manifest.permission.READ_FRAME_BUFFER;
import static android.Manifest.permission.REGISTER_WINDOW_MANAGER_LISTENERS;
+import static android.Manifest.permission.RESTRICTED_VR_ACCESS;
import static android.app.ActivityManager.DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT;
import static android.app.AppOpsManager.OP_SYSTEM_ALERT_WINDOW;
import static android.app.StatusBarManager.DISABLE_MASK;
import static android.app.admin.DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED;
import static android.content.Intent.ACTION_USER_REMOVED;
import static android.content.Intent.EXTRA_USER_HANDLE;
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.os.Process.ROOT_UID;
import static android.os.Process.SHELL_UID;
import static android.os.Process.SYSTEM_UID;
@@ -6305,6 +6307,9 @@ public class WindowManagerService extends IWindowManager.Stub
@Override
public Region getCurrentImeTouchRegion() {
+ if (mContext.checkCallingOrSelfPermission(RESTRICTED_VR_ACCESS) != PERMISSION_GRANTED) {
+ throw new SecurityException("getCurrentImeTouchRegion is restricted to VR services");
+ }
synchronized (mWindowMap) {
final Region r = new Region();
if (mInputMethodWindow != null) {
diff --git a/services/core/jni/Android.mk b/services/core/jni/Android.mk
index fdcf9df50426..c8c629fd5ea5 100644
--- a/services/core/jni/Android.mk
+++ b/services/core/jni/Android.mk
@@ -33,6 +33,7 @@ LOCAL_SRC_FILES += \
$(LOCAL_REL_DIR)/com_android_server_tv_TvInputHal.cpp \
$(LOCAL_REL_DIR)/com_android_server_vr_VrManagerService.cpp \
$(LOCAL_REL_DIR)/com_android_server_UsbDeviceManager.cpp \
+ $(LOCAL_REL_DIR)/com_android_server_UsbDescriptorParser.cpp \
$(LOCAL_REL_DIR)/com_android_server_UsbMidiDevice.cpp \
$(LOCAL_REL_DIR)/com_android_server_UsbHostManager.cpp \
$(LOCAL_REL_DIR)/com_android_server_VibratorService.cpp \
diff --git a/services/core/jni/com_android_server_GraphicsStatsService.cpp b/services/core/jni/com_android_server_GraphicsStatsService.cpp
index 5d5728da61af..61c53e6d30b3 100644
--- a/services/core/jni/com_android_server_GraphicsStatsService.cpp
+++ b/services/core/jni/com_android_server_GraphicsStatsService.cpp
@@ -43,8 +43,9 @@ static void addToDump(JNIEnv* env, jobject, jlong dumpPtr, jstring jpath, jstrin
std::string path;
const ProfileData* data = nullptr;
LOG_ALWAYS_FATAL_IF(jdata == nullptr && jpath == nullptr, "Path and data can't both be null");
+ ScopedByteArrayRO buffer{env};
if (jdata != nullptr) {
- ScopedByteArrayRO buffer(env, jdata);
+ buffer.reset(jdata);
LOG_ALWAYS_FATAL_IF(buffer.size() != sizeof(ProfileData),
"Buffer size %zu doesn't match expected %zu!", buffer.size(), sizeof(ProfileData));
data = reinterpret_cast<const ProfileData*>(buffer.get());
diff --git a/services/core/jni/com_android_server_UsbDescriptorParser.cpp b/services/core/jni/com_android_server_UsbDescriptorParser.cpp
new file mode 100644
index 000000000000..98c5ec1bd4d5
--- /dev/null
+++ b/services/core/jni/com_android_server_UsbDescriptorParser.cpp
@@ -0,0 +1,62 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "UsbHostManagerJNI"
+#include "utils/Log.h"
+
+#include "jni.h"
+#include "JNIHelp.h"
+
+#include <usbhost/usbhost.h>
+
+#define MAX_DESCRIPTORS_LENGTH 16384
+
+// com.android.server.usb.descriptors
+extern "C" {
+jbyteArray JNICALL Java_com_android_server_usb_descriptors_UsbDescriptorParser_getRawDescriptors(
+ JNIEnv* env, jobject thiz, jstring deviceAddr) {
+ const char *deviceAddrStr = env->GetStringUTFChars(deviceAddr, NULL);
+ struct usb_device* device = usb_device_open(deviceAddrStr);
+ env->ReleaseStringUTFChars(deviceAddr, deviceAddrStr);
+
+ if (!device) {
+ ALOGE("usb_device_open failed");
+ return NULL;
+ }
+
+ int fd = usb_device_get_fd(device);
+ if (fd < 0) {
+ return NULL;
+ }
+
+ // from android_hardware_UsbDeviceConnection_get_desc()
+ jbyte buffer[MAX_DESCRIPTORS_LENGTH];
+ lseek(fd, 0, SEEK_SET);
+ int numBytes = read(fd, buffer, sizeof(buffer));
+
+ usb_device_close(device);
+
+ jbyteArray ret = NULL;
+ if (numBytes != 0) {
+ ret = env->NewByteArray(numBytes);
+ env->SetByteArrayRegion(ret, 0, numBytes, buffer);
+ }
+ return ret;
+}
+
+} // extern "C"
+
+
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java b/services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java
index 711c36b8b1d4..48464e5e0248 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java
@@ -98,25 +98,4 @@ public class ActivityStackTests extends ActivityTestsBase {
testStack.stopActivityLocked(activityRecord);
}
-
- /**
- * This test verifies that {@link ActivityStack#STACK_VISIBLE_ACTIVITY_BEHIND} is returned from
- * {@link ActivityStack#shouldBeVisible(ActivityRecord)} from a fullscreen workspace stack with
- * a visible behind activity when top focused stack is the home stack.
- */
- @Test
- public void testShouldBeVisibleWithVisibleBehindActivity() throws Exception {
- final ActivityManagerService service = createActivityManagerService();
- final TaskRecord task = createTask(service, testActivityComponent,
- ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID);
- final ActivityStack fullscreenWorkspaceStackId = task.getStack();
- final ActivityStack homeStack = service.mStackSupervisor.getStack(
- ActivityManager.StackId.HOME_STACK_ID, true /*createStaticStackIfNeeded*/,
- true /*onTop*/);
- final ActivityRecord activityRecord = createActivity(service, testActivityComponent, task);
- service.mStackSupervisor.setFocusStackUnchecked("testEmptyStackShouldBeVisible", homeStack);
- service.mStackSupervisor.requestVisibleBehindLocked(activityRecord, true);
- assertEquals(ActivityStack.STACK_VISIBLE_ACTIVITY_BEHIND,
- fullscreenWorkspaceStackId.shouldBeVisible(null /*starting*/));
- }
}
diff --git a/services/usb/java/com/android/server/usb/UsbAlsaManager.java b/services/usb/java/com/android/server/usb/UsbAlsaManager.java
index d315b181c357..68c1d5f6ec7f 100644
--- a/services/usb/java/com/android/server/usb/UsbAlsaManager.java
+++ b/services/usb/java/com/android/server/usb/UsbAlsaManager.java
@@ -65,6 +65,9 @@ public final class UsbAlsaManager {
private final HashMap<UsbDevice,UsbAudioDevice>
mAudioDevices = new HashMap<UsbDevice,UsbAudioDevice>();
+ private boolean mIsInputHeadset; // as reported by UsbDescriptorParser
+ private boolean mIsOutputHeadset; // as reported by UsbDescriptorParser
+
private final HashMap<UsbDevice,UsbMidiDevice>
mMidiDevices = new HashMap<UsbDevice,UsbMidiDevice>();
@@ -184,9 +187,14 @@ public final class UsbAlsaManager {
try {
// Playback Device
if (audioDevice.mHasPlayback) {
- int device = (audioDevice == mAccessoryAudioDevice ?
- AudioSystem.DEVICE_OUT_USB_ACCESSORY :
- AudioSystem.DEVICE_OUT_USB_DEVICE);
+ int device;
+ if (mIsOutputHeadset) {
+ device = AudioSystem.DEVICE_OUT_USB_HEADSET;
+ } else {
+ device = (audioDevice == mAccessoryAudioDevice
+ ? AudioSystem.DEVICE_OUT_USB_ACCESSORY
+ : AudioSystem.DEVICE_OUT_USB_DEVICE);
+ }
if (DEBUG) {
Slog.i(TAG, "pre-call device:0x" + Integer.toHexString(device) +
" addr:" + address + " name:" + audioDevice.getDeviceName());
@@ -197,9 +205,14 @@ public final class UsbAlsaManager {
// Capture Device
if (audioDevice.mHasCapture) {
- int device = (audioDevice == mAccessoryAudioDevice ?
- AudioSystem.DEVICE_IN_USB_ACCESSORY :
- AudioSystem.DEVICE_IN_USB_DEVICE);
+ int device;
+ if (mIsInputHeadset) {
+ device = AudioSystem.DEVICE_IN_USB_HEADSET;
+ } else {
+ device = (audioDevice == mAccessoryAudioDevice
+ ? AudioSystem.DEVICE_IN_USB_ACCESSORY
+ : AudioSystem.DEVICE_IN_USB_DEVICE);
+ }
mAudioService.setWiredDeviceConnectionState(
device, state, address, audioDevice.getDeviceName(), TAG);
}
@@ -343,12 +356,16 @@ public final class UsbAlsaManager {
return selectAudioCard(mCardsParser.getDefaultCard());
}
- /* package */ void usbDeviceAdded(UsbDevice usbDevice) {
- if (DEBUG) {
- Slog.d(TAG, "deviceAdded(): " + usbDevice.getManufacturerName() +
- " nm:" + usbDevice.getProductName());
+ /* package */ void usbDeviceAdded(UsbDevice usbDevice,
+ boolean isInputHeadset, boolean isOutputHeadset) {
+ if (DEBUG) {
+ Slog.d(TAG, "deviceAdded(): " + usbDevice.getManufacturerName()
+ + " nm:" + usbDevice.getProductName());
}
+ mIsInputHeadset = isInputHeadset;
+ mIsOutputHeadset = isOutputHeadset;
+
// Is there an audio interface in there?
boolean isAudioDevice = false;
diff --git a/services/usb/java/com/android/server/usb/UsbDeviceManager.java b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
index 1a24d9571e72..a7180c989a6e 100644
--- a/services/usb/java/com/android/server/usb/UsbDeviceManager.java
+++ b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
@@ -683,8 +683,9 @@ public class UsbDeviceManager {
// Set the new USB configuration.
setUsbConfig(oemFunctions);
- if (UsbManager.containsFunction(functions, UsbManager.USB_FUNCTION_MTP)
- || UsbManager.containsFunction(functions, UsbManager.USB_FUNCTION_PTP)) {
+ if (mBootCompleted
+ && (UsbManager.containsFunction(functions, UsbManager.USB_FUNCTION_MTP)
+ || UsbManager.containsFunction(functions, UsbManager.USB_FUNCTION_PTP))) {
// Start up dependent services.
updateUsbStateBroadcastIfNeeded(true);
}
diff --git a/services/usb/java/com/android/server/usb/UsbHostManager.java b/services/usb/java/com/android/server/usb/UsbHostManager.java
index 737b572d9f29..66292f80e903 100644
--- a/services/usb/java/com/android/server/usb/UsbHostManager.java
+++ b/services/usb/java/com/android/server/usb/UsbHostManager.java
@@ -31,6 +31,7 @@ import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.IndentingPrintWriter;
+import com.android.server.usb.descriptors.UsbDescriptorParser;
import java.util.ArrayList;
import java.util.HashMap;
@@ -257,7 +258,14 @@ public class UsbHostManager {
getCurrentUserSettings().deviceAttachedForFixedHandler(mNewDevice,
usbDeviceConnectionHandler);
}
- mUsbAlsaManager.usbDeviceAdded(mNewDevice);
+ // deviceName is something like: "/dev/bus/usb/001/001"
+ UsbDescriptorParser parser = new UsbDescriptorParser();
+ if (parser.parseDevice(mNewDevice.getDeviceName())) {
+ Slog.i(TAG, "---- isHeadset[in:" + parser.isInputHeadset()
+ + " , out:" + parser.isOutputHeadset() + "]");
+ mUsbAlsaManager.usbDeviceAdded(mNewDevice,
+ parser.isInputHeadset(), parser.isOutputHeadset());
+ }
} else {
Slog.e(TAG, "mNewDevice is null in endUsbDeviceAdded");
}
diff --git a/services/usb/java/com/android/server/usb/descriptors/ByteStream.java b/services/usb/java/com/android/server/usb/descriptors/ByteStream.java
new file mode 100644
index 000000000000..d678931fd01a
--- /dev/null
+++ b/services/usb/java/com/android/server/usb/descriptors/ByteStream.java
@@ -0,0 +1,189 @@
+/*
+ * 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.server.usb.descriptors;
+
+import android.annotation.NonNull;
+
+/**
+ * @hide
+ * A stream interface wrapping a byte array. Very much like a java.io.ByteArrayInputStream
+ * but with the capability to "back up" in situations where the parser discovers that a
+ * UsbDescriptor has overrun its length.
+ */
+public class ByteStream {
+ private static final String TAG = "ByteStream";
+
+ /** The byte array being wrapped */
+ @NonNull
+ private final byte[] mBytes; // this is never null.
+
+ /**
+ * The index into the byte array to be read next.
+ * This value is altered by reading data out of the stream
+ * (using either the getByte() or unpack*() methods), or alternatively
+ * by explicitly offseting the stream position with either
+ * advance() or reverse().
+ */
+ private int mIndex;
+
+ /*
+ * This member used with resetReadCount() & getReadCount() can be used to determine how many
+ * bytes a UsbDescriptor subclass ACTUALLY reads (as opposed to what its length field says).
+ * using this info, the parser can mark a descriptor as valid or invalid and correct the stream
+ * position with advance() & reverse() to keep from "getting lost" in the descriptor stream.
+ */
+ private int mReadCount;
+
+ /**
+ * Create a ByteStream object wrapping the specified byte array.
+ *
+ * @param bytes The byte array containing the raw descriptor information retrieved from
+ * the USB device.
+ * @throws IllegalArgumentException
+ */
+ public ByteStream(@NonNull byte[] bytes) {
+ if (bytes == null) {
+ throw new IllegalArgumentException();
+ }
+ mBytes = bytes;
+ }
+
+ /**
+ * Resets the running count of bytes read so that later we can see how much more has been read.
+ */
+ public void resetReadCount() {
+ mReadCount = 0;
+ }
+
+ /**
+ * Retrieves the running count of bytes read from the stream.
+ */
+ public int getReadCount() {
+ return mReadCount;
+ }
+
+ /**
+ * @return The value of the next byte in the stream without advancing the stream.
+ * Does not affect the running count as the byte hasn't been "consumed".
+ * @throws IndexOutOfBoundsException
+ */
+ public byte peekByte() {
+ if (available() > 0) {
+ return mBytes[mIndex + 1];
+ } else {
+ throw new IndexOutOfBoundsException();
+ }
+ }
+
+ /**
+ * @return the next byte from the stream and advances the stream and the read count. Note
+ * that this is a signed byte (as is the case of byte in Java). The user may need to understand
+ * from context if it should be interpreted as an unsigned value.
+ * @throws IndexOutOfBoundsException
+ */
+ public byte getByte() {
+ if (available() > 0) {
+ mReadCount++;
+ return mBytes[mIndex++];
+ } else {
+ throw new IndexOutOfBoundsException();
+ }
+ }
+
+ /**
+ * Reads 2 bytes in *little endian format* from the stream and composes a 16-bit integer.
+ * As we are storing the 2-byte value in a 4-byte integer, the upper 2 bytes are always
+ * 0, essentially making the returned value *unsigned*.
+ * @return The 16-bit integer (packed into the lower 2 bytes of an int) encoded by the
+ * next 2 bytes in the stream.
+ * @throws IndexOutOfBoundsException
+ */
+ public int unpackUsbWord() {
+ if (available() >= 2) {
+ int b0 = getByte();
+ int b1 = getByte();
+ return ((b1 << 8) & 0x0000FF00) | (b0 & 0x000000FF);
+ } else {
+ throw new IndexOutOfBoundsException();
+ }
+ }
+
+ /**
+ * Reads 3 bytes in *little endian format* from the stream and composes a 24-bit integer.
+ * As we are storing the 3-byte value in a 4-byte integer, the upper byte is always
+ * 0, essentially making the returned value *unsigned*.
+ * @return The 24-bit integer (packed into the lower 3 bytes of an int) encoded by the
+ * next 3 bytes in the stream.
+ * @throws IndexOutOfBoundsException
+ */
+ public int unpackUsbTriple() {
+ if (available() >= 3) {
+ int b0 = getByte();
+ int b1 = getByte();
+ int b2 = getByte();
+ return ((b2 << 16) & 0x00FF0000) | ((b1 << 8) & 0x0000FF00) | (b0 & 0x000000FF);
+ } else {
+ throw new IndexOutOfBoundsException();
+ }
+ }
+
+ /**
+ * Advances the logical position in the stream. Affects the running count also.
+ * @param numBytes The number of bytes to advance.
+ * @throws IndexOutOfBoundsException
+ * @throws IllegalArgumentException
+ */
+ public void advance(int numBytes) {
+ if (numBytes < 0) {
+ // Positive offsets only
+ throw new IllegalArgumentException();
+ }
+ // do arithmetic and comparison in long to ovoid potention integer overflow
+ long longNewIndex = (long) mIndex + (long) numBytes;
+ if (longNewIndex < (long) mBytes.length) {
+ mReadCount += numBytes;
+ mIndex += numBytes;
+ } else {
+ throw new IndexOutOfBoundsException();
+ }
+ }
+
+ /**
+ * Reverse the logical position in the stream. Affects the running count also.
+ * @param numBytes The (positive) number of bytes to reverse.
+ * @throws IndexOutOfBoundsException
+ * @throws IllegalArgumentException
+ */
+ public void reverse(int numBytes) {
+ if (numBytes < 0) {
+ // Positive (reverse) offsets only
+ throw new IllegalArgumentException();
+ }
+ if (mIndex >= numBytes) {
+ mReadCount -= numBytes;
+ mIndex -= numBytes;
+ } else {
+ throw new IndexOutOfBoundsException();
+ }
+ }
+
+ /**
+ * @return The number of bytes available to be read in the stream.
+ */
+ public int available() {
+ return mBytes.length - mIndex;
+ }
+}
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbACAudioControlEndpoint.java b/services/usb/java/com/android/server/usb/descriptors/UsbACAudioControlEndpoint.java
new file mode 100644
index 000000000000..96fcc6a0b8db
--- /dev/null
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbACAudioControlEndpoint.java
@@ -0,0 +1,72 @@
+/*
+ * 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.server.usb.descriptors;
+
+/**
+ * @hide
+ * An audio class-specific Audio Control Endpoint.
+ * audio10.pdf section 4.4.2.1
+ */
+public class UsbACAudioControlEndpoint extends UsbACEndpoint {
+ private static final String TAG = "ACAudioControlEndpoint";
+
+ private byte mAddress; // 2:1 The address of the endpoint on the USB device.
+ // D7: Direction. 1 = IN endpoint
+ // D6..4: Reserved, reset to zero
+ // D3..0: The endpoint number.
+ private byte mAttribs; // 3:1 (see ATTRIBSMASK_* below
+ private int mMaxPacketSize; // 4:2 Maximum packet size this endpoint is capable of sending
+ // or receiving when this configuration is selected.
+ private byte mInterval; // 6:1
+
+ static final byte ADDRESSMASK_DIRECTION = (byte) 0x80;
+ static final byte ADDRESSMASK_ENDPOINT = 0x0F;
+
+ static final byte ATTRIBSMASK_SYNC = 0x0C;
+ static final byte ATTRIBMASK_TRANS = 0x03;
+
+ public UsbACAudioControlEndpoint(int length, byte type, byte subclass) {
+ super(length, type, subclass);
+ }
+
+ public byte getAddress() {
+ return mAddress;
+ }
+
+ public byte getAttribs() {
+ return mAttribs;
+ }
+
+ public int getMaxPacketSize() {
+ return mMaxPacketSize;
+ }
+
+ public byte getInterval() {
+ return mInterval;
+ }
+
+ @Override
+ public int parseRawDescriptors(ByteStream stream) {
+ super.parseRawDescriptors(stream);
+
+ mAddress = stream.getByte();
+ mAttribs = stream.getByte();
+ mMaxPacketSize = stream.unpackUsbWord();
+ mInterval = stream.getByte();
+
+ return mLength;
+ }
+}
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbACAudioStreamEndpoint.java b/services/usb/java/com/android/server/usb/descriptors/UsbACAudioStreamEndpoint.java
new file mode 100644
index 000000000000..d387883d3049
--- /dev/null
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbACAudioStreamEndpoint.java
@@ -0,0 +1,39 @@
+/*
+ * 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.server.usb.descriptors;
+
+/**
+ * @hide
+ * An audio class-specific Streaming Endpoint
+ * see audio10.pdf section 3.7.2
+ */
+public class UsbACAudioStreamEndpoint extends UsbACEndpoint {
+ private static final String TAG = "ACAudioStreamEndpoint";
+
+ //TODO data fields...
+ public UsbACAudioStreamEndpoint(int length, byte type, byte subclass) {
+ super(length, type, subclass);
+ }
+
+ @Override
+ public int parseRawDescriptors(ByteStream stream) {
+ super.parseRawDescriptors(stream);
+
+ //TODO Read fields
+ stream.advance(mLength - stream.getReadCount());
+ return mLength;
+ }
+}
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbACEndpoint.java b/services/usb/java/com/android/server/usb/descriptors/UsbACEndpoint.java
new file mode 100644
index 000000000000..223496ab016e
--- /dev/null
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbACEndpoint.java
@@ -0,0 +1,71 @@
+/*
+ * 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.server.usb.descriptors;
+
+import android.util.Log;
+
+/**
+ * @hide
+ * An audio class-specific Endpoint
+ * see audio10.pdf section 4.4.1.2
+ */
+abstract class UsbACEndpoint extends UsbDescriptor {
+ private static final String TAG = "ACEndpoint";
+
+ protected final byte mSubclass; // from the mSubclass member of the "enclosing"
+ // Interface Descriptor, not the stream.
+ protected byte mSubtype; // 2:1 HEADER descriptor subtype
+
+ UsbACEndpoint(int length, byte type, byte subclass) {
+ super(length, type);
+ mSubclass = subclass;
+ }
+
+ public byte getSubclass() {
+ return mSubclass;
+ }
+
+ public byte getSubtype() {
+ return mSubtype;
+ }
+
+ @Override
+ public int parseRawDescriptors(ByteStream stream) {
+ mSubtype = stream.getByte();
+
+ return mLength;
+ }
+
+ public static UsbDescriptor allocDescriptor(UsbDescriptorParser parser,
+ int length, byte type) {
+ UsbInterfaceDescriptor interfaceDesc = parser.getCurInterface();
+ byte subClass = interfaceDesc.getUsbSubclass();
+ switch (subClass) {
+ case AUDIO_AUDIOCONTROL:
+ return new UsbACAudioControlEndpoint(length, type, subClass);
+
+ case AUDIO_AUDIOSTREAMING:
+ return new UsbACAudioStreamEndpoint(length, type, subClass);
+
+ case AUDIO_MIDISTREAMING:
+ return new UsbACMidiEndpoint(length, type, subClass);
+
+ default:
+ Log.w(TAG, "Unknown Audio Class Endpoint id:0x" + Integer.toHexString(subClass));
+ return null;
+ }
+ }
+}
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbACFeatureUnit.java b/services/usb/java/com/android/server/usb/descriptors/UsbACFeatureUnit.java
new file mode 100644
index 000000000000..739fe5503a1d
--- /dev/null
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbACFeatureUnit.java
@@ -0,0 +1,76 @@
+/*
+ * 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.server.usb.descriptors;
+
+/**
+ * @hide
+ * An audio class-specific Feature Unit Interface
+ * see audio10.pdf section 3.5.5
+ */
+public class UsbACFeatureUnit extends UsbACInterface {
+ private static final String TAG = "ACFeatureUnit";
+
+ // audio10.pdf section 4.3.2.5
+ public static final int CONTROL_MASK_MUTE = 0x0001;
+ public static final int CONTROL_MASK_VOL = 0x0002;
+ public static final int CONTROL_MASK_BASS = 0x0004;
+ public static final int CONTROL_MASK_MID = 0x0008;
+ public static final int CONTROL_MASK_TREB = 0x0010;
+ public static final int CONTROL_MASK_EQ = 0x0020;
+ public static final int CONTROL_MASK_AGC = 0x0040;
+ public static final int CONTROL_MASK_DELAY = 0x0080;
+ public static final int CONTROL_MASK_BOOST = 0x0100; // BASS boost
+ public static final int CONTROL_MASK_LOUD = 0x0200; // LOUDNESS
+
+ private int mNumChannels;
+
+ private byte mUnitID; // 3:1 Constant uniquely identifying the Unit within the audio function.
+ // This value is used in all requests to address this Unit
+ private byte mSourceID; // 4:1 ID of the Unit or Terminal to which this Feature Unit
+ // is connected.
+ private byte mControlSize; // 5:1 Size in bytes of an element of the mControls array: n
+ private int[] mControls; // 6:? bitmask (see above) of supported controls in a given
+ // logical channel
+ private byte mUnitName; // ?:1 Index of a string descriptor, describing this Feature Unit.
+
+ public UsbACFeatureUnit(int length, byte type, byte subtype, byte subClass) {
+ super(length, type, subtype, subClass);
+ }
+
+ public int getNumChannels() {
+ return mNumChannels;
+ }
+
+ public byte getUnitID() {
+ return mUnitID;
+ }
+
+ public byte getSourceID() {
+ return mSourceID;
+ }
+
+ public byte getControlSize() {
+ return mControlSize;
+ }
+
+ public int[] getControls() {
+ return mControls;
+ }
+
+ public byte getUnitName() {
+ return mUnitName;
+ }
+}
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbACHeader.java b/services/usb/java/com/android/server/usb/descriptors/UsbACHeader.java
new file mode 100644
index 000000000000..e31438c58e06
--- /dev/null
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbACHeader.java
@@ -0,0 +1,78 @@
+/*
+ * 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.server.usb.descriptors;
+
+/**
+ * @hide
+ * An audio class-specific Interface Header.
+ * see audio10.pdf section 4.3.2
+ */
+public class UsbACHeader extends UsbACInterface {
+ private static final String TAG = "ACHeader";
+
+ private int mADCRelease; // 3:2 Audio Device Class Specification Release (BCD).
+ private int mTotalLength; // 5:2 Total number of bytes returned for the class-specific
+ // AudioControl interface descriptor. Includes the combined length
+ // of this descriptor header and all Unit and Terminal descriptors.
+ private byte mNumInterfaces = 0; // 7:1 The number of AudioStreaming and MIDIStreaming
+ // interfaces in the Audio Interface Collection to which this
+ // AudioControl interface belongs: n
+ private byte[] mInterfaceNums = null; // 8:n List of Audio/MIDI streaming interface
+ // numbers associate with this endpoint
+ private byte mControls; // Vers 2.0 thing
+
+ public UsbACHeader(int length, byte type, byte subtype, byte subclass) {
+ super(length, type, subtype, subclass);
+ }
+
+ public int getADCRelease() {
+ return mADCRelease;
+ }
+
+ public int getTotalLength() {
+ return mTotalLength;
+ }
+
+ public byte getNumInterfaces() {
+ return mNumInterfaces;
+ }
+
+ public byte[] getInterfaceNums() {
+ return mInterfaceNums;
+ }
+
+ public byte getControls() {
+ return mControls;
+ }
+
+ @Override
+ public int parseRawDescriptors(ByteStream stream) {
+ mADCRelease = stream.unpackUsbWord();
+
+ mTotalLength = stream.unpackUsbWord();
+ if (mADCRelease >= 0x200) {
+ mControls = stream.getByte();
+ } else {
+ mNumInterfaces = stream.getByte();
+ mInterfaceNums = new byte[mNumInterfaces];
+ for (int index = 0; index < mNumInterfaces; index++) {
+ mInterfaceNums[index] = stream.getByte();
+ }
+ }
+
+ return mLength;
+ }
+}
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbACInputTerminal.java b/services/usb/java/com/android/server/usb/descriptors/UsbACInputTerminal.java
new file mode 100644
index 000000000000..653a7de5457e
--- /dev/null
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbACInputTerminal.java
@@ -0,0 +1,64 @@
+/*
+ * 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.server.usb.descriptors;
+
+/**
+ * @hide
+ * An audio class-specific Input Terminal interface.
+ * see audio10.pdf section 4.3.2.1
+ */
+public class UsbACInputTerminal extends UsbACTerminal {
+ private static final String TAG = "ACInputTerminal";
+
+ private byte mNrChannels; // 7:1 1 Channel (0x01)
+ // Number of logical output channels in the
+ // Terminal’s output audio channel cluster
+ private int mChannelConfig; // 8:2 Mono (0x0000)
+ private byte mChannelNames; // 10:1 Unused (0x00)
+ private byte mTerminal; // 11:1 Unused (0x00)
+
+ public UsbACInputTerminal(int length, byte type, byte subtype, byte subclass) {
+ super(length, type, subtype, subclass);
+ }
+
+ public byte getNrChannels() {
+ return mNrChannels;
+ }
+
+ public int getChannelConfig() {
+ return mChannelConfig;
+ }
+
+ public byte getChannelNames() {
+ return mChannelNames;
+ }
+
+ public byte getTerminal() {
+ return mTerminal;
+ }
+
+ @Override
+ public int parseRawDescriptors(ByteStream stream) {
+ super.parseRawDescriptors(stream);
+
+ mNrChannels = stream.getByte();
+ mChannelConfig = stream.unpackUsbWord();
+ mChannelNames = stream.getByte();
+ mTerminal = stream.getByte();
+
+ return mLength;
+ }
+}
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbACInterface.java b/services/usb/java/com/android/server/usb/descriptors/UsbACInterface.java
new file mode 100644
index 000000000000..0ab7fccd2c3b
--- /dev/null
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbACInterface.java
@@ -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.
+ * 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.server.usb.descriptors;
+
+import android.util.Log;
+
+/**
+ * @hide
+ * An audio class-specific Interface.
+ * see audio10.pdf section 4.3.2
+ */
+public abstract class UsbACInterface extends UsbDescriptor {
+ private static final String TAG = "ACInterface";
+
+ // Audio Control Subtypes
+ public static final byte ACI_UNDEFINED = 0;
+ public static final byte ACI_HEADER = 1;
+ public static final byte ACI_INPUT_TERMINAL = 2;
+ public static final byte ACI_OUTPUT_TERMINAL = 3;
+ public static final byte ACI_MIXER_UNIT = 4;
+ public static final byte ACI_SELECTOR_UNIT = 5;
+ public static final byte ACI_FEATURE_UNIT = 6;
+ public static final byte ACI_PROCESSING_UNIT = 7;
+ public static final byte ACI_EXTENSION_UNIT = 8;
+
+ // Audio Streaming Subtypes
+ public static final byte ASI_UNDEFINED = 0;
+ public static final byte ASI_GENERAL = 1;
+ public static final byte ASI_FORMAT_TYPE = 2;
+ public static final byte ASI_FORMAT_SPECIFIC = 3;
+
+ // MIDI Streaming Subtypes
+ public static final byte MSI_UNDEFINED = 0;
+ public static final byte MSI_HEADER = 1;
+ public static final byte MSI_IN_JACK = 2;
+ public static final byte MSI_OUT_JACK = 3;
+ public static final byte MSI_ELEMENT = 4;
+
+ // Sample format IDs (encodings)
+ // FORMAT_I
+ public static final int FORMAT_I_UNDEFINED = 0x0000;
+ public static final int FORMAT_I_PCM = 0x0001;
+ public static final int FORMAT_I_PCM8 = 0x0002;
+ public static final int FORMAT_I_IEEE_FLOAT = 0x0003;
+ public static final int FORMAT_I_ALAW = 0x0004;
+ public static final int FORMAT_I_MULAW = 0x0005;
+ // FORMAT_II
+ public static final int FORMAT_II_UNDEFINED = 0x1000;
+ public static final int FORMAT_II_MPEG = 0x1001;
+ public static final int FORMAT_II_AC3 = 0x1002;
+ // FORMAT_III
+ public static final int FORMAT_III_UNDEFINED = 0x2000;
+ public static final int FORMAT_III_IEC1937AC3 = 0x2001;
+ public static final int FORMAT_III_IEC1937_MPEG1_Layer1 = 0x2002;
+ public static final int FORMAT_III_IEC1937_MPEG1_Layer2 = 0x2003;
+ public static final int FORMAT_III_IEC1937_MPEG2_EXT = 0x2004;
+ public static final int FORMAT_III_IEC1937_MPEG2_Layer1LS = 0x2005;
+
+ protected final byte mSubtype; // 2:1 HEADER descriptor subtype
+ protected final byte mSubclass; // from the mSubclass member of the
+ // "enclosing" Interface Descriptor
+
+ public UsbACInterface(int length, byte type, byte subtype, byte subclass) {
+ super(length, type);
+ mSubtype = subtype;
+ mSubclass = subclass;
+ }
+
+ public byte getSubtype() {
+ return mSubtype;
+ }
+
+ public byte getSubclass() {
+ return mSubclass;
+ }
+
+ private static UsbDescriptor allocAudioControlDescriptor(ByteStream stream,
+ int length, byte type, byte subtype, byte subClass) {
+ switch (subtype) {
+ case ACI_HEADER:
+ return new UsbACHeader(length, type, subtype, subClass);
+
+ case ACI_INPUT_TERMINAL:
+ return new UsbACInputTerminal(length, type, subtype, subClass);
+
+ case ACI_OUTPUT_TERMINAL:
+ return new UsbACOutputTerminal(length, type, subtype, subClass);
+
+ case ACI_SELECTOR_UNIT:
+ return new UsbACSelectorUnit(length, type, subtype, subClass);
+
+ case ACI_FEATURE_UNIT:
+ return new UsbACFeatureUnit(length, type, subtype, subClass);
+
+ case ACI_MIXER_UNIT:
+ return new UsbACMixerUnit(length, type, subtype, subClass);
+
+ case ACI_PROCESSING_UNIT:
+ case ACI_EXTENSION_UNIT:
+ case ACI_UNDEFINED:
+ // break; Fall through until we implement this descriptor
+ default:
+ Log.w(TAG, "Unknown Audio Class Interface subtype:0x"
+ + Integer.toHexString(subtype));
+ return null;
+ }
+ }
+
+ private static UsbDescriptor allocAudioStreamingDescriptor(ByteStream stream,
+ int length, byte type, byte subtype, byte subClass) {
+ switch (subtype) {
+ case ASI_GENERAL:
+ return new UsbASGeneral(length, type, subtype, subClass);
+
+ case ASI_FORMAT_TYPE:
+ return UsbASFormat.allocDescriptor(stream, length, type, subtype, subClass);
+
+ case ASI_FORMAT_SPECIFIC:
+ case ASI_UNDEFINED:
+ // break; Fall through until we implement this descriptor
+ default:
+ Log.w(TAG, "Unknown Audio Streaming Interface subtype:0x"
+ + Integer.toHexString(subtype));
+ return null;
+ }
+ }
+
+ private static UsbDescriptor allocMidiStreamingDescriptor(int length, byte type,
+ byte subtype, byte subClass) {
+ switch (subtype) {
+ case MSI_HEADER:
+ return new UsbMSMidiHeader(length, type, subtype, subClass);
+
+ case MSI_IN_JACK:
+ return new UsbMSMidiInputJack(length, type, subtype, subClass);
+
+ case MSI_OUT_JACK:
+ return new UsbMSMidiOutputJack(length, type, subtype, subClass);
+
+ case MSI_ELEMENT:
+ // break;
+ // Fall through until we implement that descriptor
+
+ case MSI_UNDEFINED:
+ // break; Fall through until we implement this descriptor
+ default:
+ Log.w(TAG, "Unknown MIDI Streaming Interface subtype:0x"
+ + Integer.toHexString(subtype));
+ return null;
+ }
+ }
+
+ /**
+ * Allocates an audio class interface subtype based on subtype and subclass.
+ */
+ public static UsbDescriptor allocDescriptor(UsbDescriptorParser parser, ByteStream stream,
+ int length, byte type) {
+ byte subtype = stream.getByte();
+ UsbInterfaceDescriptor interfaceDesc = parser.getCurInterface();
+ byte subClass = interfaceDesc.getUsbSubclass();
+ switch (subClass) {
+ case AUDIO_AUDIOCONTROL:
+ return allocAudioControlDescriptor(stream, length, type, subtype, subClass);
+
+ case AUDIO_AUDIOSTREAMING:
+ return allocAudioStreamingDescriptor(stream, length, type, subtype, subClass);
+
+ case AUDIO_MIDISTREAMING:
+ return allocMidiStreamingDescriptor(length, type, subtype, subClass);
+
+ default:
+ Log.w(TAG, "Unknown Audio Class Interface Subclass: 0x"
+ + Integer.toHexString(subClass));
+ return null;
+ }
+ }
+}
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbACMidiEndpoint.java b/services/usb/java/com/android/server/usb/descriptors/UsbACMidiEndpoint.java
new file mode 100644
index 000000000000..9c072426cc49
--- /dev/null
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbACMidiEndpoint.java
@@ -0,0 +1,52 @@
+/*
+ * 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.server.usb.descriptors;
+
+/**
+ * @hide
+ * An audio class-specific Midi Endpoint.
+ * see midi10.pdf section 6.2.2
+ */
+public class UsbACMidiEndpoint extends UsbACEndpoint {
+ private static final String TAG = "ACMidiEndpoint";
+
+ private byte mNumJacks;
+ private byte[] mJackIds;
+
+ public UsbACMidiEndpoint(int length, byte type, byte subclass) {
+ super(length, type, subclass);
+ }
+
+ public byte getNumJacks() {
+ return mNumJacks;
+ }
+
+ public byte[] getJackIds() {
+ return mJackIds;
+ }
+
+ @Override
+ public int parseRawDescriptors(ByteStream stream) {
+ super.parseRawDescriptors(stream);
+
+ mNumJacks = stream.getByte();
+ mJackIds = new byte[mNumJacks];
+ for (int jack = 0; jack < mNumJacks; jack++) {
+ mJackIds[jack] = stream.getByte();
+ }
+ return mLength;
+ }
+}
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbACMixerUnit.java b/services/usb/java/com/android/server/usb/descriptors/UsbACMixerUnit.java
new file mode 100644
index 000000000000..552b5ae308d6
--- /dev/null
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbACMixerUnit.java
@@ -0,0 +1,100 @@
+/*
+ * 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.server.usb.descriptors;
+
+/**
+ * @hide
+ * An audio class-specific Mixer Interface.
+ * see audio10.pdf section 4.3.2.3
+ */
+public class UsbACMixerUnit extends UsbACInterface {
+ private static final String TAG = "ACMixerUnit";
+
+ private byte mUnitID; // 3:1
+ private byte mNumInputs; // 4:1 Number of Input Pins of this Unit.
+ private byte[] mInputIDs; // 5...:1 ID of the Unit or Terminal to which the Input Pins
+ // are connected.
+ private byte mNumOutputs; // The number of output channels
+ private int mChannelConfig; // Spacial location of output channels
+ private byte mChanNameID; // First channel name string descriptor ID
+ private byte[] mControls; // bitmasks of which controls are present for each channel
+ private byte mNameID; // string descriptor ID of mixer name
+
+ public UsbACMixerUnit(int length, byte type, byte subtype, byte subClass) {
+ super(length, type, subtype, subClass);
+ }
+
+ public byte getUnitID() {
+ return mUnitID;
+ }
+
+ public byte getNumInputs() {
+ return mNumInputs;
+ }
+
+ public byte[] getInputIDs() {
+ return mInputIDs;
+ }
+
+ public byte getNumOutputs() {
+ return mNumOutputs;
+ }
+
+ public int getChannelConfig() {
+ return mChannelConfig;
+ }
+
+ public byte getChanNameID() {
+ return mChanNameID;
+ }
+
+ public byte[] getControls() {
+ return mControls;
+ }
+
+ public byte getNameID() {
+ return mNameID;
+ }
+
+ @Override
+ public int parseRawDescriptors(ByteStream stream) {
+ mUnitID = stream.getByte();
+ mNumInputs = stream.getByte();
+ mInputIDs = new byte[mNumInputs];
+ for (int input = 0; input < mNumInputs; input++) {
+ mInputIDs[input] = stream.getByte();
+ }
+ mNumOutputs = stream.getByte();
+ mChannelConfig = stream.unpackUsbWord();
+ mChanNameID = stream.getByte();
+
+ int controlArraySize;
+ int totalChannels = mNumInputs * mNumOutputs;
+ if (totalChannels % 8 == 0) {
+ controlArraySize = totalChannels / 8;
+ } else {
+ controlArraySize = totalChannels / 8 + 1;
+ }
+ mControls = new byte[controlArraySize];
+ for (int index = 0; index < controlArraySize; index++) {
+ mControls[index] = stream.getByte();
+ }
+
+ mNameID = stream.getByte();
+
+ return mLength;
+ }
+}
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbACOutputTerminal.java b/services/usb/java/com/android/server/usb/descriptors/UsbACOutputTerminal.java
new file mode 100644
index 000000000000..f957e3dbe217
--- /dev/null
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbACOutputTerminal.java
@@ -0,0 +1,49 @@
+/*
+ * 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.server.usb.descriptors;
+
+/**
+ * @hide
+ * An audio class-specific Output Terminal Interface.
+ * see audio10.pdf section 4.3.2.2
+ */
+public class UsbACOutputTerminal extends UsbACTerminal {
+ private static final String TAG = "ACOutputTerminal";
+
+ private byte mSourceID; // 7:1 From Input Terminal. (0x01)
+ private byte mTerminal; // 8:1 Unused.
+
+ public UsbACOutputTerminal(int length, byte type, byte subtype, byte subClass) {
+ super(length, type, subtype, subClass);
+ }
+
+ public byte getSourceID() {
+ return mSourceID;
+ }
+
+ public byte getTerminal() {
+ return mTerminal;
+ }
+
+ @Override
+ public int parseRawDescriptors(ByteStream stream) {
+ super.parseRawDescriptors(stream);
+
+ mSourceID = stream.getByte();
+ mTerminal = stream.getByte();
+ return mLength;
+ }
+}
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbACSelectorUnit.java b/services/usb/java/com/android/server/usb/descriptors/UsbACSelectorUnit.java
new file mode 100644
index 000000000000..b1f60bdcf6ed
--- /dev/null
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbACSelectorUnit.java
@@ -0,0 +1,65 @@
+/*
+ * 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.server.usb.descriptors;
+
+/**
+ * @hide
+ * An audio class-specific Selector Unit Interface.
+ * see audio10.pdf section 4.3.2.4
+ */
+public class UsbACSelectorUnit extends UsbACInterface {
+ private static final String TAG = "ACSelectorUnit";
+
+ private byte mUnitID; // 3:1 Constant uniquely identifying the Unit within the audio function.
+ // This value is used in all requests to address this Unit.
+ private byte mNumPins; // 4:1 Number of input pins in this unit
+ private byte[] mSourceIDs; // 5+mNumPins:1 ID of the Unit or Terminal to which the first
+ // Input Pin of this Selector Unit is connected.
+ private byte mNameIndex; // Index of a string descriptor, describing the Selector Unit.
+
+ public UsbACSelectorUnit(int length, byte type, byte subtype, byte subClass) {
+ super(length, type, subtype, subClass);
+ }
+
+ public byte getUnitID() {
+ return mUnitID;
+ }
+
+ public byte getNumPins() {
+ return mNumPins;
+ }
+
+ public byte[] getSourceIDs() {
+ return mSourceIDs;
+ }
+
+ public byte getNameIndex() {
+ return mNameIndex;
+ }
+
+ @Override
+ public int parseRawDescriptors(ByteStream stream) {
+ mUnitID = stream.getByte();
+ mNumPins = stream.getByte();
+ mSourceIDs = new byte[mNumPins];
+ for (int index = 0; index < mNumPins; index++) {
+ mSourceIDs[index] = stream.getByte();
+ }
+ mNameIndex = stream.getByte();
+
+ return mLength;
+ }
+}
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbACTerminal.java b/services/usb/java/com/android/server/usb/descriptors/UsbACTerminal.java
new file mode 100644
index 000000000000..ea80208ee3f3
--- /dev/null
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbACTerminal.java
@@ -0,0 +1,54 @@
+/*
+ * 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.server.usb.descriptors;
+
+/**
+ * @hide
+ */
+public abstract class UsbACTerminal extends UsbACInterface {
+ // Note that these fields are the same for both the
+ // audio class-specific Output Terminal Interface.(audio10.pdf section 4.3.2.2)
+ // and audio class-specific Input Terminal interface.(audio10.pdf section 4.3.2.1)
+ // so we may as well unify the parsing here.
+ protected byte mTerminalID; // 3:1 ID of this Output Terminal. (0x02)
+ protected int mTerminalType; // 4:2 USB Streaming. (0x0101)
+ protected byte mAssocTerminal; // 6:1 Unused (0x00)
+
+ public UsbACTerminal(int length, byte type, byte subtype, byte subclass) {
+ super(length, type, subtype, subclass);
+ }
+
+ public byte getTerminalID() {
+ return mTerminalID;
+ }
+
+ public int getTerminalType() {
+ return mTerminalType;
+ }
+
+ public byte getAssocTerminal() {
+ return mAssocTerminal;
+ }
+
+ @Override
+ public int parseRawDescriptors(ByteStream stream) {
+ mTerminalID = stream.getByte();
+ mTerminalType = stream.unpackUsbWord();
+ mAssocTerminal = stream.getByte();
+
+ return mLength;
+ }
+}
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbASFormat.java b/services/usb/java/com/android/server/usb/descriptors/UsbASFormat.java
new file mode 100644
index 000000000000..d7c84c6a0965
--- /dev/null
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbASFormat.java
@@ -0,0 +1,64 @@
+/*
+ * 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.server.usb.descriptors;
+
+/**
+ * @hide
+ * An audio class-specific Format Interface.
+ * Subclasses: UsbACFormatI and UsbACFormatII.
+ * see audio10.pdf section 4.5.3 & & Frmts10.pdf
+ */
+public abstract class UsbASFormat extends UsbACInterface {
+ private static final String TAG = "ASFormat";
+
+ private final byte mFormatType; // 3:1 FORMAT_TYPE_*
+
+ public static final byte FORMAT_TYPE_I = 1;
+ public static final byte FORMAT_TYPE_II = 2;
+
+ public UsbASFormat(int length, byte type, byte subtype, byte formatType, byte mSubclass) {
+ super(length, type, subtype, mSubclass);
+ mFormatType = formatType;
+ }
+
+ public byte getFormatType() {
+ return mFormatType;
+ }
+
+ /**
+ * Allocates the audio-class format subtype associated with the format type read from the
+ * stream.
+ */
+ public static UsbDescriptor allocDescriptor(ByteStream stream, int length, byte type,
+ byte subtype, byte subclass) {
+
+ byte formatType = stream.getByte();
+ //TODO
+ // There is an issue parsing format descriptors on (some) USB 2.0 pro-audio interfaces
+ // Since we don't need this info for headset detection, just skip these descriptors
+ // for now to avoid the (low) possibility of an IndexOutOfBounds exception.
+ switch (formatType) {
+// case FORMAT_TYPE_I:
+// return new UsbASFormatI(length, type, subtype, formatType, subclass);
+//
+// case FORMAT_TYPE_II:
+// return new UsbASFormatII(length, type, subtype, formatType, subclass);
+
+ default:
+ return null;
+ }
+ }
+}
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbASFormatI.java b/services/usb/java/com/android/server/usb/descriptors/UsbASFormatI.java
new file mode 100644
index 000000000000..347a6cffb525
--- /dev/null
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbASFormatI.java
@@ -0,0 +1,77 @@
+/*
+ * 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.server.usb.descriptors;
+
+/**
+ * @hide
+ * An audio class-specific Format I interface.
+ * see Frmts10.pdf section 2.2
+ */
+public class UsbASFormatI extends UsbASFormat {
+ private static final String TAG = "ASFormatI";
+
+ private byte mNumChannels; // 4:1
+ private byte mSubframeSize; // 5:1 frame size in bytes
+ private byte mBitResolution; // 6:1 sample size in bits
+ private byte mSampleFreqType; // 7:1
+ private int[] mSampleRates; // if mSamFreqType == 0, there will be 2 values: the
+ // min & max rates otherwise mSamFreqType rates.
+ // All 3-byte values. All rates in Hz
+
+ public UsbASFormatI(int length, byte type, byte subtype, byte formatType, byte subclass) {
+ super(length, type, subtype, formatType, subclass);
+ }
+
+ public byte getNumChannels() {
+ return mNumChannels;
+ }
+
+ public byte getSubframeSize() {
+ return mSubframeSize;
+ }
+
+ public byte getBitResolution() {
+ return mBitResolution;
+ }
+
+ public byte getSampleFreqType() {
+ return mSampleFreqType;
+ }
+
+ public int[] getSampleRates() {
+ return mSampleRates;
+ }
+
+ @Override
+ public int parseRawDescriptors(ByteStream stream) {
+ mNumChannels = stream.getByte();
+ mSubframeSize = stream.getByte();
+ mBitResolution = stream.getByte();
+ mSampleFreqType = stream.getByte();
+ if (mSampleFreqType == 0) {
+ mSampleRates = new int[2];
+ mSampleRates[0] = stream.unpackUsbTriple();
+ mSampleRates[1] = stream.unpackUsbTriple();
+ } else {
+ mSampleRates = new int[mSampleFreqType];
+ for (int index = 0; index < mSampleFreqType; index++) {
+ mSampleRates[index] = stream.unpackUsbTriple();
+ }
+ }
+
+ return mLength;
+ }
+}
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbASFormatII.java b/services/usb/java/com/android/server/usb/descriptors/UsbASFormatII.java
new file mode 100644
index 000000000000..abdc62145aa2
--- /dev/null
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbASFormatII.java
@@ -0,0 +1,72 @@
+/*
+ * 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.server.usb.descriptors;
+
+/**
+ * @hide
+ * An audio class-specific Format II interface.
+ * see Frmts10.pdf section 2.3
+ */
+public class UsbASFormatII extends UsbASFormat {
+ private static final String TAG = "ASFormatII";
+
+ private int mMaxBitRate; // 4:2 Indicates the maximum number of bits per second this
+ // interface can handle. Expressed in kbits/s.
+ private int mSamplesPerFrame; // 6:2 Indicates the number of PCM audio samples contained
+ // in one encoded audio frame.
+ private byte mSamFreqType; // Indicates how the sampling frequency can be programmed:
+ // 0: Continuous sampling frequency
+ // 1..255: The number of discrete sampling frequencies supported
+ // by the isochronous data endpoint of the AudioStreaming
+ // interface (ns)
+ private int[] mSampleRates; // if mSamFreqType == 0, there will be 2 values:
+ // the min & max rates. otherwise mSamFreqType rates.
+ // All 3-byte values. All rates in Hz
+
+ public UsbASFormatII(int length, byte type, byte subtype, byte formatType, byte subclass) {
+ super(length, type, subtype, formatType, subclass);
+ }
+
+ public int getMaxBitRate() {
+ return mMaxBitRate;
+ }
+
+ public int getSamplesPerFrame() {
+ return mSamplesPerFrame;
+ }
+
+ public byte getSamFreqType() {
+ return mSamFreqType;
+ }
+
+ public int[] getSampleRates() {
+ return mSampleRates;
+ }
+
+ @Override
+ public int parseRawDescriptors(ByteStream stream) {
+ mMaxBitRate = stream.unpackUsbWord();
+ mSamplesPerFrame = stream.unpackUsbWord();
+ mSamFreqType = stream.getByte();
+ int numFreqs = mSamFreqType == 0 ? 2 : mSamFreqType;
+ mSampleRates = new int[numFreqs];
+ for (int index = 0; index < numFreqs; index++) {
+ mSampleRates[index] = stream.unpackUsbTriple();
+ }
+
+ return mLength;
+ }
+}
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbASGeneral.java b/services/usb/java/com/android/server/usb/descriptors/UsbASGeneral.java
new file mode 100644
index 000000000000..c4f42d318213
--- /dev/null
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbASGeneral.java
@@ -0,0 +1,58 @@
+/*
+ * 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.server.usb.descriptors;
+
+/**
+ * @hide
+ * An audio class-specific General interface.
+ * see audio10.pdf section 4.5.2
+ */
+public class UsbASGeneral extends UsbACInterface {
+ private static final String TAG = "ACGeneral";
+
+ // audio10.pdf - section 4.5.2
+ private byte mTerminalLink; // 3:1 The Terminal ID of the Terminal to which the endpoint
+ // of this interface is connected.
+ private byte mDelay; // 4:1 Delay introduced by the data path (see Section 3.4,
+ // “Inter Channel Synchronization”). Expressed in number of frames.
+ private int mFormatTag; // 5:2 The Audio Data Format that has to be used to communicate
+ // with this interface.
+
+ public UsbASGeneral(int length, byte type, byte subtype, byte subclass) {
+ super(length, type, subtype, subclass);
+ }
+
+ public byte getTerminalLink() {
+ return mTerminalLink;
+ }
+
+ public byte getDelay() {
+ return mDelay;
+ }
+
+ public int getFormatTag() {
+ return mFormatTag;
+ }
+
+ @Override
+ public int parseRawDescriptors(ByteStream stream) {
+ mTerminalLink = stream.getByte();
+ mDelay = stream.getByte();
+ mFormatTag = stream.unpackUsbWord();
+
+ return mLength;
+ }
+}
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbBinaryParser.java b/services/usb/java/com/android/server/usb/descriptors/UsbBinaryParser.java
new file mode 100644
index 000000000000..185cee20b090
--- /dev/null
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbBinaryParser.java
@@ -0,0 +1,72 @@
+/*
+ * 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.server.usb.descriptors;
+
+import android.hardware.usb.UsbDeviceConnection;
+import android.util.Log;
+
+import com.android.server.usb.descriptors.report.UsbStrings;
+
+/**
+ * @hide
+ * A class that just walks the descriptors and does a hex dump of the contained values.
+ * Usefull as a debugging tool.
+ */
+public class UsbBinaryParser {
+ private static final String TAG = "UsbBinaryParser";
+ private static final boolean LOGGING = false;
+
+ private void dumpDescriptor(ByteStream stream, int length, byte type, StringBuilder builder) {
+
+ // Log
+ if (LOGGING) {
+ Log.i(TAG, "l:" + length + " t:" + Integer.toHexString(type) + " "
+ + UsbStrings.getDescriptorName(type));
+ StringBuilder sb = new StringBuilder();
+ for (int index = 2; index < length; index++) {
+ sb.append("0x" + Integer.toHexString(stream.getByte() & 0xFF) + " ");
+ }
+ Log.i(TAG, sb.toString());
+ } else {
+ // Screen Dump
+ builder.append("<p>");
+ builder.append("<b> l:" + length
+ + " t:0x" + Integer.toHexString(type) + " "
+ + UsbStrings.getDescriptorName(type) + "</b><br>");
+ for (int index = 2; index < length; index++) {
+ builder.append("0x" + Integer.toHexString(stream.getByte() & 0xFF) + " ");
+ }
+ builder.append("</p>");
+ }
+ }
+
+ /**
+ * Walk through descriptor stream and generate an HTML text report of the contents.
+ * TODO: This should be done in the model of UsbDescriptorsParser/Reporter model.
+ */
+ public void parseDescriptors(UsbDeviceConnection connection, byte[] descriptors,
+ StringBuilder builder) {
+
+ builder.append("<tt>");
+ ByteStream stream = new ByteStream(descriptors);
+ while (stream.available() > 0) {
+ int length = (int) stream.getByte() & 0x000000FF;
+ byte type = stream.getByte();
+ dumpDescriptor(stream, length, type, builder);
+ }
+ builder.append("</tt>");
+ }
+}
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbConfigDescriptor.java b/services/usb/java/com/android/server/usb/descriptors/UsbConfigDescriptor.java
new file mode 100644
index 000000000000..8ae6d0f1ee7e
--- /dev/null
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbConfigDescriptor.java
@@ -0,0 +1,75 @@
+/*
+ * 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.server.usb.descriptors;
+
+/**
+ * @hide
+ * An USB Config Descriptor.
+ * see usb11.pdf section 9.6.2
+ */
+public class UsbConfigDescriptor extends UsbDescriptor {
+ private static final String TAG = "Config";
+
+ private int mTotalLength; // 2:2 Total length in bytes of data returned
+ private byte mNumInterfaces; // 4:1 Number of Interfaces
+ private byte mConfigValue; // 5:1 Value to use as an argument to select this configuration
+ private byte mConfigIndex; // 6:1 Index of String Descriptor describing this configuration
+ private byte mAttribs; // 7:1 D7 Reserved, set to 1. (USB 1.0 Bus Powered)
+ // D6 Self Powered
+ // D5 Remote Wakeup
+ // D4..0 Reserved, set to 0.
+ private byte mMaxPower; // 8:1 Maximum Power Consumption in 2mA units
+
+ UsbConfigDescriptor(int length, byte type) {
+ super(length, type);
+ }
+
+ public int getTotalLength() {
+ return mTotalLength;
+ }
+
+ public byte getNumInterfaces() {
+ return mNumInterfaces;
+ }
+
+ public byte getConfigValue() {
+ return mConfigValue;
+ }
+
+ public byte getConfigIndex() {
+ return mConfigIndex;
+ }
+
+ public byte getAttribs() {
+ return mAttribs;
+ }
+
+ public byte getMaxPower() {
+ return mMaxPower;
+ }
+
+ @Override
+ public int parseRawDescriptors(ByteStream stream) {
+ mTotalLength = stream.unpackUsbWord();
+ mNumInterfaces = stream.getByte();
+ mConfigValue = stream.getByte();
+ mConfigIndex = stream.getByte();
+ mAttribs = stream.getByte();
+ mMaxPower = stream.getByte();
+
+ return mLength;
+ }
+}
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbDescriptor.java b/services/usb/java/com/android/server/usb/descriptors/UsbDescriptor.java
new file mode 100644
index 000000000000..63b2d7f6aed7
--- /dev/null
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbDescriptor.java
@@ -0,0 +1,223 @@
+/*
+ * 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.server.usb.descriptors;
+
+import android.hardware.usb.UsbConstants;
+import android.hardware.usb.UsbDeviceConnection;
+import android.util.Log;
+
+/*
+ * Some notes about UsbDescriptor and its subclasses.
+ *
+ * It is assumed that the user of the UsbDescriptorParser knows what they are doing
+ * so NO PROTECTION is implemented against "improper" use. Such uses are specifically:
+ * allocating a UsbDescriptor (subclass) object outside of the context of parsing/reading
+ * a rawdescriptor stream and perhaps accessing fields which have not been inialized (by
+ * parsing/reading or course).
+ */
+
+/**
+ * @hide
+ * Common superclass for all USB Descriptors.
+ */
+public abstract class UsbDescriptor {
+ private static final String TAG = "Descriptor";
+
+ protected final int mLength; // 0:1 bLength Number Size of the Descriptor in Bytes (18 bytes)
+ // we store this as an int because Java bytes are SIGNED.
+ protected final byte mType; // 1:1 bDescriptorType Constant Device Descriptor (0x01)
+
+ private byte[] mRawData;
+
+ private static final int SIZE_STRINGBUFFER = 256;
+ private static byte[] sStringBuffer = new byte[SIZE_STRINGBUFFER];
+
+ // Status
+ public static final int STATUS_UNPARSED = 0;
+ public static final int STATUS_PARSED_OK = 1;
+ public static final int STATUS_PARSED_UNDERRUN = 2;
+ public static final int STATUS_PARSED_OVERRUN = 3;
+ private int mStatus = STATUS_UNPARSED;
+
+ private static String[] sStatusStrings = {
+ "UNPARSED", "PARSED - OK", "PARSED - UNDERRUN", "PARSED - OVERRUN"};
+
+ // Descriptor Type IDs
+ public static final byte DESCRIPTORTYPE_DEVICE = 0x01; // 1
+ public static final byte DESCRIPTORTYPE_CONFIG = 0x02; // 2
+ public static final byte DESCRIPTORTYPE_STRING = 0x03; // 3
+ public static final byte DESCRIPTORTYPE_INTERFACE = 0x04; // 4
+ public static final byte DESCRIPTORTYPE_ENDPOINT = 0x05; // 5
+ public static final byte DESCRIPTORTYPE_INTERFACEASSOC = 0x0B; // 11
+ public static final byte DESCRIPTORTYPE_BOS = 0x0F; // 15
+ public static final byte DESCRIPTORTYPE_CAPABILITY = 0x10; // 16
+
+ public static final byte DESCRIPTORTYPE_HID = 0x21; // 33
+ public static final byte DESCRIPTORTYPE_REPORT = 0x22; // 34
+ public static final byte DESCRIPTORTYPE_PHYSICAL = 0x23; // 35
+ public static final byte DESCRIPTORTYPE_AUDIO_INTERFACE = 0x24; // 36
+ public static final byte DESCRIPTORTYPE_AUDIO_ENDPOINT = 0x25; // 37
+ public static final byte DESCRIPTORTYPE_HUB = 0x29; // 41
+ public static final byte DESCRIPTORTYPE_SUPERSPEED_HUB = 0x2A; // 42
+ public static final byte DESCRIPTORTYPE_ENDPOINT_COMPANION = 0x30; // 48
+
+ // Class IDs
+ public static final byte CLASSID_DEVICE = 0x00;
+ public static final byte CLASSID_AUDIO = 0x01;
+ public static final byte CLASSID_COM = 0x02;
+ public static final byte CLASSID_HID = 0x03;
+ // public static final byte CLASSID_??? = 0x04;
+ public static final byte CLASSID_PHYSICAL = 0x05;
+ public static final byte CLASSID_IMAGE = 0x06;
+ public static final byte CLASSID_PRINTER = 0x07;
+ public static final byte CLASSID_STORAGE = 0x08;
+ public static final byte CLASSID_HUB = 0x09;
+ public static final byte CLASSID_CDC_CONTROL = 0x0A;
+ public static final byte CLASSID_SMART_CARD = 0x0B;
+ //public static final byte CLASSID_??? = 0x0C;
+ public static final byte CLASSID_SECURITY = 0x0D;
+ public static final byte CLASSID_VIDEO = 0x0E;
+ public static final byte CLASSID_HEALTHCARE = 0x0F;
+ public static final byte CLASSID_AUDIOVIDEO = 0x10;
+ public static final byte CLASSID_BILLBOARD = 0x11;
+ public static final byte CLASSID_TYPECBRIDGE = 0x12;
+ public static final byte CLASSID_DIAGNOSTIC = (byte) 0xDC;
+ public static final byte CLASSID_WIRELESS = (byte) 0xE0;
+ public static final byte CLASSID_MISC = (byte) 0xEF;
+ public static final byte CLASSID_APPSPECIFIC = (byte) 0xFE;
+ public static final byte CLASSID_VENDSPECIFIC = (byte) 0xFF;
+
+ // Audio Subclass codes
+ public static final byte AUDIO_SUBCLASS_UNDEFINED = 0x00;
+ public static final byte AUDIO_AUDIOCONTROL = 0x01;
+ public static final byte AUDIO_AUDIOSTREAMING = 0x02;
+ public static final byte AUDIO_MIDISTREAMING = 0x03;
+
+ // Request IDs
+ public static final int REQUEST_GET_STATUS = 0x00;
+ public static final int REQUEST_CLEAR_FEATURE = 0x01;
+ public static final int REQUEST_SET_FEATURE = 0x03;
+ public static final int REQUEST_GET_ADDRESS = 0x05;
+ public static final int REQUEST_GET_DESCRIPTOR = 0x06;
+ public static final int REQUEST_SET_DESCRIPTOR = 0x07;
+ public static final int REQUEST_GET_CONFIGURATION = 0x08;
+ public static final int REQUEST_SET_CONFIGURATION = 0x09;
+
+ /**
+ * @throws IllegalArgumentException
+ */
+ UsbDescriptor(int length, byte type) {
+ // a descriptor has at least a length byte and type byte
+ // one could imagine an empty one otherwise
+ if (length < 2) {
+ // huh?
+ throw new IllegalArgumentException();
+ }
+
+ mLength = length;
+ mType = type;
+ }
+
+ public int getLength() {
+ return mLength;
+ }
+
+ public byte getType() {
+ return mType;
+ }
+
+ public int getStatus() {
+ return mStatus;
+ }
+
+ public void setStatus(int status) {
+ mStatus = status;
+ }
+
+ public String getStatusString() {
+ return sStatusStrings[mStatus];
+ }
+
+ public byte[] getRawData() {
+ return mRawData;
+ }
+
+ /**
+ * Called by the parser for any necessary cleanup.
+ */
+ public void postParse(ByteStream stream) {
+ // Status
+ int bytesRead = stream.getReadCount();
+ if (bytesRead < mLength) {
+ // Too cold...
+ stream.advance(mLength - bytesRead);
+ mStatus = STATUS_PARSED_UNDERRUN;
+ Log.w(TAG, "UNDERRUN t:0x" + Integer.toHexString(mType)
+ + " r:" + bytesRead + " < l:" + mLength);
+ } else if (bytesRead > mLength) {
+ // Too hot...
+ stream.reverse(bytesRead - mLength);
+ mStatus = STATUS_PARSED_OVERRUN;
+ Log.w(TAG, "OVERRRUN t:0x" + Integer.toHexString(mType)
+ + " r:" + bytesRead + " > l:" + mLength);
+ } else {
+ // Just right!
+ mStatus = STATUS_PARSED_OK;
+ }
+ }
+
+ /**
+ * Reads data fields from specified raw-data stream.
+ */
+ public int parseRawDescriptors(ByteStream stream) {
+ int numRead = stream.getReadCount();
+ int dataLen = mLength - numRead;
+ if (dataLen > 0) {
+ mRawData = new byte[dataLen];
+ for (int index = 0; index < dataLen; index++) {
+ mRawData[index] = stream.getByte();
+ }
+ }
+ return mLength;
+ }
+
+ /**
+ * Gets a string for the specified index from the USB Device's string descriptors.
+ */
+ public static String getUsbDescriptorString(UsbDeviceConnection connection, byte strIndex) {
+ String usbStr = "";
+ if (strIndex != 0) {
+ try {
+ int rdo = connection.controlTransfer(
+ UsbConstants.USB_DIR_IN | UsbConstants.USB_TYPE_STANDARD,
+ REQUEST_GET_DESCRIPTOR,
+ (DESCRIPTORTYPE_STRING << 8) | strIndex,
+ 0,
+ sStringBuffer,
+ 0xFF,
+ 0);
+ if (rdo >= 0) {
+ usbStr = new String(sStringBuffer, 2, rdo - 2, "UTF-16LE");
+ } else {
+ usbStr = "?";
+ }
+ } catch (Exception e) {
+ Log.e(TAG, "Can not communicate with USB device", e);
+ }
+ }
+ return usbStr;
+ }
+}
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbDescriptorParser.java b/services/usb/java/com/android/server/usb/descriptors/UsbDescriptorParser.java
new file mode 100644
index 000000000000..7c074dadadf9
--- /dev/null
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbDescriptorParser.java
@@ -0,0 +1,376 @@
+/*
+ * 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.server.usb.descriptors;
+
+import android.util.Log;
+
+import java.util.ArrayList;
+
+/**
+ * @hide
+ * Class for parsing a binary stream of USB Descriptors.
+ */
+public class UsbDescriptorParser {
+ private static final String TAG = "DescriptorParser";
+
+ // Descriptor Objects
+ private ArrayList<UsbDescriptor> mDescriptors = new ArrayList<UsbDescriptor>();
+
+ private UsbDeviceDescriptor mDeviceDescriptor;
+ private UsbInterfaceDescriptor mCurInterfaceDescriptor;
+
+ public UsbDescriptorParser() {}
+
+ /**
+ * The probability (as returned by getHeadsetProbability() at which we conclude
+ * the peripheral is a headset.
+ */
+ private static final float IN_HEADSET_TRIGGER = 0.75f;
+ private static final float OUT_HEADSET_TRIGGER = 0.75f;
+
+ private UsbDescriptor allocDescriptor(ByteStream stream) {
+ stream.resetReadCount();
+
+ int length = (int) stream.getByte() & 0x000000FF;
+ byte type = stream.getByte();
+
+ UsbDescriptor descriptor = null;
+ switch (type) {
+ /*
+ * Standard
+ */
+ case UsbDescriptor.DESCRIPTORTYPE_DEVICE:
+ descriptor = mDeviceDescriptor = new UsbDeviceDescriptor(length, type);
+ break;
+
+ case UsbDescriptor.DESCRIPTORTYPE_CONFIG:
+ descriptor = new UsbConfigDescriptor(length, type);
+ break;
+
+ case UsbDescriptor.DESCRIPTORTYPE_INTERFACE:
+ descriptor = mCurInterfaceDescriptor = new UsbInterfaceDescriptor(length, type);
+ break;
+
+ case UsbDescriptor.DESCRIPTORTYPE_ENDPOINT:
+ descriptor = new UsbEndpointDescriptor(length, type);
+ break;
+
+ /*
+ * HID
+ */
+ case UsbDescriptor.DESCRIPTORTYPE_HID:
+ descriptor = new UsbHIDDescriptor(length, type);
+ break;
+
+ /*
+ * Other
+ */
+ case UsbDescriptor.DESCRIPTORTYPE_INTERFACEASSOC:
+ descriptor = new UsbInterfaceAssoc(length, type);
+ break;
+
+ /*
+ * Audio Class Specific
+ */
+ case UsbDescriptor.DESCRIPTORTYPE_AUDIO_INTERFACE:
+ descriptor = UsbACInterface.allocDescriptor(this, stream, length, type);
+ break;
+
+ case UsbDescriptor.DESCRIPTORTYPE_AUDIO_ENDPOINT:
+ descriptor = UsbACEndpoint.allocDescriptor(this, length, type);
+ break;
+
+ default:
+ break;
+ }
+
+ if (descriptor == null) {
+ // Unknown Descriptor
+ Log.i(TAG, "Unknown Descriptor len:" + length + " type:0x"
+ + Integer.toHexString(type));
+ descriptor = new UsbUnknown(length, type);
+ }
+
+ return descriptor;
+ }
+
+ public UsbDeviceDescriptor getDeviceDescriptor() {
+ return mDeviceDescriptor;
+ }
+
+ public UsbInterfaceDescriptor getCurInterface() {
+ return mCurInterfaceDescriptor;
+ }
+
+ /**
+ * @hide
+ */
+ public boolean parseDescriptors(byte[] descriptors) {
+ try {
+ mDescriptors.clear();
+
+ ByteStream stream = new ByteStream(descriptors);
+ while (stream.available() > 0) {
+ UsbDescriptor descriptor = allocDescriptor(stream);
+ if (descriptor != null) {
+ // Parse
+ descriptor.parseRawDescriptors(stream);
+ mDescriptors.add(descriptor);
+
+ // Clean up
+ descriptor.postParse(stream);
+ }
+ }
+ return true;
+ } catch (Exception ex) {
+ Log.e(TAG, "Exception parsing USB descriptors.", ex);
+ }
+ return false;
+ }
+
+ /**
+ * @hide
+ */
+ public boolean parseDevice(String deviceAddr) {
+ byte[] rawDescriptors = getRawDescriptors(deviceAddr);
+ return rawDescriptors != null && parseDescriptors(rawDescriptors);
+ }
+
+ private native byte[] getRawDescriptors(String deviceAddr);
+
+ public int getParsingSpec() {
+ return mDeviceDescriptor != null ? mDeviceDescriptor.getSpec() : 0;
+ }
+
+ public ArrayList<UsbDescriptor> getDescriptors() {
+ return mDescriptors;
+ }
+
+ /**
+ * @hide
+ */
+ public ArrayList<UsbDescriptor> getDescriptors(byte type) {
+ ArrayList<UsbDescriptor> list = new ArrayList<UsbDescriptor>();
+ for (UsbDescriptor descriptor : mDescriptors) {
+ if (descriptor.getType() == type) {
+ list.add(descriptor);
+ }
+ }
+ return list;
+ }
+
+ /**
+ * @hide
+ */
+ public ArrayList<UsbDescriptor> getInterfaceDescriptorsForClass(byte usbClass) {
+ ArrayList<UsbDescriptor> list = new ArrayList<UsbDescriptor>();
+ for (UsbDescriptor descriptor : mDescriptors) {
+ // ensure that this isn't an unrecognized DESCRIPTORTYPE_INTERFACE
+ if (descriptor.getType() == UsbDescriptor.DESCRIPTORTYPE_INTERFACE) {
+ if (descriptor instanceof UsbInterfaceDescriptor) {
+ UsbInterfaceDescriptor intrDesc = (UsbInterfaceDescriptor) descriptor;
+ if (intrDesc.getUsbClass() == usbClass) {
+ list.add(descriptor);
+ }
+ } else {
+ Log.w(TAG, "Unrecognized Interface l:" + descriptor.getLength()
+ + " t:0x" + Integer.toHexString(descriptor.getType()));
+ }
+ }
+ }
+ return list;
+ }
+
+ /**
+ * @hide
+ */
+ public ArrayList<UsbDescriptor> getACInterfaceDescriptors(byte subtype, byte subclass) {
+ ArrayList<UsbDescriptor> list = new ArrayList<UsbDescriptor>();
+ for (UsbDescriptor descriptor : mDescriptors) {
+ if (descriptor.getType() == UsbDescriptor.DESCRIPTORTYPE_AUDIO_INTERFACE) {
+ // ensure that this isn't an unrecognized DESCRIPTORTYPE_AUDIO_INTERFACE
+ if (descriptor instanceof UsbACInterface) {
+ UsbACInterface acDescriptor = (UsbACInterface) descriptor;
+ if (acDescriptor.getSubtype() == subtype
+ && acDescriptor.getSubclass() == subclass) {
+ list.add(descriptor);
+ }
+ } else {
+ Log.w(TAG, "Unrecognized Audio Interface l:" + descriptor.getLength()
+ + " t:0x" + Integer.toHexString(descriptor.getType()));
+ }
+ }
+ }
+ return list;
+ }
+
+ /**
+ * @hide
+ */
+ public boolean hasHIDDescriptor() {
+ ArrayList<UsbDescriptor> descriptors =
+ getInterfaceDescriptorsForClass(UsbDescriptor.CLASSID_HID);
+ return !descriptors.isEmpty();
+ }
+
+ /**
+ * @hide
+ */
+ public boolean hasMIDIInterface() {
+ ArrayList<UsbDescriptor> descriptors =
+ getInterfaceDescriptorsForClass(UsbDescriptor.CLASSID_AUDIO);
+ for (UsbDescriptor descriptor : descriptors) {
+ // enusure that this isn't an unrecognized interface descriptor
+ if (descriptor instanceof UsbInterfaceDescriptor) {
+ UsbInterfaceDescriptor interfaceDescr = (UsbInterfaceDescriptor) descriptor;
+ if (interfaceDescr.getUsbSubclass() == UsbDescriptor.AUDIO_MIDISTREAMING) {
+ return true;
+ }
+ } else {
+ Log.w(TAG, "Undefined Audio Class Interface l:" + descriptor.getLength()
+ + " t:0x" + Integer.toHexString(descriptor.getType()));
+ }
+ }
+ return false;
+ }
+
+ /**
+ * @hide
+ */
+ public float getInputHeadsetProbability() {
+ if (hasMIDIInterface()) {
+ return 0.0f;
+ }
+
+ float probability = 0.0f;
+ ArrayList<UsbDescriptor> acDescriptors;
+
+ // Look for a microphone
+ boolean hasMic = false;
+ acDescriptors = getACInterfaceDescriptors(UsbACInterface.ACI_INPUT_TERMINAL,
+ UsbACInterface.AUDIO_AUDIOCONTROL);
+ for (UsbDescriptor descriptor : acDescriptors) {
+ if (descriptor instanceof UsbACInputTerminal) {
+ UsbACInputTerminal inDescr = (UsbACInputTerminal) descriptor;
+ if (inDescr.getTerminalType() == UsbTerminalTypes.TERMINAL_IN_MIC
+ || inDescr.getTerminalType() == UsbTerminalTypes.TERMINAL_BIDIR_HEADSET
+ || inDescr.getTerminalType() == UsbTerminalTypes.TERMINAL_BIDIR_UNDEFINED
+ || inDescr.getTerminalType() == UsbTerminalTypes.TERMINAL_EXTERN_LINE) {
+ hasMic = true;
+ break;
+ }
+ } else {
+ Log.w(TAG, "Undefined Audio Input terminal l:" + descriptor.getLength()
+ + " t:0x" + Integer.toHexString(descriptor.getType()));
+ }
+ }
+
+ // Look for a "speaker"
+ boolean hasSpeaker = false;
+ acDescriptors =
+ getACInterfaceDescriptors(UsbACInterface.ACI_OUTPUT_TERMINAL,
+ UsbACInterface.AUDIO_AUDIOCONTROL);
+ for (UsbDescriptor descriptor : acDescriptors) {
+ if (descriptor instanceof UsbACOutputTerminal) {
+ UsbACOutputTerminal outDescr = (UsbACOutputTerminal) descriptor;
+ if (outDescr.getTerminalType() == UsbTerminalTypes.TERMINAL_OUT_SPEAKER
+ || outDescr.getTerminalType()
+ == UsbTerminalTypes.TERMINAL_OUT_HEADPHONES
+ || outDescr.getTerminalType() == UsbTerminalTypes.TERMINAL_BIDIR_HEADSET) {
+ hasSpeaker = true;
+ break;
+ }
+ } else {
+ Log.w(TAG, "Undefined Audio Output terminal l:" + descriptor.getLength()
+ + " t:0x" + Integer.toHexString(descriptor.getType()));
+ }
+ }
+
+ if (hasMic && hasSpeaker) {
+ probability += 0.75f;
+ }
+
+ if (hasMic && hasHIDDescriptor()) {
+ probability += 0.25f;
+ }
+
+ return probability;
+ }
+
+ /**
+ * getInputHeadsetProbability() reports a probability of a USB Input peripheral being a
+ * headset. The probability range is between 0.0f (definitely NOT a headset) and
+ * 1.0f (definitely IS a headset). A probability of 0.75f seems sufficient
+ * to count on the peripheral being a headset.
+ */
+ public boolean isInputHeadset() {
+ return getInputHeadsetProbability() >= IN_HEADSET_TRIGGER;
+ }
+
+ /**
+ * @hide
+ */
+ public float getOutputHeadsetProbability() {
+ if (hasMIDIInterface()) {
+ return 0.0f;
+ }
+
+ float probability = 0.0f;
+ ArrayList<UsbDescriptor> acDescriptors;
+
+ // Look for a "speaker"
+ boolean hasSpeaker = false;
+ acDescriptors =
+ getACInterfaceDescriptors(UsbACInterface.ACI_OUTPUT_TERMINAL,
+ UsbACInterface.AUDIO_AUDIOCONTROL);
+ for (UsbDescriptor descriptor : acDescriptors) {
+ if (descriptor instanceof UsbACOutputTerminal) {
+ UsbACOutputTerminal outDescr = (UsbACOutputTerminal) descriptor;
+ if (outDescr.getTerminalType() == UsbTerminalTypes.TERMINAL_OUT_SPEAKER
+ || outDescr.getTerminalType()
+ == UsbTerminalTypes.TERMINAL_OUT_HEADPHONES
+ || outDescr.getTerminalType() == UsbTerminalTypes.TERMINAL_BIDIR_HEADSET) {
+ hasSpeaker = true;
+ break;
+ }
+ } else {
+ Log.w(TAG, "Undefined Audio Output terminal l:" + descriptor.getLength()
+ + " t:0x" + Integer.toHexString(descriptor.getType()));
+ }
+ }
+
+ if (hasSpeaker) {
+ probability += 0.75f;
+ }
+
+ if (hasSpeaker && hasHIDDescriptor()) {
+ probability += 0.25f;
+ }
+
+ return probability;
+ }
+
+ /**
+ * getOutputHeadsetProbability() reports a probability of a USB Output peripheral being a
+ * headset. The probability range is between 0.0f (definitely NOT a headset) and
+ * 1.0f (definitely IS a headset). A probability of 0.75f seems sufficient
+ * to count on the peripheral being a headset.
+ */
+ public boolean isOutputHeadset() {
+ return getOutputHeadsetProbability() >= OUT_HEADSET_TRIGGER;
+ }
+
+}
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbDeviceDescriptor.java b/services/usb/java/com/android/server/usb/descriptors/UsbDeviceDescriptor.java
new file mode 100644
index 000000000000..90848caba852
--- /dev/null
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbDeviceDescriptor.java
@@ -0,0 +1,109 @@
+/*
+ * 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.server.usb.descriptors;
+
+/**
+ * @hide
+ * A USB Device Descriptor.
+ * see usb11.pdf section 9.6.1
+ */
+/* public */ public class UsbDeviceDescriptor extends UsbDescriptor {
+ private static final String TAG = "Device";
+
+ private int mSpec; // 2:2 bcdUSB 2 BCD USB Specification Number - BCD
+ private byte mDevClass; // 4:1 class code
+ private byte mDevSubClass; // 5:1 subclass code
+ private byte mProtocol; // 6:1 protocol
+ private byte mPacketSize; // 7:1 Maximum Packet Size for Zero Endpoint.
+ // Valid Sizes are 8, 16, 32, 64
+ private int mVendorID; // 8:2 vendor ID
+ private int mProductID; // 10:2 product ID
+ private int mDeviceRelease; // 12:2 Device Release number - BCD
+ private byte mMfgIndex; // 14:1 Index of Manufacturer String Descriptor
+ private byte mProductIndex; // 15:1 Index of Product String Descriptor
+ private byte mSerialNum; // 16:1 Index of Serial Number String Descriptor
+ private byte mNumConfigs; // 17:1 Number of Possible Configurations
+
+ UsbDeviceDescriptor(int length, byte type) {
+ super(length, type);
+ }
+
+ public int getSpec() {
+ return mSpec;
+ }
+
+ public byte getDevClass() {
+ return mDevClass;
+ }
+
+ public byte getDevSubClass() {
+ return mDevSubClass;
+ }
+
+ public byte getProtocol() {
+ return mProtocol;
+ }
+
+ public byte getPacketSize() {
+ return mPacketSize;
+ }
+
+ public int getVendorID() {
+ return mVendorID;
+ }
+
+ public int getProductID() {
+ return mProductID;
+ }
+
+ public int getDeviceRelease() {
+ return mDeviceRelease;
+ }
+
+ public byte getMfgIndex() {
+ return mMfgIndex;
+ }
+
+ public byte getProductIndex() {
+ return mProductIndex;
+ }
+
+ public byte getSerialNum() {
+ return mSerialNum;
+ }
+
+ public byte getNumConfigs() {
+ return mNumConfigs;
+ }
+
+ @Override
+ public int parseRawDescriptors(ByteStream stream) {
+ mSpec = stream.unpackUsbWord();
+ mDevClass = stream.getByte();
+ mDevSubClass = stream.getByte();
+ mProtocol = stream.getByte();
+ mPacketSize = stream.getByte();
+ mVendorID = stream.unpackUsbWord();
+ mProductID = stream.unpackUsbWord();
+ mDeviceRelease = stream.unpackUsbWord();
+ mMfgIndex = stream.getByte();
+ mProductIndex = stream.getByte();
+ mSerialNum = stream.getByte();
+ mNumConfigs = stream.getByte();
+
+ return mLength;
+ }
+}
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbEndpointDescriptor.java b/services/usb/java/com/android/server/usb/descriptors/UsbEndpointDescriptor.java
new file mode 100644
index 000000000000..def670093e6e
--- /dev/null
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbEndpointDescriptor.java
@@ -0,0 +1,117 @@
+/*
+ * 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.server.usb.descriptors;
+
+/**
+ * @hide
+ * A Usb Endpoint Descriptor.
+ * see usb11.pdf section 9.6.4
+ */
+public class UsbEndpointDescriptor extends UsbDescriptor {
+ private static final String TAG = "EndPoint";
+
+ public static final byte MASK_ENDPOINT_ADDRESS = 0b0001111;
+ public static final byte MASK_ENDPOINT_DIRECTION = (byte) 0b10000000;
+ public static final byte DIRECTION_OUTPUT = 0x00;
+ public static final byte DIRECTION_INPUT = (byte) 0x80;
+
+ public static final byte MASK_ATTRIBS_TRANSTYPE = 0b00000011;
+ public static final byte TRANSTYPE_CONTROL = 0x00;
+ public static final byte TRANSTYPE_ISO = 0x01;
+ public static final byte TRANSTYPE_BULK = 0x02;
+ public static final byte TRANSTYPE_INTERRUPT = 0x03;
+
+ public static final byte MASK_ATTRIBS_SYNCTYPE = 0b00001100;
+ public static final byte SYNCTYPE_NONE = 0b00000000;
+ public static final byte SYNCTYPE_ASYNC = 0b00000100;
+ public static final byte SYNCTYPE_ADAPTSYNC = 0b00001000;
+ public static final byte SYNCTYPE_RESERVED = 0b00001100;
+
+ public static final byte MASK_ATTRIBS_USEAGE = 0b00110000;
+ public static final byte USEAGE_DATA = 0b00000000;
+ public static final byte USEAGE_FEEDBACK = 0b00010000;
+ public static final byte USEAGE_EXPLICIT = 0b00100000;
+ public static final byte USEAGE_RESERVED = 0b00110000;
+
+ private byte mEndpointAddress; // 2:1 Endpoint Address
+ // Bits 0..3b Endpoint Number.
+ // Bits 4..6b Reserved. Set to Zero
+ // Bits 7 Direction 0 = Out, 1 = In
+ // (Ignored for Control Endpoints)
+ private byte mAttributes; // 3:1 Various flags
+ // Bits 0..1 Transfer Type:
+ // 00 = Control, 01 = Isochronous, 10 = Bulk, 11 = Interrupt
+ // Bits 2..7 are reserved. If Isochronous endpoint,
+ // Bits 3..2 = Synchronisation Type (Iso Mode)
+ // 00 = No Synchonisation
+ // 01 = Asynchronous
+ // 10 = Adaptive
+ // 11 = Synchronous
+ // Bits 5..4 = Usage Type (Iso Mode)
+ // 00: Data Endpoint
+ // 01:Feedback Endpoint 10
+ // Explicit Feedback Data Endpoint
+ // 11: Reserved
+ private int mPacketSize; // 4:2 Maximum Packet Size this endpoint is capable of
+ // sending or receiving
+ private byte mInterval; // 6:1 Interval for polling endpoint data transfers. Value in
+ // frame counts.
+ // Ignored for Bulk & Control Endpoints. Isochronous must equal
+ // 1 and field may range from 1 to 255 for interrupt endpoints.
+ private byte mRefresh;
+ private byte mSyncAddress;
+
+ public UsbEndpointDescriptor(int length, byte type) {
+ super(length, type);
+ }
+
+ public byte getEndpointAddress() {
+ return mEndpointAddress;
+ }
+
+ public byte getAttributes() {
+ return mAttributes;
+ }
+
+ public int getPacketSize() {
+ return mPacketSize;
+ }
+
+ public byte getInterval() {
+ return mInterval;
+ }
+
+ public byte getRefresh() {
+ return mRefresh;
+ }
+
+ public byte getSyncAddress() {
+ return mSyncAddress;
+ }
+
+ @Override
+ public int parseRawDescriptors(ByteStream stream) {
+ mEndpointAddress = stream.getByte();
+ mAttributes = stream.getByte();
+ mPacketSize = stream.unpackUsbWord();
+ mInterval = stream.getByte();
+ if (mLength == 9) {
+ mRefresh = stream.getByte();
+ mSyncAddress = stream.getByte();
+ }
+ return mLength;
+ }
+}
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbHIDDescriptor.java b/services/usb/java/com/android/server/usb/descriptors/UsbHIDDescriptor.java
new file mode 100644
index 000000000000..56c07ec9a071
--- /dev/null
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbHIDDescriptor.java
@@ -0,0 +1,70 @@
+/*
+ * 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.server.usb.descriptors;
+
+/**
+ * @hide
+ * A USB HID (Human Interface Descriptor).
+ * see HID1_11.pdf - 6.2.1
+ */
+public class UsbHIDDescriptor extends UsbDescriptor {
+ private static final String TAG = "HID";
+
+ private int mRelease; // 2:2 the HID Class Specification release.
+ private byte mCountryCode; // 4:1 country code of the localized hardware.
+ private byte mNumDescriptors; // number of descriptors (always at least one
+ // i.e. Report descriptor.)
+ private byte mDescriptorType; // 6:1 type of class descriptor.
+ // See Section 7.1.2: Set_Descriptor
+ // Request for a table of class descriptor constants.
+ private int mDescriptorLen; // 7:2 Numeric expression that is the total size of
+ // the Report descriptor.
+
+ public UsbHIDDescriptor(int length, byte type) {
+ super(length, type);
+ }
+
+ public int getRelease() {
+ return mRelease;
+ }
+
+ public byte getCountryCode() {
+ return mCountryCode;
+ }
+
+ public byte getNumDescriptors() {
+ return mNumDescriptors;
+ }
+
+ public byte getDescriptorType() {
+ return mDescriptorType;
+ }
+
+ public int getDescriptorLen() {
+ return mDescriptorLen;
+ }
+
+ @Override
+ public int parseRawDescriptors(ByteStream stream) {
+ mRelease = stream.unpackUsbWord();
+ mCountryCode = stream.getByte();
+ mNumDescriptors = stream.getByte();
+ mDescriptorType = stream.getByte();
+ mDescriptorLen = stream.unpackUsbWord();
+
+ return mLength;
+ }
+}
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbInterfaceAssoc.java b/services/usb/java/com/android/server/usb/descriptors/UsbInterfaceAssoc.java
new file mode 100644
index 000000000000..4b18a01b1c8b
--- /dev/null
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbInterfaceAssoc.java
@@ -0,0 +1,73 @@
+/*
+ * 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.server.usb.descriptors;
+
+/**
+ * @hide
+ * A USB Interface Association Descriptor.
+ * found this one here: http://www.usb.org/developers/docs/whitepapers/iadclasscode_r10.pdf
+ * also: https://msdn.microsoft.com/en-us/library/windows/hardware/ff540054(v=vs.85).aspx
+ */
+public class UsbInterfaceAssoc extends UsbDescriptor {
+ private static final String TAG = "InterfaceAssoc";
+
+ private byte mFirstInterface;
+ private byte mInterfaceCount;
+ private byte mFunctionClass;
+ private byte mFunctionSubClass;
+ private byte mFunctionProtocol;
+ private byte mFunction;
+
+ public UsbInterfaceAssoc(int length, byte type) {
+ super(length, type);
+ }
+
+ public byte getFirstInterface() {
+ return mFirstInterface;
+ }
+
+ public byte getInterfaceCount() {
+ return mInterfaceCount;
+ }
+
+ public byte getFunctionClass() {
+ return mFunctionClass;
+ }
+
+ public byte getFunctionSubClass() {
+ return mFunctionSubClass;
+ }
+
+ public byte getFunctionProtocol() {
+ return mFunctionProtocol;
+ }
+
+ public byte getFunction() {
+ return mFunction;
+ }
+
+ @Override
+ public int parseRawDescriptors(ByteStream stream) {
+ mFirstInterface = stream.getByte();
+ mInterfaceCount = stream.getByte();
+ mFunctionClass = stream.getByte();
+ mFunctionSubClass = stream.getByte();
+ mFunctionProtocol = stream.getByte();
+ mFunction = stream.getByte();
+
+ return mLength;
+ }
+}
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbInterfaceDescriptor.java b/services/usb/java/com/android/server/usb/descriptors/UsbInterfaceDescriptor.java
new file mode 100644
index 000000000000..21b5e0cbaa1b
--- /dev/null
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbInterfaceDescriptor.java
@@ -0,0 +1,78 @@
+/*
+ * 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.server.usb.descriptors;
+
+/**
+ * @hide
+ * A common super-class for all USB Interface Descritor subtypes.
+ * see usb11.pdf section 9.6.3
+ */
+public class UsbInterfaceDescriptor extends UsbDescriptor {
+ private static final String TAG = "Interface";
+
+ protected byte mInterfaceNumber; // 2:1 Number of Interface
+ protected byte mAlternateSetting; // 3:1 Value used to select alternative setting
+ protected byte mNumEndpoints; // 4:1 Number of Endpoints used for this interface
+ protected byte mUsbClass; // 5:1 Class Code
+ protected byte mUsbSubclass; // 6:1 Subclass Code
+ protected byte mProtocol; // 7:1 Protocol Code
+ protected byte mDescrIndex; // 8:1 Index of String Descriptor Describing this interface
+
+ UsbInterfaceDescriptor(int length, byte type) {
+ super(length, type);
+ }
+
+ @Override
+ public int parseRawDescriptors(ByteStream stream) {
+ mInterfaceNumber = stream.getByte();
+ mAlternateSetting = stream.getByte();
+ mNumEndpoints = stream.getByte();
+ mUsbClass = stream.getByte();
+ mUsbSubclass = stream.getByte();
+ mProtocol = stream.getByte();
+ mDescrIndex = stream.getByte();
+
+ return mLength;
+ }
+
+ public byte getInterfaceNumber() {
+ return mInterfaceNumber;
+ }
+
+ public byte getAlternateSetting() {
+ return mAlternateSetting;
+ }
+
+ public byte getNumEndpoints() {
+ return mNumEndpoints;
+ }
+
+ public byte getUsbClass() {
+ return mUsbClass;
+ }
+
+ public byte getUsbSubclass() {
+ return mUsbSubclass;
+ }
+
+ public byte getProtocol() {
+ return mProtocol;
+ }
+
+ public byte getDescrIndex() {
+ return mDescrIndex;
+ }
+}
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbMSMidiHeader.java b/services/usb/java/com/android/server/usb/descriptors/UsbMSMidiHeader.java
new file mode 100644
index 000000000000..4452b23cb6ae
--- /dev/null
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbMSMidiHeader.java
@@ -0,0 +1,36 @@
+/*
+ * 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.server.usb.descriptors;
+
+/**
+ * @hide
+ * An audio class-specific Midi Streaming Interface.
+ * see midi10.pdf section 6.1.2.1
+ */
+public class UsbMSMidiHeader extends UsbACInterface {
+ private static final String TAG = "MSMidiHeader";
+
+ public UsbMSMidiHeader(int length, byte type, byte subtype, byte subclass) {
+ super(length, type, subtype, subclass);
+ }
+
+ @Override
+ public int parseRawDescriptors(ByteStream stream) {
+ // TODO - read data memebers
+ stream.advance(mLength - stream.getReadCount());
+ return mLength;
+ }
+}
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbMSMidiInputJack.java b/services/usb/java/com/android/server/usb/descriptors/UsbMSMidiInputJack.java
new file mode 100644
index 000000000000..2d33ba7727dd
--- /dev/null
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbMSMidiInputJack.java
@@ -0,0 +1,36 @@
+/*
+ * 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.server.usb.descriptors;
+
+/**
+ * @hide
+ * An audio class-specific Midi Input Jack Interface.
+ * see midi10.pdf section B.4.3
+ */
+public class UsbMSMidiInputJack extends UsbACInterface {
+ private static final String TAG = "MSMidiInputJack";
+
+ UsbMSMidiInputJack(int length, byte type, byte subtype, byte subclass) {
+ super(length, type, subtype, subclass);
+ }
+
+ @Override
+ public int parseRawDescriptors(ByteStream stream) {
+ // TODO - read data memebers
+ stream.advance(mLength - stream.getReadCount());
+ return mLength;
+ }
+}
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbMSMidiOutputJack.java b/services/usb/java/com/android/server/usb/descriptors/UsbMSMidiOutputJack.java
new file mode 100644
index 000000000000..bd2dc11d57df
--- /dev/null
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbMSMidiOutputJack.java
@@ -0,0 +1,36 @@
+/*
+ * 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.server.usb.descriptors;
+
+/**
+ * @hide
+ * An audio class-specific Midi Output Jack Interface.
+ * see midi10.pdf section B.4.4
+ */
+public class UsbMSMidiOutputJack extends UsbACInterface {
+ private static final String TAG = "MSMidiOutputJack";
+
+ public UsbMSMidiOutputJack(int length, byte type, byte subtype, byte subclass) {
+ super(length, type, subtype, subclass);
+ }
+
+ @Override
+ public int parseRawDescriptors(ByteStream stream) {
+ // TODO - read data memebers
+ stream.advance(mLength - stream.getReadCount());
+ return mLength;
+ }
+}
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbTerminalTypes.java b/services/usb/java/com/android/server/usb/descriptors/UsbTerminalTypes.java
new file mode 100644
index 000000000000..b5214625126a
--- /dev/null
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbTerminalTypes.java
@@ -0,0 +1,93 @@
+/*
+ * 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.server.usb.descriptors;
+
+/**
+ * @hide
+ * A class for decoding information in Terminal Descriptors.
+ * see termt10.pdf
+ */
+public class UsbTerminalTypes {
+ private static final String TAG = "TerminalTypes";
+
+ // USB
+ public static final int TERMINAL_USB_STREAMING = 0x0101;
+
+ // Inputs
+ public static final int TERMINAL_IN_UNDEFINED = 0x0200;
+ public static final int TERMINAL_IN_MIC = 0x0201;
+ public static final int TERMINAL_IN_DESKTOP_MIC = 0x0202;
+ public static final int TERMINAL_IN_PERSONAL_MIC = 0x0203;
+ public static final int TERMINAL_IN_OMNI_MIC = 0x0204;
+ public static final int TERMINAL_IN_MIC_ARRAY = 0x0205;
+ public static final int TERMINAL_IN_PROC_MIC_ARRAY = 0x0206;
+
+ // Outputs
+ public static final int TERMINAL_OUT_UNDEFINED = 0x0300;
+ public static final int TERMINAL_OUT_SPEAKER = 0x0301;
+ public static final int TERMINAL_OUT_HEADPHONES = 0x0302;
+ public static final int TERMINAL_OUT_HEADMOUNTED = 0x0303;
+ public static final int TERMINAL_OUT_DESKTOPSPEAKER = 0x0304;
+ public static final int TERMINAL_OUT_ROOMSPEAKER = 0x0305;
+ public static final int TERMINAL_OUT_COMSPEAKER = 0x0306;
+ public static final int TERMINAL_OUT_LFSPEAKER = 0x0307;
+
+ // Bi-directional
+ public static final int TERMINAL_BIDIR_UNDEFINED = 0x0400;
+ public static final int TERMINAL_BIDIR_HANDSET = 0x0401;
+ public static final int TERMINAL_BIDIR_HEADSET = 0x0402;
+ public static final int TERMINAL_BIDIR_SKRPHONE = 0x0403;
+ public static final int TERMINAL_BIDIR_SKRPHONE_SUPRESS = 0x0404;
+ public static final int TERMINAL_BIDIR_SKRPHONE_CANCEL = 0x0405;
+
+ // Telephony
+ public static final int TERMINAL_TELE_UNDEFINED = 0x0500;
+ public static final int TERMINAL_TELE_PHONELINE = 0x0501;
+ public static final int TERMINAL_TELE_PHONE = 0x0502;
+ public static final int TERMINAL_TELE_DOWNLINEPHONE = 0x0503;
+
+ // External
+ public static final int TERMINAL_EXTERN_UNDEFINED = 0x0600;
+ public static final int TERMINAL_EXTERN_ANALOG = 0x0601;
+ public static final int TERMINAL_EXTERN_DIGITAL = 0x0602;
+ public static final int TERMINAL_EXTERN_LINE = 0x0603;
+ public static final int TERMINAL_EXTERN_LEGACY = 0x0604;
+ public static final int TERMINAL_EXTERN_SPIDF = 0x0605;
+ public static final int TERMINAL_EXTERN_1394DA = 0x0606;
+ public static final int TERMINAL_EXTERN_1394DV = 0x0607;
+
+ public static final int TERMINAL_EMBED_UNDEFINED = 0x0700;
+ public static final int TERMINAL_EMBED_CALNOISE = 0x0701;
+ public static final int TERMINAL_EMBED_EQNOISE = 0x0702;
+ public static final int TERMINAL_EMBED_CDPLAYER = 0x0703;
+ public static final int TERMINAL_EMBED_DAT = 0x0704;
+ public static final int TERMINAL_EMBED_DCC = 0x0705;
+ public static final int TERMINAL_EMBED_MINIDISK = 0x0706;
+ public static final int TERMINAL_EMBED_ANALOGTAPE = 0x0707;
+ public static final int TERMINAL_EMBED_PHONOGRAPH = 0x0708;
+ public static final int TERMINAL_EMBED_VCRAUDIO = 0x0709;
+ public static final int TERMINAL_EMBED_VIDDISKAUDIO = 0x070A;
+ public static final int TERMINAL_EMBED_DVDAUDIO = 0x070B;
+ public static final int TERMINAL_EMBED_TVAUDIO = 0x070C;
+ public static final int TERMINAL_EMBED_SATELLITEAUDIO = 0x070D;
+ public static final int TERMINAL_EMBED_CABLEAUDIO = 0x070E;
+ public static final int TERMINAL_EMBED_DSSAUDIO = 0x070F;
+ public static final int TERMINAL_EMBED_RADIOAUDIO = 0x0710;
+ public static final int TERMINAL_EMBED_RADIOTRANSMITTER = 0x0711;
+ public static final int TERMINAL_EMBED_MULTITRACK = 0x0712;
+ public static final int TERMINAL_EMBED_SYNTHESIZER = 0x0713;
+
+}
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbUnknown.java b/services/usb/java/com/android/server/usb/descriptors/UsbUnknown.java
new file mode 100644
index 000000000000..a6fe8bba3508
--- /dev/null
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbUnknown.java
@@ -0,0 +1,28 @@
+/*
+ * 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.server.usb.descriptors;
+
+/**
+ * @hide
+ * A holder for any unrecognized descriptor encountered in the descriptor stream.
+ */
+public class UsbUnknown extends UsbDescriptor {
+ static final String TAG = "Unknown";
+
+ public UsbUnknown(int length, byte type) {
+ super(length, type);
+ }
+}
diff --git a/services/usb/java/com/android/server/usb/descriptors/report/HTMLReporter.java b/services/usb/java/com/android/server/usb/descriptors/report/HTMLReporter.java
new file mode 100644
index 000000000000..c98789d880a0
--- /dev/null
+++ b/services/usb/java/com/android/server/usb/descriptors/report/HTMLReporter.java
@@ -0,0 +1,572 @@
+/*
+ * 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.server.usb.descriptors.report;
+
+import android.hardware.usb.UsbDeviceConnection;
+
+import com.android.server.usb.descriptors.UsbACAudioControlEndpoint;
+import com.android.server.usb.descriptors.UsbACAudioStreamEndpoint;
+import com.android.server.usb.descriptors.UsbACFeatureUnit;
+import com.android.server.usb.descriptors.UsbACHeader;
+import com.android.server.usb.descriptors.UsbACInputTerminal;
+import com.android.server.usb.descriptors.UsbACInterface;
+import com.android.server.usb.descriptors.UsbACMidiEndpoint;
+import com.android.server.usb.descriptors.UsbACMixerUnit;
+import com.android.server.usb.descriptors.UsbACOutputTerminal;
+import com.android.server.usb.descriptors.UsbACSelectorUnit;
+import com.android.server.usb.descriptors.UsbACTerminal;
+import com.android.server.usb.descriptors.UsbASFormat;
+import com.android.server.usb.descriptors.UsbASFormatI;
+import com.android.server.usb.descriptors.UsbASFormatII;
+import com.android.server.usb.descriptors.UsbASGeneral;
+import com.android.server.usb.descriptors.UsbConfigDescriptor;
+import com.android.server.usb.descriptors.UsbDescriptor;
+import com.android.server.usb.descriptors.UsbDeviceDescriptor;
+import com.android.server.usb.descriptors.UsbEndpointDescriptor;
+import com.android.server.usb.descriptors.UsbHIDDescriptor;
+import com.android.server.usb.descriptors.UsbInterfaceAssoc;
+import com.android.server.usb.descriptors.UsbInterfaceDescriptor;
+import com.android.server.usb.descriptors.UsbMSMidiHeader;
+import com.android.server.usb.descriptors.UsbMSMidiInputJack;
+import com.android.server.usb.descriptors.UsbMSMidiOutputJack;
+import com.android.server.usb.descriptors.UsbUnknown;
+
+/**
+ * Implements the Reporter inteface to provide HTML reporting for UsbDescriptor subclasses.
+ */
+public class HTMLReporter implements Reporter {
+ private final StringBuilder mStringBuilder;
+ private final UsbDeviceConnection mConnection;
+
+ public HTMLReporter(StringBuilder stringBuilder, UsbDeviceConnection connection) {
+ mStringBuilder = stringBuilder;
+ mConnection = connection;
+ }
+
+ /*
+ * HTML Helpers
+ */
+ private void writeHeader(int level, String text) {
+ mStringBuilder
+ .append("<h").append(level).append('>')
+ .append(text)
+ .append("</h").append(level).append('>');
+ }
+
+ private void openParagraph() {
+ mStringBuilder.append("<p>");
+ }
+
+ private void closeParagraph() {
+ mStringBuilder.append("</p>");
+ }
+
+ private void writeParagraph(String text) {
+ openParagraph();
+ mStringBuilder.append(text);
+ closeParagraph();
+ }
+
+ private void openList() {
+ mStringBuilder.append("<ul>");
+ }
+
+ private void closeList() {
+ mStringBuilder.append("</ul>");
+ }
+
+ private void openListItem() {
+ mStringBuilder.append("<li>");
+ }
+
+ private void closeListItem() {
+ mStringBuilder.append("</li>");
+ }
+
+ private void writeListItem(String text) {
+ openListItem();
+ mStringBuilder.append(text);
+ closeListItem();
+ }
+
+ /*
+ * Data Formating Helpers
+ */
+ private static String getHexString(byte value) {
+ return "0x" + Integer.toHexString(((int) value) & 0xFF).toUpperCase();
+ }
+
+ private static String getBCDString(int value) {
+ int major = value >> 8;
+ int minor = (value >> 4) & 0x0F;
+ int subminor = value & 0x0F;
+
+ return "" + major + "." + minor + subminor;
+ }
+
+ private static String getHexString(int value) {
+ int intValue = value & 0xFFFF;
+ return "0x" + Integer.toHexString(intValue).toUpperCase();
+ }
+
+ private void dumpHexArray(byte[] rawData, StringBuilder builder) {
+ if (rawData != null) {
+ // Assume the type and Length and perhaps sub-type have been displayed
+ openParagraph();
+ for (int index = 0; index < rawData.length; index++) {
+ builder.append(getHexString(rawData[index]) + " ");
+ }
+ closeParagraph();
+ }
+ }
+
+ /**
+ * Decode ACTUAL UsbDescriptor sub classes and call type-specific report methods.
+ */
+ @Override
+ public void report(UsbDescriptor descriptor) {
+ if (descriptor instanceof UsbDeviceDescriptor) {
+ tsReport((UsbDeviceDescriptor) descriptor);
+ } else if (descriptor instanceof UsbConfigDescriptor) {
+ tsReport((UsbConfigDescriptor) descriptor);
+ } else if (descriptor instanceof UsbInterfaceDescriptor) {
+ tsReport((UsbInterfaceDescriptor) descriptor);
+ } else if (descriptor instanceof UsbEndpointDescriptor) {
+ tsReport((UsbEndpointDescriptor) descriptor);
+ } else if (descriptor instanceof UsbHIDDescriptor) {
+ tsReport((UsbHIDDescriptor) descriptor);
+ } else if (descriptor instanceof UsbACAudioControlEndpoint) {
+ tsReport((UsbACAudioControlEndpoint) descriptor);
+ } else if (descriptor instanceof UsbACAudioStreamEndpoint) {
+ tsReport((UsbACAudioStreamEndpoint) descriptor);
+ } else if (descriptor instanceof UsbACHeader) {
+ tsReport((UsbACHeader) descriptor);
+ } else if (descriptor instanceof UsbACFeatureUnit) {
+ tsReport((UsbACFeatureUnit) descriptor);
+ } else if (descriptor instanceof UsbACInputTerminal) {
+ tsReport((UsbACInputTerminal) descriptor);
+ } else if (descriptor instanceof UsbACOutputTerminal) {
+ tsReport((UsbACOutputTerminal) descriptor);
+ } else if (descriptor instanceof UsbACMidiEndpoint) {
+ tsReport((UsbACMidiEndpoint) descriptor);
+ } else if (descriptor instanceof UsbACMixerUnit) {
+ tsReport((UsbACMixerUnit) descriptor);
+ } else if (descriptor instanceof UsbACSelectorUnit) {
+ tsReport((UsbACSelectorUnit) descriptor);
+ } else if (descriptor instanceof UsbASFormatI) {
+ tsReport((UsbASFormatI) descriptor);
+ } else if (descriptor instanceof UsbASFormatII) {
+ tsReport((UsbASFormatII) descriptor);
+ } else if (descriptor instanceof UsbASFormat) {
+ tsReport((UsbASFormat) descriptor);
+ } else if (descriptor instanceof UsbASGeneral) {
+ tsReport((UsbASGeneral) descriptor);
+ } else if (descriptor instanceof UsbInterfaceAssoc) {
+ tsReport((UsbInterfaceAssoc) descriptor);
+ } else if (descriptor instanceof UsbMSMidiHeader) {
+ tsReport((UsbMSMidiHeader) descriptor);
+ } else if (descriptor instanceof UsbMSMidiInputJack) {
+ tsReport((UsbMSMidiInputJack) descriptor);
+ } else if (descriptor instanceof UsbMSMidiOutputJack) {
+ tsReport((UsbMSMidiOutputJack) descriptor);
+ } else if (descriptor instanceof UsbUnknown) {
+ tsReport((UsbUnknown) descriptor);
+ } else if (descriptor instanceof UsbACInterface) {
+ tsReport((UsbACInterface) descriptor);
+ } else if (descriptor instanceof UsbDescriptor) {
+ tsReport((UsbDescriptor) descriptor);
+ }
+ }
+
+ //
+ // Type-specific report() implementations
+ //
+ private void tsReport(UsbDescriptor descriptor) {
+ int length = descriptor.getLength();
+ byte type = descriptor.getType();
+ int status = descriptor.getStatus();
+
+ String descTypeStr = UsbStrings.getDescriptorName(type);
+ writeParagraph(descTypeStr + ":" + type + " l:" + length + " s:" + status);
+ }
+
+ private void tsReport(UsbDeviceDescriptor descriptor) {
+ writeHeader(1, "Device len:" + descriptor.getLength());
+ openList();
+
+ int spec = descriptor.getSpec();
+ writeListItem("spec:" + getBCDString(spec));
+
+ byte devClass = descriptor.getDevClass();
+ String classStr = UsbStrings.getClassName(devClass);
+ byte devSubClass = descriptor.getDevSubClass();
+ String subClasStr = UsbStrings.getClassName(devSubClass);
+ writeListItem("class " + devClass + ":" + classStr + " subclass"
+ + devSubClass + ":" + subClasStr);
+ writeListItem("vendorID:" + descriptor.getVendorID()
+ + " prodID:" + descriptor.getProductID()
+ + " prodRel:" + getBCDString(descriptor.getDeviceRelease()));
+
+ byte mfgIndex = descriptor.getMfgIndex();
+ String manufacturer = UsbDescriptor.getUsbDescriptorString(mConnection, mfgIndex);
+ byte productIndex = descriptor.getProductIndex();
+ String product = UsbDescriptor.getUsbDescriptorString(mConnection, productIndex);
+
+ writeListItem("mfg " + mfgIndex + ":" + manufacturer
+ + " prod " + productIndex + ":" + product);
+ closeList();
+ }
+
+ private void tsReport(UsbConfigDescriptor descriptor) {
+ writeHeader(2, "Config #" + descriptor.getConfigValue()
+ + " len:" + descriptor.getLength());
+
+ openList();
+ writeListItem(descriptor.getNumInterfaces() + " interfaces.");
+ writeListItem("attribs:" + getHexString(descriptor.getAttribs()));
+ closeList();
+ }
+
+ private void tsReport(UsbInterfaceDescriptor descriptor) {
+ byte usbClass = descriptor.getUsbClass();
+ byte usbSubclass = descriptor.getUsbSubclass();
+ String descr = UsbStrings.getDescriptorName(descriptor.getType());
+ String className = UsbStrings.getClassName(usbClass);
+ String subclassName = "";
+ if (usbClass == UsbDescriptor.CLASSID_AUDIO) {
+ subclassName = UsbStrings.getAudioSubclassName(usbSubclass);
+ }
+
+ writeHeader(2, descr + " #" + descriptor.getInterfaceNumber()
+ + " len:" + descriptor.getLength());
+ String descrStr =
+ UsbDescriptor.getUsbDescriptorString(mConnection, descriptor.getDescrIndex());
+ if (descrStr.length() > 0) {
+ mStringBuilder.append("<br>" + descrStr);
+ }
+ openList();
+ writeListItem("class " + getHexString(usbClass) + ":" + className
+ + " subclass " + getHexString(usbSubclass) + ":" + subclassName);
+ writeListItem("" + descriptor.getNumEndpoints() + " endpoints");
+ closeList();
+ }
+
+ private void tsReport(UsbEndpointDescriptor descriptor) {
+ writeHeader(3, "Endpoint " + getHexString(descriptor.getType())
+ + " len:" + descriptor.getLength());
+ openList();
+
+ byte address = descriptor.getEndpointAddress();
+ writeListItem("address:"
+ + getHexString(address & UsbEndpointDescriptor.MASK_ENDPOINT_ADDRESS)
+ + ((address & UsbEndpointDescriptor.MASK_ENDPOINT_DIRECTION)
+ == UsbEndpointDescriptor.DIRECTION_OUTPUT ? " [out]" : " [in]"));
+
+ byte attributes = descriptor.getAttributes();
+ openListItem();
+ mStringBuilder.append("attribs:" + getHexString(attributes) + " ");
+ switch (attributes & UsbEndpointDescriptor.MASK_ATTRIBS_TRANSTYPE) {
+ case UsbEndpointDescriptor.TRANSTYPE_CONTROL:
+ mStringBuilder.append("Control");
+ break;
+ case UsbEndpointDescriptor.TRANSTYPE_ISO:
+ mStringBuilder.append("Iso");
+ break;
+ case UsbEndpointDescriptor.TRANSTYPE_BULK:
+ mStringBuilder.append("Bulk");
+ break;
+ case UsbEndpointDescriptor.TRANSTYPE_INTERRUPT:
+ mStringBuilder.append("Interrupt");
+ break;
+ }
+ closeListItem();
+
+ // These flags are only relevant for ISO transfer type
+ if ((attributes & UsbEndpointDescriptor.MASK_ATTRIBS_TRANSTYPE)
+ == UsbEndpointDescriptor.TRANSTYPE_ISO) {
+ openListItem();
+ mStringBuilder.append("sync:");
+ switch (attributes & UsbEndpointDescriptor.MASK_ATTRIBS_SYNCTYPE) {
+ case UsbEndpointDescriptor.SYNCTYPE_NONE:
+ mStringBuilder.append("NONE");
+ break;
+ case UsbEndpointDescriptor.SYNCTYPE_ASYNC:
+ mStringBuilder.append("ASYNC");
+ break;
+ case UsbEndpointDescriptor.SYNCTYPE_ADAPTSYNC:
+ mStringBuilder.append("ADAPTIVE ASYNC");
+ break;
+ }
+ closeListItem();
+
+ openListItem();
+ mStringBuilder.append("useage:");
+ switch (attributes & UsbEndpointDescriptor.MASK_ATTRIBS_USEAGE) {
+ case UsbEndpointDescriptor.USEAGE_DATA:
+ mStringBuilder.append("DATA");
+ break;
+ case UsbEndpointDescriptor.USEAGE_FEEDBACK:
+ mStringBuilder.append("FEEDBACK");
+ break;
+ case UsbEndpointDescriptor.USEAGE_EXPLICIT:
+ mStringBuilder.append("EXPLICIT FEEDBACK");
+ break;
+ case UsbEndpointDescriptor.USEAGE_RESERVED:
+ mStringBuilder.append("RESERVED");
+ break;
+ }
+ closeListItem();
+ }
+ writeListItem("package size:" + descriptor.getPacketSize());
+ writeListItem("interval:" + descriptor.getInterval());
+ closeList();
+ }
+
+ private void tsReport(UsbHIDDescriptor descriptor) {
+ String descr = UsbStrings.getDescriptorName(descriptor.getType());
+ writeHeader(2, descr + " len:" + descriptor.getLength());
+ openList();
+ writeListItem("spec:" + getBCDString(descriptor.getRelease()));
+ writeListItem("type:" + getBCDString(descriptor.getDescriptorType()));
+ writeListItem("descriptor.getNumDescriptors() descriptors len:"
+ + descriptor.getDescriptorLen());
+ closeList();
+ }
+
+ private void tsReport(UsbACAudioControlEndpoint descriptor) {
+ writeHeader(3, "AC Audio Control Endpoint:" + getHexString(descriptor.getType())
+ + " length:" + descriptor.getLength());
+ }
+
+ private void tsReport(UsbACAudioStreamEndpoint descriptor) {
+ writeHeader(3, "AC Audio Streaming Endpoint:"
+ + getHexString(descriptor.getType())
+ + " length:" + descriptor.getLength());
+ }
+
+ private void tsReport(UsbACHeader descriptor) {
+ tsReport((UsbACInterface) descriptor);
+
+ openList();
+ writeListItem("spec:" + getBCDString(descriptor.getADCRelease()));
+ int numInterfaces = descriptor.getNumInterfaces();
+ writeListItem("" + numInterfaces + " interfaces");
+ if (numInterfaces > 0) {
+ openListItem();
+ mStringBuilder.append("[");
+ byte[] interfaceNums = descriptor.getInterfaceNums();
+ if (numInterfaces != 0 && interfaceNums != null) {
+ for (int index = 0; index < numInterfaces; index++) {
+ mStringBuilder.append("" + interfaceNums[index]);
+ if (index < numInterfaces - 1) {
+ mStringBuilder.append(" ");
+ }
+ }
+ }
+ mStringBuilder.append("]");
+ closeListItem();
+ }
+ writeListItem("controls:" + getHexString(descriptor.getControls()));
+ closeList();
+ }
+
+ private void tsReport(UsbACFeatureUnit descriptor) {
+ tsReport((UsbACInterface) descriptor);
+ }
+
+ private void tsReport(UsbACInterface descriptor) {
+ String subClassName =
+ descriptor.getSubclass() == UsbDescriptor.AUDIO_AUDIOCONTROL
+ ? "AC Control"
+ : "AC Streaming";
+ byte subtype = descriptor.getSubtype();
+ String subTypeStr = UsbStrings.getACControlInterfaceName(subtype);
+ writeHeader(4, subClassName + " - " + getHexString(subtype)
+ + ":" + subTypeStr + " len:" + descriptor.getLength());
+ }
+
+ private void tsReport(UsbACTerminal descriptor) {
+ tsReport((UsbACInterface) descriptor);
+ }
+
+ private void tsReport(UsbACInputTerminal descriptor) {
+ tsReport((UsbACTerminal) descriptor);
+
+ openList();
+ writeListItem("ID:" + getHexString(descriptor.getTerminalID()));
+ int terminalType = descriptor.getTerminalType();
+ writeListItem("Type:<b>" + getHexString(terminalType) + ":"
+ + UsbStrings.getTerminalName(terminalType) + "</b>");
+ writeListItem("AssocTerminal:" + getHexString(descriptor.getAssocTerminal()));
+ writeListItem("" + descriptor.getNrChannels() + " chans. config:"
+ + getHexString(descriptor.getChannelConfig()));
+ closeList();
+ }
+
+ private void tsReport(UsbACOutputTerminal descriptor) {
+ tsReport((UsbACTerminal) descriptor);
+
+ openList();
+ writeListItem("ID:" + getHexString(descriptor.getTerminalID()));
+ int terminalType = descriptor.getTerminalType();
+ writeListItem("Type:<b>" + getHexString(terminalType) + ":"
+ + UsbStrings.getTerminalName(terminalType) + "</b>");
+ writeListItem("AssocTerminal:" + getHexString(descriptor.getAssocTerminal()));
+ writeListItem("Source:" + getHexString(descriptor.getSourceID()));
+ closeList();
+ }
+
+ private void tsReport(UsbACMidiEndpoint descriptor) {
+ writeHeader(3, "AC Midi Endpoint:" + getHexString(descriptor.getType())
+ + " length:" + descriptor.getLength());
+ openList();
+ writeListItem("" + descriptor.getNumJacks() + " jacks.");
+ closeList();
+ }
+
+ private void tsReport(UsbACMixerUnit descriptor) {
+ tsReport((UsbACInterface) descriptor);
+ openList();
+
+ writeListItem("Unit ID:" + getHexString(descriptor.getUnitID()));
+ byte numInputs = descriptor.getNumInputs();
+ byte[] inputIDs = descriptor.getInputIDs();
+ openListItem();
+ mStringBuilder.append("Num Inputs:" + numInputs + " [");
+ for (int input = 0; input < numInputs; input++) {
+ mStringBuilder.append("" + getHexString(inputIDs[input]));
+ if (input < numInputs - 1) {
+ mStringBuilder.append(" ");
+ }
+ }
+ mStringBuilder.append("]");
+ closeListItem();
+
+ writeListItem("Num Outputs:" + descriptor.getNumOutputs());
+ writeListItem("Chan Config:" + getHexString(descriptor.getChannelConfig()));
+
+ byte[] controls = descriptor.getControls();
+ openListItem();
+ mStringBuilder.append("controls:" + controls.length + " [");
+ for (int ctrl = 0; ctrl < controls.length; ctrl++) {
+ mStringBuilder.append("" + controls[ctrl]);
+ if (ctrl < controls.length - 1) {
+ mStringBuilder.append(" ");
+ }
+ }
+ mStringBuilder.append("]");
+ closeListItem();
+ closeList();
+ // byte mChanNameID; // First channel name string descriptor ID
+ // byte mNameID; // string descriptor ID of mixer name
+ }
+
+ private void tsReport(UsbACSelectorUnit descriptor) {
+ tsReport((UsbACInterface) descriptor);
+ }
+
+ private void tsReport(UsbASFormat descriptor) {
+ writeHeader(4, "AC Streaming Format "
+ + (descriptor.getFormatType() == UsbASFormat.FORMAT_TYPE_I ? "I" : "II")
+ + " - " + getHexString(descriptor.getSubtype()) + ":"
+ + " len:" + descriptor.getLength());
+ }
+
+ private void tsReport(UsbASFormatI descriptor) {
+ tsReport((UsbASFormat) descriptor);
+ openList();
+ writeListItem("chans:" + descriptor.getNumChannels());
+ writeListItem("subframe size:" + descriptor.getSubframeSize());
+ writeListItem("bit resolution:" + descriptor.getBitResolution());
+ byte sampleFreqType = descriptor.getSampleFreqType();
+ int[] sampleRates = descriptor.getSampleRates();
+ writeListItem("sample freq type:" + sampleFreqType);
+ if (sampleFreqType == 0) {
+ openList();
+ writeListItem("min:" + sampleRates[0]);
+ writeListItem("max:" + sampleRates[1]);
+ closeList();
+ } else {
+ openList();
+ for (int index = 0; index < sampleFreqType; index++) {
+ writeListItem("" + sampleRates[index]);
+ }
+ closeList();
+ }
+ closeList();
+ }
+
+ private void tsReport(UsbASFormatII descriptor) {
+ tsReport((UsbASFormat) descriptor);
+ openList();
+ writeListItem("max bit rate:" + descriptor.getMaxBitRate());
+ writeListItem("samples per frame:" + descriptor.getMaxBitRate());
+ byte sampleFreqType = descriptor.getSamFreqType();
+ int[] sampleRates = descriptor.getSampleRates();
+ writeListItem("sample freq type:" + sampleFreqType);
+ if (sampleFreqType == 0) {
+ openList();
+ writeListItem("min:" + sampleRates[0]);
+ writeListItem("max:" + sampleRates[1]);
+ closeList();
+ } else {
+ openList();
+ for (int index = 0; index < sampleFreqType; index++) {
+ writeListItem("" + sampleRates[index]);
+ }
+ closeList();
+ }
+
+ closeList();
+ }
+
+ private void tsReport(UsbASGeneral descriptor) {
+ tsReport((UsbACInterface) descriptor);
+ openList();
+ int formatTag = descriptor.getFormatTag();
+ writeListItem("fmt:" + UsbStrings.getAudioFormatName(formatTag) + " - "
+ + getHexString(formatTag));
+ closeList();
+ }
+
+ private void tsReport(UsbInterfaceAssoc descriptor) {
+ tsReport((UsbDescriptor) descriptor);
+ }
+
+ private void tsReport(UsbMSMidiHeader descriptor) {
+ writeHeader(3, "MS Midi Header:" + getHexString(descriptor.getType())
+ + " subType:" + getHexString(descriptor.getSubclass())
+ + " length:" + descriptor.getSubclass());
+ }
+
+ private void tsReport(UsbMSMidiInputJack descriptor) {
+ writeHeader(3, "MS Midi Input Jack:" + getHexString(descriptor.getType())
+ + " subType:" + getHexString(descriptor.getSubclass())
+ + " length:" + descriptor.getSubclass());
+ }
+
+ private void tsReport(UsbMSMidiOutputJack descriptor) {
+ writeHeader(3, "MS Midi Output Jack:" + getHexString(descriptor.getType())
+ + " subType:" + getHexString(descriptor.getSubclass())
+ + " length:" + descriptor.getSubclass());
+ }
+
+ private void tsReport(UsbUnknown descriptor) {
+ writeParagraph("<i><b>Unknown Descriptor " + getHexString(descriptor.getType())
+ + " len:" + descriptor.getLength() + "</b></i>");
+ dumpHexArray(descriptor.getRawData(), mStringBuilder);
+ }
+}
diff --git a/services/usb/java/com/android/server/usb/descriptors/report/Reporter.java b/services/usb/java/com/android/server/usb/descriptors/report/Reporter.java
new file mode 100644
index 000000000000..2944c10796f6
--- /dev/null
+++ b/services/usb/java/com/android/server/usb/descriptors/report/Reporter.java
@@ -0,0 +1,40 @@
+/*
+ * 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.server.usb.descriptors.report;
+
+import com.android.server.usb.descriptors.UsbDescriptor;
+
+/**
+ * Declares the Reporter interface to provide HTML reporting for UsbDescriptor (sub)classes.
+ *
+ * NOTE: It is the responsibility of the implementor of this interface to correctly
+ * interpret/decode the SPECIFIC UsbDescriptor subclass (perhaps with 'instanceof') that is
+ * passed and handle that in the appropriate manner. This appears to be a
+ * not very object-oriented approach, and that is true. This approach DOES however move the
+ * complexity and 'plumbing' of reporting into the Reporter implementation and avoids needing
+ * a (trivial) type-specific call to 'report()' in each UsbDescriptor (sub)class, instead
+ * having just one in the top-level UsbDescriptor class. It also removes the need to add new
+ * type-specific 'report()' methods to be added to Reporter interface whenever a
+ * new UsbDescriptor subclass is defined. This seems like a pretty good trade-off.
+ *
+ * See HTMLReporter.java in this package for an example of type decoding.
+ */
+public interface Reporter {
+ /**
+ * Generate report for this UsbDescriptor descriptor
+ */
+ void report(UsbDescriptor descriptor);
+}
diff --git a/services/usb/java/com/android/server/usb/descriptors/report/Reporting.java b/services/usb/java/com/android/server/usb/descriptors/report/Reporting.java
new file mode 100644
index 000000000000..c13111b3e81c
--- /dev/null
+++ b/services/usb/java/com/android/server/usb/descriptors/report/Reporting.java
@@ -0,0 +1,27 @@
+/*
+ * 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.server.usb.descriptors.report;
+
+/**
+ * Declares the interface for classes that provide reporting functionality.
+ * (This is the double-indirection aspect of the "Visitor" pattern.
+ */
+public interface Reporting {
+ /**
+ * Declares the report method that UsbDescriptor subclasses call.
+ */
+ void report(Reporter reporter);
+}
diff --git a/services/usb/java/com/android/server/usb/descriptors/report/UsbStrings.java b/services/usb/java/com/android/server/usb/descriptors/report/UsbStrings.java
new file mode 100644
index 000000000000..0461150abd27
--- /dev/null
+++ b/services/usb/java/com/android/server/usb/descriptors/report/UsbStrings.java
@@ -0,0 +1,312 @@
+/*
+ * 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.server.usb.descriptors.report;
+
+import com.android.server.usb.descriptors.UsbACInterface;
+import com.android.server.usb.descriptors.UsbDescriptor;
+import com.android.server.usb.descriptors.UsbTerminalTypes;
+
+import java.util.HashMap;
+
+/**
+ * @hide
+ * A class to provide human-readable strings for various USB constants.
+ */
+public class UsbStrings {
+ private static final String TAG = "UsbStrings";
+
+ private static HashMap<Byte, String> sDescriptorNames;
+ private static HashMap<Byte, String> sACControlInterfaceNames;
+ private static HashMap<Byte, String> sACStreamingInterfaceNames;
+ private static HashMap<Byte, String> sClassNames;
+ private static HashMap<Byte, String> sAudioSubclassNames;
+ private static HashMap<Integer, String> sAudioEncodingNames;
+ private static HashMap<Integer, String> sTerminalNames;
+
+ private static void initDescriptorNames() {
+ sDescriptorNames = new HashMap<Byte, String>();
+ sDescriptorNames.put(UsbDescriptor.DESCRIPTORTYPE_DEVICE, "Device");
+ sDescriptorNames.put(UsbDescriptor.DESCRIPTORTYPE_CONFIG, "Config");
+ sDescriptorNames.put(UsbDescriptor.DESCRIPTORTYPE_STRING, "String");
+ sDescriptorNames.put(UsbDescriptor.DESCRIPTORTYPE_INTERFACE, "Interface");
+ sDescriptorNames.put(UsbDescriptor.DESCRIPTORTYPE_ENDPOINT, "Endpoint");
+ sDescriptorNames.put(UsbDescriptor.DESCRIPTORTYPE_BOS, "BOS (whatever that means)");
+ sDescriptorNames.put(UsbDescriptor.DESCRIPTORTYPE_INTERFACEASSOC,
+ "Interface Association");
+ sDescriptorNames.put(UsbDescriptor.DESCRIPTORTYPE_CAPABILITY, "Capability");
+ sDescriptorNames.put(UsbDescriptor.DESCRIPTORTYPE_HID, "HID");
+ sDescriptorNames.put(UsbDescriptor.DESCRIPTORTYPE_REPORT, "Report");
+ sDescriptorNames.put(UsbDescriptor.DESCRIPTORTYPE_PHYSICAL, "Physical");
+ sDescriptorNames.put(UsbDescriptor.DESCRIPTORTYPE_AUDIO_INTERFACE,
+ "Audio Class Interface");
+ sDescriptorNames.put(UsbDescriptor.DESCRIPTORTYPE_AUDIO_ENDPOINT, "Audio Class Endpoint");
+ sDescriptorNames.put(UsbDescriptor.DESCRIPTORTYPE_HUB, "Hub");
+ sDescriptorNames.put(UsbDescriptor.DESCRIPTORTYPE_SUPERSPEED_HUB, "Superspeed Hub");
+ sDescriptorNames.put(UsbDescriptor.DESCRIPTORTYPE_ENDPOINT_COMPANION,
+ "Endpoint Companion");
+ }
+
+ private static void initACControlInterfaceNames() {
+ sACControlInterfaceNames = new HashMap<Byte, String>();
+ sACControlInterfaceNames.put(UsbACInterface.ACI_UNDEFINED, "Undefined");
+ sACControlInterfaceNames.put(UsbACInterface.ACI_HEADER, "Header");
+ sACControlInterfaceNames.put(UsbACInterface.ACI_INPUT_TERMINAL, "Input Terminal");
+ sACControlInterfaceNames.put(UsbACInterface.ACI_OUTPUT_TERMINAL, "Output Terminal");
+ sACControlInterfaceNames.put(UsbACInterface.ACI_MIXER_UNIT, "Mixer Unit");
+ sACControlInterfaceNames.put(UsbACInterface.ACI_SELECTOR_UNIT, "Selector Unit");
+ sACControlInterfaceNames.put(UsbACInterface.ACI_FEATURE_UNIT, "Feature Unit");
+ sACControlInterfaceNames.put(UsbACInterface.ACI_PROCESSING_UNIT, "Processing Unit");
+ sACControlInterfaceNames.put(UsbACInterface.ACI_EXTENSION_UNIT, "Extension Unit");
+ }
+
+ private static void initACStreamingInterfaceNames() {
+ sACStreamingInterfaceNames = new HashMap<Byte, String>();
+ sACStreamingInterfaceNames.put(UsbACInterface.ASI_UNDEFINED, "Undefined");
+ sACStreamingInterfaceNames.put(UsbACInterface.ASI_GENERAL, "General");
+ sACStreamingInterfaceNames.put(UsbACInterface.ASI_FORMAT_TYPE, "Format Type");
+ sACStreamingInterfaceNames.put(UsbACInterface.ASI_FORMAT_SPECIFIC, "Format Specific");
+ }
+
+ private static void initClassNames() {
+ sClassNames = new HashMap<Byte, String>();
+ sClassNames.put(UsbDescriptor.CLASSID_DEVICE, "Device");
+ sClassNames.put(UsbDescriptor.CLASSID_AUDIO, "Audio");
+ sClassNames.put(UsbDescriptor.CLASSID_COM, "Communications");
+ sClassNames.put(UsbDescriptor.CLASSID_HID, "HID");
+ sClassNames.put(UsbDescriptor.CLASSID_PHYSICAL, "Physical");
+ sClassNames.put(UsbDescriptor.CLASSID_IMAGE, "Image");
+ sClassNames.put(UsbDescriptor.CLASSID_PRINTER, "Printer");
+ sClassNames.put(UsbDescriptor.CLASSID_STORAGE, "Storage");
+ sClassNames.put(UsbDescriptor.CLASSID_HUB, "Hub");
+ sClassNames.put(UsbDescriptor.CLASSID_CDC_CONTROL, "CDC Control");
+ sClassNames.put(UsbDescriptor.CLASSID_SMART_CARD, "Smart Card");
+ sClassNames.put(UsbDescriptor.CLASSID_SECURITY, "Security");
+ sClassNames.put(UsbDescriptor.CLASSID_VIDEO, "Video");
+ sClassNames.put(UsbDescriptor.CLASSID_HEALTHCARE, "Healthcare");
+ sClassNames.put(UsbDescriptor.CLASSID_AUDIOVIDEO, "Audio/Video");
+ sClassNames.put(UsbDescriptor.CLASSID_BILLBOARD, "Billboard");
+ sClassNames.put(UsbDescriptor.CLASSID_TYPECBRIDGE, "Type C Bridge");
+ sClassNames.put(UsbDescriptor.CLASSID_DIAGNOSTIC, "Diagnostic");
+ sClassNames.put(UsbDescriptor.CLASSID_WIRELESS, "Wireless");
+ sClassNames.put(UsbDescriptor.CLASSID_MISC, "Misc");
+ sClassNames.put(UsbDescriptor.CLASSID_APPSPECIFIC, "Application Specific");
+ sClassNames.put(UsbDescriptor.CLASSID_VENDSPECIFIC, "Vendor Specific");
+ }
+
+ private static void initAudioSubclassNames() {
+ sAudioSubclassNames = new HashMap<Byte, String>();
+ sAudioSubclassNames.put(UsbDescriptor.AUDIO_SUBCLASS_UNDEFINED, "Undefinded");
+ sAudioSubclassNames.put(UsbDescriptor.AUDIO_AUDIOCONTROL, "Audio Control");
+ sAudioSubclassNames.put(UsbDescriptor.AUDIO_AUDIOSTREAMING, "Audio Streaming");
+ sAudioSubclassNames.put(UsbDescriptor.AUDIO_MIDISTREAMING, "MIDI Streaming");
+ }
+
+ private static void initAudioEncodingNames() {
+ sAudioEncodingNames = new HashMap<Integer, String>();
+ sAudioEncodingNames.put(UsbACInterface.FORMAT_I_UNDEFINED, "Format I Undefined");
+ sAudioEncodingNames.put(UsbACInterface.FORMAT_I_PCM, "Format I PCM");
+ sAudioEncodingNames.put(UsbACInterface.FORMAT_I_PCM8, "Format I PCM8");
+ sAudioEncodingNames.put(UsbACInterface.FORMAT_I_IEEE_FLOAT, "Format I FLOAT");
+ sAudioEncodingNames.put(UsbACInterface.FORMAT_I_ALAW, "Format I ALAW");
+ sAudioEncodingNames.put(UsbACInterface.FORMAT_I_MULAW, "Format I MuLAW");
+ sAudioEncodingNames.put(UsbACInterface.FORMAT_II_UNDEFINED, "FORMAT_II Undefined");
+ sAudioEncodingNames.put(UsbACInterface.FORMAT_II_MPEG, "FORMAT_II MPEG");
+ sAudioEncodingNames.put(UsbACInterface.FORMAT_II_AC3, "FORMAT_II AC3");
+ sAudioEncodingNames.put(UsbACInterface.FORMAT_III_UNDEFINED, "FORMAT_III Undefined");
+ sAudioEncodingNames.put(UsbACInterface.FORMAT_III_IEC1937AC3, "FORMAT_III IEC1937 AC3");
+ sAudioEncodingNames.put(UsbACInterface.FORMAT_III_IEC1937_MPEG1_Layer1,
+ "FORMAT_III MPEG1 Layer 1");
+ sAudioEncodingNames.put(UsbACInterface.FORMAT_III_IEC1937_MPEG1_Layer2,
+ "FORMAT_III MPEG1 Layer 2");
+ sAudioEncodingNames.put(UsbACInterface.FORMAT_III_IEC1937_MPEG2_EXT,
+ "FORMAT_III MPEG2 EXT");
+ sAudioEncodingNames.put(UsbACInterface.FORMAT_III_IEC1937_MPEG2_Layer1LS,
+ "FORMAT_III MPEG2 Layer1LS");
+ }
+
+ private static void initTerminalNames() {
+ sTerminalNames = new HashMap<Integer, String>();
+ sTerminalNames.put(UsbTerminalTypes.TERMINAL_USB_STREAMING, "USB Streaming");
+
+ sTerminalNames.put(UsbTerminalTypes.TERMINAL_IN_UNDEFINED, "Undefined");
+ sTerminalNames.put(UsbTerminalTypes.TERMINAL_IN_MIC, "Microphone");
+ sTerminalNames.put(UsbTerminalTypes.TERMINAL_IN_DESKTOP_MIC, "Desktop Microphone");
+ sTerminalNames.put(UsbTerminalTypes.TERMINAL_IN_PERSONAL_MIC,
+ "Personal (headset) Microphone");
+ sTerminalNames.put(UsbTerminalTypes.TERMINAL_IN_OMNI_MIC, "Omni Microphone");
+ sTerminalNames.put(UsbTerminalTypes.TERMINAL_IN_MIC_ARRAY, "Microphone Array");
+ sTerminalNames.put(UsbTerminalTypes.TERMINAL_IN_PROC_MIC_ARRAY,
+ "Proecessing Microphone Array");
+
+ sTerminalNames.put(UsbTerminalTypes.TERMINAL_OUT_UNDEFINED, "Undefined");
+ sTerminalNames.put(UsbTerminalTypes.TERMINAL_OUT_SPEAKER, "Speaker");
+ sTerminalNames.put(UsbTerminalTypes.TERMINAL_OUT_HEADPHONES, "Headphones");
+ sTerminalNames.put(UsbTerminalTypes.TERMINAL_OUT_HEADMOUNTED, "Head Mounted Speaker");
+ sTerminalNames.put(UsbTerminalTypes.TERMINAL_OUT_DESKTOPSPEAKER, "Desktop Speaker");
+ sTerminalNames.put(UsbTerminalTypes.TERMINAL_OUT_ROOMSPEAKER, "Room Speaker");
+ sTerminalNames.put(UsbTerminalTypes.TERMINAL_OUT_COMSPEAKER, "Communications Speaker");
+ sTerminalNames.put(UsbTerminalTypes.TERMINAL_OUT_LFSPEAKER, "Low Frequency Speaker");
+
+ sTerminalNames.put(UsbTerminalTypes.TERMINAL_BIDIR_UNDEFINED, "Undefined");
+ sTerminalNames.put(UsbTerminalTypes.TERMINAL_BIDIR_HANDSET, "Handset");
+ sTerminalNames.put(UsbTerminalTypes.TERMINAL_BIDIR_HEADSET, "Headset");
+ sTerminalNames.put(UsbTerminalTypes.TERMINAL_BIDIR_SKRPHONE, "Speaker Phone");
+ sTerminalNames.put(UsbTerminalTypes.TERMINAL_BIDIR_SKRPHONE_SUPRESS,
+ "Speaker Phone (echo supressing)");
+ sTerminalNames.put(UsbTerminalTypes.TERMINAL_BIDIR_SKRPHONE_CANCEL,
+ "Speaker Phone (echo canceling)");
+
+ sTerminalNames.put(UsbTerminalTypes.TERMINAL_TELE_UNDEFINED, "Undefined");
+ sTerminalNames.put(UsbTerminalTypes.TERMINAL_TELE_PHONELINE, "Phone Line");
+ sTerminalNames.put(UsbTerminalTypes.TERMINAL_TELE_PHONE, "Telephone");
+ sTerminalNames.put(UsbTerminalTypes.TERMINAL_TELE_DOWNLINEPHONE, "Down Line Phone");
+
+ sTerminalNames.put(UsbTerminalTypes.TERMINAL_EXTERN_UNDEFINED, "Undefined");
+ sTerminalNames.put(UsbTerminalTypes.TERMINAL_EXTERN_ANALOG, "Analog Connector");
+ sTerminalNames.put(UsbTerminalTypes.TERMINAL_EXTERN_DIGITAL, "Digital Connector");
+ sTerminalNames.put(UsbTerminalTypes.TERMINAL_EXTERN_LINE, "Line Connector");
+ sTerminalNames.put(UsbTerminalTypes.TERMINAL_EXTERN_LEGACY, "Legacy Audio Connector");
+ sTerminalNames.put(UsbTerminalTypes.TERMINAL_EXTERN_SPIDF, "S/PIDF Interface");
+ sTerminalNames.put(UsbTerminalTypes.TERMINAL_EXTERN_1394DA, "1394 Audio");
+ sTerminalNames.put(UsbTerminalTypes.TERMINAL_EXTERN_1394DV, "1394 Audio/Video");
+
+ sTerminalNames.put(UsbTerminalTypes.TERMINAL_EMBED_UNDEFINED, "Undefined");
+ sTerminalNames.put(UsbTerminalTypes.TERMINAL_EMBED_CALNOISE, "Calibration Nose");
+ sTerminalNames.put(UsbTerminalTypes.TERMINAL_EMBED_EQNOISE, "EQ Noise");
+ sTerminalNames.put(UsbTerminalTypes.TERMINAL_EMBED_CDPLAYER, "CD Player");
+ sTerminalNames.put(UsbTerminalTypes.TERMINAL_EMBED_DAT, "DAT");
+ sTerminalNames.put(UsbTerminalTypes.TERMINAL_EMBED_DCC, "DCC");
+ sTerminalNames.put(UsbTerminalTypes.TERMINAL_EMBED_MINIDISK, "Mini Disk");
+ sTerminalNames.put(UsbTerminalTypes.TERMINAL_EMBED_ANALOGTAPE, "Analog Tap");
+ sTerminalNames.put(UsbTerminalTypes.TERMINAL_EMBED_PHONOGRAPH, "Phonograph");
+ sTerminalNames.put(UsbTerminalTypes.TERMINAL_EMBED_VCRAUDIO, "VCR Audio");
+ sTerminalNames.put(UsbTerminalTypes.TERMINAL_EMBED_VIDDISKAUDIO, "Video Disk Audio");
+ sTerminalNames.put(UsbTerminalTypes.TERMINAL_EMBED_DVDAUDIO, "DVD Audio");
+ sTerminalNames.put(UsbTerminalTypes.TERMINAL_EMBED_TVAUDIO, "TV Audio");
+ sTerminalNames.put(UsbTerminalTypes.TERMINAL_EMBED_SATELLITEAUDIO, "Satellite Audio");
+ sTerminalNames.put(UsbTerminalTypes.TERMINAL_EMBED_CABLEAUDIO, "Cable Tuner Audio");
+ sTerminalNames.put(UsbTerminalTypes.TERMINAL_EMBED_DSSAUDIO, "DSS Audio");
+ sTerminalNames.put(UsbTerminalTypes.TERMINAL_EMBED_RADIOTRANSMITTER, "Radio Transmitter");
+ sTerminalNames.put(UsbTerminalTypes.TERMINAL_EMBED_MULTITRACK, "Multitrack Recorder");
+ sTerminalNames.put(UsbTerminalTypes.TERMINAL_EMBED_SYNTHESIZER, "Synthesizer");
+ }
+
+ /**
+ * Retrieves the terminal name for the specified terminal type ID.
+ */
+ public static String getTerminalName(int terminalType) {
+ String name = sTerminalNames.get(terminalType);
+ return name != null
+ ? name
+ : "Unknown Terminal Type 0x" + Integer.toHexString(terminalType);
+ }
+ /**
+ * Initializes string tables.
+ */
+ public static void allocUsbStrings() {
+ initDescriptorNames();
+ initACControlInterfaceNames();
+ initACStreamingInterfaceNames();
+ initClassNames();
+ initAudioSubclassNames();
+ initAudioEncodingNames();
+ initTerminalNames();
+ }
+
+ /**
+ * Initializes string tables.
+ */
+ public static void releaseUsbStrings() {
+ sDescriptorNames = null;
+ sACControlInterfaceNames = null;
+ sACStreamingInterfaceNames = null;
+ sClassNames = null;
+ sAudioSubclassNames = null;
+ sAudioEncodingNames = null;
+ sTerminalNames = null;
+ }
+
+ /**
+ * Retrieves the name for the specified descriptor ID.
+ */
+ public static String getDescriptorName(byte descriptorID) {
+ String name = sDescriptorNames.get(descriptorID);
+ int iDescriptorID = descriptorID & 0xFF;
+ return name != null
+ ? name
+ : "Unknown Descriptor [0x" + Integer.toHexString(iDescriptorID)
+ + ":" + iDescriptorID + "]";
+ }
+
+ /**
+ * Retrieves the audio-class control interface name for the specified audio-class subtype.
+ */
+ public static String getACControlInterfaceName(byte subtype) {
+ String name = sACControlInterfaceNames.get(subtype);
+ int iSubType = subtype & 0xFF;
+ return name != null
+ ? name
+ : "Unknown subtype [0x" + Integer.toHexString(iSubType)
+ + ":" + iSubType + "]";
+ }
+
+ /**
+ * Retrieves the audio-class streaming interface name for the specified audio-class subtype.
+ */
+ public static String getACStreamingInterfaceName(byte subtype) {
+ String name = sACStreamingInterfaceNames.get(subtype);
+ int iSubType = subtype & 0xFF;
+ return name != null
+ ? name
+ : "Unknown Subtype [0x" + Integer.toHexString(iSubType) + ":"
+ + iSubType + "]";
+ }
+
+ /**
+ * Retrieves the name for the specified USB class ID.
+ */
+ public static String getClassName(byte classID) {
+ String name = sClassNames.get(classID);
+ int iClassID = classID & 0xFF;
+ return name != null
+ ? name
+ : "Unknown Class ID [0x" + Integer.toHexString(iClassID) + ":"
+ + iClassID + "]";
+ }
+
+ /**
+ * Retrieves the name for the specified USB audio subclass ID.
+ */
+ public static String getAudioSubclassName(byte subClassID) {
+ String name = sAudioSubclassNames.get(subClassID);
+ int iSubclassID = subClassID & 0xFF;
+ return name != null
+ ? name
+ : "Unknown Audio Subclass [0x" + Integer.toHexString(iSubclassID) + ":"
+ + iSubclassID + "]";
+ }
+
+ /**
+ * Retrieves the name for the specified USB audio format ID.
+ */
+ public static String getAudioFormatName(int formatID) {
+ String name = sAudioEncodingNames.get(formatID);
+ return name != null
+ ? name
+ : "Unknown Format (encoding) ID [0x" + Integer.toHexString(formatID) + ":"
+ + formatID + "]";
+ }
+}
diff --git a/telephony/java/android/telephony/MbmsDownloadManager.java b/telephony/java/android/telephony/MbmsDownloadManager.java
index 14ba7e5d274a..b3f46c22ba0a 100644
--- a/telephony/java/android/telephony/MbmsDownloadManager.java
+++ b/telephony/java/android/telephony/MbmsDownloadManager.java
@@ -194,27 +194,23 @@ public class MbmsDownloadManager {
private AtomicReference<IMbmsDownloadService> mService = new AtomicReference<>(null);
private final IMbmsDownloadManagerCallback mCallback;
- private final String mDownloadAppName;
- private MbmsDownloadManager(Context context, IMbmsDownloadManagerCallback callback,
- String downloadAppName, int subId) {
+ private MbmsDownloadManager(Context context, IMbmsDownloadManagerCallback callback, int subId) {
mContext = context;
mCallback = callback;
- mDownloadAppName = downloadAppName;
mSubscriptionId = subId;
}
/**
* Create a new MbmsDownloadManager using the system default data subscription ID.
- * See {@link #create(Context, IMbmsDownloadManagerCallback, String, int)}
+ * See {@link #create(Context, IMbmsDownloadManagerCallback, int)}
*
* @hide
*/
public static MbmsDownloadManager create(Context context,
- IMbmsDownloadManagerCallback listener, String downloadAppName)
+ IMbmsDownloadManagerCallback listener)
throws MbmsException {
- return create(context, listener, downloadAppName,
- SubscriptionManager.getDefaultSubscriptionId());
+ return create(context, listener, SubscriptionManager.getDefaultSubscriptionId());
}
/**
@@ -229,15 +225,13 @@ public class MbmsDownloadManager {
*
* @param context The instance of {@link Context} to use
* @param listener A callback to get asynchronous error messages and file service updates.
- * @param downloadAppName The app name, as negotiated with the eMBMS provider
* @param subscriptionId The data subscription ID to use
* @hide
*/
public static MbmsDownloadManager create(Context context,
- IMbmsDownloadManagerCallback listener, String downloadAppName, int subscriptionId)
+ IMbmsDownloadManagerCallback listener, int subscriptionId)
throws MbmsException {
- MbmsDownloadManager mdm = new MbmsDownloadManager(context, listener, downloadAppName,
- subscriptionId);
+ MbmsDownloadManager mdm = new MbmsDownloadManager(context, listener, subscriptionId);
mdm.bindAndInitialize();
return mdm;
}
@@ -250,8 +244,7 @@ public class MbmsDownloadManager {
IMbmsDownloadService downloadService =
IMbmsDownloadService.Stub.asInterface(service);
try {
- downloadService.initialize(
- mDownloadAppName, mSubscriptionId, mCallback);
+ downloadService.initialize(mSubscriptionId, mCallback);
} catch (RemoteException e) {
Log.e(LOG_TAG, "Service died before initialization");
return;
@@ -293,8 +286,7 @@ public class MbmsDownloadManager {
throw new MbmsException(MbmsException.ERROR_MIDDLEWARE_NOT_BOUND);
}
try {
- int returnCode = downloadService.getFileServices(
- mDownloadAppName, mSubscriptionId, classList);
+ int returnCode = downloadService.getFileServices(mSubscriptionId, classList);
if (returnCode != MbmsException.SUCCESS) {
throw new MbmsException(returnCode);
}
@@ -345,8 +337,7 @@ public class MbmsDownloadManager {
}
try {
- int result = downloadService.setTempFileRootDirectory(
- mDownloadAppName, mSubscriptionId, filePath);
+ int result = downloadService.setTempFileRootDirectory(mSubscriptionId, filePath);
if (result != MbmsException.SUCCESS) {
throw new MbmsException(result);
}
@@ -396,7 +387,6 @@ public class MbmsDownloadManager {
tempRootDirectory.mkdirs();
setTempFileRootDirectory(tempRootDirectory);
}
- request.setAppName(mDownloadAppName);
checkValidDownloadDestination(request);
writeDownloadRequestToken(request);
@@ -404,6 +394,7 @@ public class MbmsDownloadManager {
downloadService.download(request, callback);
} catch (RemoteException e) {
mService.set(null);
+ throw new MbmsException(MbmsException.ERROR_SERVICE_LOST);
}
}
@@ -422,18 +413,31 @@ public class MbmsDownloadManager {
}
/**
- * Attempts to cancel the specified DownloadRequest.
+ * Attempts to cancel the specified {@link DownloadRequest}.
*
- * May throw a RemoteException.
+ * If the middleware is not aware of the specified download request, an MbmsException will be
+ * thrown with error code {@link MbmsException#ERROR_UNKNOWN_DOWNLOAD_REQUEST}.
*
- * Synchronous responses may include
- * <li>SUCCESS</li>
- * <li>ERROR_MSDC_CONCURRENT_SERVICE_LIMIT_REACHED</li>
- * <li>ERROR_MSDC_UNKNOWN_REQUEST</li>
+ * If this method returns without throwing an exception, you may assume that cancellation
+ * was successful.
+ * @param downloadRequest The download request that you wish to cancel.
*/
- public int cancelDownload(DownloadRequest downloadRequest) {
- // TODO: don't forget to delete the token
- return 0;
+ public void cancelDownload(DownloadRequest downloadRequest) throws MbmsException {
+ IMbmsDownloadService downloadService = mService.get();
+ if (downloadService == null) {
+ throw new MbmsException(MbmsException.ERROR_MIDDLEWARE_NOT_BOUND);
+ }
+
+ try {
+ int result = downloadService.cancelDownload(downloadRequest);
+ if (result != MbmsException.SUCCESS) {
+ throw new MbmsException(result);
+ }
+ } catch (RemoteException e) {
+ mService.set(null);
+ throw new MbmsException(MbmsException.ERROR_SERVICE_LOST);
+ }
+ deleteDownloadRequestToken(downloadRequest);
}
/**
@@ -472,6 +476,21 @@ public class MbmsDownloadManager {
return 0;
}
+ public void dispose() {
+ try {
+ IMbmsDownloadService downloadService = mService.get();
+ if (downloadService == null) {
+ Log.i(LOG_TAG, "Service already dead");
+ return;
+ }
+ downloadService.dispose(mSubscriptionId);
+ mService.set(null);
+ } catch (RemoteException e) {
+ // Ignore
+ Log.i(LOG_TAG, "Remote exception while disposing of service");
+ }
+ }
+
/**
* Retrieves the {@link ComponentName} for the {@link android.content.BroadcastReceiver} that
* the various intents from the middleware should be targeted towards.
@@ -502,32 +521,13 @@ public class MbmsDownloadManager {
return null;
}
- public void dispose() {
- try {
- IMbmsDownloadService downloadService = mService.get();
- if (downloadService == null) {
- Log.i(LOG_TAG, "Service already dead");
- return;
- }
- downloadService.dispose(mDownloadAppName, mSubscriptionId);
- mService.set(null);
- } catch (RemoteException e) {
- // Ignore
- Log.i(LOG_TAG, "Remote exception while disposing of service");
- }
- }
-
private void writeDownloadRequestToken(DownloadRequest request) {
- File tempFileLocation = MbmsUtils.getEmbmsTempFileDirForService(mContext,
- request.getFileServiceInfo());
- if (!tempFileLocation.exists()) {
- tempFileLocation.mkdirs();
+ File token = getDownloadRequestTokenPath(request);
+ if (!token.getParentFile().exists()) {
+ token.getParentFile().mkdirs();
}
- String downloadTokenFileName = request.getHash()
- + MbmsDownloadReceiver.DOWNLOAD_TOKEN_SUFFIX;
- File token = new File(tempFileLocation, downloadTokenFileName);
if (token.exists()) {
- Log.w(LOG_TAG, "Download token " + downloadTokenFileName + " already exists");
+ Log.w(LOG_TAG, "Download token " + token.getName() + " already exists");
return;
}
try {
@@ -541,6 +541,25 @@ public class MbmsDownloadManager {
}
}
+ private void deleteDownloadRequestToken(DownloadRequest request) {
+ File token = getDownloadRequestTokenPath(request);
+ if (!token.isFile()) {
+ Log.w(LOG_TAG, "Attempting to delete non-existent download token at " + token);
+ return;
+ }
+ if (!token.delete()) {
+ Log.w(LOG_TAG, "Couldn't delete download token at " + token);
+ }
+ }
+
+ private File getDownloadRequestTokenPath(DownloadRequest request) {
+ File tempFileLocation = MbmsUtils.getEmbmsTempFileDirForService(mContext,
+ request.getFileServiceInfo());
+ String downloadTokenFileName = request.getHash()
+ + MbmsDownloadReceiver.DOWNLOAD_TOKEN_SUFFIX;
+ return new File(tempFileLocation, downloadTokenFileName);
+ }
+
/**
* Verifies the following:
* If a request is multi-part,
diff --git a/telephony/java/android/telephony/MbmsStreamingManager.java b/telephony/java/android/telephony/MbmsStreamingManager.java
index af7f333390d8..fb3d5025dafa 100644
--- a/telephony/java/android/telephony/MbmsStreamingManager.java
+++ b/telephony/java/android/telephony/MbmsStreamingManager.java
@@ -43,16 +43,14 @@ public class MbmsStreamingManager {
private AtomicReference<IMbmsStreamingService> mService = new AtomicReference<>(null);
private MbmsStreamingManagerCallback mCallbackToApp;
- private final String mAppName;
private final Context mContext;
private int mSubscriptionId = INVALID_SUBSCRIPTION_ID;
/** @hide */
private MbmsStreamingManager(Context context, MbmsStreamingManagerCallback listener,
- String streamingAppName, int subscriptionId) {
+ int subscriptionId) {
mContext = context;
- mAppName = streamingAppName;
mCallbackToApp = listener;
mSubscriptionId = subscriptionId;
}
@@ -67,27 +65,24 @@ public class MbmsStreamingManager {
* @param context The {@link Context} to use.
* @param listener A callback object on which you wish to receive results of asynchronous
* operations.
- * @param streamingAppName The name of the streaming app, as specified by the carrier.
* @param subscriptionId The subscription ID to use.
*/
public static MbmsStreamingManager create(Context context,
- MbmsStreamingManagerCallback listener, String streamingAppName, int subscriptionId)
+ MbmsStreamingManagerCallback listener, int subscriptionId)
throws MbmsException {
- MbmsStreamingManager manager = new MbmsStreamingManager(context, listener,
- streamingAppName, subscriptionId);
+ MbmsStreamingManager manager = new MbmsStreamingManager(context, listener, subscriptionId);
manager.bindAndInitialize();
return manager;
}
/**
* Create a new MbmsStreamingManager using the system default data subscription ID.
- * See {@link #create(Context, MbmsStreamingManagerCallback, String, int)}.
+ * See {@link #create(Context, MbmsStreamingManagerCallback, int)}.
*/
public static MbmsStreamingManager create(Context context,
- MbmsStreamingManagerCallback listener, String streamingAppName)
+ MbmsStreamingManagerCallback listener)
throws MbmsException {
- return create(context, listener, streamingAppName,
- SubscriptionManager.getDefaultSubscriptionId());
+ return create(context, listener, SubscriptionManager.getDefaultSubscriptionId());
}
/**
@@ -101,7 +96,7 @@ public class MbmsStreamingManager {
return;
}
try {
- streamingService.dispose(mAppName, mSubscriptionId);
+ streamingService.dispose(mSubscriptionId);
} catch (RemoteException e) {
// Ignore for now
}
@@ -132,8 +127,7 @@ public class MbmsStreamingManager {
throw new MbmsException(MbmsException.ERROR_MIDDLEWARE_NOT_BOUND);
}
try {
- int returnCode = streamingService.getStreamingServices(
- mAppName, mSubscriptionId, classList);
+ int returnCode = streamingService.getStreamingServices(mSubscriptionId, classList);
if (returnCode != MbmsException.SUCCESS) {
throw new MbmsException(returnCode);
}
@@ -168,7 +162,7 @@ public class MbmsStreamingManager {
try {
int returnCode = streamingService.startStreaming(
- mAppName, mSubscriptionId, serviceInfo.getServiceId(), listener);
+ mSubscriptionId, serviceInfo.getServiceId(), listener);
if (returnCode != MbmsException.SUCCESS) {
throw new MbmsException(returnCode);
}
@@ -178,8 +172,7 @@ public class MbmsStreamingManager {
throw new MbmsException(MbmsException.ERROR_SERVICE_LOST);
}
- return new StreamingService(
- mAppName, mSubscriptionId, streamingService, serviceInfo, listener);
+ return new StreamingService(mSubscriptionId, streamingService, serviceInfo, listener);
}
private void bindAndInitialize() throws MbmsException {
@@ -190,12 +183,12 @@ public class MbmsStreamingManager {
IMbmsStreamingService streamingService =
IMbmsStreamingService.Stub.asInterface(service);
try {
- streamingService.initialize(mCallbackToApp, mAppName, mSubscriptionId);
+ streamingService.initialize(mCallbackToApp, mSubscriptionId);
} catch (RemoteException e) {
Log.e(LOG_TAG, "Service died before initialization");
return;
}
- mService.set(null);
+ mService.set(streamingService);
}
@Override
diff --git a/telephony/java/android/telephony/mbms/DownloadRequest.java b/telephony/java/android/telephony/mbms/DownloadRequest.java
index 907b0cbd8004..345729d82dea 100644
--- a/telephony/java/android/telephony/mbms/DownloadRequest.java
+++ b/telephony/java/android/telephony/mbms/DownloadRequest.java
@@ -22,11 +22,11 @@ import android.os.Parcel;
import android.os.Parcelable;
import android.util.Base64;
-import java.lang.IllegalStateException;
import java.net.URISyntaxException;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
+import java.util.Objects;
/**
* A Parcelable class describing a pending Cell-Broadcast download request
@@ -83,7 +83,7 @@ public class DownloadRequest implements Parcelable {
public DownloadRequest build() {
return new DownloadRequest(id, serviceInfo, source, dest,
- subscriptionId, appIntent, null, version);
+ subscriptionId, appIntent, version);
}
}
@@ -94,18 +94,16 @@ public class DownloadRequest implements Parcelable {
private final int subscriptionId;
private final String serializedResultIntentForApp;
private final int version;
- private String appName; // not the Android app Name, the embms app name
private DownloadRequest(int id, FileServiceInfo serviceInfo,
Uri source, Uri dest,
- int sub, String appIntent, String name, int version) {
+ int sub, String appIntent, int version) {
downloadId = id;
fileServiceInfo = serviceInfo;
sourceUri = source;
destinationUri = dest;
subscriptionId = sub;
serializedResultIntentForApp = appIntent;
- appName = name;
this.version = version;
}
@@ -120,7 +118,6 @@ public class DownloadRequest implements Parcelable {
destinationUri = dr.destinationUri;
subscriptionId = dr.subscriptionId;
serializedResultIntentForApp = dr.serializedResultIntentForApp;
- appName = dr.appName;
version = dr.version;
}
@@ -131,7 +128,6 @@ public class DownloadRequest implements Parcelable {
destinationUri = in.readParcelable(getClass().getClassLoader());
subscriptionId = in.readInt();
serializedResultIntentForApp = in.readString();
- appName = in.readString();
version = in.readInt();
}
@@ -146,7 +142,6 @@ public class DownloadRequest implements Parcelable {
out.writeParcelable(destinationUri, flags);
out.writeInt(subscriptionId);
out.writeString(serializedResultIntentForApp);
- out.writeString(appName);
out.writeInt(version);
}
@@ -178,18 +173,6 @@ public class DownloadRequest implements Parcelable {
}
}
- /** @hide */
- public synchronized void setAppName(String newAppName) {
- if (appName != null) {
- throw new IllegalStateException("Attempting to reset appName");
- }
- appName = newAppName;
- }
-
- public String getAppName() {
- return appName;
- }
-
public int getVersion() {
return version;
}
@@ -234,4 +217,29 @@ public class DownloadRequest implements Parcelable {
// Add updates for future versions here
return Base64.encodeToString(digest.digest(), Base64.URL_SAFE | Base64.NO_WRAP);
}
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null) {
+ return false;
+ }
+ if (!(o instanceof DownloadRequest)) {
+ return false;
+ }
+ DownloadRequest request = (DownloadRequest) o;
+ return downloadId == request.downloadId &&
+ subscriptionId == request.subscriptionId &&
+ version == request.version &&
+ Objects.equals(fileServiceInfo, request.fileServiceInfo) &&
+ Objects.equals(sourceUri, request.sourceUri) &&
+ Objects.equals(destinationUri, request.destinationUri) &&
+ Objects.equals(serializedResultIntentForApp, request.serializedResultIntentForApp);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(downloadId, fileServiceInfo, sourceUri, destinationUri,
+ subscriptionId, serializedResultIntentForApp, version);
+ }
}
diff --git a/telephony/java/android/telephony/mbms/MbmsDownloadReceiver.java b/telephony/java/android/telephony/mbms/MbmsDownloadReceiver.java
index 1ce82d94b7e5..5bc53a82a3bb 100644
--- a/telephony/java/android/telephony/mbms/MbmsDownloadReceiver.java
+++ b/telephony/java/android/telephony/mbms/MbmsDownloadReceiver.java
@@ -29,16 +29,16 @@ import android.util.Log;
import java.io.File;
import java.io.FileFilter;
-import java.io.FilenameFilter;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
import java.util.ArrayList;
-import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Objects;
-import java.util.Set;
import java.util.UUID;
-import java.util.function.Predicate;
/**
* @hide
@@ -431,17 +431,16 @@ public class MbmsDownloadReceiver extends BroadcastReceiver {
}
toFile.getParentFile().mkdirs();
- // TODO: This will not work if the two files are on different filesystems. Add manual
- // copy later.
if (fromFile.renameTo(toFile)) {
return Uri.fromFile(toFile);
+ } else if (manualMove(fromFile, toFile)) {
+ return Uri.fromFile(toFile);
}
return null;
}
private static boolean verifyTempFilePath(Context context, FileServiceInfo serviceInfo,
Uri filePath) {
- // TODO: modify pursuant to final decision on temp file path scheme
if (!ContentResolver.SCHEME_FILE.equals(filePath.getScheme())) {
Log.w(LOG_TAG, "Uri " + filePath + " does not have a file scheme");
return false;
@@ -493,4 +492,40 @@ public class MbmsDownloadReceiver extends BroadcastReceiver {
}
return mMiddlewarePackageNameCache;
}
+
+ private static boolean manualMove(File src, File dst) {
+ InputStream in = null;
+ OutputStream out = null;
+ try {
+ if (!dst.exists()) {
+ dst.createNewFile();
+ }
+ in = new FileInputStream(src);
+ out = new FileOutputStream(dst);
+ byte[] buffer = new byte[2048];
+ int len;
+ do {
+ len = in.read(buffer);
+ out.write(buffer, 0, len);
+ } while (len > 0);
+ } catch (IOException e) {
+ Log.w(LOG_TAG, "Manual file move failed due to exception " + e);
+ if (dst.exists()) {
+ dst.delete();
+ }
+ return false;
+ } finally {
+ try {
+ if (in != null) {
+ in.close();
+ }
+ if (out != null) {
+ out.close();
+ }
+ } catch (IOException e) {
+ Log.w(LOG_TAG, "Error closing streams: " + e);
+ }
+ }
+ return true;
+ }
}
diff --git a/telephony/java/android/telephony/mbms/MbmsException.java b/telephony/java/android/telephony/mbms/MbmsException.java
index 8260b728a52f..e190623f5529 100644
--- a/telephony/java/android/telephony/mbms/MbmsException.java
+++ b/telephony/java/android/telephony/mbms/MbmsException.java
@@ -37,6 +37,8 @@ public class MbmsException extends Exception {
public static final int ERROR_UNABLE_TO_READ_SIM = 16;
public static final int ERROR_CARRIER_CHANGE_NOT_ALLOWED = 17;
public static final int ERROR_CANNOT_CHANGE_TEMP_FILE_ROOT = 18;
+ public static final int ERROR_UNKNOWN_DOWNLOAD_REQUEST = 19;
+ public static final int ERROR_UNABLE_TO_INITIALIZE = 20;
private final int mErrorCode;
diff --git a/telephony/java/android/telephony/mbms/ServiceInfo.java b/telephony/java/android/telephony/mbms/ServiceInfo.java
index b5675b2576dc..f9ad44c63118 100644
--- a/telephony/java/android/telephony/mbms/ServiceInfo.java
+++ b/telephony/java/android/telephony/mbms/ServiceInfo.java
@@ -26,6 +26,7 @@ import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
+import java.util.Objects;
import java.util.Set;
/**
@@ -175,4 +176,26 @@ public class ServiceInfo implements Parcelable {
return sessionEndTime;
}
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null) {
+ return false;
+ }
+ if (!(o instanceof ServiceInfo)) {
+ return false;
+ }
+ ServiceInfo that = (ServiceInfo) o;
+ return Objects.equals(names, that.names) &&
+ Objects.equals(className, that.className) &&
+ Objects.equals(locales, that.locales) &&
+ Objects.equals(serviceId, that.serviceId) &&
+ Objects.equals(sessionStartTime, that.sessionStartTime) &&
+ Objects.equals(sessionEndTime, that.sessionEndTime);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(names, className, locales, serviceId, sessionStartTime, sessionEndTime);
+ }
}
diff --git a/telephony/java/android/telephony/mbms/StreamingService.java b/telephony/java/android/telephony/mbms/StreamingService.java
index 608d3a921dc7..85731a14277f 100644
--- a/telephony/java/android/telephony/mbms/StreamingService.java
+++ b/telephony/java/android/telephony/mbms/StreamingService.java
@@ -42,7 +42,6 @@ public class StreamingService {
public final static int BROADCAST_METHOD = 1;
public final static int UNICAST_METHOD = 2;
- private final String mAppName;
private final int mSubscriptionId;
private final StreamingServiceInfo mServiceInfo;
private final IStreamingServiceCallback mCallback;
@@ -51,12 +50,10 @@ public class StreamingService {
/**
* @hide
*/
- public StreamingService(String appName,
- int subscriptionId,
+ public StreamingService(int subscriptionId,
IMbmsStreamingService service,
StreamingServiceInfo streamingServiceInfo,
IStreamingServiceCallback callback) {
- mAppName = appName;
mSubscriptionId = subscriptionId;
mService = service;
mServiceInfo = streamingServiceInfo;
@@ -77,7 +74,7 @@ public class StreamingService {
}
try {
- return mService.getPlaybackUri(mAppName, mSubscriptionId, mServiceInfo.getServiceId());
+ return mService.getPlaybackUri(mSubscriptionId, mServiceInfo.getServiceId());
} catch (RemoteException e) {
Log.w(LOG_TAG, "Remote process died");
mService = null;
@@ -103,7 +100,7 @@ public class StreamingService {
}
try {
- mService.stopStreaming(mAppName, mSubscriptionId, mServiceInfo.getServiceId());
+ mService.stopStreaming(mSubscriptionId, mServiceInfo.getServiceId());
} catch (RemoteException e) {
Log.w(LOG_TAG, "Remote process died");
mService = null;
@@ -117,7 +114,7 @@ public class StreamingService {
}
try {
- mService.disposeStream(mAppName, mSubscriptionId, mServiceInfo.getServiceId());
+ mService.disposeStream(mSubscriptionId, mServiceInfo.getServiceId());
} catch (RemoteException e) {
Log.w(LOG_TAG, "Remote process died");
mService = null;
diff --git a/telephony/java/android/telephony/mbms/vendor/IMbmsDownloadService.aidl b/telephony/java/android/telephony/mbms/vendor/IMbmsDownloadService.aidl
index ff7d233bbf2c..7112e13d5650 100755
--- a/telephony/java/android/telephony/mbms/vendor/IMbmsDownloadService.aidl
+++ b/telephony/java/android/telephony/mbms/vendor/IMbmsDownloadService.aidl
@@ -29,31 +29,15 @@ import android.telephony.mbms.IDownloadCallback;
*/
interface IMbmsDownloadService
{
- /**
- * Initialize download service
- * Registers this listener, subId with this appName
- *
- * No return value. Async errors may be reported, but none expected (not doing anything yet).
- */
- void initialize(String appName, int subId, IMbmsDownloadManagerCallback listener);
+ void initialize(int subId, IMbmsDownloadManagerCallback listener);
- /**
- * - Registers serviceClasses of interest with the uid/appName/subId key.
- * - Starts asynch fetching data on download services of matching classes to be reported
- * later by callback.
- *
- * Note that subsequent calls with the same callback, appName, subId and uid will replace
- * the service class list.
- */
- int getFileServices(String appName, int subId, in List<String> serviceClasses);
+ int getFileServices(int subId, in List<String> serviceClasses);
+
+ int setTempFileRootDirectory(int subId, String rootDirectoryPath);
- int setTempFileRootDirectory(String appName, int subId, String rootDirectoryPath);
- /**
- * should move the params into a DownloadRequest parcelable
- */
int download(in DownloadRequest downloadRequest, IDownloadCallback listener);
- List<DownloadRequest> listPendingDownloads(String appName, int subscriptionId);
+ List<DownloadRequest> listPendingDownloads(int subscriptionId);
int cancelDownload(in DownloadRequest downloadRequest);
@@ -66,9 +50,5 @@ interface IMbmsDownloadService
*/
void resetDownloadKnowledge(in DownloadRequest downloadRequest);
- /**
- * End of life for this MbmsDownloadManager.
- * Any pending downloads remain in affect and may start up independently in the future.
- */
- void dispose(String appName, int subId);
+ void dispose(int subId);
}
diff --git a/telephony/java/android/telephony/mbms/vendor/IMbmsStreamingService.aidl b/telephony/java/android/telephony/mbms/vendor/IMbmsStreamingService.aidl
index 8ff7fa7c54f3..1370b83857ec 100755
--- a/telephony/java/android/telephony/mbms/vendor/IMbmsStreamingService.aidl
+++ b/telephony/java/android/telephony/mbms/vendor/IMbmsStreamingService.aidl
@@ -27,28 +27,18 @@ import android.telephony.mbms.StreamingServiceInfo;
*/
interface IMbmsStreamingService
{
- int initialize(IMbmsStreamingManagerCallback listener, String appName, int subId);
+ int initialize(IMbmsStreamingManagerCallback listener, int subId);
- int getStreamingServices(String appName, int subId, in List<String> serviceClasses);
+ int getStreamingServices(int subId, in List<String> serviceClasses);
- int startStreaming(String appName, int subId, String serviceId,
+ int startStreaming(int subId, String serviceId,
IStreamingServiceCallback listener);
- /**
- * Per-stream api. Note each specifies what stream they apply to.
- */
+ Uri getPlaybackUri(int subId, String serviceId);
- Uri getPlaybackUri(String appName, int subId, String serviceId);
+ void stopStreaming(int subId, String serviceId);
- void stopStreaming(String appName, int subId, String serviceId);
+ void disposeStream(int subId, String serviceId);
- void disposeStream(String appName, int subId, String serviceId);
-
- /**
- * End of life for all MbmsStreamingManager's created by this uid/appName/subId.
- * Ends any streams run under this uid/appname/subId and calls the disposed methods
- * an callbacks registered for this uid/appName/subId and the disposed methods on any
- * listeners registered with startStreaming.
- */
- void dispose(String appName, int subId);
+ void dispose(int subId);
}
diff --git a/telephony/java/android/telephony/mbms/vendor/MbmsDownloadServiceBase.java b/telephony/java/android/telephony/mbms/vendor/MbmsDownloadServiceBase.java
index 9577dd2e3d78..58bda6480999 100644
--- a/telephony/java/android/telephony/mbms/vendor/MbmsDownloadServiceBase.java
+++ b/telephony/java/android/telephony/mbms/vendor/MbmsDownloadServiceBase.java
@@ -21,6 +21,7 @@ import android.telephony.mbms.DownloadRequest;
import android.telephony.mbms.DownloadStatus;
import android.telephony.mbms.IDownloadCallback;
import android.telephony.mbms.IMbmsDownloadManagerCallback;
+import android.telephony.mbms.MbmsException;
import java.util.List;
@@ -31,23 +32,66 @@ import java.util.List;
* TODO: future systemapi
*/
public class MbmsDownloadServiceBase extends IMbmsDownloadService.Stub {
+ /**
+ * Initialize the download service for this app and subId, registering the listener.
+ *
+ * May throw an {@link IllegalArgumentException} or a {@link SecurityException}
+ *
+ * @param listener The callback to use to communicate with the app.
+ * @param subscriptionId The subscription ID to use.
+ */
@Override
- public void initialize(String appName, int subscriptionId,
+ public void initialize(int subscriptionId,
IMbmsDownloadManagerCallback listener) throws RemoteException {
}
+ /**
+ * Registers serviceClasses of interest with the appName/subId key.
+ * Starts async fetching data on streaming services of matching classes to be reported
+ * later via {@link IMbmsDownloadManagerCallback#fileServicesUpdated(List)}
+ *
+ * Note that subsequent calls with the same uid and subId will replace
+ * the service class list.
+ *
+ * May throw an {@link IllegalArgumentException} or an {@link IllegalStateException}
+ *
+ * @param subscriptionId The subscription id to use.
+ * @param serviceClasses The service classes that the app wishes to get info on. The strings
+ * may contain arbitrary data as negotiated between the app and the
+ * carrier.
+ * @return One of {@link MbmsException#SUCCESS},
+ * {@link MbmsException#ERROR_MIDDLEWARE_NOT_YET_READY},
+ * {@link MbmsException#ERROR_CONCURRENT_SERVICE_LIMIT_REACHED}
+ */
@Override
- public int getFileServices(String appName, int subscriptionId, List<String> serviceClasses)
+ public int getFileServices(int subscriptionId, List<String> serviceClasses)
throws RemoteException {
return 0;
}
+ /**
+ * Sets the temp file root directory for this app/subscriptionId combination. The middleware
+ * should persist {@code rootDirectoryPath} and send it back when sending intents to the
+ * app's {@link android.telephony.mbms.MbmsDownloadReceiver}.
+ * @param subscriptionId The subscription id the download is operating under.
+ * @param rootDirectoryPath The path to the app's temp file root directory.
+ * @return {@link MbmsException#ERROR_MIDDLEWARE_NOT_YET_READY},
+ * {@link MbmsException#ERROR_CANNOT_CHANGE_TEMP_FILE_ROOT},
+ * or {@link MbmsException#ERROR_CONCURRENT_SERVICE_LIMIT_REACHED}
+ */
@Override
- public int setTempFileRootDirectory(String appName, int subscriptionId,
+ public int setTempFileRootDirectory(int subscriptionId,
String rootDirectoryPath) throws RemoteException {
return 0;
}
+ /**
+ * Issues a request to download a set of files.
+ * @param downloadRequest An object describing the set of files to be downloaded.
+ * @param listener A listener through which the middleware can provide progress updates to
+ * the app while both are still running.
+ * @return TODO: enumerate possible return values
+ */
@Override
public int download(DownloadRequest downloadRequest, IDownloadCallback listener)
throws RemoteException {
@@ -55,11 +99,23 @@ public class MbmsDownloadServiceBase extends IMbmsDownloadService.Stub {
}
@Override
- public List<DownloadRequest> listPendingDownloads(String appName, int subscriptionId)
+ public List<DownloadRequest> listPendingDownloads(int subscriptionId)
throws RemoteException {
return null;
}
+ /**
+ * Issues a request to cancel the specified download request.
+ *
+ * If the middleware is unable to cancel the request for whatever reason, it should return
+ * synchronously with an error. If this method returns {@link MbmsException#SUCCESS}, the app
+ * will no longer be expecting any more file-completed intents from the middleware for this
+ * {@link DownloadRequest}.
+ * @param downloadRequest The request to cancel
+ * @return {@link MbmsException#SUCCESS},
+ * {@link MbmsException#ERROR_UNKNOWN_DOWNLOAD_REQUEST},
+ * {@link MbmsException#ERROR_MIDDLEWARE_NOT_YET_READY}
+ */
@Override
public int cancelDownload(DownloadRequest downloadRequest) throws RemoteException {
return 0;
@@ -76,7 +132,21 @@ public class MbmsDownloadServiceBase extends IMbmsDownloadService.Stub {
throws RemoteException {
}
+ /**
+ * Signals that the app wishes to dispose of the session identified by the
+ * {@code subscriptionId} argument and the caller's uid. No notification back to the
+ * app is required for this operation, and the corresponding callback provided via
+ * {@link #initialize(int, IMbmsDownloadManagerCallback)} should no longer be used
+ * after this method has been called by the app.
+ *
+ * Any download requests issued by the app should remain in effect until the app calls
+ * {@link #cancelDownload(DownloadRequest)} on another session.
+ *
+ * May throw an {@link IllegalStateException}
+ *
+ * @param subscriptionId The subscription id to use.
+ */
@Override
- public void dispose(String appName, int subscriptionId) throws RemoteException {
+ public void dispose(int subscriptionId) throws RemoteException {
}
}
diff --git a/telephony/java/android/telephony/mbms/vendor/MbmsStreamingServiceBase.java b/telephony/java/android/telephony/mbms/vendor/MbmsStreamingServiceBase.java
index 2c1f085b67d3..b6cf628d4036 100644
--- a/telephony/java/android/telephony/mbms/vendor/MbmsStreamingServiceBase.java
+++ b/telephony/java/android/telephony/mbms/vendor/MbmsStreamingServiceBase.java
@@ -36,13 +36,12 @@ public class MbmsStreamingServiceBase extends IMbmsStreamingService.Stub {
* May throw an {@link IllegalArgumentException} or a {@link SecurityException}
*
* @param listener The callback to use to communicate with the app.
- * @param appName The app name as negotiated with the wireless carrier.
* @param subscriptionId The subscription ID to use.
* @return {@link MbmsException#SUCCESS} or {@link MbmsException#ERROR_ALREADY_INITIALIZED}
*/
@Override
- public int initialize(IMbmsStreamingManagerCallback listener, String appName,
- int subscriptionId) throws RemoteException {
+ public int initialize(IMbmsStreamingManagerCallback listener, int subscriptionId)
+ throws RemoteException {
return 0;
}
@@ -51,22 +50,21 @@ public class MbmsStreamingServiceBase extends IMbmsStreamingService.Stub {
* Starts async fetching data on streaming services of matching classes to be reported
* later via {@link IMbmsStreamingManagerCallback#streamingServicesUpdated(List)}
*
- * Note that subsequent calls with the same uid, appName and subId will replace
+ * Note that subsequent calls with the same uid and subId will replace
* the service class list.
*
* May throw an {@link IllegalArgumentException} or an {@link IllegalStateException}
*
- * @param appName The app name as negotiated with the wireless carrier.
* @param subscriptionId The subscription id to use.
* @param serviceClasses The service classes that the app wishes to get info on. The strings
* may contain arbitrary data as negotiated between the app and the
* carrier.
* @return One of {@link MbmsException#SUCCESS},
- * {@link MbmsException#ERROR_MIDDLEWARE_NOT_BOUND},
+ * {@link MbmsException#ERROR_MIDDLEWARE_NOT_YET_READY},
* {@link MbmsException#ERROR_CONCURRENT_SERVICE_LIMIT_REACHED}
*/
@Override
- public int getStreamingServices(String appName, int subscriptionId,
+ public int getStreamingServices(int subscriptionId,
List<String> serviceClasses) throws RemoteException {
return 0;
}
@@ -74,11 +72,10 @@ public class MbmsStreamingServiceBase extends IMbmsStreamingService.Stub {
/**
* Starts streaming on a particular service. This method may perform asynchronous work. When
* the middleware is ready to send bits to the frontend, it should inform the app via
- * {@link IStreamingServiceCallback#streamStateChanged(int)}.
+ * {@link IStreamingServiceCallback#streamStateUpdated(int)}.
*
* May throw an {@link IllegalArgumentException} or an {@link IllegalStateException}
*
- * @param appName The app name as negotiated with the wireless carrier.
* @param subscriptionId The subscription id to use.
* @param serviceId The ID of the streaming service that the app has requested.
* @param listener The listener object on which the app wishes to receive updates.
@@ -86,8 +83,8 @@ public class MbmsStreamingServiceBase extends IMbmsStreamingService.Stub {
* or {@link MbmsException#ERROR_UNABLE_TO_START_SERVICE}.
*/
@Override
- public int startStreaming(String appName, int subscriptionId,
- String serviceId, IStreamingServiceCallback listener) throws RemoteException {
+ public int startStreaming(int subscriptionId, String serviceId,
+ IStreamingServiceCallback listener) throws RemoteException {
return 0;
}
@@ -97,13 +94,12 @@ public class MbmsStreamingServiceBase extends IMbmsStreamingService.Stub {
*
* May throw an {@link IllegalArgumentException} or an {@link IllegalStateException}
*
- * @param appName The app name as negotiated with the wireless carrier.
* @param subscriptionId The subscription id to use.
* @param serviceId The ID of the streaming service that the app has requested.
* @return An opaque {@link Uri} to be passed to a video player that understands the format.
*/
@Override
- public @Nullable Uri getPlaybackUri(String appName, int subscriptionId, String serviceId)
+ public @Nullable Uri getPlaybackUri(int subscriptionId, String serviceId)
throws RemoteException {
return null;
}
@@ -111,16 +107,15 @@ public class MbmsStreamingServiceBase extends IMbmsStreamingService.Stub {
/**
* Stop streaming the stream identified by {@code serviceId}. Notification of the resulting
* stream state change should be reported to the app via
- * {@link IStreamingServiceCallback#streamStateChanged(int)}.
+ * {@link IStreamingServiceCallback#streamStateUpdated(int)}.
*
* May throw an {@link IllegalArgumentException} or an {@link IllegalStateException}
*
- * @param appName The app name as negotiated with the wireless carrier.
* @param subscriptionId The subscription id to use.
* @param serviceId The ID of the streaming service that the app wishes to stop.
*/
@Override
- public void stopStreaming(String appName, int subscriptionId, String serviceId)
+ public void stopStreaming(int subscriptionId, String serviceId)
throws RemoteException {
}
@@ -128,33 +123,31 @@ public class MbmsStreamingServiceBase extends IMbmsStreamingService.Stub {
* Dispose of the stream identified by {@code serviceId} for the app identified by the
* {@code appName} and {@code subscriptionId} arguments along with the caller's uid.
* No notification back to the app is required for this operation, and the callback provided via
- * {@link #startStreaming(String, int, String, IStreamingServiceCallback)} should no longer be
+ * {@link #startStreaming(int, String, IStreamingServiceCallback)} should no longer be
* used after this method has called by the app.
*
* May throw an {@link IllegalArgumentException} or an {@link IllegalStateException}
*
- * @param appName The app name as negotiated with the wireless carrier.
* @param subscriptionId The subscription id to use.
* @param serviceId The ID of the streaming service that the app wishes to dispose of.
*/
@Override
- public void disposeStream(String appName, int subscriptionId, String serviceId)
+ public void disposeStream(int subscriptionId, String serviceId)
throws RemoteException {
}
/**
- * Signals that the app wishes to dispose of the session identified by the {@code appName} and
- * {@code subscriptionId} arguments, as well as the caller's uid. No notification back to the
+ * Signals that the app wishes to dispose of the session identified by the
+ * {@code subscriptionId} argument and the caller's uid. No notification back to the
* app is required for this operation, and the corresponding callback provided via
- * {@link #initialize(IMbmsStreamingManagerCallback, String, int)} should no longer be used
+ * {@link #initialize(IMbmsStreamingManagerCallback, int)} should no longer be used
* after this method has been called by the app.
*
* May throw an {@link IllegalStateException}
*
- * @param appName The app name as negotiated with the wireless carrier.
* @param subscriptionId The subscription id to use.
*/
@Override
- public void dispose(String appName, int subscriptionId) throws RemoteException {
+ public void dispose(int subscriptionId) throws RemoteException {
}
}
diff --git a/tests/ShowWhenLockedApp/Android.mk b/tests/ShowWhenLockedApp/Android.mk
new file mode 100644
index 000000000000..006416791dd9
--- /dev/null
+++ b/tests/ShowWhenLockedApp/Android.mk
@@ -0,0 +1,10 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
+
+LOCAL_PACKAGE_NAME := ShowWhenLocked
+
+LOCAL_MODULE_TAGS := tests
+
+include $(BUILD_PACKAGE) \ No newline at end of file
diff --git a/tests/ShowWhenLockedApp/AndroidManifest.xml b/tests/ShowWhenLockedApp/AndroidManifest.xml
new file mode 100644
index 000000000000..a872e061526f
--- /dev/null
+++ b/tests/ShowWhenLockedApp/AndroidManifest.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * 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
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.showwhenlocked">
+ <application android:label="ShowWhenLocked">
+ <activity android:name=".ShowWhenLockedActivity"
+ android:showWhenLocked="true"
+ android:turnScreenOn="true"
+ android:launchMode="singleTask">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN"/>
+ <category android:name="android.intent.category.LAUNCHER"/>
+ </intent-filter>
+ </activity>
+ </application>
+</manifest>
diff --git a/tests/ShowWhenLockedApp/src/com/android/showwhenlocked/ShowWhenLockedActivity.java b/tests/ShowWhenLockedApp/src/com/android/showwhenlocked/ShowWhenLockedActivity.java
new file mode 100644
index 000000000000..f71483182676
--- /dev/null
+++ b/tests/ShowWhenLockedApp/src/com/android/showwhenlocked/ShowWhenLockedActivity.java
@@ -0,0 +1,164 @@
+/*
+ * 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.showwhenlocked;
+
+import android.app.Activity;
+import android.app.KeyguardManager;
+import android.content.Intent;
+import android.os.Bundle;
+import android.util.Log;
+
+/**
+ * Sample app to test the manifest attrs {@link android.R.attr#showWhenLocked}
+ * and {@link android.R.attr#turnScreenOn}.
+ *
+ * <p>Run with adb shell am start -n com.android.showwhenlocked/.ShowWhenLockedActivity to test
+ * when the phone has a keyguard enabled and/or the screen is off.
+ *
+ * Use the extra {@link #EXTRA_SHOW_WHEN_LOCKED} and {@link #EXTRA_TURN_SCREEN_ON} to test
+ * multiple scenarios.
+ *
+ * Ex: adb shell am start -n com.android.showwhenlocked/.ShowWhenLockedActivity --ez \
+ * showWhenLocked false \
+ * setTurnScreenOnAtStop false
+ *
+ * Note: Behavior may change if values are set to true after the Activity is already created
+ * and only brought to the front. For example, turnScreenOn only takes effect on the next launch
+ * if set using the extra value.
+ */
+public class ShowWhenLockedActivity extends Activity {
+ private static final String TAG = ShowWhenLockedActivity.class.getSimpleName();
+
+ /**
+ * The value set for this extra sets {@link #setShowWhenLocked(boolean)} as soon as the app
+ * is launched. This may cause delays in when the value set takes affect.
+ */
+ private static final String EXTRA_SHOW_WHEN_LOCKED = "showWhenLocked";
+
+ /**
+ * The value set for this extra sets {@link #setTurnScreenOn(boolean)} as soon as the app
+ * is launched. This may cause delays in when the value set takes affect.
+ */
+ private static final String EXTRA_TURN_SCREEN_ON = "turnScreenOn";
+
+ /**
+ * The value set for this extra will call {@link #setShowWhenLocked(boolean)} at onStop so
+ * it take effect on the next launch.
+ */
+ private static final String EXTRA_SHOW_WHEN_LOCKED_STOP = "setShowWhenLockedAtStop";
+
+ /**
+ * The value set for this extra will call {@link #setTurnScreenOn(boolean)} at onStop so
+ * it take effect on the next launch.
+ */
+ private static final String EXTRA_TURN_SCREEN_ON_STOP = "setTurnScreenOnAtStop";
+
+ /**
+ * The value set for this extra will call
+ * {@link KeyguardManager#requestDismissKeyguard(Activity, KeyguardManager.KeyguardDismissCallback)}
+ * as soon as the app is launched.
+ */
+ private static final String EXTRA_DISMISS_KEYGUARD = "dismissKeyguard";
+
+ private boolean showWhenLockedAtStop = true;
+ private boolean turnScreenOnAtStop = true;
+
+ private KeyguardManager mKeyguardManager;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ Log.v(TAG, "onCreate");
+ mKeyguardManager = (KeyguardManager) getSystemService(KEYGUARD_SERVICE);
+ handleExtras(getIntent().getExtras());
+ }
+
+ @Override
+ protected void onStart() {
+ super.onStart();
+ Log.v(TAG, "onStart");
+ }
+
+ @Override
+ protected void onNewIntent(Intent intent) {
+ super.onNewIntent(intent);
+ handleExtras(intent.getExtras());
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ Log.v(TAG, "onResume");
+ }
+
+ @Override
+ protected void onPause() {
+ super.onPause();
+ Log.v(TAG, "onPause");
+ }
+
+ @Override
+ protected void onStop() {
+ super.onStop();
+ Log.v(TAG, "onStop");
+
+ setShowWhenLocked(showWhenLockedAtStop);
+ setTurnScreenOn(turnScreenOnAtStop);
+ }
+
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+ Log.v(TAG, "onDestroy");
+ }
+
+ private void handleExtras(Bundle extras) {
+ if (extras == null) {
+ return;
+ }
+
+ if (extras.containsKey(EXTRA_SHOW_WHEN_LOCKED)) {
+ boolean showWhenLocked = extras.getBoolean(EXTRA_SHOW_WHEN_LOCKED, true);
+ Log.v(TAG, "Setting showWhenLocked to " + showWhenLocked);
+ setShowWhenLocked(showWhenLocked);
+ }
+
+ if (extras.containsKey(EXTRA_TURN_SCREEN_ON)) {
+ boolean turnScreenOn = extras.getBoolean(EXTRA_TURN_SCREEN_ON, true);
+ Log.v(TAG, "Setting turnScreenOn to " + turnScreenOn);
+ setTurnScreenOn(turnScreenOn);
+ }
+
+ if (extras.containsKey(EXTRA_SHOW_WHEN_LOCKED_STOP)) {
+ showWhenLockedAtStop = extras.getBoolean(EXTRA_SHOW_WHEN_LOCKED_STOP, true);
+ Log.v(TAG, "Setting showWhenLockedAtStop to " + showWhenLockedAtStop);
+ }
+
+ if (extras.containsKey(EXTRA_TURN_SCREEN_ON_STOP)) {
+ turnScreenOnAtStop = extras.getBoolean(EXTRA_TURN_SCREEN_ON_STOP, true);
+ Log.v(TAG, "Setting turnScreenOnAtStop to " + turnScreenOnAtStop);
+ }
+
+ if (extras.containsKey(EXTRA_DISMISS_KEYGUARD)) {
+ if (extras.getBoolean(EXTRA_DISMISS_KEYGUARD, false)) {
+ Log.v(TAG, "Requesting dismiss keyguard");
+ mKeyguardManager.requestDismissKeyguard(this, null);
+ }
+ }
+ }
+}
+
diff --git a/tools/aapt2/ConfigDescription.cpp b/tools/aapt2/ConfigDescription.cpp
index 7ff0c7227c9c..f16d806fe5c7 100644
--- a/tools/aapt2/ConfigDescription.cpp
+++ b/tools/aapt2/ConfigDescription.cpp
@@ -987,4 +987,8 @@ bool ConfigDescription::IsCompatibleWith(const ConfigDescription& o) const {
return !ConflictsWith(o) && !Dominates(o) && !o.Dominates(*this);
}
+::std::ostream& operator<<(::std::ostream& out, const ConfigDescription& o) {
+ return out << o.toString().string();
+}
+
} // namespace aapt
diff --git a/tools/aapt2/ConfigDescription.h b/tools/aapt2/ConfigDescription.h
index 65c96175091c..8c519a1a913c 100644
--- a/tools/aapt2/ConfigDescription.h
+++ b/tools/aapt2/ConfigDescription.h
@@ -24,30 +24,22 @@
namespace aapt {
-/*
- * Subclass of ResTable_config that adds convenient
- * initialization and comparison methods.
- */
+// Subclass of ResTable_config that adds convenient
+// initialization and comparison methods.
struct ConfigDescription : public android::ResTable_config {
- /**
- * Returns an immutable default config.
- */
+ // Returns an immutable default config.
static const ConfigDescription& DefaultConfig();
- /*
- * Parse a string of the form 'fr-sw600dp-land' and fill in the
- * given ResTable_config with resulting configuration parameters.
- *
- * The resulting configuration has the appropriate sdkVersion defined
- * for backwards compatibility.
- */
+ // Parse a string of the form 'fr-sw600dp-land' and fill in the
+ // given ResTable_config with resulting configuration parameters.
+ //
+ // The resulting configuration has the appropriate sdkVersion defined
+ // for backwards compatibility.
static bool Parse(const android::StringPiece& str, ConfigDescription* out = nullptr);
- /**
- * If the configuration uses an axis that was added after
- * the original Android release, make sure the SDK version
- * is set accordingly.
- */
+ // If the configuration uses an axis that was added after
+ // the original Android release, make sure the SDK version
+ // is set accordingly.
static void ApplyVersionForCompatibility(ConfigDescription* config);
ConfigDescription();
@@ -61,38 +53,30 @@ struct ConfigDescription : public android::ResTable_config {
ConfigDescription CopyWithoutSdkVersion() const;
- /**
- * A configuration X dominates another configuration Y, if X has at least the
- * precedence of Y and X is strictly more general than Y: for any type defined
- * by X, the same type is defined by Y with a value equal to or, in the case
- * of ranges, more specific than that of X.
- *
- * For example, the configuration 'en-w800dp' dominates 'en-rGB-w1024dp'. It
- * does not dominate 'fr', 'en-w720dp', or 'mcc001-en-w800dp'.
- */
+ // A configuration X dominates another configuration Y, if X has at least the
+ // precedence of Y and X is strictly more general than Y: for any type defined
+ // by X, the same type is defined by Y with a value equal to or, in the case
+ // of ranges, more specific than that of X.
+ //
+ // For example, the configuration 'en-w800dp' dominates 'en-rGB-w1024dp'. It
+ // does not dominate 'fr', 'en-w720dp', or 'mcc001-en-w800dp'.
bool Dominates(const ConfigDescription& o) const;
- /**
- * Returns true if this configuration defines a more important configuration
- * parameter than o. For example, "en" has higher precedence than "v23",
- * whereas "en" has the same precedence as "en-v23".
- */
+ // Returns true if this configuration defines a more important configuration
+ // parameter than o. For example, "en" has higher precedence than "v23",
+ // whereas "en" has the same precedence as "en-v23".
bool HasHigherPrecedenceThan(const ConfigDescription& o) const;
- /**
- * A configuration conflicts with another configuration if both
- * configurations define an incompatible configuration parameter. An
- * incompatible configuration parameter is a non-range, non-density parameter
- * that is defined in both configurations as a different, non-default value.
- */
+ // A configuration conflicts with another configuration if both
+ // configurations define an incompatible configuration parameter. An
+ // incompatible configuration parameter is a non-range, non-density parameter
+ // that is defined in both configurations as a different, non-default value.
bool ConflictsWith(const ConfigDescription& o) const;
- /**
- * A configuration is compatible with another configuration if both
- * configurations can match a common concrete device configuration and are
- * unrelated by domination. For example, land-v11 conflicts with port-v21
- * but is compatible with v21 (both land-v11 and v21 would match en-land-v23).
- */
+ // A configuration is compatible with another configuration if both
+ // configurations can match a common concrete device configuration and are
+ // unrelated by domination. For example, land-v11 conflicts with port-v21
+ // but is compatible with v21 (both land-v11 and v21 would match en-land-v23).
bool IsCompatibleWith(const ConfigDescription& o) const;
bool MatchWithDensity(const ConfigDescription& o) const;
@@ -105,6 +89,8 @@ struct ConfigDescription : public android::ResTable_config {
bool operator>(const ConfigDescription& o) const;
};
+::std::ostream& operator<<(::std::ostream& out, const ConfigDescription& o);
+
inline ConfigDescription::ConfigDescription() {
memset(this, 0, sizeof(*this));
size = sizeof(android::ResTable_config);
@@ -123,15 +109,13 @@ inline ConfigDescription::ConfigDescription(ConfigDescription&& o) {
*this = o;
}
-inline ConfigDescription& ConfigDescription::operator=(
- const android::ResTable_config& o) {
+inline ConfigDescription& ConfigDescription::operator=(const android::ResTable_config& o) {
*static_cast<android::ResTable_config*>(this) = o;
size = sizeof(android::ResTable_config);
return *this;
}
-inline ConfigDescription& ConfigDescription::operator=(
- const ConfigDescription& o) {
+inline ConfigDescription& ConfigDescription::operator=(const ConfigDescription& o) {
*static_cast<android::ResTable_config*>(this) = o;
return *this;
}
@@ -141,8 +125,7 @@ inline ConfigDescription& ConfigDescription::operator=(ConfigDescription&& o) {
return *this;
}
-inline bool ConfigDescription::MatchWithDensity(
- const ConfigDescription& o) const {
+inline bool ConfigDescription::MatchWithDensity(const ConfigDescription& o) const {
return match(o) && (density == 0 || density == o.density);
}
@@ -170,11 +153,6 @@ inline bool ConfigDescription::operator>(const ConfigDescription& o) const {
return compare(o) > 0;
}
-inline ::std::ostream& operator<<(::std::ostream& out,
- const ConfigDescription& o) {
- return out << o.toString().string();
-}
-
} // namespace aapt
#endif // AAPT_CONFIG_DESCRIPTION_H
diff --git a/tools/aapt2/cmd/Link.cpp b/tools/aapt2/cmd/Link.cpp
index e6bf3a6f9f56..353d734b69c6 100644
--- a/tools/aapt2/cmd/Link.cpp
+++ b/tools/aapt2/cmd/Link.cpp
@@ -60,6 +60,7 @@
#include "unflatten/BinaryResourceParser.h"
#include "util/Files.h"
#include "xml/XmlDom.h"
+#include "xml/XmlUtil.h"
using android::StringPiece;
using android::base::StringPrintf;
@@ -342,16 +343,18 @@ class ResourceFileFlattener {
ConfigDescription config;
// The entry this file came from.
- ResourceEntry* entry;
+ ResourceEntry* entry = nullptr;
// The file to copy as-is.
- io::IFile* file_to_copy;
+ io::IFile* file_to_copy = nullptr;
// The XML to process and flatten.
std::unique_ptr<xml::XmlResource> xml_to_flatten;
// The destination to write this file to.
std::string dst_path;
+
+ bool skip_versioning = false;
};
uint32_t GetCompressionFlags(const StringPiece& str);
@@ -431,19 +434,6 @@ uint32_t ResourceFileFlattener::GetCompressionFlags(const StringPiece& str) {
return ArchiveEntry::kCompress;
}
-static bool IsTransitionElement(const std::string& name) {
- return name == "fade" || name == "changeBounds" || name == "slide" || name == "explode" ||
- name == "changeImageTransform" || name == "changeTransform" ||
- name == "changeClipBounds" || name == "autoTransition" || name == "recolor" ||
- name == "changeScroll" || name == "transitionSet" || name == "transition" ||
- name == "transitionManager";
-}
-
-static bool IsVectorElement(const std::string& name) {
- return name == "vector" || name == "animated-vector" || name == "pathInterpolator" ||
- name == "objectAnimator";
-}
-
template <typename T>
std::vector<T> make_singleton_vec(T&& val) {
std::vector<T> vec;
@@ -476,21 +466,10 @@ std::vector<std::unique_ptr<xml::XmlResource>> ResourceFileFlattener::LinkAndVer
}
}
- if (options_.no_auto_version) {
+ if (options_.no_auto_version || file_op->skip_versioning) {
return make_singleton_vec(std::move(file_op->xml_to_flatten));
}
- if (options_.no_version_vectors || options_.no_version_transitions) {
- // Skip this if it is a vector or animated-vector.
- xml::Element* el = xml::FindRootElement(doc);
- if (el && el->namespace_uri.empty()) {
- if ((options_.no_version_vectors && IsVectorElement(el->name)) ||
- (options_.no_version_transitions && IsTransitionElement(el->name))) {
- return make_singleton_vec(std::move(file_op->xml_to_flatten));
- }
- }
- }
-
const ConfigDescription& config = file_op->config;
ResourceEntry* entry = file_op->entry;
@@ -504,15 +483,26 @@ bool ResourceFileFlattener::Flatten(ResourceTable* table, IArchiveWriter* archiv
bool error = false;
std::map<std::pair<ConfigDescription, StringPiece>, FileOperation> config_sorted_files;
+ int tag_version_options = 0;
+ if (options_.no_version_vectors) {
+ tag_version_options |= xml::kNoVersionVector;
+ }
+
+ if (options_.no_version_transitions) {
+ tag_version_options |= xml::kNoVersionTransition;
+ }
+
for (auto& pkg : table->packages) {
for (auto& type : pkg->types) {
// Sort by config and name, so that we get better locality in the zip file.
config_sorted_files.clear();
- std::queue<FileOperation> file_operations;
// Populate the queue with all files in the ResourceTable.
for (auto& entry : type->entries) {
- for (auto& config_value : entry->values) {
+ const auto values_end = entry->values.end();
+ for (auto values_iter = entry->values.begin(); values_iter != values_end; ++values_iter) {
+ ResourceConfigValue* config_value = values_iter->get();
+
// WARNING! Do not insert or remove any resources while executing in this scope. It will
// corrupt the iteration order.
@@ -554,6 +544,44 @@ bool ResourceFileFlattener::Flatten(ResourceTable* table, IArchiveWriter* archiv
file_op.xml_to_flatten->file.config = config_value->config;
file_op.xml_to_flatten->file.source = file_ref->GetSource();
file_op.xml_to_flatten->file.name = ResourceName(pkg->name, type->type, entry->name);
+
+ // Check if this file needs to be versioned based on tag rules.
+ xml::Element* root_el = xml::FindRootElement(file_op.xml_to_flatten.get());
+ if (root_el == nullptr) {
+ context_->GetDiagnostics()->Error(DiagMessage(file->GetSource())
+ << "failed to find the root XML element");
+ return false;
+ }
+
+ if (root_el->namespace_uri.empty()) {
+ if (Maybe<xml::TagApiVersionResult> result =
+ xml::GetXmlTagApiVersion(root_el->name, tag_version_options)) {
+ file_op.skip_versioning = result.value().skip_version;
+ if (result.value().api_version && config_value->config.sdkVersion == 0u) {
+ const ApiVersion min_tag_version = result.value().api_version.value();
+ // Only version it if it doesn't specify its own version and the version is
+ // greater than the minSdk.
+ const util::Range<ApiVersion> valid_range{
+ context_->GetMinSdkVersion() + 1,
+ FindNextApiVersionForConfigInSortedVector(values_iter, values_end)};
+ if (valid_range.Contains(min_tag_version)) {
+ // Update the configurations. The iteration order will not be affected
+ // since sdkVersions in ConfigDescriptions are the last property compared
+ // in the sort function.
+ if (context_->IsVerbose()) {
+ context_->GetDiagnostics()->Note(DiagMessage(config_value->value->GetSource())
+ << "auto-versioning XML resource to API "
+ << min_tag_version);
+ }
+ const_cast<ConfigDescription&>(config_value->config).sdkVersion =
+ static_cast<uint16_t>(min_tag_version);
+ file_op.config.sdkVersion = static_cast<uint16_t>(min_tag_version);
+ file_op.xml_to_flatten->file.config.sdkVersion =
+ static_cast<uint16_t>(min_tag_version);
+ }
+ }
+ }
+ }
}
// NOTE(adamlesinski): Explicitly construct a StringPiece here, or
diff --git a/tools/aapt2/integration-tests/AppOne/res/drawable/adaptive-icon.xml b/tools/aapt2/integration-tests/AppOne/res/drawable/adaptive-icon.xml
new file mode 100644
index 000000000000..e3cda7e1e979
--- /dev/null
+++ b/tools/aapt2/integration-tests/AppOne/res/drawable/adaptive-icon.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<adaptive-icon xmlns:android="http://schema.android.com/apk/res/android">
+ <background android:drawable="@android:color/white" />
+ <foreground android:drawable="@drawable/image" />
+</adaptive-icon>
diff --git a/tools/aapt2/link/AutoVersioner.cpp b/tools/aapt2/link/AutoVersioner.cpp
index f80c6e9b34d3..4ac70d91bcc9 100644
--- a/tools/aapt2/link/AutoVersioner.cpp
+++ b/tools/aapt2/link/AutoVersioner.cpp
@@ -34,6 +34,19 @@ bool ShouldGenerateVersionedResource(const ResourceEntry* entry, const ConfigDes
return sdk_version_to_generate < FindNextApiVersionForConfig(entry, config);
}
+ApiVersion FindNextApiVersionForConfigInSortedVector(
+ std::vector<std::unique_ptr<ResourceConfigValue>>::const_iterator start,
+ std::vector<std::unique_ptr<ResourceConfigValue>>::const_iterator end) {
+ const ConfigDescription start_config = (*start)->config.CopyWithoutSdkVersion();
+ ++start;
+ if (start != end) {
+ if ((*start)->config.CopyWithoutSdkVersion() == start_config) {
+ return static_cast<ApiVersion>((*start)->config.sdkVersion);
+ }
+ }
+ return std::numeric_limits<ApiVersion>::max();
+}
+
ApiVersion FindNextApiVersionForConfig(const ResourceEntry* entry,
const ConfigDescription& config) {
const auto end_iter = entry->values.end();
@@ -46,25 +59,7 @@ ApiVersion FindNextApiVersionForConfig(const ResourceEntry* entry,
// The source config came from this list, so it should be here.
CHECK(iter != entry->values.end());
- ++iter;
-
- // The next configuration either only varies in sdkVersion, or it is completely different
- // and therefore incompatible. If it is incompatible, we must generate the versioned resource.
-
- // NOTE: The ordering of configurations takes sdkVersion as higher precedence than other
- // qualifiers, so we need to iterate through the entire list to be sure there
- // are no higher sdk level versions of this resource.
- ConfigDescription temp_config(config);
- for (; iter != end_iter; ++iter) {
- temp_config.sdkVersion = (*iter)->config.sdkVersion;
- if (temp_config == (*iter)->config) {
- // The two configs are the same, return the sdkVersion.
- return (*iter)->config.sdkVersion;
- }
- }
-
- // Didn't find another config with a different sdk version, so return the highest possible value.
- return std::numeric_limits<ApiVersion>::max();
+ return FindNextApiVersionForConfigInSortedVector(iter, end_iter);
}
bool AutoVersioner::Consume(IAaptContext* context, ResourceTable* table) {
diff --git a/tools/aapt2/link/AutoVersioner_test.cpp b/tools/aapt2/link/AutoVersioner_test.cpp
index 49639f8ad549..88a831b9e801 100644
--- a/tools/aapt2/link/AutoVersioner_test.cpp
+++ b/tools/aapt2/link/AutoVersioner_test.cpp
@@ -42,8 +42,8 @@ TEST(AutoVersionerTest, GenerateVersionedResourceWhenHigherVersionExists) {
ResourceEntry entry("foo");
entry.values.push_back(util::make_unique<ResourceConfigValue>(ConfigDescription::DefaultConfig(), ""));
- entry.values.push_back(util::make_unique<ResourceConfigValue>(sw600dp_v13_config, ""));
entry.values.push_back(util::make_unique<ResourceConfigValue>(v21_config, ""));
+ entry.values.push_back(util::make_unique<ResourceConfigValue>(sw600dp_v13_config, ""));
EXPECT_TRUE(ShouldGenerateVersionedResource(&entry, ConfigDescription::DefaultConfig(), 17));
EXPECT_FALSE(ShouldGenerateVersionedResource(&entry, ConfigDescription::DefaultConfig(), 22));
diff --git a/tools/aapt2/link/Linkers.h b/tools/aapt2/link/Linkers.h
index 5527f9092c87..493b6b1181b9 100644
--- a/tools/aapt2/link/Linkers.h
+++ b/tools/aapt2/link/Linkers.h
@@ -23,6 +23,7 @@
#include "android-base/macros.h"
#include "Resource.h"
+#include "ResourceTable.h"
#include "SdkConstants.h"
#include "process/IResourceTableConsumer.h"
#include "xml/XmlDom.h"
@@ -41,17 +42,19 @@ struct CallSite {
ResourceNameRef resource;
};
-/**
- * Determines whether a versioned resource should be created. If a versioned
- * resource already exists, it takes precedence.
- */
+// Determines whether a versioned resource should be created. If a versioned resource already
+// exists, it takes precedence.
bool ShouldGenerateVersionedResource(const ResourceEntry* entry, const ConfigDescription& config,
const ApiVersion sdk_version_to_generate);
-// Finds the next largest ApiVersion of the config which is identical to the given config except
-// for sdkVersion.
+// Finds the next largest ApiVersion of `config` for values defined for `entry`.
ApiVersion FindNextApiVersionForConfig(const ResourceEntry* entry, const ConfigDescription& config);
+// Finds the next largest ApiVersion of the config pointed to by the iterator `start`.
+ApiVersion FindNextApiVersionForConfigInSortedVector(
+ std::vector<std::unique_ptr<ResourceConfigValue>>::const_iterator start,
+ std::vector<std::unique_ptr<ResourceConfigValue>>::const_iterator end);
+
class AutoVersioner : public IResourceTableConsumer {
public:
AutoVersioner() = default;
diff --git a/tools/aapt2/readme.md b/tools/aapt2/readme.md
index ebcd4698d8d5..8c476fac3b89 100644
--- a/tools/aapt2/readme.md
+++ b/tools/aapt2/readme.md
@@ -9,6 +9,7 @@
(bug 62839863)
- Fixed issue where Java classes referenced from fragments and menus were not added to
the set of Proguard keep rules. (bug 62216174)
+- Automatically version XML `<adaptive-icon>` resources to v26. (bug 62316340)
## Version 2.17
### `aapt2 ...`
diff --git a/tools/aapt2/util/TypeTraits.h b/tools/aapt2/util/TypeTraits.h
index b6539edce6d9..6fcb4bdb4145 100644
--- a/tools/aapt2/util/TypeTraits.h
+++ b/tools/aapt2/util/TypeTraits.h
@@ -38,6 +38,7 @@ namespace aapt {
DEFINE_HAS_BINARY_OP_TRAIT(has_eq_op, ==);
DEFINE_HAS_BINARY_OP_TRAIT(has_lt_op, <);
+DEFINE_HAS_BINARY_OP_TRAIT(has_lte_op, <=);
/**
* Type trait that checks if two types can be equated (==) and compared (<).
diff --git a/tools/aapt2/util/Util.h b/tools/aapt2/util/Util.h
index 386f74b0301a..8bca9dd739bf 100644
--- a/tools/aapt2/util/Util.h
+++ b/tools/aapt2/util/Util.h
@@ -48,6 +48,11 @@ template <typename T>
struct Range {
T start;
T end;
+
+ typename std::enable_if<has_lte_op<const T, const T>::value, bool>::type Contains(
+ const T& t) const {
+ return start <= t && t < end;
+ }
};
std::vector<std::string> Split(const android::StringPiece& str, char sep);
diff --git a/tools/aapt2/xml/XmlUtil.cpp b/tools/aapt2/xml/XmlUtil.cpp
index fb8cee8b5634..fa1b0f049678 100644
--- a/tools/aapt2/xml/XmlUtil.cpp
+++ b/tools/aapt2/xml/XmlUtil.cpp
@@ -80,5 +80,65 @@ void TransformReferenceFromNamespace(IPackageDeclStack* decl_stack,
}
}
+namespace {
+
+struct TagCompat {
+ ApiVersion api_version;
+
+ enum class XmlType {
+ kVector,
+ kTransition,
+ kAdaptiveIcon,
+ };
+
+ XmlType type;
+};
+
+std::unordered_map<StringPiece, TagCompat> sTagVersions = {
+ {"fade", {SDK_LOLLIPOP, TagCompat::XmlType::kTransition}},
+ {"changeBounds", {SDK_LOLLIPOP, TagCompat::XmlType::kTransition}},
+ {"slide", {SDK_LOLLIPOP, TagCompat::XmlType::kTransition}},
+ {"explode", {SDK_LOLLIPOP, TagCompat::XmlType::kTransition}},
+ {"changeImageTransform", {SDK_LOLLIPOP, TagCompat::XmlType::kTransition}},
+ {"changeTransform", {SDK_LOLLIPOP, TagCompat::XmlType::kTransition}},
+ {"changeClipBounds", {SDK_LOLLIPOP, TagCompat::XmlType::kTransition}},
+ {"autoTransition", {SDK_LOLLIPOP, TagCompat::XmlType::kTransition}},
+ {"recolor", {SDK_LOLLIPOP, TagCompat::XmlType::kTransition}},
+ {"changeScroll", {SDK_LOLLIPOP, TagCompat::XmlType::kTransition}},
+ {"transitionSet", {SDK_LOLLIPOP, TagCompat::XmlType::kTransition}},
+ {"transition", {SDK_LOLLIPOP, TagCompat::XmlType::kTransition}},
+ {"transitionManager", {SDK_LOLLIPOP, TagCompat::XmlType::kTransition}},
+
+ {"vector", {SDK_LOLLIPOP, TagCompat::XmlType::kVector}},
+ {"animated-vector", {SDK_LOLLIPOP, TagCompat::XmlType::kVector}},
+ {"pathInterpolator", {SDK_LOLLIPOP, TagCompat::XmlType::kVector}},
+ {"objectAnimator", {SDK_LOLLIPOP, TagCompat::XmlType::kVector}},
+
+ {"adaptive-icon", {SDK_O, TagCompat::XmlType::kAdaptiveIcon}},
+};
+
+} // namespace
+
+Maybe<TagApiVersionResult> GetXmlTagApiVersion(const StringPiece& tag_name, int options) {
+ auto iter = sTagVersions.find(tag_name);
+ if (iter == sTagVersions.end()) {
+ return {};
+ }
+
+ const TagCompat& tag_compat = iter->second;
+ if (options & kNoVersionVector) {
+ if (tag_compat.type == TagCompat::XmlType::kVector) {
+ return TagApiVersionResult{{}, true /*skip_version*/};
+ }
+ }
+
+ if (options & kNoVersionTransition) {
+ if (tag_compat.type == TagCompat::XmlType::kTransition) {
+ return TagApiVersionResult{{}, true /*skip_version*/};
+ }
+ }
+ return TagApiVersionResult{tag_compat.api_version, false /*skip_version*/};
+}
+
} // namespace xml
} // namespace aapt
diff --git a/tools/aapt2/xml/XmlUtil.h b/tools/aapt2/xml/XmlUtil.h
index 1650ac2124ac..552f42adc464 100644
--- a/tools/aapt2/xml/XmlUtil.h
+++ b/tools/aapt2/xml/XmlUtil.h
@@ -20,18 +20,16 @@
#include <string>
#include "ResourceValues.h"
+#include "SdkConstants.h"
#include "util/Maybe.h"
namespace aapt {
namespace xml {
constexpr const char* kSchemaAuto = "http://schemas.android.com/apk/res-auto";
-constexpr const char* kSchemaPublicPrefix =
- "http://schemas.android.com/apk/res/";
-constexpr const char* kSchemaPrivatePrefix =
- "http://schemas.android.com/apk/prv/res/";
-constexpr const char* kSchemaAndroid =
- "http://schemas.android.com/apk/res/android";
+constexpr const char* kSchemaPublicPrefix = "http://schemas.android.com/apk/res/";
+constexpr const char* kSchemaPrivatePrefix = "http://schemas.android.com/apk/prv/res/";
+constexpr const char* kSchemaAndroid = "http://schemas.android.com/apk/res/android";
constexpr const char* kSchemaTools = "http://schemas.android.com/tools";
constexpr const char* kSchemaAapt = "http://schemas.android.com/aapt";
@@ -102,6 +100,24 @@ struct IPackageDeclStack {
void TransformReferenceFromNamespace(IPackageDeclStack* decl_stack,
const android::StringPiece& local_package, Reference* in_ref);
+struct TagApiVersionResult {
+ // If set, the API version to apply.
+ Maybe<ApiVersion> api_version;
+
+ // Whether to skip any auto-versioning.
+ bool skip_version;
+};
+
+enum TagVersionOptions : int {
+ // Skip versioning XML resources that deal with vector drawables.
+ kNoVersionVector,
+
+ // Skip versioning XML resources that deal with transitions.
+ kNoVersionTransition,
+};
+
+Maybe<TagApiVersionResult> GetXmlTagApiVersion(const android::StringPiece& tag_name, int options);
+
} // namespace xml
} // namespace aapt