Merge "Register for fallback vibration's death"
diff --git a/Android.bp b/Android.bp
index d1970fa..05fb3c0 100644
--- a/Android.bp
+++ b/Android.bp
@@ -578,7 +578,6 @@
"wifi/java/android/net/wifi/rtt/IWifiRttManager.aidl",
"wifi/java/android/net/wifi/hotspot2/IProvisioningCallback.aidl",
"wifi/java/android/net/wifi/IWifiScanner.aidl",
- "wifi/java/android/net/wifi/IRttManager.aidl",
"packages/services/PacProcessor/com/android/net/IProxyService.aidl",
"packages/services/Proxy/com/android/net/IProxyCallback.aidl",
"packages/services/Proxy/com/android/net/IProxyPortListener.aidl",
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
index 711c12d..2a67b75 100644
--- a/PREUPLOAD.cfg
+++ b/PREUPLOAD.cfg
@@ -1,6 +1,7 @@
[Hook Scripts]
checkstyle_hook = ${REPO_ROOT}/prebuilts/checkstyle/checkstyle.py --sha ${PREUPLOAD_COMMIT}
-fw core/java/android/
+ graphics/java/android
core/tests/coretests/src/android/
packages/PrintRecommendationService/
packages/PrintSpooler/
diff --git a/apct-tests/perftests/core/src/android/text/StaticLayoutMultithreadPerfTest.java b/apct-tests/perftests/core/src/android/text/StaticLayoutMultithreadPerfTest.java
new file mode 100644
index 0000000..60c6d89
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/text/StaticLayoutMultithreadPerfTest.java
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2018 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.text;
+
+import android.perftests.utils.BenchmarkState;
+import android.perftests.utils.PerfStatusReporter;
+
+import android.support.test.filters.LargeTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Random;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.TimeUnit;
+
+@LargeTest
+@RunWith(AndroidJUnit4.class)
+public class StaticLayoutMultithreadPerfTest {
+ private static final int WORD_LENGTH = 9; // Random word has 9 characters.
+ private static final int WORDS_IN_LINE = 8; // Roughly, 8 words in a line.
+ private static final boolean NO_STYLE_TEXT = false;
+
+ private static TextPaint PAINT = new TextPaint();
+ private static final int TEXT_WIDTH = WORDS_IN_LINE * WORD_LENGTH * (int) PAINT.getTextSize();
+
+ public StaticLayoutMultithreadPerfTest() {}
+
+ @Rule
+ public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+
+ private CountDownLatch mStartLatch;
+ private AtomicBoolean mThreadState; // True for running, False for stopped.
+
+ private static final long TIMEOUT_MS = 5000;
+
+ private Thread[] startBackgroundThread(int numOfThreads) {
+ mStartLatch = new CountDownLatch(numOfThreads);
+ mThreadState = new AtomicBoolean(true);
+
+ Thread[] threads = new Thread[numOfThreads];
+ for (int i = 0; i < numOfThreads; ++i) {
+ final int seed = i + 1;
+ threads[i] = new Thread(new Runnable() {
+ @Override
+ public void run() {
+ final TextPerfUtils util = new TextPerfUtils();
+ util.resetRandom(seed);
+
+ mStartLatch.countDown();
+ while (mThreadState.get()) {
+ final CharSequence text = util.nextRandomParagraph(
+ WORD_LENGTH, NO_STYLE_TEXT);
+ StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH)
+ .build();
+ }
+ }
+ });
+ }
+
+ for (int i = 0; i < numOfThreads; ++i) {
+ threads[i].start();
+ }
+
+ try {
+ mStartLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS);
+ } catch (InterruptedException e) {
+ throw new RuntimeException(e);
+ }
+ return threads;
+ }
+
+ private void finishThreads(Thread[] threads) {
+ mThreadState.set(false);
+ for (Thread thread : threads) {
+ try {
+ thread.join();
+ } catch (InterruptedException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ mStartLatch = null;
+ mThreadState = null;
+ }
+
+ private void runRandomTest(int numOfTotalThreads) {
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ final TextPerfUtils util = new TextPerfUtils();
+ Thread[] threads = startBackgroundThread(numOfTotalThreads - 1);
+ while (state.keepRunning()) {
+ state.pauseTiming();
+ final CharSequence text = util.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT);
+ state.resumeTiming();
+
+ StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH)
+ .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NONE)
+ .setBreakStrategy(Layout.BREAK_STRATEGY_SIMPLE)
+ .build();
+ }
+ finishThreads(threads);
+ }
+
+ @Test
+ public void testCreate_RandomText_Thread_1() {
+ runRandomTest(1);
+ }
+
+ @Test
+ public void testCreate_RandomText_Thread_2() {
+ runRandomTest(2);
+ }
+
+ @Test
+ public void testCreate_RandomText_Thread_4() {
+ runRandomTest(4);
+ }
+}
diff --git a/api/current.txt b/api/current.txt
index c03798f..afd59cb 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -73,6 +73,7 @@
field public static final java.lang.String DUMP = "android.permission.DUMP";
field public static final java.lang.String EXPAND_STATUS_BAR = "android.permission.EXPAND_STATUS_BAR";
field public static final java.lang.String FACTORY_TEST = "android.permission.FACTORY_TEST";
+ field public static final java.lang.String FOREGROUND_SERVICE = "android.permission.FOREGROUND_SERVICE";
field public static final java.lang.String GET_ACCOUNTS = "android.permission.GET_ACCOUNTS";
field public static final java.lang.String GET_ACCOUNTS_PRIVILEGED = "android.permission.GET_ACCOUNTS_PRIVILEGED";
field public static final java.lang.String GET_PACKAGE_SIZE = "android.permission.GET_PACKAGE_SIZE";
@@ -6739,6 +6740,7 @@
field public static final int TAG_APP_PROCESS_START = 210005; // 0x33455
field public static final int TAG_CERT_AUTHORITY_INSTALLED = 210029; // 0x3346d
field public static final int TAG_CERT_AUTHORITY_REMOVED = 210030; // 0x3346e
+ field public static final int TAG_CRYPTO_SELF_TEST_COMPLETED = 210031; // 0x3346f
field public static final int TAG_KEYGUARD_DISABLED_FEATURES_SET = 210021; // 0x33465
field public static final int TAG_KEYGUARD_DISMISSED = 210006; // 0x33456
field public static final int TAG_KEYGUARD_DISMISS_AUTH_ATTEMPT = 210007; // 0x33457
@@ -23369,6 +23371,7 @@
method public android.media.MediaDrm.CryptoSession getCryptoSession(byte[], java.lang.String, java.lang.String);
method public android.media.MediaDrm.KeyRequest getKeyRequest(byte[], byte[], java.lang.String, int, java.util.HashMap<java.lang.String, java.lang.String>) throws android.media.NotProvisionedException;
method public int getMaxHdcpLevel();
+ method public static int getMaxSecurityLevel();
method public int getMaxSessionCount();
method public android.os.PersistableBundle getMetrics();
method public int getOpenSessionCount();
@@ -23382,6 +23385,7 @@
method public static boolean isCryptoSchemeSupported(java.util.UUID);
method public static boolean isCryptoSchemeSupported(java.util.UUID, java.lang.String);
method public byte[] openSession() throws android.media.NotProvisionedException, android.media.ResourceBusyException;
+ method public byte[] openSession(int) throws android.media.NotProvisionedException, android.media.ResourceBusyException;
method public byte[] provideKeyResponse(byte[], byte[]) throws android.media.DeniedByServerException, android.media.NotProvisionedException;
method public void provideProvisionResponse(byte[]) throws android.media.DeniedByServerException;
method public java.util.HashMap<java.lang.String, java.lang.String> queryKeyStatus(byte[]);
@@ -23397,7 +23401,6 @@
method public void setOnKeyStatusChangeListener(android.media.MediaDrm.OnKeyStatusChangeListener, android.os.Handler);
method public void setPropertyByteArray(java.lang.String, byte[]);
method public void setPropertyString(java.lang.String, java.lang.String);
- method public void setSecurityLevel(byte[], int);
field public static final deprecated int EVENT_KEY_EXPIRED = 3; // 0x3
field public static final int EVENT_KEY_REQUIRED = 2; // 0x2
field public static final deprecated int EVENT_PROVISION_REQUIRED = 1; // 0x1
@@ -40504,6 +40507,7 @@
public final class Call {
method public void answer(int);
method public void conference(android.telecom.Call);
+ method public void deflect(android.net.Uri);
method public void disconnect();
method public java.util.List<java.lang.String> getCannedTextResponses();
method public java.util.List<android.telecom.Call> getChildren();
@@ -40614,6 +40618,7 @@
field public static final int CAPABILITY_SUPPORTS_VT_REMOTE_BIDIRECTIONAL = 3072; // 0xc00
field public static final int CAPABILITY_SUPPORTS_VT_REMOTE_RX = 1024; // 0x400
field public static final int CAPABILITY_SUPPORTS_VT_REMOTE_TX = 2048; // 0x800
+ field public static final int CAPABILITY_SUPPORT_DEFLECT = 16777216; // 0x1000000
field public static final int CAPABILITY_SUPPORT_HOLD = 2; // 0x2
field public static final int CAPABILITY_SWAP_CONFERENCE = 8; // 0x8
field public static final int PROPERTY_ASSISTED_DIALING_USED = 512; // 0x200
@@ -40761,6 +40766,7 @@
method public void onAnswer();
method public void onCallAudioStateChanged(android.telecom.CallAudioState);
method public void onCallEvent(java.lang.String, android.os.Bundle);
+ method public void onDeflect(android.net.Uri);
method public void onDisconnect();
method public void onExtrasChanged(android.os.Bundle);
method public void onHandoverComplete();
@@ -40829,6 +40835,7 @@
field public static final int CAPABILITY_SUPPORTS_VT_REMOTE_BIDIRECTIONAL = 3072; // 0xc00
field public static final int CAPABILITY_SUPPORTS_VT_REMOTE_RX = 1024; // 0x400
field public static final int CAPABILITY_SUPPORTS_VT_REMOTE_TX = 2048; // 0x800
+ field public static final int CAPABILITY_SUPPORT_DEFLECT = 33554432; // 0x2000000
field public static final int CAPABILITY_SUPPORT_HOLD = 2; // 0x2
field public static final int CAPABILITY_SWAP_CONFERENCE = 8; // 0x8
field public static final java.lang.String EVENT_CALL_MERGE_FAILED = "android.telecom.event.CALL_MERGE_FAILED";
@@ -44897,6 +44904,7 @@
method public abstract int getAttributeListValue(int, java.lang.String[], int);
method public abstract java.lang.String getAttributeName(int);
method public abstract int getAttributeNameResource(int);
+ method public default java.lang.String getAttributeNamespace(int);
method public abstract int getAttributeResourceValue(java.lang.String, java.lang.String, int);
method public abstract int getAttributeResourceValue(int, int);
method public abstract int getAttributeUnsignedIntValue(java.lang.String, java.lang.String, int);
diff --git a/api/system-current.txt b/api/system-current.txt
index 2d3b65a..3dbb333 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -30,8 +30,8 @@
field public static final java.lang.String BIND_RUNTIME_PERMISSION_PRESENTER_SERVICE = "android.permission.BIND_RUNTIME_PERMISSION_PRESENTER_SERVICE";
field public static final java.lang.String BIND_SETTINGS_SUGGESTIONS_SERVICE = "android.permission.BIND_SETTINGS_SUGGESTIONS_SERVICE";
field public static final java.lang.String BIND_TELEPHONY_DATA_SERVICE = "android.permission.BIND_TELEPHONY_DATA_SERVICE";
- field public static final java.lang.String BIND_TEXTCLASSIFIER_SERVICE = "android.permission.BIND_TEXTCLASSIFIER_SERVICE";
field public static final java.lang.String BIND_TELEPHONY_NETWORK_SERVICE = "android.permission.BIND_TELEPHONY_NETWORK_SERVICE";
+ field public static final java.lang.String BIND_TEXTCLASSIFIER_SERVICE = "android.permission.BIND_TEXTCLASSIFIER_SERVICE";
field public static final java.lang.String BIND_TRUST_AGENT = "android.permission.BIND_TRUST_AGENT";
field public static final java.lang.String BIND_TV_REMOTE_SERVICE = "android.permission.BIND_TV_REMOTE_SERVICE";
field public static final java.lang.String BLUETOOTH_PRIVILEGED = "android.permission.BLUETOOTH_PRIVILEGED";
@@ -362,10 +362,12 @@
public final class StatsManager {
method public boolean addConfiguration(long, byte[], java.lang.String, java.lang.String);
+ method public boolean addConfiguration(long, byte[]);
method public byte[] getData(long);
method public byte[] getMetadata();
method public boolean removeConfiguration(long);
method public boolean setBroadcastSubscriber(long, long, android.app.PendingIntent);
+ method public boolean setDataFetchOperation(long, android.app.PendingIntent);
field public static final java.lang.String ACTION_STATSD_STARTED = "android.app.action.STATSD_STARTED";
field public static final java.lang.String EXTRA_STATS_CONFIG_KEY = "android.app.extra.STATS_CONFIG_KEY";
field public static final java.lang.String EXTRA_STATS_CONFIG_UID = "android.app.extra.STATS_CONFIG_UID";
@@ -687,6 +689,12 @@
field public static final java.lang.String SERVICE_INTERFACE = "android.app.usage.CacheQuotaService";
}
+ public static final class UsageEvents.Event {
+ method public int getStandbyBucket();
+ field public static final int NOTIFICATION_SEEN = 10; // 0xa
+ field public static final int STANDBY_BUCKET_CHANGED = 11; // 0xb
+ }
+
public final class UsageStatsManager {
method public int getAppStandbyBucket(java.lang.String);
method public java.util.Map<java.lang.String, java.lang.Integer> getAppStandbyBuckets();
@@ -791,7 +799,7 @@
field public static final java.lang.String STATS_MANAGER = "stats";
field public static final java.lang.String SYSTEM_UPDATE_SERVICE = "system_update";
field public static final java.lang.String VR_SERVICE = "vrmanager";
- field public static final java.lang.String WIFI_RTT_SERVICE = "rttmanager";
+ field public static final deprecated java.lang.String WIFI_RTT_SERVICE = "rttmanager";
field public static final java.lang.String WIFI_SCANNING_SERVICE = "wifiscanner";
}
@@ -821,6 +829,7 @@
field public static final java.lang.String ACTION_RESOLVE_INSTANT_APP_PACKAGE = "android.intent.action.RESOLVE_INSTANT_APP_PACKAGE";
field public static final java.lang.String ACTION_REVIEW_PERMISSIONS = "android.intent.action.REVIEW_PERMISSIONS";
field public static final deprecated java.lang.String ACTION_SIM_STATE_CHANGED = "android.intent.action.SIM_STATE_CHANGED";
+ field public static final java.lang.String ACTION_SPLIT_CONFIGURATION_CHANGED = "android.intent.action.SPLIT_CONFIGURATION_CHANGED";
field public static final java.lang.String ACTION_UPGRADE_SETUP = "android.intent.action.UPGRADE_SETUP";
field public static final java.lang.String ACTION_USER_REMOVED = "android.intent.action.USER_REMOVED";
field public static final java.lang.String ACTION_VOICE_ASSIST = "android.intent.action.VOICE_ASSIST";
@@ -3091,7 +3100,7 @@
package android.net.wifi {
- public class RttManager {
+ public deprecated class RttManager {
method public void disableResponder(android.net.wifi.RttManager.ResponderCallback);
method public void enableResponder(android.net.wifi.RttManager.ResponderCallback);
method public deprecated android.net.wifi.RttManager.Capabilities getCapabilities();
@@ -3167,22 +3176,22 @@
field public int supportedType;
}
- public static class RttManager.ParcelableRttParams implements android.os.Parcelable {
+ public static deprecated class RttManager.ParcelableRttParams implements android.os.Parcelable {
field public android.net.wifi.RttManager.RttParams[] mParams;
}
- public static class RttManager.ParcelableRttResults implements android.os.Parcelable {
+ public static deprecated class RttManager.ParcelableRttResults implements android.os.Parcelable {
ctor public RttManager.ParcelableRttResults(android.net.wifi.RttManager.RttResult[]);
field public android.net.wifi.RttManager.RttResult[] mResults;
}
- public static abstract class RttManager.ResponderCallback {
+ public static abstract deprecated class RttManager.ResponderCallback {
ctor public RttManager.ResponderCallback();
method public abstract void onResponderEnableFailure(int);
method public abstract void onResponderEnabled(android.net.wifi.RttManager.ResponderConfig);
}
- public static class RttManager.ResponderConfig implements android.os.Parcelable {
+ public static deprecated class RttManager.ResponderConfig implements android.os.Parcelable {
ctor public RttManager.ResponderConfig();
method public int describeContents();
method public void writeToParcel(android.os.Parcel, int);
@@ -3195,7 +3204,7 @@
field public int preamble;
}
- public static class RttManager.RttCapabilities implements android.os.Parcelable {
+ public static deprecated class RttManager.RttCapabilities implements android.os.Parcelable {
ctor public RttManager.RttCapabilities();
field public int bwSupported;
field public boolean lciSupported;
@@ -3210,13 +3219,13 @@
field public boolean twoSided11McRttSupported;
}
- public static abstract interface RttManager.RttListener {
+ public static abstract deprecated interface RttManager.RttListener {
method public abstract void onAborted();
method public abstract void onFailure(int, java.lang.String);
method public abstract void onSuccess(android.net.wifi.RttManager.RttResult[]);
}
- public static class RttManager.RttParams {
+ public static deprecated class RttManager.RttParams {
ctor public RttManager.RttParams();
field public boolean LCIRequest;
field public boolean LCRRequest;
@@ -3240,7 +3249,7 @@
field public boolean secure;
}
- public static class RttManager.RttResult {
+ public static deprecated class RttManager.RttResult {
ctor public RttManager.RttResult();
field public android.net.wifi.RttManager.WifiInformationElement LCI;
field public android.net.wifi.RttManager.WifiInformationElement LCR;
@@ -3277,7 +3286,7 @@
field public deprecated int tx_rate;
}
- public static class RttManager.WifiInformationElement {
+ public static deprecated class RttManager.WifiInformationElement {
ctor public RttManager.WifiInformationElement();
field public byte[] data;
field public byte id;
@@ -5035,22 +5044,19 @@
}
public class UiccSlotInfo implements android.os.Parcelable {
- ctor public UiccSlotInfo(boolean, boolean, java.lang.String, int);
+ ctor public UiccSlotInfo(boolean, boolean, java.lang.String, int, int);
method public int describeContents();
method public java.lang.String getCardId();
method public int getCardStateInfo();
method public boolean getIsActive();
method public boolean getIsEuicc();
+ method public int getLogicalSlotIdx();
method public void writeToParcel(android.os.Parcel, int);
field public static final int CARD_STATE_INFO_ABSENT = 1; // 0x1
field public static final int CARD_STATE_INFO_ERROR = 3; // 0x3
field public static final int CARD_STATE_INFO_PRESENT = 2; // 0x2
field public static final int CARD_STATE_INFO_RESTRICTED = 4; // 0x4
field public static final android.os.Parcelable.Creator<android.telephony.UiccSlotInfo> CREATOR;
- field public final java.lang.String cardId;
- field public final int cardStateInfo;
- field public final boolean isActive;
- field public final boolean isEuicc;
}
public abstract class VisualVoicemailService extends android.app.Service {
@@ -5711,6 +5717,7 @@
ctor public ImsCallSessionImplBase();
method public void accept(int, android.telephony.ims.ImsStreamMediaProfile);
method public void close();
+ method public void deflect(java.lang.String);
method public void extendToConference(java.lang.String[]);
method public java.lang.String getCallId();
method public android.telephony.ims.ImsCallProfile getCallProfile();
diff --git a/api/test-current.txt b/api/test-current.txt
index 4cfe401..b02da04 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -1038,6 +1038,7 @@
public class AccessibilityNodeInfo implements android.os.Parcelable {
method public static void setNumInstancesInUseCounter(java.util.concurrent.atomic.AtomicInteger);
+ method public void writeToParcelNoRecycle(android.os.Parcel, int);
}
public final class AccessibilityWindowInfo implements android.os.Parcelable {
diff --git a/cmds/incidentd/src/PrivacyBuffer.cpp b/cmds/incidentd/src/PrivacyBuffer.cpp
index 03faa92..f53befe 100644
--- a/cmds/incidentd/src/PrivacyBuffer.cpp
+++ b/cmds/incidentd/src/PrivacyBuffer.cpp
@@ -142,7 +142,7 @@
PrivacyBuffer::clear()
{
mSize = 0;
- mProto = ProtoOutputStream();
+ mProto.clear();
}
size_t
diff --git a/cmds/statsd/Android.mk b/cmds/statsd/Android.mk
index b46c5c1..8eea944 100644
--- a/cmds/statsd/Android.mk
+++ b/cmds/statsd/Android.mk
@@ -44,6 +44,7 @@
src/external/KernelUidCpuActiveTimeReader.cpp \
src/external/KernelUidCpuClusterTimeReader.cpp \
src/external/StatsPullerManagerImpl.cpp \
+ src/external/puller_util.cpp \
src/logd/LogEvent.cpp \
src/logd/LogListener.cpp \
src/logd/LogReader.cpp \
@@ -175,6 +176,7 @@
tests/AnomalyMonitor_test.cpp \
tests/anomaly/AnomalyTracker_test.cpp \
tests/ConfigManager_test.cpp \
+ tests/external/puller_util_test.cpp \
tests/indexed_priority_queue_test.cpp \
tests/LogEntryMatcher_test.cpp \
tests/LogReader_test.cpp \
@@ -240,7 +242,8 @@
LOCAL_SRC_FILES := $(statsd_common_src) \
benchmark/main.cpp \
benchmark/hello_world_benchmark.cpp \
- benchmark/log_event_benchmark.cpp
+ benchmark/log_event_benchmark.cpp \
+ benchmark/stats_write_benchmark.cpp
LOCAL_C_INCLUDES := $(statsd_common_c_includes)
@@ -259,7 +262,8 @@
$(statsd_common_static_libraries)
LOCAL_SHARED_LIBRARIES := $(statsd_common_shared_libraries) \
- libgtest_prod
+ libgtest_prod \
+ libstatslog
LOCAL_PROTOC_OPTIMIZE_TYPE := lite
diff --git a/cmds/statsd/AndroidTest.xml b/cmds/statsd/AndroidTest.xml
new file mode 100644
index 0000000..afe30a2
--- /dev/null
+++ b/cmds/statsd/AndroidTest.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.
+-->
+<configuration description="Config for statsd_test">
+ <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+ <option name="cleanup" value="true" />
+ <option name="push" value="statsd_test->/data/nativetest/statsd_test" />
+ </target_preparer>
+ <option name="test-suite-tag" value="apct" />
+ <test class="com.android.tradefed.testtype.GTest" >
+ <option name="native-test-device-path" value="/data/nativetest" />
+ <option name="module-name" value="statsd_test" />
+ </test>
+</configuration>
\ No newline at end of file
diff --git a/cmds/statsd/benchmark/stats_write_benchmark.cpp b/cmds/statsd/benchmark/stats_write_benchmark.cpp
new file mode 100644
index 0000000..f5a0cd5
--- /dev/null
+++ b/cmds/statsd/benchmark/stats_write_benchmark.cpp
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include "benchmark/benchmark.h"
+#include <statslog.h>
+
+namespace android {
+namespace os {
+namespace statsd {
+
+static void BM_StatsWrite(benchmark::State& state) {
+ const char* reason = "test";
+ int64_t boot_end_time = 1234567;
+ int64_t total_duration = 100;
+ int64_t bootloader_duration = 10;
+ int64_t time_since_last_boot = 99999999;
+ while (state.KeepRunning()) {
+ android::util::stats_write(
+ android::util::BOOT_SEQUENCE_REPORTED, reason, reason,
+ boot_end_time, total_duration, bootloader_duration, time_since_last_boot);
+ total_duration++;
+ }
+}
+BENCHMARK(BM_StatsWrite);
+
+} // namespace statsd
+} // namespace os
+} // namespace android
diff --git a/cmds/statsd/src/HashableDimensionKey.cpp b/cmds/statsd/src/HashableDimensionKey.cpp
index f0eaeff..8483b02 100644
--- a/cmds/statsd/src/HashableDimensionKey.cpp
+++ b/cmds/statsd/src/HashableDimensionKey.cpp
@@ -181,7 +181,9 @@
return toString().compare(that.toString()) < 0;
};
-
+bool compareDimensionsValue(const DimensionsValue& s1, const DimensionsValue& s2) {
+ return EqualsTo(s1, s2);
+}
} // namespace statsd
} // namespace os
} // namespace android
\ No newline at end of file
diff --git a/cmds/statsd/src/StatsLogProcessor.cpp b/cmds/statsd/src/StatsLogProcessor.cpp
index e610fd7..7662c40 100644
--- a/cmds/statsd/src/StatsLogProcessor.cpp
+++ b/cmds/statsd/src/StatsLogProcessor.cpp
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#define DEBUG true // STOPSHIP if true
+#define DEBUG false // STOPSHIP if true
#include "Log.h"
#include "statslog.h"
@@ -32,6 +32,7 @@
#include <log/log_event_list.h>
#include <utils/Errors.h>
+#include <utils/SystemClock.h>
using namespace android;
using android::base::StringPrintf;
@@ -60,6 +61,8 @@
// for ConfigMetricsReport
const int FIELD_ID_METRICS = 1;
const int FIELD_ID_UID_MAP = 2;
+const int FIELD_ID_LAST_REPORT_NANOS = 3;
+const int FIELD_ID_CURRENT_REPORT_NANOS = 4;
#define STATS_DATA_DIR "/data/misc/stats-data"
@@ -166,7 +169,7 @@
void StatsLogProcessor::OnConfigUpdated(const ConfigKey& key, const StatsdConfig& config) {
std::lock_guard<std::mutex> lock(mMetricsMutex);
- ALOGD("Updated configuration for key %s", key.ToString().c_str());
+ VLOG("Updated configuration for key %s", key.ToString().c_str());
sp<MetricsManager> newMetricsManager = new MetricsManager(key, config, mTimeBaseSec, mUidMap);
auto it = mMetricsManagers.find(key);
if (it == mMetricsManagers.end() && mMetricsManagers.size() > StatsdStats::kMaxConfigCount) {
@@ -264,6 +267,12 @@
uidMap.SerializeToArray(&uidMapBuffer[0], uidMapSize);
proto.write(FIELD_TYPE_MESSAGE | FIELD_ID_UID_MAP, uidMapBuffer, uidMapSize);
+ // Fill in the timestamps.
+ proto.write(FIELD_TYPE_INT64 | FIELD_ID_LAST_REPORT_NANOS,
+ (long long)it->second->getLastReportTimeNs());
+ proto.write(FIELD_TYPE_INT64 | FIELD_ID_CURRENT_REPORT_NANOS,
+ (long long)::android::elapsedRealtimeNano());
+
// End of ConfigMetricsReport (reports).
proto.end(reportsToken);
diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp
index 3efe9b1..c24efec 100644
--- a/cmds/statsd/src/StatsService.cpp
+++ b/cmds/statsd/src/StatsService.cpp
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#define DEBUG true // STOPSHIP if true
+#define DEBUG false // STOPSHIP if true
#include "Log.h"
#include "StatsService.h"
@@ -77,18 +77,18 @@
: mAnomalyMonitor(new AnomalyMonitor(MIN_DIFF_TO_UPDATE_REGISTERED_ALARM_SECS))
{
mUidMap = new UidMap();
+ StatsPuller::SetUidMap(mUidMap);
mConfigManager = new ConfigManager();
mProcessor = new StatsLogProcessor(mUidMap, mAnomalyMonitor, time(nullptr), [this](const ConfigKey& key) {
sp<IStatsCompanionService> sc = getStatsCompanionService();
auto receiver = mConfigManager->GetConfigReceiver(key);
if (sc == nullptr) {
VLOG("Could not find StatsCompanionService");
- } else if (receiver.first.size() == 0) {
+ } else if (receiver == nullptr) {
VLOG("Statscompanion could not find a broadcast receiver for %s",
key.ToString().c_str());
} else {
- sc->sendBroadcast(String16(receiver.first.c_str()),
- String16(receiver.second.c_str()));
+ sc->sendDataBroadcast(receiver);
}
});
@@ -366,12 +366,14 @@
}
auto receiver = mConfigManager->GetConfigReceiver(ConfigKey(uid, StrToInt64(name)));
sp<IStatsCompanionService> sc = getStatsCompanionService();
- if (sc != nullptr) {
- sc->sendBroadcast(String16(receiver.first.c_str()), String16(receiver.second.c_str()));
+ if (sc == nullptr) {
+ VLOG("Could not access statsCompanion");
+ } else if (receiver == nullptr) {
+ VLOG("Could not find receiver for %s, %s", args[1].c_str(), args[2].c_str())
+ } else {
+ sc->sendDataBroadcast(receiver);
VLOG("StatsService::trigger broadcast succeeded to %s, %s", args[1].c_str(),
args[2].c_str());
- } else {
- VLOG("Could not access statsCompanion");
}
return NO_ERROR;
@@ -799,7 +801,6 @@
Status StatsService::addConfiguration(int64_t key,
const vector <uint8_t>& config,
- const String16& package, const String16& cls,
bool* success) {
IPCThreadState* ipc = IPCThreadState::self();
if (checkCallingPermission(String16(kPermissionDump))) {
@@ -810,8 +811,33 @@
return Status::ok();
}
mConfigManager->UpdateConfig(configKey, cfg);
- mConfigManager->SetConfigReceiver(configKey, string(String8(package).string()),
- string(String8(cls).string()));
+ *success = true;
+ return Status::ok();
+ } else {
+ *success = false;
+ return Status::fromExceptionCode(binder::Status::EX_SECURITY);
+ }
+}
+
+Status StatsService::removeDataFetchOperation(int64_t key, bool* success) {
+ IPCThreadState* ipc = IPCThreadState::self();
+ if (checkCallingPermission(String16(kPermissionDump))) {
+ ConfigKey configKey(ipc->getCallingUid(), key);
+ mConfigManager->RemoveConfigReceiver(configKey);
+ *success = true;
+ return Status::ok();
+ } else {
+ *success = false;
+ return Status::fromExceptionCode(binder::Status::EX_SECURITY);
+ }
+}
+
+Status StatsService::setDataFetchOperation(int64_t key, const sp<android::IBinder>& intentSender,
+ bool* success) {
+ IPCThreadState* ipc = IPCThreadState::self();
+ if (checkCallingPermission(String16(kPermissionDump))) {
+ ConfigKey configKey(ipc->getCallingUid(), key);
+ mConfigManager->SetConfigReceiver(configKey, intentSender);
*success = true;
return Status::ok();
} else {
diff --git a/cmds/statsd/src/StatsService.h b/cmds/statsd/src/StatsService.h
index 109752b..3dc19fe 100644
--- a/cmds/statsd/src/StatsService.h
+++ b/cmds/statsd/src/StatsService.h
@@ -90,9 +90,19 @@
* Binder call to let clients send a configuration and indicate they're interested when they
* should requestData for this configuration.
*/
- virtual Status addConfiguration(int64_t key, const vector <uint8_t>& config,
- const String16& package, const String16& cls, bool* success)
- override;
+ virtual Status addConfiguration(int64_t key, const vector<uint8_t>& config,
+ bool* success) override;
+
+ /**
+ * Binder call to let clients register the data fetch operation for a configuration.
+ */
+ virtual Status setDataFetchOperation(int64_t key, const sp<android::IBinder>& intentSender,
+ bool* success) override;
+
+ /**
+ * Binder call to remove the data fetch operation for the specified config key.
+ */
+ virtual Status removeDataFetchOperation(int64_t key, bool* success) override;
/**
* Binder call to allow clients to remove the specified configuration.
diff --git a/cmds/statsd/src/anomaly/AnomalyMonitor.cpp b/cmds/statsd/src/anomaly/AnomalyMonitor.cpp
index 4912648..72d29d0 100644
--- a/cmds/statsd/src/anomaly/AnomalyMonitor.cpp
+++ b/cmds/statsd/src/anomaly/AnomalyMonitor.cpp
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#define DEBUG true
+#define DEBUG false
#include "Log.h"
#include "anomaly/AnomalyMonitor.h"
@@ -36,10 +36,10 @@
sp<IStatsCompanionService> tmpForLock = mStatsCompanionService;
mStatsCompanionService = statsCompanionService;
if (statsCompanionService == nullptr) {
- if (DEBUG) ALOGD("Erasing link to statsCompanionService");
+ VLOG("Erasing link to statsCompanionService");
return;
}
- if (DEBUG) ALOGD("Creating link to statsCompanionService");
+ VLOG("Creating link to statsCompanionService");
const sp<const AnomalyAlarm> top = mPq.top();
if (top != nullptr) {
updateRegisteredAlarmTime_l(top->timestampSec);
@@ -58,7 +58,7 @@
return;
}
// TODO: Ensure that refractory period is respected.
- if (DEBUG) ALOGD("Adding alarm with time %u", alarm->timestampSec);
+ VLOG("Adding alarm with time %u", alarm->timestampSec);
mPq.push(alarm);
if (mRegisteredAlarmTimeSec < 1 ||
alarm->timestampSec + mMinUpdateTimeSec < mRegisteredAlarmTimeSec) {
@@ -72,16 +72,16 @@
ALOGW("Asked to remove a null alarm.");
return;
}
- if (DEBUG) ALOGD("Removing alarm with time %u", alarm->timestampSec);
+ VLOG("Removing alarm with time %u", alarm->timestampSec);
bool wasPresent = mPq.remove(alarm);
if (!wasPresent) return;
if (mPq.empty()) {
- if (DEBUG) ALOGD("Queue is empty. Cancel any alarm.");
+ VLOG("Queue is empty. Cancel any alarm.");
cancelRegisteredAlarmTime_l();
return;
}
uint32_t soonestAlarmTimeSec = mPq.top()->timestampSec;
- if (DEBUG) ALOGD("Soonest alarm is %u", soonestAlarmTimeSec);
+ VLOG("Soonest alarm is %u", soonestAlarmTimeSec);
if (soonestAlarmTimeSec > mRegisteredAlarmTimeSec + mMinUpdateTimeSec) {
updateRegisteredAlarmTime_l(soonestAlarmTimeSec);
}
@@ -91,7 +91,7 @@
// updates to the registered alarm.
unordered_set<sp<const AnomalyAlarm>, SpHash<AnomalyAlarm>> AnomalyMonitor::popSoonerThan(
uint32_t timestampSec) {
- if (DEBUG) ALOGD("Removing alarms with time <= %u", timestampSec);
+ VLOG("Removing alarms with time <= %u", timestampSec);
unordered_set<sp<const AnomalyAlarm>, SpHash<AnomalyAlarm>> oldAlarms;
std::lock_guard<std::mutex> lock(mLock);
@@ -103,7 +103,7 @@
// Always update registered alarm time (if anything has changed).
if (!oldAlarms.empty()) {
if (mPq.empty()) {
- if (DEBUG) ALOGD("Queue is empty. Cancel any alarm.");
+ VLOG("Queue is empty. Cancel any alarm.");
cancelRegisteredAlarmTime_l();
} else {
// Always update the registered alarm in this case (unlike remove()).
@@ -114,7 +114,7 @@
}
void AnomalyMonitor::updateRegisteredAlarmTime_l(uint32_t timestampSec) {
- if (DEBUG) ALOGD("Updating reg alarm time to %u", timestampSec);
+ VLOG("Updating reg alarm time to %u", timestampSec);
mRegisteredAlarmTimeSec = timestampSec;
if (mStatsCompanionService != nullptr) {
mStatsCompanionService->setAnomalyAlarm(secToMs(mRegisteredAlarmTimeSec));
@@ -123,7 +123,7 @@
}
void AnomalyMonitor::cancelRegisteredAlarmTime_l() {
- if (DEBUG) ALOGD("Cancelling reg alarm.");
+ VLOG("Cancelling reg alarm.");
mRegisteredAlarmTimeSec = 0;
if (mStatsCompanionService != nullptr) {
mStatsCompanionService->cancelAnomalyAlarm();
diff --git a/cmds/statsd/src/anomaly/AnomalyTracker.cpp b/cmds/statsd/src/anomaly/AnomalyTracker.cpp
index 7c5e45e..ba16ec83 100644
--- a/cmds/statsd/src/anomaly/AnomalyTracker.cpp
+++ b/cmds/statsd/src/anomaly/AnomalyTracker.cpp
@@ -14,12 +14,11 @@
* limitations under the License.
*/
-#define DEBUG true // STOPSHIP if true
+#define DEBUG false // STOPSHIP if true
#include "Log.h"
#include "AnomalyTracker.h"
#include "external/Perfetto.h"
-#include "frameworks/base/libs/incident/proto/android/os/header.pb.h"
#include "guardrail/StatsdStats.h"
#include "subscriber/IncidentdReporter.h"
#include "subscriber/SubscriberReporter.h"
diff --git a/cmds/statsd/src/anomaly/DurationAnomalyTracker.cpp b/cmds/statsd/src/anomaly/DurationAnomalyTracker.cpp
index e459f76..fa0bc0c 100644
--- a/cmds/statsd/src/anomaly/DurationAnomalyTracker.cpp
+++ b/cmds/statsd/src/anomaly/DurationAnomalyTracker.cpp
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#define DEBUG true // STOPSHIP if true
+#define DEBUG false // STOPSHIP if true
#include "Log.h"
#include "DurationAnomalyTracker.h"
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index 6bca16f..4c6a36b 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -24,6 +24,7 @@
import "frameworks/base/core/proto/android/app/enums.proto";
import "frameworks/base/core/proto/android/os/enums.proto";
import "frameworks/base/core/proto/android/server/enums.proto";
+import "frameworks/base/core/proto/android/telecomm/enums.proto";
import "frameworks/base/core/proto/android/telephony/enums.proto";
import "frameworks/base/core/proto/android/view/enums.proto";
@@ -98,6 +99,7 @@
DaveyOccurred davey_occurred = 58;
OverlayStateChanged overlay_state_changed = 59;
ForegroundServiceStateChanged foreground_service_state_changed = 60;
+ CallStateChanged call_state_changed = 61;
// TODO: Reorder the numbering so that the most frequent occur events occur in the first 15.
}
@@ -725,6 +727,33 @@
optional int64 time_since_last_boot = 6;
}
+
+/**
+ * Logs call state and disconnect cause (if applicable).
+ *
+ * Logged from:
+ * packages/services/Telecomm/src/com/android/server/telecom/Call.java
+ */
+message CallStateChanged {
+ // The state of the call. Eg. DIALING, ACTIVE, ON_HOLD, DISCONNECTED.
+ // From frameworks/base/core/proto/android/telecomm/enums.proto.
+ optional android.telecom.CallStateEnum call_state = 1;
+
+ // The reason the call disconnected. Eg. ERROR, MISSED, REJECTED, BUSY.
+ // This value is only applicable when the call_state is DISCONNECTED, and
+ // should always be UNKNOWN if the call_state is not DISCONNECTED.
+ // From frameworks/base/core/proto/android/telecomm/enums.proto.
+ optional android.telecom.DisconnectCauseEnum disconnect_cause = 2;
+
+ // True if the call is self-managed, which are apps that use the
+ // telecom infrastructure to make their own calls.
+ optional bool self_managed = 3;
+
+ // True if call is external. External calls are calls on connected Wear
+ // devices but show up in Telecom so the user can pull them onto the device.
+ optional bool external_call = 4;
+}
+
/**
* Logs the duration of a davey (jank of >=700ms) when it occurs
*
diff --git a/cmds/statsd/src/config/ConfigManager.cpp b/cmds/statsd/src/config/ConfigManager.cpp
index 61eeee3..06ff603 100644
--- a/cmds/statsd/src/config/ConfigManager.cpp
+++ b/cmds/statsd/src/config/ConfigManager.cpp
@@ -75,8 +75,8 @@
}
}
-void ConfigManager::SetConfigReceiver(const ConfigKey& key, const string& pkg, const string& cls) {
- mConfigReceivers[key] = pair<string, string>(pkg, cls);
+void ConfigManager::SetConfigReceiver(const ConfigKey& key, const sp<IBinder>& intentSender) {
+ mConfigReceivers[key] = intentSender;
}
void ConfigManager::RemoveConfigReceiver(const ConfigKey& key) {
@@ -159,10 +159,10 @@
return ret;
}
-const pair<string, string> ConfigManager::GetConfigReceiver(const ConfigKey& key) const {
+const sp<android::IBinder> ConfigManager::GetConfigReceiver(const ConfigKey& key) const {
auto it = mConfigReceivers.find(key);
if (it == mConfigReceivers.end()) {
- return pair<string,string>();
+ return nullptr;
} else {
return it->second;
}
@@ -175,8 +175,7 @@
fprintf(out, " %6d %lld\n", key.GetUid(), (long long)key.GetId());
auto receiverIt = mConfigReceivers.find(key);
if (receiverIt != mConfigReceivers.end()) {
- fprintf(out, " -> received by %s, %s\n", receiverIt->second.first.c_str(),
- receiverIt->second.second.c_str());
+ fprintf(out, " -> received by PendingIntent as binder\n");
}
}
}
diff --git a/cmds/statsd/src/config/ConfigManager.h b/cmds/statsd/src/config/ConfigManager.h
index ad666bc..a2b2a0c 100644
--- a/cmds/statsd/src/config/ConfigManager.h
+++ b/cmds/statsd/src/config/ConfigManager.h
@@ -16,6 +16,7 @@
#pragma once
+#include "binder/IBinder.h"
#include "config/ConfigKey.h"
#include "config/ConfigListener.h"
@@ -68,12 +69,12 @@
/**
* Sets the broadcast receiver for a configuration key.
*/
- void SetConfigReceiver(const ConfigKey& key, const std::string& pkg, const std::string& cls);
+ void SetConfigReceiver(const ConfigKey& key, const sp<IBinder>& intentSender);
/**
* Returns the package name and class name representing the broadcast receiver for this config.
*/
- const std::pair<std::string, std::string> GetConfigReceiver(const ConfigKey& key) const;
+ const sp<android::IBinder> GetConfigReceiver(const ConfigKey& key) const;
/**
* Returns all config keys registered.
@@ -124,10 +125,10 @@
std::set<ConfigKey> mConfigs;
/**
- * Each config key can be subscribed by up to one receiver, specified as the package name and
- * class name.
+ * Each config key can be subscribed by up to one receiver, specified as IBinder from
+ * PendingIntent.
*/
- std::map<ConfigKey, std::pair<std::string, std::string>> mConfigReceivers;
+ std::map<ConfigKey, sp<android::IBinder>> mConfigReceivers;
/**
* The ConfigListeners that will be told about changes.
diff --git a/cmds/statsd/src/external/CpuTimePerUidFreqPuller.cpp b/cmds/statsd/src/external/CpuTimePerUidFreqPuller.cpp
index a751273..d0d2f93 100644
--- a/cmds/statsd/src/external/CpuTimePerUidFreqPuller.cpp
+++ b/cmds/statsd/src/external/CpuTimePerUidFreqPuller.cpp
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#define DEBUG true // STOPSHIP if true
+#define DEBUG false // STOPSHIP if true
#include "Log.h"
#include <fstream>
diff --git a/cmds/statsd/src/external/CpuTimePerUidPuller.cpp b/cmds/statsd/src/external/CpuTimePerUidPuller.cpp
index e7ea4b9..d9aeb46 100644
--- a/cmds/statsd/src/external/CpuTimePerUidPuller.cpp
+++ b/cmds/statsd/src/external/CpuTimePerUidPuller.cpp
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#define DEBUG true // STOPSHIP if true
+#define DEBUG false // STOPSHIP if true
#include "Log.h"
#include <fstream>
diff --git a/cmds/statsd/src/external/KernelUidCpuActiveTimeReader.cpp b/cmds/statsd/src/external/KernelUidCpuActiveTimeReader.cpp
index 7a2d199..0e126e7 100644
--- a/cmds/statsd/src/external/KernelUidCpuActiveTimeReader.cpp
+++ b/cmds/statsd/src/external/KernelUidCpuActiveTimeReader.cpp
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#define DEBUG true // STOPSHIP if true
+#define DEBUG false // STOPSHIP if true
#include "Log.h"
#include <fstream>
diff --git a/cmds/statsd/src/external/KernelUidCpuClusterTimeReader.cpp b/cmds/statsd/src/external/KernelUidCpuClusterTimeReader.cpp
index 7426e74..7684ed4 100644
--- a/cmds/statsd/src/external/KernelUidCpuClusterTimeReader.cpp
+++ b/cmds/statsd/src/external/KernelUidCpuClusterTimeReader.cpp
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#define DEBUG true // STOPSHIP if true
+#define DEBUG false // STOPSHIP if true
#include "Log.h"
#include <fstream>
diff --git a/cmds/statsd/src/external/Perfetto.cpp b/cmds/statsd/src/external/Perfetto.cpp
index 1d8a968..b09d373 100644
--- a/cmds/statsd/src/external/Perfetto.cpp
+++ b/cmds/statsd/src/external/Perfetto.cpp
@@ -14,6 +14,7 @@
* limitations under the License.
*/
+#define DEBUG false // STOPSHIP if true
#include "Log.h"
#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" // Alert
@@ -36,7 +37,7 @@
namespace statsd {
bool CollectPerfettoTraceAndUploadToDropbox(const PerfettoDetails& config) {
- ALOGD("Starting trace collection through perfetto");
+ VLOG("Starting trace collection through perfetto");
if (!config.has_trace_config()) {
ALOGE("The perfetto trace config is empty, aborting");
@@ -118,7 +119,7 @@
return false;
}
- ALOGD("CollectPerfettoTraceAndUploadToDropbox() succeeded");
+ VLOG("CollectPerfettoTraceAndUploadToDropbox() succeeded");
return true;
}
diff --git a/cmds/statsd/src/external/StatsCompanionServicePuller.cpp b/cmds/statsd/src/external/StatsCompanionServicePuller.cpp
index b955f1c..8210c8d 100644
--- a/cmds/statsd/src/external/StatsCompanionServicePuller.cpp
+++ b/cmds/statsd/src/external/StatsCompanionServicePuller.cpp
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#define DEBUG true
+#define DEBUG false
#include "Log.h"
#include <android/os/IStatsCompanionService.h>
@@ -64,7 +64,7 @@
std::copy(it.bytes.begin(), it.bytes.end(), tmp.buf + kLogMsgHeaderSize);
data->push_back(make_shared<LogEvent>(tmp));
}
- ALOGD("StatsCompanionServicePuller::pull succeeded for %d", mTagId);
+ VLOG("StatsCompanionServicePuller::pull succeeded for %d", mTagId);
return true;
} else {
ALOGW("statsCompanion not found!");
diff --git a/cmds/statsd/src/external/StatsPuller.cpp b/cmds/statsd/src/external/StatsPuller.cpp
index 41e4705..fc0ad7c 100644
--- a/cmds/statsd/src/external/StatsPuller.cpp
+++ b/cmds/statsd/src/external/StatsPuller.cpp
@@ -19,6 +19,7 @@
#include "StatsPuller.h"
#include "guardrail/StatsdStats.h"
+#include "puller_util.h"
namespace android {
namespace os {
@@ -26,6 +27,9 @@
using std::lock_guard;
+sp<UidMap> StatsPuller::mUidMap = nullptr;
+void StatsPuller::SetUidMap(const sp<UidMap>& uidMap) { mUidMap = uidMap; }
+
// ValueMetric has a minimum bucket size of 10min so that we don't pull too frequently
StatsPuller::StatsPuller(const int tagId)
: mTagId(tagId) {
@@ -54,7 +58,8 @@
mLastPullTimeSec = curTime;
bool ret = PullInternal(&mCachedData);
if (ret) {
- (*data) = mCachedData;
+ mergeIsolatedUidsToHostUid(mCachedData, mUidMap, mTagId);
+ (*data) = mCachedData;
}
return ret;
}
diff --git a/cmds/statsd/src/external/StatsPuller.h b/cmds/statsd/src/external/StatsPuller.h
index 3446f9b..82a8611 100644
--- a/cmds/statsd/src/external/StatsPuller.h
+++ b/cmds/statsd/src/external/StatsPuller.h
@@ -18,11 +18,14 @@
#include <android/os/StatsLogEventWrapper.h>
#include <utils/String16.h>
+#include <utils/RefBase.h>
#include <mutex>
#include <vector>
+#include "packages/UidMap.h"
-#include "logd/LogEvent.h"
#include "guardrail/StatsdStats.h"
+#include "logd/LogEvent.h"
+#include "puller_util.h"
using android::os::StatsLogEventWrapper;
@@ -30,7 +33,7 @@
namespace os {
namespace statsd {
-class StatsPuller {
+class StatsPuller : public virtual RefBase {
public:
StatsPuller(const int tagId);
@@ -44,7 +47,9 @@
// Clear cache if elapsed time is more than cooldown time
int ClearCacheIfNecessary(long timestampSec);
-protected:
+ static void SetUidMap(const sp<UidMap>& uidMap);
+
+ protected:
// The atom tag id this puller pulls
const int mTagId;
@@ -67,6 +72,8 @@
long mLastPullTimeSec;
int clearCache();
+
+ static sp<UidMap> mUidMap;
};
} // namespace statsd
diff --git a/cmds/statsd/src/external/StatsPullerManagerImpl.cpp b/cmds/statsd/src/external/StatsPullerManagerImpl.cpp
index 19b3a86..4c676a7 100644
--- a/cmds/statsd/src/external/StatsPullerManagerImpl.cpp
+++ b/cmds/statsd/src/external/StatsPullerManagerImpl.cpp
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#define DEBUG true
+#define DEBUG false
#include "Log.h"
#include <android/os/IStatsCompanionService.h>
@@ -24,6 +24,9 @@
#include "CpuTimePerUidFreqPuller.h"
#include "CpuTimePerUidPuller.h"
#include "ResourceHealthManagerPuller.h"
+#include "KernelUidCpuActiveTimeReader.h"
+#include "KernelUidCpuClusterTimeReader.h"
+#include "SubsystemSleepStatePuller.h"
#include "StatsCompanionServicePuller.h"
#include "StatsPullerManagerImpl.h"
#include "StatsService.h"
@@ -44,62 +47,89 @@
namespace os {
namespace statsd {
+const std::map<int, PullAtomInfo> StatsPullerManagerImpl::kAllPullAtomInfo = {
+ // wifi_bytes_transfer
+ {android::util::WIFI_BYTES_TRANSFER,
+ {{2, 3, 4, 5},
+ {},
+ 1,
+ new StatsCompanionServicePuller(android::util::WIFI_BYTES_TRANSFER)}},
+ // wifi_bytes_transfer_by_fg_bg
+ {android::util::WIFI_BYTES_TRANSFER_BY_FG_BG,
+ {{3, 4, 5, 6},
+ {2},
+ 1,
+ new StatsCompanionServicePuller(android::util::WIFI_BYTES_TRANSFER_BY_FG_BG)}},
+ // mobile_bytes_transfer
+ {android::util::MOBILE_BYTES_TRANSFER,
+ {{2, 3, 4, 5},
+ {},
+ 1,
+ new StatsCompanionServicePuller(android::util::MOBILE_BYTES_TRANSFER)}},
+ // mobile_bytes_transfer_by_fg_bg
+ {android::util::MOBILE_BYTES_TRANSFER_BY_FG_BG,
+ {{3, 4, 5, 6},
+ {2},
+ 1,
+ new StatsCompanionServicePuller(android::util::MOBILE_BYTES_TRANSFER_BY_FG_BG)}},
+ // bluetooth_bytes_transfer
+ {android::util::BLUETOOTH_BYTES_TRANSFER,
+ {{2, 3}, {}, 1, new StatsCompanionServicePuller(android::util::BLUETOOTH_BYTES_TRANSFER)}},
+ // kernel_wakelock
+ {android::util::KERNEL_WAKELOCK,
+ {{}, {}, 1, new StatsCompanionServicePuller(android::util::KERNEL_WAKELOCK)}},
+ // subsystem_sleep_state
+ {android::util::SUBSYSTEM_SLEEP_STATE, {{}, {}, 1, new SubsystemSleepStatePuller()}},
+ // cpu_time_per_freq
+ {android::util::CPU_TIME_PER_FREQ,
+ {{3}, {2}, 1, new StatsCompanionServicePuller(android::util::CPU_TIME_PER_FREQ)}},
+ // cpu_time_per_uid
+ {android::util::CPU_TIME_PER_UID, {{2, 3}, {}, 1, new CpuTimePerUidPuller()}},
+ // cpu_time_per_uid_freq
+ {android::util::CPU_TIME_PER_UID_FREQ, {{3}, {2}, 1, new CpuTimePerUidFreqPuller()}},
+ // wifi_activity_energy_info
+ {android::util::WIFI_ACTIVITY_ENERGY_INFO,
+ {{}, {}, 1, new StatsCompanionServicePuller(android::util::WIFI_ACTIVITY_ENERGY_INFO)}},
+ // modem_activity_info
+ {android::util::MODEM_ACTIVITY_INFO,
+ {{}, {}, 1, new StatsCompanionServicePuller(android::util::MODEM_ACTIVITY_INFO)}},
+ // bluetooth_activity_info
+ {android::util::BLUETOOTH_ACTIVITY_INFO,
+ {{}, {}, 1, new StatsCompanionServicePuller(android::util::BLUETOOTH_ACTIVITY_INFO)}},
+ // system_elapsed_realtime
+ {android::util::SYSTEM_ELAPSED_REALTIME,
+ {{}, {}, 1, new StatsCompanionServicePuller(android::util::SYSTEM_ELAPSED_REALTIME)}},
+ // system_uptime
+ {android::util::SYSTEM_UPTIME,
+ {{}, {}, 1, new StatsCompanionServicePuller(android::util::SYSTEM_UPTIME)}},
+ // cpu_active_time
+ {android::util::CPU_ACTIVE_TIME, {{3}, {2}, 1, new KernelUidCpuActiveTimeReader()}},
+ // cpu_cluster_time
+ {android::util::CPU_CLUSTER_TIME, {{3}, {2}, 1, new KernelUidCpuClusterTimeReader()}},
+ // disk_space
+ {android::util::DISK_SPACE,
+ {{}, {}, 1, new StatsCompanionServicePuller(android::util::DISK_SPACE)}},
+ // remaining_battery_capacity
+ {android::util::REMAINING_BATTERY_CAPACITY,
+ {{}, {}, 1, new ResourceHealthManagerPuller(android::util::REMAINING_BATTERY_CAPACITY)}},
+ // full_battery_capacity
+ {android::util::FULL_BATTERY_CAPACITY,
+ {{}, {}, 1, new ResourceHealthManagerPuller(android::util::FULL_BATTERY_CAPACITY)}}};
+
StatsPullerManagerImpl::StatsPullerManagerImpl()
: mCurrentPullingInterval(LONG_MAX) {
- mPullers.insert({android::util::KERNEL_WAKELOCK,
- make_shared<StatsCompanionServicePuller>(android::util::KERNEL_WAKELOCK)});
- mPullers.insert({android::util::WIFI_BYTES_TRANSFER,
- make_shared<StatsCompanionServicePuller>(android::util::WIFI_BYTES_TRANSFER)});
- mPullers.insert(
- {android::util::MOBILE_BYTES_TRANSFER,
- make_shared<StatsCompanionServicePuller>(android::util::MOBILE_BYTES_TRANSFER)});
- mPullers.insert({android::util::WIFI_BYTES_TRANSFER_BY_FG_BG,
- make_shared<StatsCompanionServicePuller>(
- android::util::WIFI_BYTES_TRANSFER_BY_FG_BG)});
- mPullers.insert({android::util::MOBILE_BYTES_TRANSFER_BY_FG_BG,
- make_shared<StatsCompanionServicePuller>(
- android::util::MOBILE_BYTES_TRANSFER_BY_FG_BG)});
- mPullers.insert(
- {android::util::SUBSYSTEM_SLEEP_STATE,
- make_shared<SubsystemSleepStatePuller>()});
- mPullers.insert({android::util::CPU_TIME_PER_FREQ, make_shared<StatsCompanionServicePuller>(android::util::CPU_TIME_PER_FREQ)});
- mPullers.insert({android::util::CPU_TIME_PER_UID, make_shared<CpuTimePerUidPuller>()});
- mPullers.insert({android::util::CPU_TIME_PER_UID_FREQ, make_shared<CpuTimePerUidFreqPuller>()});
- mPullers.insert(
- {android::util::SYSTEM_ELAPSED_REALTIME,
- make_shared<StatsCompanionServicePuller>(android::util::SYSTEM_ELAPSED_REALTIME)});
- mPullers.insert({android::util::SYSTEM_UPTIME,
- make_shared<StatsCompanionServicePuller>(android::util::SYSTEM_UPTIME)});
- mPullers.insert({android::util::DISK_SPACE,
- make_shared<StatsCompanionServicePuller>(android::util::DISK_SPACE)});
- mPullers.insert(
- {android::util::BLUETOOTH_ACTIVITY_INFO,
- make_shared<StatsCompanionServicePuller>(android::util::BLUETOOTH_ACTIVITY_INFO)});
-
- mPullers.insert(
- {android::util::BLUETOOTH_BYTES_TRANSFER,
- make_shared<StatsCompanionServicePuller>(android::util::BLUETOOTH_BYTES_TRANSFER)});
- mPullers.insert(
- {android::util::WIFI_ACTIVITY_ENERGY_INFO,
- make_shared<StatsCompanionServicePuller>(android::util::WIFI_ACTIVITY_ENERGY_INFO)});
- mPullers.insert({android::util::MODEM_ACTIVITY_INFO,
- make_shared<StatsCompanionServicePuller>(android::util::MODEM_ACTIVITY_INFO)});
- mPullers.insert({android::util::REMAINING_BATTERY_CAPACITY,
- make_shared<ResourceHealthManagerPuller>(android::util::REMAINING_BATTERY_CAPACITY)});
- mPullers.insert({android::util::FULL_BATTERY_CAPACITY,
- make_shared<ResourceHealthManagerPuller>(android::util::FULL_BATTERY_CAPACITY)});
mStatsCompanionService = StatsService::getStatsCompanionService();
}
bool StatsPullerManagerImpl::Pull(int tagId, vector<shared_ptr<LogEvent>>* data) {
- if (DEBUG) ALOGD("Initiating pulling %d", tagId);
+ VLOG("Initiating pulling %d", tagId);
- if (mPullers.find(tagId) != mPullers.end()) {
- bool ret = mPullers.find(tagId)->second->Pull(data);
- ALOGD("pulled %d items", (int)data->size());
+ if (kAllPullAtomInfo.find(tagId) != kAllPullAtomInfo.end()) {
+ bool ret = kAllPullAtomInfo.find(tagId)->second.puller->Pull(data);
+ VLOG("pulled %d items", (int)data->size());
return ret;
} else {
- ALOGD("Unknown tagId %d", tagId);
+ VLOG("Unknown tagId %d", tagId);
return false; // Return early since we don't know what to pull.
}
}
@@ -110,7 +140,7 @@
}
bool StatsPullerManagerImpl::PullerForMatcherExists(int tagId) const {
- return mPullers.find(tagId) != mPullers.end();
+ return kAllPullAtomInfo.find(tagId) != kAllPullAtomInfo.end();
}
void StatsPullerManagerImpl::RegisterReceiver(int tagId, wp<PullDataReceiver> receiver,
@@ -201,16 +231,16 @@
int StatsPullerManagerImpl::ForceClearPullerCache() {
int totalCleared = 0;
- for (auto puller : mPullers) {
- totalCleared += puller.second->ForceClearCache();
+ for (const auto& pulledAtom : kAllPullAtomInfo) {
+ totalCleared += pulledAtom.second.puller->ForceClearCache();
}
return totalCleared;
}
int StatsPullerManagerImpl::ClearPullerCacheIfNecessary(long timestampSec) {
int totalCleared = 0;
- for (auto puller : mPullers) {
- totalCleared += puller.second->ClearCacheIfNecessary(timestampSec);
+ for (const auto& pulledAtom : kAllPullAtomInfo) {
+ totalCleared += pulledAtom.second.puller->ClearCacheIfNecessary(timestampSec);
}
return totalCleared;
}
diff --git a/cmds/statsd/src/external/StatsPullerManagerImpl.h b/cmds/statsd/src/external/StatsPullerManagerImpl.h
index 3535fa3..76a4c14 100644
--- a/cmds/statsd/src/external/StatsPullerManagerImpl.h
+++ b/cmds/statsd/src/external/StatsPullerManagerImpl.h
@@ -32,6 +32,20 @@
namespace os {
namespace statsd {
+typedef struct {
+ // The field numbers of the fields that need to be summed when merging
+ // isolated uid with host uid.
+ std::vector<int> additiveFields;
+ // The field numbers of the fields that can't be merged when merging
+ // data belong to isolated uid and host uid.
+ std::vector<int> nonAdditiveFields;
+ // How long should the puller wait before doing an actual pull again. Default
+ // 1 sec. Set this to 0 if this is handled elsewhere.
+ long coolDownSec = 1;
+ // The actual puller
+ sp<StatsPuller> puller;
+} PullAtomInfo;
+
class StatsPullerManagerImpl : public virtual RefBase {
public:
static StatsPullerManagerImpl& GetInstance();
@@ -53,7 +67,9 @@
int ClearPullerCacheIfNecessary(long timestampSec);
-private:
+ const static std::map<int, PullAtomInfo> kAllPullAtomInfo;
+
+ private:
StatsPullerManagerImpl();
// use this to update alarm
@@ -61,9 +77,6 @@
sp<IStatsCompanionService> get_stats_companion_service();
- // mapping from simple matcher tagId to puller
- std::map<int, std::shared_ptr<StatsPuller>> mPullers;
-
typedef struct {
// pull_interval_sec : last_pull_time_sec
std::pair<uint64_t, uint64_t> timeInfo;
@@ -81,8 +94,6 @@
// bucket size. All pulled metrics start pulling based on this time, so that they can be
// correctly attributed to the correct buckets.
long mTimeBaseSec;
-
- LogEvent parse_pulled_data(String16 data);
};
} // namespace statsd
diff --git a/cmds/statsd/src/external/SubsystemSleepStatePuller.cpp b/cmds/statsd/src/external/SubsystemSleepStatePuller.cpp
index 550a064..65a1df0e 100644
--- a/cmds/statsd/src/external/SubsystemSleepStatePuller.cpp
+++ b/cmds/statsd/src/external/SubsystemSleepStatePuller.cpp
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#define DEBUG true // STOPSHIP if true
+#define DEBUG false // STOPSHIP if true
#include "Log.h"
#include <android/hardware/power/1.0/IPower.h>
diff --git a/cmds/statsd/src/external/puller_util.cpp b/cmds/statsd/src/external/puller_util.cpp
new file mode 100644
index 0000000..7cfc1d48
--- /dev/null
+++ b/cmds/statsd/src/external/puller_util.cpp
@@ -0,0 +1,156 @@
+/*
+ * Copyright (C) 2018 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 DEBUG false // STOPSHIP if true
+#include "Log.h"
+
+#include "StatsPullerManagerImpl.h"
+#include "field_util.h"
+#include "puller_util.h"
+#include "statslog.h"
+
+namespace android {
+namespace os {
+namespace statsd {
+
+using std::map;
+using std::shared_ptr;
+using std::vector;
+
+DimensionsValue* getFieldValue(shared_ptr<LogEvent> event, int tagId, int fieldNum) {
+ Field field;
+ buildSimpleAtomField(tagId, fieldNum, &field);
+ return event->findFieldValueOrNull(field);
+}
+
+bool shouldMerge(shared_ptr<LogEvent>& lhs, shared_ptr<LogEvent>& rhs,
+ const vector<int>& nonAdditiveFields, int tagId) {
+ for (int f : nonAdditiveFields) {
+ DimensionsValue* lValue = getFieldValue(lhs, tagId, f);
+ DimensionsValue* rValue = getFieldValue(rhs, tagId, f);
+ if (!compareDimensionsValue(*lValue, *rValue)) {
+ return false;
+ }
+ }
+ return true;
+}
+
+// merge rhs to lhs
+void mergeEvent(shared_ptr<LogEvent>& lhs, shared_ptr<LogEvent>& rhs,
+ const vector<int>& additiveFields, int tagId) {
+ for (int f : additiveFields) {
+ DimensionsValue* lValue = getFieldValue(lhs, tagId, f);
+ DimensionsValue* rValue = getFieldValue(rhs, tagId, f);
+ if (lValue->has_value_int()) {
+ lValue->set_value_int(lValue->value_int() + rValue->value_int());
+ } else if (lValue->has_value_long()) {
+ lValue->set_value_long(lValue->value_long() + rValue->value_long());
+ }
+ }
+}
+
+// process all data and merge isolated with host if necessary
+void mergeIsolatedUidsToHostUid(vector<shared_ptr<LogEvent>>& data,
+ const sp<UidMap>& uidMap, int tagId) {
+ if (StatsPullerManagerImpl::kAllPullAtomInfo.find(tagId) ==
+ StatsPullerManagerImpl::kAllPullAtomInfo.end()) {
+ VLOG("Unknown pull atom id %d", tagId);
+ return;
+ }
+ if (android::util::kAtomsWithUidField.find(tagId) ==
+ android::util::kAtomsWithUidField.end()) {
+ VLOG("No uid to merge for atom %d", tagId);
+ return;
+ }
+ const vector<int>& additiveFields =
+ StatsPullerManagerImpl::kAllPullAtomInfo.find(tagId)
+ ->second.additiveFields;
+ const vector<int>& nonAdditiveFields =
+ StatsPullerManagerImpl::kAllPullAtomInfo.find(tagId)
+ ->second.nonAdditiveFields;
+
+ // map of host uid to isolated uid data index in the original vector.
+ // because of non additive fields, there could be multiple of them that can't
+ // be merged into one
+ map<int, vector<int>> hostToIsolated;
+ // map of host uid to their position in the original vector
+ map<int, vector<int>> hostPosition;
+ vector<int> isolatedUidPos;
+ // all uids in the original vector
+ vector<int> allUids;
+ for (size_t i = 0; i < data.size(); i++) {
+ // uid field is always first primitive filed, if present
+ DimensionsValue* uidField = getFieldValue(data[i], tagId, 1);
+ if (!uidField) {
+ VLOG("Bad data for %d, %s", tagId, data[i]->ToString().c_str());
+ return;
+ }
+ int uid = uidField->value_int();
+ allUids.push_back(uid);
+ const int hostUid = uidMap->getHostUidOrSelf(uid);
+ if (hostUid != uid) {
+ uidField->set_value_int(hostUid);
+ hostToIsolated[hostUid].push_back(i);
+ isolatedUidPos.push_back(i);
+ }
+ }
+ vector<shared_ptr<LogEvent>> mergedData;
+ for (size_t i = 0; i < allUids.size(); i++) {
+ if (hostToIsolated.find(allUids[i]) != hostToIsolated.end()) {
+ hostPosition[allUids[i]].push_back(i);
+ } else if (std::find(isolatedUidPos.begin(), isolatedUidPos.end(), i) != isolatedUidPos.end()) {
+ continue;
+ } else {
+ mergedData.push_back(data[i]);
+ }
+ }
+ for (auto iter = hostToIsolated.begin(); iter != hostToIsolated.end();
+ iter++) {
+ int uid = iter->first;
+ vector<int>& isolated = hostToIsolated[uid];
+ vector<int> toBeMerged;
+ toBeMerged.insert(toBeMerged.begin(), isolated.begin(), isolated.end());
+ if (hostPosition.find(uid) != hostPosition.end()) {
+ vector<int>& host = hostPosition[uid];
+ toBeMerged.insert(toBeMerged.end(), host.begin(), host.end());
+ }
+ vector<bool> used(toBeMerged.size());
+ for (size_t i = 0; i < toBeMerged.size(); i++) {
+ if (used[i] == true) {
+ continue;
+ }
+ for (size_t j = i + 1; j < toBeMerged.size(); j++) {
+ shared_ptr<LogEvent>& lhs = data[toBeMerged[i]];
+ shared_ptr<LogEvent>& rhs = data[toBeMerged[j]];
+ if (shouldMerge(lhs, rhs, nonAdditiveFields, tagId)) {
+ mergeEvent(lhs, rhs, additiveFields, tagId);
+ used[j] = true;
+ }
+ }
+ }
+ for (size_t i = 0; i < toBeMerged.size(); i++) {
+ if (used[i] == false) {
+ mergedData.push_back(data[i]);
+ }
+ }
+ }
+ data.clear();
+ data = mergedData;
+}
+
+} // namespace statsd
+} // namespace os
+} // namespace android
diff --git a/cmds/statsd/src/external/puller_util.h b/cmds/statsd/src/external/puller_util.h
new file mode 100644
index 0000000..70d5321
--- /dev/null
+++ b/cmds/statsd/src/external/puller_util.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+#pragma once
+
+#include <vector>
+#include "HashableDimensionKey.h"
+#include "StatsPuller.h"
+#include "logd/LogEvent.h"
+#include "packages/UidMap.h"
+
+namespace android {
+namespace os {
+namespace statsd {
+
+void mergeIsolatedUidsToHostUid(std::vector<std::shared_ptr<LogEvent>>& data,
+ const sp<UidMap>& uidMap, int tagId);
+
+} // namespace statsd
+} // namespace os
+} // namespace android
diff --git a/cmds/statsd/src/guardrail/MemoryLeakTrackUtil.cpp b/cmds/statsd/src/guardrail/MemoryLeakTrackUtil.cpp
index e1947c4..01c7587 100644
--- a/cmds/statsd/src/guardrail/MemoryLeakTrackUtil.cpp
+++ b/cmds/statsd/src/guardrail/MemoryLeakTrackUtil.cpp
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#define DEBUG true // STOPSHIP if true
+#define DEBUG false // STOPSHIP if true
#include "Log.h"
#include <sstream>
@@ -63,7 +63,7 @@
size_t count;
if (info == nullptr || overallSize == 0 || infoSize == 0 ||
(count = overallSize / infoSize) == 0) {
- ALOGD("no malloc info, libc.debug.malloc.program property should be set");
+ VLOG("no malloc info, libc.debug.malloc.program property should be set");
return std::string();
}
diff --git a/cmds/statsd/src/guardrail/StatsdStats.cpp b/cmds/statsd/src/guardrail/StatsdStats.cpp
index 77f5456..06c5b00 100644
--- a/cmds/statsd/src/guardrail/StatsdStats.cpp
+++ b/cmds/statsd/src/guardrail/StatsdStats.cpp
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-#define DEBUG true // STOPSHIP if true
+#define DEBUG false // STOPSHIP if true
#include "Log.h"
#include "StatsdStats.h"
diff --git a/cmds/statsd/src/logd/LogEvent.cpp b/cmds/statsd/src/logd/LogEvent.cpp
index 1dcd853..e1ab5d5 100644
--- a/cmds/statsd/src/logd/LogEvent.cpp
+++ b/cmds/statsd/src/logd/LogEvent.cpp
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#define DEBUG true // STOPSHIP if true
+#define DEBUG false // STOPSHIP if true
#include "logd/LogEvent.h"
#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
@@ -294,6 +294,28 @@
}
}
+int LogEvent::GetInt(size_t key, status_t* err) const {
+ DimensionsValue value;
+ if (!GetSimpleAtomDimensionsValueProto(key, &value)) {
+ *err = BAD_INDEX;
+ return 0;
+ }
+ const DimensionsValue* leafValue = getSingleLeafValue(&value);
+ switch (leafValue->value_case()) {
+ case DimensionsValue::ValueCase::kValueInt:
+ return leafValue->value_int();
+ case DimensionsValue::ValueCase::kValueLong:
+ case DimensionsValue::ValueCase::kValueBool:
+ case DimensionsValue::ValueCase::kValueFloat:
+ case DimensionsValue::ValueCase::kValueTuple:
+ case DimensionsValue::ValueCase::kValueStr:
+ case DimensionsValue::ValueCase::VALUE_NOT_SET: {
+ *err = BAD_TYPE;
+ return 0;
+ }
+ }
+}
+
const char* LogEvent::GetString(size_t key, status_t* err) const {
DimensionsValue value;
if (!GetSimpleAtomDimensionsValueProto(key, &value)) {
diff --git a/cmds/statsd/src/logd/LogEvent.h b/cmds/statsd/src/logd/LogEvent.h
index eb2c008..d521e09 100644
--- a/cmds/statsd/src/logd/LogEvent.h
+++ b/cmds/statsd/src/logd/LogEvent.h
@@ -76,6 +76,7 @@
* Returns BAD_TYPE if the index is available but the data is the wrong type.
*/
int64_t GetLong(size_t key, status_t* err) const;
+ int GetInt(size_t key, status_t* err) const;
const char* GetString(size_t key, status_t* err) const;
bool GetBool(size_t key, status_t* err) const;
float GetFloat(size_t key, status_t* err) const;
diff --git a/cmds/statsd/src/metrics/CountMetricProducer.cpp b/cmds/statsd/src/metrics/CountMetricProducer.cpp
index 5b5b57b..5a042b6 100644
--- a/cmds/statsd/src/metrics/CountMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/CountMetricProducer.cpp
@@ -45,8 +45,6 @@
// for StatsLogReport
const int FIELD_ID_ID = 1;
-const int FIELD_ID_START_REPORT_NANOS = 2;
-const int FIELD_ID_END_REPORT_NANOS = 3;
const int FIELD_ID_COUNT_METRICS = 5;
// for CountMetricDataWrapper
const int FIELD_ID_DATA = 1;
@@ -97,7 +95,6 @@
void CountMetricProducer::onDumpReportLocked(const uint64_t dumpTimeNs, StatsLogReport* report) {
flushIfNeededLocked(dumpTimeNs);
report->set_metric_id(mMetricId);
- report->set_start_report_nanos(mStartTimeNs);
auto count_metrics = report->mutable_count_metrics();
for (const auto& counter : mPastBuckets) {
@@ -123,7 +120,6 @@
}
protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_ID, (long long)mMetricId);
- protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_START_REPORT_NANOS, (long long)mStartTimeNs);
long long protoToken = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_ID_COUNT_METRICS);
VLOG("metric %lld dump report now...",(long long)mMetricId);
@@ -167,7 +163,6 @@
}
protoOutput->end(protoToken);
- protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_END_REPORT_NANOS, (long long)dumpTimeNs);
mPastBuckets.clear();
diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.cpp b/cmds/statsd/src/metrics/DurationMetricProducer.cpp
index 2400eba1..65cbc4a 100644
--- a/cmds/statsd/src/metrics/DurationMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/DurationMetricProducer.cpp
@@ -44,8 +44,6 @@
// for StatsLogReport
const int FIELD_ID_ID = 1;
-const int FIELD_ID_START_REPORT_NANOS = 2;
-const int FIELD_ID_END_REPORT_NANOS = 3;
const int FIELD_ID_DURATION_METRICS = 6;
// for DurationMetricDataWrapper
const int FIELD_ID_DATA = 1;
@@ -179,7 +177,6 @@
void DurationMetricProducer::onDumpReportLocked(const uint64_t dumpTimeNs, StatsLogReport* report) {
flushIfNeededLocked(dumpTimeNs);
report->set_metric_id(mMetricId);
- report->set_start_report_nanos(mStartTimeNs);
auto duration_metrics = report->mutable_duration_metrics();
for (const auto& pair : mPastBuckets) {
@@ -205,7 +202,6 @@
}
protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_ID, (long long)mMetricId);
- protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_START_REPORT_NANOS, (long long)mStartTimeNs);
long long protoToken = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_ID_DURATION_METRICS);
VLOG("metric %lld dump report now...", (long long)mMetricId);
@@ -250,7 +246,6 @@
}
protoOutput->end(protoToken);
- protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_END_REPORT_NANOS, (long long)dumpTimeNs);
mPastBuckets.clear();
}
diff --git a/cmds/statsd/src/metrics/EventMetricProducer.cpp b/cmds/statsd/src/metrics/EventMetricProducer.cpp
index a021e0a..936a2ef1 100644
--- a/cmds/statsd/src/metrics/EventMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/EventMetricProducer.cpp
@@ -42,8 +42,6 @@
// for StatsLogReport
const int FIELD_ID_ID = 1;
-const int FIELD_ID_START_REPORT_NANOS = 2;
-const int FIELD_ID_END_REPORT_NANOS = 3;
const int FIELD_ID_EVENT_METRICS = 4;
// for EventMetricDataWrapper
const int FIELD_ID_DATA = 1;
@@ -61,9 +59,7 @@
metric.links().end());
mConditionSliced = true;
}
-
- startNewProtoOutputStreamLocked();
-
+ mProto = std::make_unique<ProtoOutputStream>();
VLOG("metric %lld created. bucket size %lld start_time: %lld", (long long)metric.id(),
(long long)mBucketSizeNs, (long long)mStartTimeNs);
}
@@ -72,10 +68,6 @@
VLOG("~EventMetricProducer() called");
}
-void EventMetricProducer::startNewProtoOutputStreamLocked() {
- mProto = std::make_unique<ProtoOutputStream>();
-}
-
void EventMetricProducer::onSlicedConditionMayChangeLocked(const uint64_t eventTime) {
}
@@ -106,8 +98,6 @@
return;
}
protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_ID, (long long)mMetricId);
- protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_START_REPORT_NANOS, (long long)mStartTimeNs);
- protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_END_REPORT_NANOS, (long long)dumpTimeNs);
size_t bufferSize = mProto->size();
VLOG("metric %lld dump report now... proto size: %zu ",
@@ -117,7 +107,7 @@
protoOutput->write(FIELD_TYPE_MESSAGE | FIELD_ID_EVENT_METRICS,
reinterpret_cast<char*>(buffer.get()->data()), buffer.get()->size());
- startNewProtoOutputStreamLocked();
+ mProto->clear();
}
void EventMetricProducer::onConditionChangedLocked(const bool conditionMet,
diff --git a/cmds/statsd/src/metrics/EventMetricProducer.h b/cmds/statsd/src/metrics/EventMetricProducer.h
index 935f206..394ed23 100644
--- a/cmds/statsd/src/metrics/EventMetricProducer.h
+++ b/cmds/statsd/src/metrics/EventMetricProducer.h
@@ -40,9 +40,6 @@
virtual ~EventMetricProducer();
-protected:
- void startNewProtoOutputStreamLocked();
-
private:
void onMatchedLogEventInternalLocked(
const size_t matcherIndex, const MetricDimensionKey& eventKey,
diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
index 4190f00..62ee6ef 100644
--- a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
@@ -45,8 +45,6 @@
// for StatsLogReport
const int FIELD_ID_ID = 1;
-const int FIELD_ID_START_REPORT_NANOS = 2;
-const int FIELD_ID_END_REPORT_NANOS = 3;
const int FIELD_ID_GAUGE_METRICS = 8;
// for GaugeMetricDataWrapper
const int FIELD_ID_DATA = 1;
@@ -134,7 +132,6 @@
}
protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_ID, (long long)mMetricId);
- protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_START_REPORT_NANOS, (long long)mStartTimeNs);
long long protoToken = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_ID_GAUGE_METRICS);
for (const auto& pair : mPastBuckets) {
@@ -188,7 +185,6 @@
protoOutput->end(wrapperToken);
}
protoOutput->end(protoToken);
- protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_END_REPORT_NANOS, (long long)dumpTimeNs);
mPastBuckets.clear();
// TODO: Clear mDimensionKeyMap once the report is dumped.
diff --git a/cmds/statsd/src/metrics/MetricsManager.cpp b/cmds/statsd/src/metrics/MetricsManager.cpp
index 6573a89..6c21b05 100644
--- a/cmds/statsd/src/metrics/MetricsManager.cpp
+++ b/cmds/statsd/src/metrics/MetricsManager.cpp
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-#define DEBUG true // STOPSHIP if true
+#define DEBUG false // STOPSHIP if true
#include "Log.h"
#include "MetricsManager.h"
#include "statslog.h"
@@ -29,6 +29,7 @@
#include <log/logprint.h>
#include <private/android_filesystem_config.h>
+#include <utils/SystemClock.h>
using android::util::FIELD_COUNT_REPEATED;
using android::util::FIELD_TYPE_MESSAGE;
@@ -48,7 +49,7 @@
MetricsManager::MetricsManager(const ConfigKey& key, const StatsdConfig& config,
const long timeBaseSec, sp<UidMap> uidMap)
- : mConfigKey(key), mUidMap(uidMap) {
+ : mConfigKey(key), mUidMap(uidMap), mLastReportTimeNs(0) {
mConfigValid =
initStatsdConfig(key, config, *uidMap, timeBaseSec, mTagIds, mAllAtomMatchers, mAllConditionTrackers,
mAllMetricProducers, mAllAnomalyTrackers, mConditionToMetricMap,
@@ -184,6 +185,7 @@
protoOutput->end(token);
}
}
+ mLastReportTimeNs = ::android::elapsedRealtimeNano();
VLOG("=========================Metric Reports End==========================");
}
diff --git a/cmds/statsd/src/metrics/MetricsManager.h b/cmds/statsd/src/metrics/MetricsManager.h
index 9cae70a..2b30f44 100644
--- a/cmds/statsd/src/metrics/MetricsManager.h
+++ b/cmds/statsd/src/metrics/MetricsManager.h
@@ -64,6 +64,11 @@
void dumpStates(FILE* out, bool verbose);
+ // Returns the elapsed realtime when this metric manager last reported metrics.
+ uint64_t getLastReportTimeNs() {
+ return mLastReportTimeNs;
+ };
+
// Config source owner can call onDumpReport() to get all the metrics collected.
virtual void onDumpReport(android::util::ProtoOutputStream* protoOutput);
virtual void onDumpReport(const uint64_t& dumpTimeStampNs, ConfigMetricsReport* report);
@@ -78,6 +83,8 @@
bool mConfigValid = false;
+ uint64_t mLastReportTimeNs;
+
// The uid log sources from StatsdConfig.
std::vector<int32_t> mAllowedUid;
diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.cpp b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
index 31d9ff8..7b1944c 100644
--- a/cmds/statsd/src/metrics/ValueMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
@@ -48,8 +48,6 @@
// for StatsLogReport
const int FIELD_ID_ID = 1;
-const int FIELD_ID_START_REPORT_NANOS = 2;
-const int FIELD_ID_END_REPORT_NANOS = 3;
const int FIELD_ID_VALUE_METRICS = 7;
// for ValueMetricDataWrapper
const int FIELD_ID_DATA = 1;
@@ -122,7 +120,6 @@
void ValueMetricProducer::onDumpReportLocked(const uint64_t dumpTimeNs, StatsLogReport* report) {
flushIfNeededLocked(dumpTimeNs);
report->set_metric_id(mMetricId);
- report->set_start_report_nanos(mStartTimeNs);
auto value_metrics = report->mutable_value_metrics();
for (const auto& pair : mPastBuckets) {
ValueMetricData* metricData = value_metrics->add_data();
@@ -147,7 +144,6 @@
return;
}
protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_ID, (long long)mMetricId);
- protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_START_REPORT_NANOS, (long long)mStartTimeNs);
long long protoToken = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_ID_VALUE_METRICS);
for (const auto& pair : mPastBuckets) {
@@ -186,7 +182,6 @@
protoOutput->end(wrapperToken);
}
protoOutput->end(protoToken);
- protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_END_REPORT_NANOS, (long long)dumpTimeNs);
VLOG("metric %lld dump report now...", (long long)mMetricId);
mPastBuckets.clear();
diff --git a/cmds/statsd/src/metrics/metrics_manager_util.cpp b/cmds/statsd/src/metrics/metrics_manager_util.cpp
index a0173ee..205c8e4 100644
--- a/cmds/statsd/src/metrics/metrics_manager_util.cpp
+++ b/cmds/statsd/src/metrics/metrics_manager_util.cpp
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#define DEBUG true // STOPSHIP if true
+#define DEBUG false // STOPSHIP if true
#include "Log.h"
#include "../condition/CombinationConditionTracker.h"
@@ -541,7 +541,7 @@
ALOGE("initLogMatchingTrackers failed");
return false;
}
- ALOGD("initLogMatchingTrackers succeed...");
+ VLOG("initLogMatchingTrackers succeed...");
if (!initConditions(key, config, logTrackerMap, conditionTrackerMap, allConditionTrackers,
trackerToConditionMap)) {
diff --git a/cmds/statsd/src/packages/UidMap.cpp b/cmds/statsd/src/packages/UidMap.cpp
index 691423e..0d7b722 100644
--- a/cmds/statsd/src/packages/UidMap.cpp
+++ b/cmds/statsd/src/packages/UidMap.cpp
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-#define DEBUG true // STOPSHIP if true
+#define DEBUG false // STOPSHIP if true
#include "Log.h"
#include "guardrail/StatsdStats.h"
diff --git a/cmds/statsd/src/packages/UidMap.h b/cmds/statsd/src/packages/UidMap.h
index 3304f6c..f1da452 100644
--- a/cmds/statsd/src/packages/UidMap.h
+++ b/cmds/statsd/src/packages/UidMap.h
@@ -91,7 +91,7 @@
void removeIsolatedUid(int isolatedUid, int parentUid);
// Returns the host uid if it exists. Otherwise, returns the same uid that was passed-in.
- int getHostUidOrSelf(int uid) const;
+ virtual int getHostUidOrSelf(int uid) const;
// Gets the output. If every config key has received the output, then the output is cleared.
UidMapping getOutput(const ConfigKey& key);
diff --git a/cmds/statsd/src/stats_log.proto b/cmds/statsd/src/stats_log.proto
index af21ca4..b56cffb 100644
--- a/cmds/statsd/src/stats_log.proto
+++ b/cmds/statsd/src/stats_log.proto
@@ -143,9 +143,7 @@
message StatsLogReport {
optional int64 metric_id = 1;
- optional int64 start_report_nanos = 2;
-
- optional int64 end_report_nanos = 3;
+ // Fields 2 and 3 are reserved.
message EventMetricDataWrapper {
repeated EventMetricData data = 1;
@@ -177,6 +175,10 @@
repeated StatsLogReport metrics = 1;
optional UidMapping uid_map = 2;
+
+ optional int64 last_report_nanos = 3;
+
+ optional int64 current_report_nanos = 4;
}
message ConfigMetricsReportList {
diff --git a/cmds/statsd/src/storage/StorageManager.cpp b/cmds/statsd/src/storage/StorageManager.cpp
index 00d8658..23bd5561 100644
--- a/cmds/statsd/src/storage/StorageManager.cpp
+++ b/cmds/statsd/src/storage/StorageManager.cpp
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#define DEBUG true // STOPSHIP if true
+#define DEBUG false // STOPSHIP if true
#include "Log.h"
#include "android-base/stringprintf.h"
diff --git a/cmds/statsd/tests/external/puller_util_test.cpp b/cmds/statsd/tests/external/puller_util_test.cpp
new file mode 100644
index 0000000..7d9c8a8
--- /dev/null
+++ b/cmds/statsd/tests/external/puller_util_test.cpp
@@ -0,0 +1,269 @@
+// Copyright (C) 2018 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "external/puller_util.h"
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <stdio.h>
+#include <vector>
+#include "../metrics/metrics_test_helper.h"
+
+#ifdef __ANDROID__
+
+namespace android {
+namespace os {
+namespace statsd {
+
+using namespace testing;
+using std::make_shared;
+using std::shared_ptr;
+using std::vector;
+using testing::Contains;
+/*
+ * Test merge isolated and host uid
+ */
+
+int uidAtomTagId = android::util::CPU_TIME_PER_UID_FREQ;
+int nonUidAtomTagId = android::util::SYSTEM_UPTIME;
+int timestamp = 1234;
+int isolatedUid = 30;
+int isolatedAdditiveData = 31;
+int isolatedNonAdditiveData = 32;
+int hostUid = 20;
+int hostAdditiveData = 21;
+int hostNonAdditiveData = 22;
+
+void extractIntoVector(vector<shared_ptr<LogEvent>> events,
+ vector<vector<int>>& ret) {
+ ret.clear();
+ status_t err;
+ for (const auto& event : events) {
+ vector<int> vec;
+ vec.push_back(event->GetInt(1, &err));
+ vec.push_back(event->GetInt(2, &err));
+ vec.push_back(event->GetInt(3, &err));
+ ret.push_back(vec);
+ }
+}
+
+TEST(puller_util, MergeNoDimension) {
+ vector<shared_ptr<LogEvent>> inputData;
+ shared_ptr<LogEvent> event = make_shared<LogEvent>(uidAtomTagId, timestamp);
+ // 30->22->31
+ event->write(isolatedUid);
+ event->write(hostNonAdditiveData);
+ event->write(isolatedAdditiveData);
+ event->init();
+ inputData.push_back(event);
+
+ // 20->22->21
+ event = make_shared<LogEvent>(uidAtomTagId, timestamp);
+ event->write(hostUid);
+ event->write(hostNonAdditiveData);
+ event->write(hostAdditiveData);
+ event->init();
+ inputData.push_back(event);
+
+ sp<MockUidMap> uidMap = new NaggyMock<MockUidMap>();
+ EXPECT_CALL(*uidMap, getHostUidOrSelf(isolatedUid))
+ .WillRepeatedly(Return(hostUid));
+ EXPECT_CALL(*uidMap, getHostUidOrSelf(Ne(isolatedUid)))
+ .WillRepeatedly(ReturnArg<0>());
+ mergeIsolatedUidsToHostUid(inputData, uidMap, uidAtomTagId);
+
+ vector<vector<int>> actual;
+ extractIntoVector(inputData, actual);
+ vector<int> expectedV1 = {20, 22, 52};
+ EXPECT_EQ(1, (int)actual.size());
+ EXPECT_THAT(actual, Contains(expectedV1));
+}
+
+TEST(puller_util, MergeWithDimension) {
+ vector<shared_ptr<LogEvent>> inputData;
+ shared_ptr<LogEvent> event = make_shared<LogEvent>(uidAtomTagId, timestamp);
+ // 30->32->31
+ event->write(isolatedUid);
+ event->write(isolatedNonAdditiveData);
+ event->write(isolatedAdditiveData);
+ event->init();
+ inputData.push_back(event);
+
+ // 20->32->21
+ event = make_shared<LogEvent>(uidAtomTagId, timestamp);
+ event->write(hostUid);
+ event->write(isolatedNonAdditiveData);
+ event->write(hostAdditiveData);
+ event->init();
+ inputData.push_back(event);
+
+ // 20->22->21
+ event = make_shared<LogEvent>(uidAtomTagId, timestamp);
+ event->write(hostUid);
+ event->write(hostNonAdditiveData);
+ event->write(hostAdditiveData);
+ event->init();
+ inputData.push_back(event);
+
+ sp<MockUidMap> uidMap = new NaggyMock<MockUidMap>();
+ EXPECT_CALL(*uidMap, getHostUidOrSelf(isolatedUid))
+ .WillRepeatedly(Return(hostUid));
+ EXPECT_CALL(*uidMap, getHostUidOrSelf(Ne(isolatedUid)))
+ .WillRepeatedly(ReturnArg<0>());
+ mergeIsolatedUidsToHostUid(inputData, uidMap, uidAtomTagId);
+
+ vector<vector<int>> actual;
+ extractIntoVector(inputData, actual);
+ vector<int> expectedV1 = {20, 22, 21};
+ vector<int> expectedV2 = {20, 32, 52};
+ EXPECT_EQ(2, (int)actual.size());
+ EXPECT_THAT(actual, Contains(expectedV1));
+ EXPECT_THAT(actual, Contains(expectedV2));
+}
+
+TEST(puller_util, NoMergeHostUidOnly) {
+ vector<shared_ptr<LogEvent>> inputData;
+ shared_ptr<LogEvent> event = make_shared<LogEvent>(uidAtomTagId, timestamp);
+ // 20->32->31
+ event->write(hostUid);
+ event->write(isolatedNonAdditiveData);
+ event->write(isolatedAdditiveData);
+ event->init();
+ inputData.push_back(event);
+
+ // 20->22->21
+ event = make_shared<LogEvent>(uidAtomTagId, timestamp);
+ event->write(hostUid);
+ event->write(hostNonAdditiveData);
+ event->write(hostAdditiveData);
+ event->init();
+ inputData.push_back(event);
+
+ sp<MockUidMap> uidMap = new NaggyMock<MockUidMap>();
+ EXPECT_CALL(*uidMap, getHostUidOrSelf(isolatedUid))
+ .WillRepeatedly(Return(hostUid));
+ EXPECT_CALL(*uidMap, getHostUidOrSelf(Ne(isolatedUid)))
+ .WillRepeatedly(ReturnArg<0>());
+ mergeIsolatedUidsToHostUid(inputData, uidMap, uidAtomTagId);
+
+ // 20->32->31
+ // 20->22->21
+ vector<vector<int>> actual;
+ extractIntoVector(inputData, actual);
+ vector<int> expectedV1 = {20, 32, 31};
+ vector<int> expectedV2 = {20, 22, 21};
+ EXPECT_EQ(2, (int)actual.size());
+ EXPECT_THAT(actual, Contains(expectedV1));
+ EXPECT_THAT(actual, Contains(expectedV2));
+}
+
+TEST(puller_util, IsolatedUidOnly) {
+ vector<shared_ptr<LogEvent>> inputData;
+ shared_ptr<LogEvent> event = make_shared<LogEvent>(uidAtomTagId, timestamp);
+ // 30->32->31
+ event->write(hostUid);
+ event->write(isolatedNonAdditiveData);
+ event->write(isolatedAdditiveData);
+ event->init();
+ inputData.push_back(event);
+
+ // 30->22->21
+ event = make_shared<LogEvent>(uidAtomTagId, timestamp);
+ event->write(hostUid);
+ event->write(hostNonAdditiveData);
+ event->write(hostAdditiveData);
+ event->init();
+ inputData.push_back(event);
+
+ sp<MockUidMap> uidMap = new NaggyMock<MockUidMap>();
+ EXPECT_CALL(*uidMap, getHostUidOrSelf(isolatedUid))
+ .WillRepeatedly(Return(hostUid));
+ EXPECT_CALL(*uidMap, getHostUidOrSelf(Ne(isolatedUid)))
+ .WillRepeatedly(ReturnArg<0>());
+ mergeIsolatedUidsToHostUid(inputData, uidMap, uidAtomTagId);
+
+ // 20->32->31
+ // 20->22->21
+ vector<vector<int>> actual;
+ extractIntoVector(inputData, actual);
+ vector<int> expectedV1 = {20, 32, 31};
+ vector<int> expectedV2 = {20, 22, 21};
+ EXPECT_EQ(2, (int)actual.size());
+ EXPECT_THAT(actual, Contains(expectedV1));
+ EXPECT_THAT(actual, Contains(expectedV2));
+}
+
+TEST(puller_util, MultipleIsolatedUidToOneHostUid) {
+ vector<shared_ptr<LogEvent>> inputData;
+ shared_ptr<LogEvent> event = make_shared<LogEvent>(uidAtomTagId, timestamp);
+ // 30->32->31
+ event->write(isolatedUid);
+ event->write(isolatedNonAdditiveData);
+ event->write(isolatedAdditiveData);
+ event->init();
+ inputData.push_back(event);
+
+ // 31->32->21
+ event = make_shared<LogEvent>(uidAtomTagId, timestamp);
+ event->write(isolatedUid + 1);
+ event->write(isolatedNonAdditiveData);
+ event->write(hostAdditiveData);
+ event->init();
+ inputData.push_back(event);
+
+ // 20->32->21
+ event = make_shared<LogEvent>(uidAtomTagId, timestamp);
+ event->write(hostUid);
+ event->write(isolatedNonAdditiveData);
+ event->write(hostAdditiveData);
+ event->init();
+ inputData.push_back(event);
+
+ sp<MockUidMap> uidMap = new NaggyMock<MockUidMap>();
+ EXPECT_CALL(*uidMap, getHostUidOrSelf(_)).WillRepeatedly(Return(hostUid));
+ mergeIsolatedUidsToHostUid(inputData, uidMap, uidAtomTagId);
+
+ vector<vector<int>> actual;
+ extractIntoVector(inputData, actual);
+ vector<int> expectedV1 = {20, 32, 73};
+ EXPECT_EQ(1, (int)actual.size());
+ EXPECT_THAT(actual, Contains(expectedV1));
+}
+
+TEST(puller_util, NoNeedToMerge) {
+ vector<shared_ptr<LogEvent>> inputData;
+ shared_ptr<LogEvent> event =
+ make_shared<LogEvent>(nonUidAtomTagId, timestamp);
+ // 32
+ event->write(isolatedNonAdditiveData);
+ event->init();
+ inputData.push_back(event);
+
+ event = make_shared<LogEvent>(nonUidAtomTagId, timestamp);
+ // 22
+ event->write(hostNonAdditiveData);
+ event->init();
+ inputData.push_back(event);
+
+ sp<MockUidMap> uidMap = new NaggyMock<MockUidMap>();
+ mergeIsolatedUidsToHostUid(inputData, uidMap, nonUidAtomTagId);
+
+ EXPECT_EQ(2, (int)inputData.size());
+}
+
+} // namespace statsd
+} // namespace os
+} // namespace android
+#else
+GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
diff --git a/cmds/statsd/tests/metrics/metrics_test_helper.h b/cmds/statsd/tests/metrics/metrics_test_helper.h
index 0a97456..b48de54 100644
--- a/cmds/statsd/tests/metrics/metrics_test_helper.h
+++ b/cmds/statsd/tests/metrics/metrics_test_helper.h
@@ -15,6 +15,7 @@
#include "src/condition/ConditionWizard.h"
#include "src/external/StatsPullerManager.h"
+#include "src/packages/UidMap.h"
#include <gmock/gmock.h>
#include <gtest/gtest.h>
@@ -40,6 +41,11 @@
MOCK_METHOD2(Pull, bool(const int pullCode, vector<std::shared_ptr<LogEvent>>* data));
};
+class MockUidMap : public UidMap {
+ public:
+ MOCK_CONST_METHOD1(getHostUidOrSelf, int(int uid));
+};
+
HashableDimensionKey getMockedDimensionKey(int tagId, int key, std::string value);
MetricDimensionKey getMockedMetricDimensionKey(int tagId, int key, std::string value);
diff --git a/cmds/statsd/tools/dogfood/AndroidManifest.xml b/cmds/statsd/tools/dogfood/AndroidManifest.xml
index cd76c9d..52673fb 100644
--- a/cmds/statsd/tools/dogfood/AndroidManifest.xml
+++ b/cmds/statsd/tools/dogfood/AndroidManifest.xml
@@ -37,5 +37,7 @@
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
+
+ <service android:name=".MainActivity$ReceiverIntentService" android:exported="true" />
</application>
</manifest>
diff --git a/cmds/statsd/tools/dogfood/res/layout/activity_main.xml b/cmds/statsd/tools/dogfood/res/layout/activity_main.xml
index 5d35c29..784ed40 100644
--- a/cmds/statsd/tools/dogfood/res/layout/activity_main.xml
+++ b/cmds/statsd/tools/dogfood/res/layout/activity_main.xml
@@ -31,6 +31,18 @@
android:layout_height="wrap_content"
android:background="@android:color/holo_green_light"
android:text="@string/push_config"/>
+ <Button
+ android:id="@+id/set_receiver"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:background="@android:color/holo_green_light"
+ android:text="@string/set_receiver"/>
+ <Button
+ android:id="@+id/remove_receiver"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:background="@android:color/holo_green_light"
+ android:text="@string/remove_receiver"/>
<LinearLayout android:layout_width="wrap_content"
android:layout_height="wrap_content"
diff --git a/cmds/statsd/tools/dogfood/res/values/strings.xml b/cmds/statsd/tools/dogfood/res/values/strings.xml
index 0eab0f4..60948a1 100644
--- a/cmds/statsd/tools/dogfood/res/values/strings.xml
+++ b/cmds/statsd/tools/dogfood/res/values/strings.xml
@@ -24,6 +24,8 @@
<string name="statsd_not_running">Statsd NOT Running</string>
<string name="push_config">Push baseline config</string>
+ <string name="set_receiver">Set pendingintent</string>
+ <string name="remove_receiver">Remove pendingintent</string>
<string name="app_a_foreground">App A foreground</string>
<string name="app_b_foreground">App B foreground</string>
diff --git a/cmds/statsd/tools/dogfood/src/com/android/statsd/dogfood/DisplayProtoUtils.java b/cmds/statsd/tools/dogfood/src/com/android/statsd/dogfood/DisplayProtoUtils.java
index 33c8abf..03e5fef 100644
--- a/cmds/statsd/tools/dogfood/src/com/android/statsd/dogfood/DisplayProtoUtils.java
+++ b/cmds/statsd/tools/dogfood/src/com/android/statsd/dogfood/DisplayProtoUtils.java
@@ -32,11 +32,13 @@
for (StatsLog.ConfigMetricsReport report : reports.getReportsList()) {
sb.append("StatsLogReport size: ").append(report.getMetricsCount()).append("\n");
+ sb.append("Last report time:").append(getDateStr(report.getLastReportNanos())).
+ append("\n");
+ sb.append("Current report time:").append(getDateStr(report.getCurrentReportNanos())).
+ append("\n");
for (StatsLog.StatsLogReport log : report.getMetricsList()) {
sb.append("\n\n");
sb.append("metric id: ").append(log.getMetricId()).append("\n");
- sb.append("start time:").append(getDateStr(log.getStartReportNanos())).append("\n");
- sb.append("end time:").append(getDateStr(log.getEndReportNanos())).append("\n");
switch (log.getDataCase()) {
case DURATION_METRICS:
diff --git a/cmds/statsd/tools/dogfood/src/com/android/statsd/dogfood/MainActivity.java b/cmds/statsd/tools/dogfood/src/com/android/statsd/dogfood/MainActivity.java
index 57575ae..0e6c933 100644
--- a/cmds/statsd/tools/dogfood/src/com/android/statsd/dogfood/MainActivity.java
+++ b/cmds/statsd/tools/dogfood/src/com/android/statsd/dogfood/MainActivity.java
@@ -16,7 +16,10 @@
package com.android.statsd.dogfood;
import android.app.Activity;
+import android.app.PendingIntent;
+import android.app.IntentService;
import android.app.StatsManager;
+import android.content.Context;
import android.content.Intent;
import android.content.res.Resources;
import android.os.Bundle;
@@ -48,69 +51,69 @@
findViewById(R.id.app_a_wake_lock_acquire1).setOnClickListener(
new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- onWakeLockAcquire(0, "wl_1");
- }
- });
+ @Override
+ public void onClick(View view) {
+ onWakeLockAcquire(0, "wl_1");
+ }
+ });
findViewById(R.id.app_b_wake_lock_acquire1).setOnClickListener(
new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- onWakeLockAcquire(1, "wl_1");
- }
- });
+ @Override
+ public void onClick(View view) {
+ onWakeLockAcquire(1, "wl_1");
+ }
+ });
findViewById(R.id.app_a_wake_lock_acquire2).setOnClickListener(
new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- onWakeLockAcquire(0, "wl_2");
- }
- });
+ @Override
+ public void onClick(View view) {
+ onWakeLockAcquire(0, "wl_2");
+ }
+ });
findViewById(R.id.app_b_wake_lock_acquire2).setOnClickListener(
new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- onWakeLockAcquire(1, "wl_2");
- }
- });
+ @Override
+ public void onClick(View view) {
+ onWakeLockAcquire(1, "wl_2");
+ }
+ });
findViewById(R.id.app_a_wake_lock_release1).setOnClickListener(
new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- onWakeLockRelease(0, "wl_1");
- }
- });
+ @Override
+ public void onClick(View view) {
+ onWakeLockRelease(0, "wl_1");
+ }
+ });
findViewById(R.id.app_b_wake_lock_release1).setOnClickListener(
new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- onWakeLockRelease(1, "wl_1");
- }
- });
+ @Override
+ public void onClick(View view) {
+ onWakeLockRelease(1, "wl_1");
+ }
+ });
findViewById(R.id.app_a_wake_lock_release2).setOnClickListener(
new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- onWakeLockRelease(0, "wl_2");
- }
- });
+ @Override
+ public void onClick(View view) {
+ onWakeLockRelease(0, "wl_2");
+ }
+ });
findViewById(R.id.app_b_wake_lock_release2).setOnClickListener(
new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- onWakeLockRelease(1, "wl_2");
- }
- });
+ @Override
+ public void onClick(View view) {
+ onWakeLockRelease(1, "wl_2");
+ }
+ });
findViewById(R.id.plug).setOnClickListener(new View.OnClickListener() {
@@ -191,8 +194,7 @@
byte[] config = new byte[inputStream.available()];
inputStream.read(config);
if (mStatsManager != null) {
- if (mStatsManager.addConfiguration(CONFIG_ID,
- config, getPackageName(), MainActivity.this.getClass().getName())) {
+ if (mStatsManager.addConfiguration(CONFIG_ID, config)) {
Toast.makeText(
MainActivity.this, "Config pushed", Toast.LENGTH_LONG).show();
} else {
@@ -205,6 +207,55 @@
}
}
});
+
+ PendingIntent pi = PendingIntent.getService(this, 0,
+ new Intent(this, ReceiverIntentService.class), PendingIntent.FLAG_UPDATE_CURRENT);
+ findViewById(R.id.set_receiver).setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ try {
+ if (!statsdRunning()) {
+ return;
+ }
+ if (mStatsManager != null) {
+ if (mStatsManager.setDataFetchOperation(CONFIG_ID, pi)) {
+ Toast.makeText(MainActivity.this,
+ "Receiver specified to pending intent", Toast.LENGTH_LONG)
+ .show();
+ } else {
+ Toast.makeText(MainActivity.this, "Statsd did not set receiver",
+ Toast.LENGTH_LONG)
+ .show();
+ }
+ }
+ } catch (Exception e) {
+ Toast.makeText(MainActivity.this, "failed to set receiver", Toast.LENGTH_LONG);
+ }
+ }
+ });
+ findViewById(R.id.remove_receiver).setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ try {
+ if (!statsdRunning()) {
+ return;
+ }
+ if (mStatsManager != null) {
+ if (mStatsManager.setDataFetchOperation(CONFIG_ID, null)) {
+ Toast.makeText(MainActivity.this, "Receiver remove", Toast.LENGTH_LONG)
+ .show();
+ } else {
+ Toast.makeText(MainActivity.this, "Statsd did not remove receiver",
+ Toast.LENGTH_LONG)
+ .show();
+ }
+ }
+ } catch (Exception e) {
+ Toast.makeText(
+ MainActivity.this, "failed to remove receiver", Toast.LENGTH_LONG);
+ }
+ }
+ });
mStatsManager = (StatsManager) getSystemService("stats");
}
@@ -257,8 +308,8 @@
Log.d(TAG, "invalid pkg id");
return;
}
- int[] uids = new int[] {mUids[id]};
- String[] tags = new String[] {"acquire"};
+ int[] uids = new int[]{mUids[id]};
+ String[] tags = new String[]{"acquire"};
StatsLog.write(StatsLog.WAKELOCK_STATE_CHANGED, uids, tags,
StatsLog.WAKELOCK_STATE_CHANGED__LEVEL__PARTIAL_WAKE_LOCK, name,
StatsLog.WAKELOCK_STATE_CHANGED__STATE__ACQUIRE);
@@ -273,8 +324,8 @@
Log.d(TAG, "invalid pkg id");
return;
}
- int[] uids = new int[] {mUids[id]};
- String[] tags = new String[] {"release"};
+ int[] uids = new int[]{mUids[id]};
+ String[] tags = new String[]{"release"};
StatsLog.write(StatsLog.WAKELOCK_STATE_CHANGED, uids, tags,
StatsLog.WAKELOCK_STATE_CHANGED__LEVEL__PARTIAL_WAKE_LOCK, name,
StatsLog.WAKELOCK_STATE_CHANGED__STATE__RELEASE);
@@ -283,4 +334,20 @@
.append(", ").append(name).append(", 0);");
Toast.makeText(this, sb.toString(), Toast.LENGTH_LONG).show();
}
+
+ public static class ReceiverIntentService extends IntentService {
+ public ReceiverIntentService() {
+ super("ReceiverIntentService");
+ }
+
+ /**
+ * The IntentService calls this method from the default worker thread with
+ * the intent that started the service. When this method returns, IntentService
+ * stops the service, as appropriate.
+ */
+ @Override
+ protected void onHandleIntent(Intent intent) {
+ Log.i(TAG, "Received notification that we should call getData");
+ }
+ }
}
diff --git a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/DisplayProtoUtils.java b/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/DisplayProtoUtils.java
index 75489ad..87b82c2 100644
--- a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/DisplayProtoUtils.java
+++ b/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/DisplayProtoUtils.java
@@ -36,6 +36,10 @@
int numMetrics = 0;
for (StatsLog.ConfigMetricsReport report : reports.getReportsList()) {
sb.append("StatsLogReport size: ").append(report.getMetricsCount()).append("\n");
+ sb.append("Last report time:").append(getDateStr(report.getLastReportNanos())).
+ append("\n");
+ sb.append("Current report time:").append(getDateStr(report.getCurrentReportNanos())).
+ append("\n");
for (StatsLog.StatsLogReport log : report.getMetricsList()) {
numMetrics++;
if (numMetrics > MAX_NUM_METRICS_TO_DISPLAY) {
@@ -45,8 +49,6 @@
}
sb.append("\n");
sb.append("metric id: ").append(log.getMetricId()).append("\n");
- sb.append("start time:").append(getDateStr(log.getStartReportNanos())).append("\n");
- sb.append("end time:").append(getDateStr(log.getEndReportNanos())).append("\n");
switch (log.getDataCase()) {
case DURATION_METRICS:
diff --git a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/LoadtestActivity.java b/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/LoadtestActivity.java
index 652f6b2..bed4d98 100644
--- a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/LoadtestActivity.java
+++ b/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/LoadtestActivity.java
@@ -47,10 +47,12 @@
import android.widget.Spinner;
import android.widget.TextView;
import android.widget.Toast;
+
import com.android.os.StatsLog.ConfigMetricsReport;
import com.android.os.StatsLog.ConfigMetricsReportList;
import com.android.os.StatsLog.StatsdStatsReport;
import com.android.internal.os.StatsdConfigProto.TimeUnit;
+
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
@@ -60,18 +62,18 @@
* Runs a load test for statsd.
* How it works:
* <ul>
- * <li> Sets up and pushes a custom config with metrics that exercise a large swath of code paths.
- * <li> Periodically logs certain atoms into logd.
- * <li> Impact on battery can be printed to logcat, or a bug report can be filed and analyzed
- * in battery Historian.
+ * <li> Sets up and pushes a custom config with metrics that exercise a large swath of code paths.
+ * <li> Periodically logs certain atoms into logd.
+ * <li> Impact on battery can be printed to logcat, or a bug report can be filed and analyzed
+ * in battery Historian.
* </ul>
* The load depends on how demanding the config is, as well as how frequently atoms are pushsed
* to logd. Those are all controlled by 4 adjustable parameters:
* <ul>
- * <li> The 'replication' parameter artificially multiplies the number of metrics in the config.
- * <li> The bucket size controls the time-bucketing the aggregate metrics.
- * <li> The period parameter controls how frequently atoms are pushed to logd.
- * <li> The 'burst' parameter controls how many atoms are pushed at the same time (per period).
+ * <li> The 'replication' parameter artificially multiplies the number of metrics in the config.
+ * <li> The bucket size controls the time-bucketing the aggregate metrics.
+ * <li> The period parameter controls how frequently atoms are pushed to logd.
+ * <li> The 'burst' parameter controls how many atoms are pushed at the same time (per period).
* </ul>
*/
public class LoadtestActivity extends Activity implements AdapterView.OnItemSelectedListener {
@@ -93,7 +95,7 @@
Intent activityIntent = new Intent(context, LoadtestActivity.class);
activityIntent.putExtra(TYPE, PUSH_ALARM);
context.startActivity(activityIntent);
- }
+ }
}
public final static class StopperAlarmReceiver extends BroadcastReceiver {
@@ -102,7 +104,7 @@
Intent activityIntent = new Intent(context, LoadtestActivity.class);
activityIntent.putExtra(TYPE, STOP);
context.startActivity(activityIntent);
- }
+ }
}
private static Map<String, TimeUnit> initializeTimeUnitMap() {
@@ -119,6 +121,7 @@
labels.put("1s", TimeUnit.CTS);
return labels;
}
+
private static List<String> initializeTimeUnitLabels() {
List<String> labels = new ArrayList();
labels.add("1s");
@@ -136,10 +139,14 @@
private AlarmManager mAlarmMgr;
- /** Used to periodically log atoms to logd. */
+ /**
+ * Used to periodically log atoms to logd.
+ */
private PendingIntent mPushPendingIntent;
- /** Used to end the loadtest. */
+ /**
+ * Used to end the loadtest.
+ */
private PendingIntent mStopPendingIntent;
private Button mStartStop;
@@ -156,13 +163,19 @@
private CheckBox mValueMetricCheckBox;
private CheckBox mGaugeMetricCheckBox;
- /** When the load test started. */
+ /**
+ * When the load test started.
+ */
private long mStartedTimeMillis;
- /** For measuring perf data. */
+ /**
+ * For measuring perf data.
+ */
private PerfData mPerfData;
- /** For communicating with statsd. */
+ /**
+ * For communicating with statsd.
+ */
private StatsManager mStatsManager;
private PowerManager mPowerManager;
@@ -199,34 +212,54 @@
*/
private boolean mIncludeGaugeMetric;
- /** The burst size. */
+ /**
+ * The burst size.
+ */
private int mBurst;
- /** The metrics replication. */
+ /**
+ * The metrics replication.
+ */
private int mReplication;
- /** The period, in seconds, at which batches of atoms are pushed. */
+ /**
+ * The period, in seconds, at which batches of atoms are pushed.
+ */
private long mPeriodSecs;
- /** The bucket size, in minutes, for aggregate metrics. */
+ /**
+ * The bucket size, in minutes, for aggregate metrics.
+ */
private TimeUnit mBucket;
- /** The duration, in minutes, of the loadtest. */
+ /**
+ * The duration, in minutes, of the loadtest.
+ */
private long mDurationMins;
- /** Whether the loadtest has started. */
+ /**
+ * Whether the loadtest has started.
+ */
private boolean mStarted = false;
- /** Orchestrates the logging of pushed events into logd. */
+ /**
+ * Orchestrates the logging of pushed events into logd.
+ */
private SequencePusher mPusher;
- /** Generates statsd configs. */
+ /**
+ * Generates statsd configs.
+ */
private ConfigFactory mFactory;
- /** For intra-minute periods. */
+ /**
+ * For intra-minute periods.
+ */
private final Handler mHandler = new Handler();
- /** Number of metrics in the current config. */
+ /**
+ * Number of metrics in the current config.
+ */
private int mNumMetrics;
@Override
@@ -250,7 +283,7 @@
@Override
public boolean onTouch(View v, MotionEvent event) {
InputMethodManager imm =
- (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
+ (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
if (getCurrentFocus() != null) {
imm.hideSoftInputFromWindow(getCurrentFocus().getWindowToken(), 0);
}
@@ -380,10 +413,10 @@
}
// Piggy-back on that alarm to show the elapsed time.
long elapsedTimeMins = (long) Math.floor(
- (SystemClock.elapsedRealtime() - mStartedTimeMillis) / 60 / 1000);
+ (SystemClock.elapsedRealtime() - mStartedTimeMillis) / 60 / 1000);
mReportText.setText("Loadtest in progress.\n"
- + "num metrics =" + mNumMetrics
- + "\nElapsed time = " + elapsedTimeMins + " min(s)");
+ + "num metrics =" + mNumMetrics
+ + "\nElapsed time = " + elapsedTimeMins + " min(s)");
}
private void onAlarm() {
@@ -402,12 +435,14 @@
mWakeLock = null;
}
- /** Schedules the next cycle of pushing atoms into logd. */
+ /**
+ * Schedules the next cycle of pushing atoms into logd.
+ */
private void scheduleNext() {
Intent intent = new Intent(this, PusherAlarmReceiver.class);
intent.putExtra(TYPE, PUSH_ALARM);
mPushPendingIntent = PendingIntent.getBroadcast(this, 0, intent, 0);
- long nextTime = SystemClock.elapsedRealtime() + mPeriodSecs * 1000;
+ long nextTime = SystemClock.elapsedRealtime() + mPeriodSecs * 1000;
mAlarmMgr.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP, nextTime, mPushPendingIntent);
}
@@ -433,7 +468,7 @@
Intent intent = new Intent(this, StopperAlarmReceiver.class);
intent.putExtra(TYPE, STOP);
mStopPendingIntent = PendingIntent.getBroadcast(this, 0, intent, 0);
- long nextTime = SystemClock.elapsedRealtime() + mDurationMins * 60 * 1000;
+ long nextTime = SystemClock.elapsedRealtime() + mDurationMins * 60 * 1000;
mAlarmMgr.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP, nextTime, mStopPendingIntent);
// Log atoms.
@@ -441,8 +476,8 @@
// Start tracking performance.
mPerfData = new PerfData(this, mPlacebo, mReplication, mBucket, mPeriodSecs, mBurst,
- mIncludeCountMetric, mIncludeDurationMetric, mIncludeEventMetric, mIncludeValueMetric,
- mIncludeGaugeMetric);
+ mIncludeCountMetric, mIncludeDurationMetric, mIncludeEventMetric, mIncludeValueMetric,
+ mIncludeGaugeMetric);
mPerfData.startRecording(this);
mReportText.setText("Loadtest in progress.\nnum metrics =" + mNumMetrics);
@@ -476,7 +511,7 @@
getData();
long elapsedTimeMins = (long) Math.floor(
- (SystemClock.elapsedRealtime() - mStartedTimeMillis) / 60 / 1000);
+ (SystemClock.elapsedRealtime() - mStartedTimeMillis) / 60 / 1000);
mReportText.setText("Loadtest ended. Elapsed time = " + elapsedTimeMins + " min(s)");
clearConfigs();
updateStarted(false);
@@ -485,7 +520,7 @@
private synchronized void updateStarted(boolean started) {
mStarted = started;
mStartStop.setBackgroundColor(started ?
- Color.parseColor("#FFFF0000") : Color.parseColor("#FF00FF00"));
+ Color.parseColor("#FFFF0000") : Color.parseColor("#FF00FF00"));
mStartStop.setText(started ? getString(R.string.stop) : getString(R.string.start));
updateControlsEnabled();
}
@@ -538,22 +573,21 @@
}
private boolean setConfig(ConfigFactory.ConfigMetadata configData) {
- if (mStatsManager != null) {
- if (mStatsManager.addConfiguration(ConfigFactory.CONFIG_ID,
- configData.bytes, getPackageName(), LoadtestActivity.this.getClass().getName())) {
+ if (mStatsManager != null) {
+ if (mStatsManager.addConfiguration(ConfigFactory.CONFIG_ID, configData.bytes)) {
mNumMetrics = configData.numMetrics;
Log.d(TAG, "Config pushed to statsd");
return true;
} else {
Log.d(TAG, "Failed to push config to statsd");
}
- }
- return false;
+ }
+ return false;
}
private synchronized void setReplication(int replication) {
if (mStarted) {
- return;
+ return;
}
mReplicationText.setText("" + replication);
}
@@ -614,13 +648,13 @@
mBucketSpinner = (Spinner) findViewById(R.id.bucket_spinner);
ArrayAdapter<String> dataAdapter = new ArrayAdapter<String>(
- this, R.layout.spinner_item, TIME_UNIT_LABELS);
+ this, R.layout.spinner_item, TIME_UNIT_LABELS);
mBucketSpinner.setAdapter(dataAdapter);
mBucketSpinner.setOnItemSelectedListener(this);
for (String label : TIME_UNIT_MAP.keySet()) {
- if (defaultValue.equals(TIME_UNIT_MAP.get(label).toString())) {
+ if (defaultValue.equals(TIME_UNIT_MAP.get(label).toString())) {
mBucketSpinner.setSelection(dataAdapter.getPosition(label));
}
}
diff --git a/config/hiddenapi-light-greylist.txt b/config/hiddenapi-light-greylist.txt
index 54e971f..279bc11 100644
--- a/config/hiddenapi-light-greylist.txt
+++ b/config/hiddenapi-light-greylist.txt
@@ -7,15 +7,18 @@
Landroid/app/Activity;->getActivityOptions()Landroid/app/ActivityOptions;
Landroid/app/Activity;->getActivityToken()Landroid/os/IBinder;
Landroid/app/Activity;->mActivityInfo:Landroid/content/pm/ActivityInfo;
+Landroid/app/ActivityManager;->addOnUidImportanceListener(Landroid/app/ActivityManager$OnUidImportanceListener;I)V
Landroid/app/ActivityManager;->clearApplicationUserData(Ljava/lang/String;Landroid/content/pm/IPackageDataObserver;)Z
Landroid/app/ActivityManager;->forceStopPackage(Ljava/lang/String;)V
Landroid/app/ActivityManager;->getCurrentUser()I
+Landroid/app/ActivityManager;->getPackageImportance(Ljava/lang/String;)I
Landroid/app/ActivityManager;->isLowRamDeviceStatic()Z
Landroid/app/ActivityManager;->isUserRunning(I)Z
Landroid/app/ActivityManager;->mContext:Landroid/content/Context;
Landroid/app/ActivityManagerNative;->getDefault()Landroid/app/IActivityManager;
Landroid/app/ActivityManager;->PROCESS_STATE_TOP:I
Landroid/app/ActivityManager$RecentTaskInfo;->firstActiveTime:J
+Landroid/app/ActivityManager;->removeOnUidImportanceListener(Landroid/app/ActivityManager$OnUidImportanceListener;)V
Landroid/app/ActivityManager$RunningAppProcessInfo;->flags:I
Landroid/app/ActivityManager$RunningAppProcessInfo;->processState:I
Landroid/app/Activity;->mApplication:Landroid/app/Application;
@@ -43,7 +46,6 @@
Landroid/app/ActivityThread$H;->CREATE_SERVICE:I
Landroid/app/ActivityThread$H;->EXIT_APPLICATION:I
Landroid/app/ActivityThread$H;->RECEIVER:I
-Landroid/app/ActivityThread$H;->RELAUNCH_ACTIVITY:I
Landroid/app/ActivityThread$H;->REMOVE_PROVIDER:I
Landroid/app/ActivityThread$H;->SERVICE_ARGS:I
Landroid/app/ActivityThread$H;->STOP_SERVICE:I
@@ -58,6 +60,9 @@
Landroid/app/ActivityThread;->mPackages:Landroid/util/ArrayMap;
Landroid/app/ActivityThread;->performStopActivity(Landroid/os/IBinder;ZLjava/lang/String;)V
Landroid/app/ActivityThread;->sPackageManager:Landroid/content/pm/IPackageManager;
+Landroid/app/admin/DevicePolicyManager;->getDeviceOwnerComponentOnAnyUser()Landroid/content/ComponentName;
+Landroid/app/admin/DevicePolicyManager;->getDeviceOwner()Ljava/lang/String;
+Landroid/app/admin/DevicePolicyManager;->getProfileOwner()Landroid/content/ComponentName;
Landroid/app/admin/DevicePolicyManager;->getTrustAgentConfiguration(Landroid/content/ComponentName;Landroid/content/ComponentName;I)Ljava/util/List;
Landroid/app/admin/DevicePolicyManager;->notifyPendingSystemUpdate(J)V
Landroid/app/admin/DevicePolicyManager;->notifyPendingSystemUpdate(JZ)V
@@ -69,6 +74,8 @@
Landroid/app/AlarmManager;->FLAG_IDLE_UNTIL:I
Landroid/app/AlarmManager;->FLAG_STANDALONE:I
Landroid/app/AlarmManager;->FLAG_WAKE_FROM_IDLE:I
+Landroid/app/AlarmManager;->set(IJJJLandroid/app/AlarmManager$OnAlarmListener;Landroid/os/Handler;Landroid/os/WorkSource;)V
+Landroid/app/AlarmManager;->set(IJJJLandroid/app/PendingIntent;Landroid/os/WorkSource;)V
Landroid/app/AlarmManager;->WINDOW_EXACT:J
Landroid/app/AlarmManager;->WINDOW_HEURISTIC:J
Landroid/app/AlertDialog$Builder;->P:Lcom/android/internal/app/AlertController$AlertParams;
@@ -92,9 +99,32 @@
Landroid/app/AppOpsManager;->OP_WRITE_SMS:I
Landroid/app/AppOpsManager;->setMode(IILjava/lang/String;I)V
Landroid/app/AppOpsManager;->strOpToOp(Ljava/lang/String;)I
+Landroid/app/backup/BackupDataInput;-><init>(Ljava/io/FileDescriptor;)V
Landroid/app/backup/BackupDataInputStream;->dataSize:I
Landroid/app/backup/BackupDataInputStream;->key:Ljava/lang/String;
+Landroid/app/backup/BackupDataOutput;-><init>(Ljava/io/FileDescriptor;)V
+Landroid/app/backup/BackupManager;->backupNow()V
+Landroid/app/backup/BackupManager;->beginRestoreSession()Landroid/app/backup/RestoreSession;
+Landroid/app/backup/BackupManager;->cancelBackups()V
+Landroid/app/backup/BackupManager;->getAvailableRestoreToken(Ljava/lang/String;)J
+Landroid/app/backup/BackupManager;->getCurrentTransport()Ljava/lang/String;
+Landroid/app/backup/BackupManager;->isBackupEnabled()Z
+Landroid/app/backup/BackupManager;->listAllTransports()[Ljava/lang/String;
+Landroid/app/backup/BackupManagerMonitor;-><init>()V
+Landroid/app/backup/BackupManager;->requestBackup([Ljava/lang/String;Landroid/app/backup/BackupObserver;Landroid/app/backup/BackupManagerMonitor;I)I
+Landroid/app/backup/BackupManager;->selectBackupTransport(Landroid/content/ComponentName;Landroid/app/backup/SelectBackupTransportCallback;)V
+Landroid/app/backup/BackupManager;->selectBackupTransport(Ljava/lang/String;)Ljava/lang/String;
+Landroid/app/backup/BackupManager;->setAutoRestore(Z)V
+Landroid/app/backup/BackupManager;->setBackupEnabled(Z)V
+Landroid/app/backup/BackupObserver;-><init>()V
+Landroid/app/backup/BackupTransport;-><init>()V
Landroid/app/backup/FullBackup;->backupToTar(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Landroid/app/backup/FullBackupDataOutput;)I
+Landroid/app/backup/RestoreDescription;-><init>(Ljava/lang/String;I)V
+Landroid/app/backup/RestoreSession;->endRestoreSession()V
+Landroid/app/backup/RestoreSession;->getAvailableRestoreSets(Landroid/app/backup/RestoreObserver;)I
+Landroid/app/backup/RestoreSession;->restoreAll(JLandroid/app/backup/RestoreObserver;)I
+Landroid/app/backup/RestoreSet;-><init>(Ljava/lang/String;Ljava/lang/String;J)V
+Landroid/app/backup/SelectBackupTransportCallback;-><init>()V
Landroid/app/ContextImpl;->createActivityContext(Landroid/app/ActivityThread;Landroid/app/LoadedApk;Landroid/content/pm/ActivityInfo;Landroid/os/IBinder;ILandroid/content/res/Configuration;)Landroid/app/ContextImpl;
Landroid/app/ContextImpl;->getActivityToken()Landroid/os/IBinder;
Landroid/app/ContextImpl;->mMainThread:Landroid/app/ActivityThread;
@@ -113,6 +143,8 @@
Landroid/app/IActivityManager;->getLaunchedFromPackage(Landroid/os/IBinder;)Ljava/lang/String;
Landroid/app/IActivityManager;->resumeAppSwitches()V
Landroid/app/IApplicationThread;->scheduleTrimMemory(I)V
+Landroid/app/InstantAppResolverService;-><init>()V
+Landroid/app/InstantAppResolverService$InstantAppResolutionCallback;->onInstantAppResolveInfo(Ljava/util/List;)V
Landroid/app/IntentService;->mServiceHandler:Landroid/app/IntentService$ServiceHandler;
Landroid/app/IWallpaperManager;->getWallpaper(Ljava/lang/String;Landroid/app/IWallpaperManagerCallback;ILandroid/os/Bundle;I)Landroid/os/ParcelFileDescriptor;
Landroid/app/LoadedApk;->mApplication:Landroid/app/Application;
@@ -139,6 +171,9 @@
Landroid/app/StatusBarManager;->expandSettingsPanel(Ljava/lang/String;)V
Landroid/app/StatusBarManager;->expandSettingsPanel()V
Landroid/app/TimePickerDialog;->mTimePicker:Landroid/widget/TimePicker;
+Landroid/app/usage/UsageStatsManager;->getAppStandbyBuckets()Ljava/util/Map;
+Landroid/app/usage/UsageStatsManager;->setAppStandbyBuckets(Ljava/util/Map;)V
+Landroid/app/usage/UsageStatsManager;->whitelistAppTemporarily(Ljava/lang/String;JLandroid/os/UserHandle;)V
Landroid/app/WallpaperColors;->getColorHints()I
Landroid/app/WallpaperManager;->getBitmap()Landroid/graphics/Bitmap;
Landroid/app/WallpaperManager;->getBitmap(Z)Landroid/graphics/Bitmap;
@@ -150,9 +185,13 @@
Landroid/appwidget/AppWidgetManager;->bindAppWidgetId(ILandroid/content/ComponentName;Landroid/os/Bundle;)V
Landroid/appwidget/AppWidgetManager;->bindAppWidgetId(ILandroid/content/ComponentName;)V
Landroid/bluetooth/BluetoothA2dp;->connect(Landroid/bluetooth/BluetoothDevice;)Z
+Landroid/bluetooth/BluetoothAdapter;->disableBLE()Z
Landroid/bluetooth/BluetoothAdapter;->disable(Z)Z
+Landroid/bluetooth/BluetoothAdapter;->enableBLE()Z
Landroid/bluetooth/BluetoothAdapter;->enableNoAutoConnect()Z
Landroid/bluetooth/BluetoothAdapter;->getDiscoverableTimeout()I
+Landroid/bluetooth/BluetoothAdapter;->isBleScanAlwaysAvailable()Z
+Landroid/bluetooth/BluetoothAdapter;->isLeEnabled()Z
Landroid/bluetooth/BluetoothAdapter;->setScanMode(II)Z
Landroid/bluetooth/BluetoothAdapter;->setScanMode(I)Z
Landroid/bluetooth/BluetoothDevice;->cancelBondProcess()Z
@@ -171,6 +210,8 @@
Landroid/bluetooth/BluetoothHeadset;->setPriority(Landroid/bluetooth/BluetoothDevice;I)Z
Landroid/bluetooth/BluetoothUuid;->RESERVED_UUIDS:[Landroid/os/ParcelUuid;
Landroid/bluetooth/IBluetooth$Stub;->asInterface(Landroid/os/IBinder;)Landroid/bluetooth/IBluetooth;
+Landroid/bluetooth/le/BluetoothLeScanner;->startScanFromSource(Ljava/util/List;Landroid/bluetooth/le/ScanSettings;Landroid/os/WorkSource;Landroid/bluetooth/le/ScanCallback;)V
+Landroid/bluetooth/le/ScanSettings$Builder;->setScanResultType(I)Landroid/bluetooth/le/ScanSettings$Builder;
Landroid/content/AsyncTaskLoader;->mExecutor:Ljava/util/concurrent/Executor;
Landroid/content/ContentProviderOperation;->mSelection:Ljava/lang/String;
Landroid/content/ContentProviderOperation;->mType:I
@@ -178,13 +219,17 @@
Landroid/content/ContentResolver;->getSyncStatus(Landroid/accounts/Account;Ljava/lang/String;)Landroid/content/SyncStatusInfo;
Landroid/content/ContentResolver;->mContext:Landroid/content/Context;
Landroid/content/ContentValues;->mValues:Ljava/util/HashMap;
+Landroid/content/Context;->bindServiceAsUser(Landroid/content/Intent;Landroid/content/ServiceConnection;ILandroid/os/UserHandle;)Z
+Landroid/content/Context;->createCredentialProtectedStorageContext()Landroid/content/Context;
Landroid/content/Context;->createPackageContextAsUser(Ljava/lang/String;ILandroid/os/UserHandle;)Landroid/content/Context;
Landroid/content/Context;->getSharedPrefsFile(Ljava/lang/String;)Ljava/io/File;
Landroid/content/Context;->getThemeResId()I
+Landroid/content/Context;->isCredentialProtectedStorage()Z
Landroid/content/Context;->sendBroadcastAsUser(Landroid/content/Intent;Landroid/os/UserHandle;Ljava/lang/String;I)V
Landroid/content/Context;->sendBroadcastAsUser(Landroid/content/Intent;Landroid/os/UserHandle;Ljava/lang/String;Landroid/os/Bundle;)V
Landroid/content/Context;->sendOrderedBroadcastAsUser(Landroid/content/Intent;Landroid/os/UserHandle;Ljava/lang/String;ILandroid/content/BroadcastReceiver;Landroid/os/Handler;ILjava/lang/String;Landroid/os/Bundle;)V
Landroid/content/Context;->sendOrderedBroadcastAsUser(Landroid/content/Intent;Landroid/os/UserHandle;Ljava/lang/String;ILandroid/os/Bundle;Landroid/content/BroadcastReceiver;Landroid/os/Handler;ILjava/lang/String;Landroid/os/Bundle;)V
+Landroid/content/ContextWrapper;->createCredentialProtectedStorageContext()Landroid/content/Context;
Landroid/content/ContextWrapper;->mBase:Landroid/content/Context;
Landroid/content/CursorLoader;->mCancellationSignal:Landroid/os/CancellationSignal;
Landroid/content/CursorLoader;->mObserver:Landroid/content/Loader$ForceLoadContentObserver;
@@ -192,6 +237,7 @@
Landroid/content/IContentService;->getMasterSyncAutomatically()Z
Landroid/content/IContentService;->setMasterSyncAutomatically(Z)V
Landroid/content/Intent;->ACTION_ALARM_CHANGED:Ljava/lang/String;
+Landroid/content/IntentFilter;->setOrder(I)V
Landroid/content/Intent;->putExtra(Ljava/lang/String;Landroid/os/IBinder;)Landroid/content/Intent;
Landroid/content/pm/ApplicationInfo;->installLocation:I
Landroid/content/pm/ApplicationInfo;->isForwardLocked()Z
@@ -200,12 +246,19 @@
Landroid/content/pm/ApplicationInfo;->primaryCpuAbi:Ljava/lang/String;
Landroid/content/pm/ApplicationInfo;->privateFlags:I
Landroid/content/pm/ApplicationInfo;->targetSandboxVersion:I
+Landroid/content/pm/InstantAppIntentFilter;-><init>(Ljava/lang/String;Ljava/util/List;)V
+Landroid/content/pm/InstantAppResolveInfo;->getPackageName()Ljava/lang/String;
+Landroid/content/pm/InstantAppResolveInfo;-><init>(Landroid/content/pm/InstantAppResolveInfo$InstantAppDigest;Ljava/lang/String;Ljava/util/List;I)V
+Landroid/content/pm/InstantAppResolveInfo$InstantAppDigest;->getDigestPrefix()[I
+Landroid/content/pm/InstantAppResolveInfo$InstantAppDigest;-><init>(Ljava/lang/String;)V
Landroid/content/pm/IPackageManager;->getLastChosenActivity(Landroid/content/Intent;Ljava/lang/String;I)Landroid/content/pm/ResolveInfo;
Landroid/content/pm/IPackageManager;->setLastChosenActivity(Landroid/content/Intent;Ljava/lang/String;ILandroid/content/IntentFilter;ILandroid/content/ComponentName;)V
+Landroid/content/pm/IPackageStatsObserver$Stub;-><init>()V
Landroid/content/pm/LauncherApps;->mPm:Landroid/content/pm/PackageManager;
Landroid/content/pm/LauncherApps;->startShortcut(Ljava/lang/String;Ljava/lang/String;Landroid/graphics/Rect;Landroid/os/Bundle;I)V
Landroid/content/pm/PackageInstaller$SessionParams;->setGrantedRuntimePermissions([Ljava/lang/String;)V
Landroid/content/pm/PackageInstaller$SessionParams;->setInstallAsInstantApp(Z)V
+Landroid/content/pm/PackageManager;->addOnPermissionsChangeListener(Landroid/content/pm/PackageManager$OnPermissionsChangedListener;)V
Landroid/content/pm/PackageManager;->freeStorageAndNotify(JLandroid/content/pm/IPackageDataObserver;)V
Landroid/content/pm/PackageManager;->freeStorageAndNotify(Ljava/lang/String;JLandroid/content/pm/IPackageDataObserver;)V
Landroid/content/pm/PackageManager;->freeStorage(JLandroid/content/IntentSender;)V
@@ -217,6 +270,8 @@
Landroid/content/pm/PackageManager;->getResourcesForApplicationAsUser(Ljava/lang/String;I)Landroid/content/res/Resources;
Landroid/content/pm/PackageManager;->movePackage(Ljava/lang/String;Landroid/os/storage/VolumeInfo;)I
Landroid/content/pm/PackageManager;->queryBroadcastReceivers(Landroid/content/Intent;II)Ljava/util/List;
+Landroid/content/pm/PackageManager;->removeOnPermissionsChangeListener(Landroid/content/pm/PackageManager$OnPermissionsChangedListener;)V
+Landroid/content/pm/PackageManager;->verifyIntentFilter(IILjava/util/List;)V
Landroid/content/pm/PackageParser;->collectCertificates(Landroid/content/pm/PackageParser$Package;Ljava/io/File;Z)V
Landroid/content/pm/PackageParser;->collectCertificates(Landroid/content/pm/PackageParser$Package;Z)V
Landroid/content/pm/PackageParser;->generatePackageInfo(Landroid/content/pm/PackageParser$Package;[IIJJLjava/util/Set;Landroid/content/pm/PackageUserState;I)Landroid/content/pm/PackageInfo;
@@ -227,13 +282,28 @@
Landroid/content/pm/UserInfo;->id:I
Landroid/content/pm/UserInfo;->isPrimary()Z
Landroid/content/res/AssetManager;->addAssetPath(Ljava/lang/String;)I
+Landroid/content/res/AssetManager;->addAssetPaths([Ljava/lang/String;)[I
+Landroid/content/res/AssetManager;->applyStyle(JIIJ[IIJJ)V
+Landroid/content/res/AssetManager;->getArraySize(I)I
Landroid/content/res/AssetManager;->getAssignedPackageIdentifiers()Landroid/util/SparseArray;
+Landroid/content/res/AssetManager;->getCookieName(I)Ljava/lang/String;
Landroid/content/res/AssetManager;->getResourceBagText(II)Ljava/lang/CharSequence;
+Landroid/content/res/AssetManager;->loadResourceBagValue(IILandroid/util/TypedValue;Z)I
+Landroid/content/res/AssetManager;->loadResourceValue(ISLandroid/util/TypedValue;Z)I
+Landroid/content/res/AssetManager;->loadThemeAttributeValue(JILandroid/util/TypedValue;Z)I
Landroid/content/res/AssetManager;->mObject:J
+Landroid/content/res/AssetManager;->openNonAssetFdNative(ILjava/lang/String;[J)Landroid/os/ParcelFileDescriptor;
Landroid/content/res/AssetManager;->openNonAsset(ILjava/lang/String;I)Ljava/io/InputStream;
Landroid/content/res/AssetManager;->openNonAsset(ILjava/lang/String;)Ljava/io/InputStream;
Landroid/content/res/AssetManager;->openNonAsset(Ljava/lang/String;I)Ljava/io/InputStream;
Landroid/content/res/AssetManager;->openNonAsset(Ljava/lang/String;)Ljava/io/InputStream;
+Landroid/content/res/AssetManager;->openNonAssetNative(ILjava/lang/String;I)J
+Landroid/content/res/AssetManager;->openXmlAssetNative(ILjava/lang/String;)J
+Landroid/content/res/AssetManager;->resolveAttrs(JII[I[I[I[I)Z
+Landroid/content/res/AssetManager;->retrieveArray(I[I)I
+Landroid/content/res/AssetManager;->retrieveAttributes(J[I[I[I)Z
+Landroid/content/res/AssetManager;->STYLE_NUM_ENTRIES:I
+Landroid/content/res/AssetManager;->STYLE_RESOURCE_ID:I
Landroid/content/res/DrawableCache;->getInstance(JLandroid/content/res/Resources;Landroid/content/res/Resources$Theme;)Landroid/graphics/drawable/Drawable;
Landroid/content/res/Resources;->getCompatibilityInfo()Landroid/content/res/CompatibilityInfo;
Landroid/content/res/ResourcesImpl;->mAccessLock:Ljava/lang/Object;
@@ -263,6 +333,7 @@
Landroid/content/res/TypedArray;->mTheme:Landroid/content/res/Resources$Theme;
Landroid/content/res/TypedArray;->mValue:Landroid/util/TypedValue;
Landroid/content/res/TypedArray;->mXml:Landroid/content/res/XmlBlock$Parser;
+Landroid/content/res/XmlBlock;->close()V
Landroid/content/res/XmlBlock;->newParser()Landroid/content/res/XmlResourceParser;
Landroid/content/res/XmlBlock$Parser;->mParseState:J
Landroid/content/SyncStatusInfo;->lastSuccessTime:J
@@ -306,18 +377,64 @@
Landroid/graphics/Typeface;->sSystemFontMap:Ljava/util/Map;
Landroid/hardware/Camera;->addCallbackBuffer([BI)V
Landroid/hardware/Camera;->openLegacy(II)Landroid/hardware/Camera;
+Landroid/hardware/display/DisplayManager;->getStableDisplaySize()Landroid/graphics/Point;
Landroid/hardware/display/WifiDisplayStatus;->mActiveDisplay:Landroid/hardware/display/WifiDisplay;
Landroid/hardware/display/WifiDisplayStatus;->mDisplays:[Landroid/hardware/display/WifiDisplay;
Landroid/hardware/input/IInputManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/hardware/input/IInputManager;
Landroid/hardware/input/InputManager;->getInstance()Landroid/hardware/input/InputManager;
Landroid/hardware/input/InputManager;->mIm:Landroid/hardware/input/IInputManager;
+Landroid/hardware/location/ContextHubInfo;->getId()I
+Landroid/hardware/location/ContextHubInfo;->getMaxPacketLengthBytes()I
+Landroid/hardware/location/ContextHubManager$Callback;-><init>()V
+Landroid/hardware/location/ContextHubManager;->findNanoAppOnHub(ILandroid/hardware/location/NanoAppFilter;)[I
+Landroid/hardware/location/ContextHubManager;->getContextHubHandles()[I
+Landroid/hardware/location/ContextHubManager;->getContextHubInfo(I)Landroid/hardware/location/ContextHubInfo;
+Landroid/hardware/location/ContextHubManager;->getNanoAppInstanceInfo(I)Landroid/hardware/location/NanoAppInstanceInfo;
+Landroid/hardware/location/ContextHubManager;->loadNanoApp(ILandroid/hardware/location/NanoApp;)I
+Landroid/hardware/location/ContextHubManager;->registerCallback(Landroid/hardware/location/ContextHubManager$Callback;)I
+Landroid/hardware/location/ContextHubManager;->sendMessage(IILandroid/hardware/location/ContextHubMessage;)I
+Landroid/hardware/location/ContextHubManager;->unloadNanoApp(I)I
+Landroid/hardware/location/ContextHubMessage;->getData()[B
+Landroid/hardware/location/ContextHubMessage;->getMsgType()I
+Landroid/hardware/location/ContextHubMessage;->getVersion()I
+Landroid/hardware/location/ContextHubMessage;-><init>(II[B)V
+Landroid/hardware/location/GeofenceHardware;->addGeofence(IILandroid/hardware/location/GeofenceHardwareRequest;Landroid/hardware/location/GeofenceHardwareCallback;)Z
+Landroid/hardware/location/GeofenceHardwareCallback;-><init>()V
+Landroid/hardware/location/GeofenceHardwareCallback;->onGeofenceAdd(II)V
+Landroid/hardware/location/GeofenceHardwareCallback;->onGeofenceRemove(II)V
+Landroid/hardware/location/GeofenceHardwareCallback;->onGeofenceTransition(IILandroid/location/Location;JI)V
+Landroid/hardware/location/GeofenceHardware;->getMonitoringTypes()[I
+Landroid/hardware/location/GeofenceHardware;->getStatusOfMonitoringType(I)I
+Landroid/hardware/location/GeofenceHardwareMonitorCallback;-><init>()V
+Landroid/hardware/location/GeofenceHardwareMonitorCallback;->onMonitoringSystemChange(IZLandroid/location/Location;)V
+Landroid/hardware/location/GeofenceHardware;->registerForMonitorStateChangeCallback(ILandroid/hardware/location/GeofenceHardwareMonitorCallback;)Z
+Landroid/hardware/location/GeofenceHardware;->removeGeofence(II)Z
+Landroid/hardware/location/GeofenceHardwareRequest;->createCircularGeofence(DDD)Landroid/hardware/location/GeofenceHardwareRequest;
+Landroid/hardware/location/GeofenceHardwareRequest;->setLastTransition(I)V
+Landroid/hardware/location/GeofenceHardwareRequest;->setMonitorTransitions(I)V
+Landroid/hardware/location/GeofenceHardwareRequest;->setNotificationResponsiveness(I)V
+Landroid/hardware/location/GeofenceHardwareRequest;->setUnknownTimer(I)V
+Landroid/hardware/location/NanoAppFilter;-><init>(JIIJ)V
+Landroid/hardware/location/NanoApp;-><init>(J[B)V
+Landroid/hardware/location/NanoAppInstanceInfo;->getAppId()J
+Landroid/hardware/location/NanoAppInstanceInfo;->getAppVersion()I
+Landroid/hardware/location/NanoAppInstanceInfo;->getHandle()I
+Landroid/hardware/location/NanoAppInstanceInfo;->getName()Ljava/lang/String;
Landroid/hardware/usb/UsbManager;->setCurrentFunction(Ljava/lang/String;Z)V
+Landroid/location/GeocoderParams;->getClientPackage()Ljava/lang/String;
+Landroid/location/GeocoderParams;->getLocale()Ljava/util/Locale;
+Landroid/location/LocationManager;->requestLocationUpdates(Landroid/location/LocationRequest;Landroid/location/LocationListener;Landroid/os/Looper;)V
Landroid/location/LocationRequest;->createFromDeprecatedProvider(Ljava/lang/String;JFZ)Landroid/location/LocationRequest;
+Landroid/location/LocationRequest;->setHideFromAppOps(Z)V
Landroid/location/LocationRequest;->setWorkSource(Landroid/os/WorkSource;)V
Landroid/location/Location;->setIsFromMockProvider(Z)V
Landroid/media/AudioAttributes$Builder;->setInternalCapturePreset(I)Landroid/media/AudioAttributes$Builder;
+Landroid/media/AudioFocusInfo;->getClientId()Ljava/lang/String;
+Landroid/media/AudioFocusInfo;->getClientUid()I
+Landroid/media/AudioFocusInfo;->getLossReceived()I
Landroid/media/AudioManager;->abandonAudioFocus(Landroid/media/AudioManager$OnAudioFocusChangeListener;Landroid/media/AudioAttributes;)I
Landroid/media/AudioManager;->mAudioFocusIdListenerMap:Ljava/util/concurrent/ConcurrentHashMap;
+Landroid/media/AudioManager;->registerAudioPolicy(Landroid/media/audiopolicy/AudioPolicy;)I
Landroid/media/AudioManager;->requestAudioFocus(Landroid/media/AudioFocusRequest;Landroid/media/audiopolicy/AudioPolicy;)I
Landroid/media/AudioManager;->requestAudioFocus(Landroid/media/AudioManager$OnAudioFocusChangeListener;Landroid/media/AudioAttributes;II)I
Landroid/media/AudioManager;->requestAudioFocus(Landroid/media/AudioManager$OnAudioFocusChangeListener;Landroid/media/AudioAttributes;IILandroid/media/audiopolicy/AudioPolicy;)I
@@ -325,6 +442,21 @@
Landroid/media/AudioManager;->STREAM_BLUETOOTH_SCO:I
Landroid/media/AudioManager;->STREAM_SYSTEM_ENFORCED:I
Landroid/media/AudioManager;->STREAM_TTS:I
+Landroid/media/AudioManager;->unregisterAudioPolicyAsync(Landroid/media/audiopolicy/AudioPolicy;)V
+Landroid/media/audiopolicy/AudioMix$Builder;->build()Landroid/media/audiopolicy/AudioMix;
+Landroid/media/audiopolicy/AudioMix$Builder;-><init>(Landroid/media/audiopolicy/AudioMixingRule;)V
+Landroid/media/audiopolicy/AudioMix$Builder;->setFormat(Landroid/media/AudioFormat;)Landroid/media/audiopolicy/AudioMix$Builder;
+Landroid/media/audiopolicy/AudioMix$Builder;->setRouteFlags(I)Landroid/media/audiopolicy/AudioMix$Builder;
+Landroid/media/audiopolicy/AudioMixingRule$Builder;->addRule(Landroid/media/AudioAttributes;I)Landroid/media/audiopolicy/AudioMixingRule$Builder;
+Landroid/media/audiopolicy/AudioMixingRule$Builder;->build()Landroid/media/audiopolicy/AudioMixingRule;
+Landroid/media/audiopolicy/AudioMixingRule$Builder;-><init>()V
+Landroid/media/audiopolicy/AudioPolicy$AudioPolicyFocusListener;-><init>()V
+Landroid/media/audiopolicy/AudioPolicy$Builder;->addMix(Landroid/media/audiopolicy/AudioMix;)Landroid/media/audiopolicy/AudioPolicy$Builder;
+Landroid/media/audiopolicy/AudioPolicy$Builder;->build()Landroid/media/audiopolicy/AudioPolicy;
+Landroid/media/audiopolicy/AudioPolicy$Builder;-><init>(Landroid/content/Context;)V
+Landroid/media/audiopolicy/AudioPolicy$Builder;->setAudioPolicyFocusListener(Landroid/media/audiopolicy/AudioPolicy$AudioPolicyFocusListener;)V
+Landroid/media/audiopolicy/AudioPolicy$Builder;->setLooper(Landroid/os/Looper;)Landroid/media/audiopolicy/AudioPolicy$Builder;
+Landroid/media/audiopolicy/AudioPolicy;->createAudioRecordSink(Landroid/media/audiopolicy/AudioMix;)Landroid/media/AudioRecord;
Landroid/media/AudioSystem;->setDeviceConnectionState(IILjava/lang/String;Ljava/lang/String;)I
Landroid/media/AudioTrack;->getLatency()I
Landroid/media/IAudioService$Stub;->asInterface(Landroid/os/IBinder;)Landroid/media/IAudioService;
@@ -354,6 +486,19 @@
Landroid/media/Ringtone;->setVolume(F)V
Landroid/media/SubtitleController;->mHandler:Landroid/os/Handler;
Landroid/media/ThumbnailUtils;->createImageThumbnail(Ljava/lang/String;I)Landroid/graphics/Bitmap;
+Landroid/metrics/LogMaker;->getCategory()I
+Landroid/metrics/LogMaker;->getCounterBucket()J
+Landroid/metrics/LogMaker;->getCounterName()Ljava/lang/String;
+Landroid/metrics/LogMaker;->getCounterValue()I
+Landroid/metrics/LogMaker;->getTimestamp()J
+Landroid/metrics/LogMaker;->isLongCounterBucket()Z
+Landroid/metrics/LogMaker;->serialize()[Ljava/lang/Object;
+Landroid/metrics/MetricsReader;->checkpoint()V
+Landroid/metrics/MetricsReader;->hasNext()Z
+Landroid/metrics/MetricsReader;-><init>()V
+Landroid/metrics/MetricsReader;->next()Landroid/metrics/LogMaker;
+Landroid/metrics/MetricsReader;->read(J)V
+Landroid/metrics/MetricsReader;->reset()V
Landroid/net/ConnectivityManager;->ACTION_TETHER_STATE_CHANGED:Ljava/lang/String;
Landroid/net/ConnectivityManager;->EXTRA_ACTIVE_TETHER:Ljava/lang/String;
Landroid/net/ConnectivityManager;->getActiveLinkProperties()Landroid/net/LinkProperties;
@@ -366,11 +511,20 @@
Landroid/net/ConnectivityManager;->isNetworkTypeMobile(I)Z
Landroid/net/ConnectivityManager;->isTetheringSupported()Z
Landroid/net/ConnectivityManager;->mService:Landroid/net/IConnectivityManager;
+Landroid/net/ConnectivityManager$OnStartTetheringCallback;-><init>()V
Landroid/net/ConnectivityManager;->requestRouteToHostAddress(ILjava/net/InetAddress;)Z
Landroid/net/ConnectivityManager;->requestRouteToHost(II)Z
+Landroid/net/ConnectivityManager;->startTethering(IZLandroid/net/ConnectivityManager$OnStartTetheringCallback;Landroid/os/Handler;)V
+Landroid/net/ConnectivityManager;->startTethering(IZLandroid/net/ConnectivityManager$OnStartTetheringCallback;)V
+Landroid/net/ConnectivityManager;->stopTethering(I)V
Landroid/net/INetworkStatsService$Stub$Proxy;->getMobileIfaces()[Ljava/lang/String;
Landroid/net/LinkProperties;->setHttpProxy(Landroid/net/ProxyInfo;)V
+Landroid/net/NetworkKey;-><init>(Landroid/net/WifiKey;)V
Landroid/net/NetworkPolicyManager;->mService:Landroid/net/INetworkPolicyManager;
+Landroid/net/NetworkRecommendationProvider;-><init>(Landroid/content/Context;Ljava/util/concurrent/Executor;)V
+Landroid/net/NetworkScoreManager;->clearScores()Z
+Landroid/net/NetworkScoreManager;->getActiveScorerPackage()Ljava/lang/String;
+Landroid/net/NetworkScoreManager;->updateScores([Landroid/net/ScoredNetwork;)Z
Landroid/net/NetworkStats;->capacity:I
Landroid/net/NetworkStats;->defaultNetwork:[I
Landroid/net/NetworkStats;->iface:[Ljava/lang/String;
@@ -385,37 +539,90 @@
Landroid/net/NetworkStats;->txBytes:[J
Landroid/net/NetworkStats;->txPackets:[J
Landroid/net/NetworkStats;->uid:[I
+Landroid/net/RssiCurve;-><init>(II[BI)V
+Landroid/net/RssiCurve;-><init>(II[B)V
+Landroid/net/RssiCurve;->lookupScore(IZ)B
+Landroid/net/ScoredNetwork;-><init>(Landroid/net/NetworkKey;Landroid/net/RssiCurve;)V
+Landroid/net/ScoredNetwork;-><init>(Landroid/net/NetworkKey;Landroid/net/RssiCurve;ZLandroid/os/Bundle;)V
+Landroid/net/ScoredNetwork;-><init>(Landroid/net/NetworkKey;Landroid/net/RssiCurve;Z)V
+Landroid/net/SSLCertificateSocketFactory;->castToOpenSSLSocket(Ljava/net/Socket;)Lcom/android/org/conscrypt/OpenSSLSocketImpl;
+Landroid/net/SSLCertificateSocketFactory;->getAlpnSelectedProtocol(Ljava/net/Socket;)[B
+Landroid/net/SSLCertificateSocketFactory;->getDelegate()Ljavax/net/ssl/SSLSocketFactory;
Landroid/net/SSLCertificateSocketFactory;->getHttpSocketFactory(ILandroid/net/SSLSessionCache;)Lorg/apache/http/conn/ssl/SSLSocketFactory;
+Landroid/net/SSLCertificateSocketFactory;-><init>(ILandroid/net/SSLSessionCache;Z)V
+Landroid/net/SSLCertificateSocketFactory;->INSECURE_TRUST_MANAGER:[Ljavax/net/ssl/TrustManager;
+Landroid/net/SSLCertificateSocketFactory;->isSslCheckRelaxed()Z
+Landroid/net/SSLCertificateSocketFactory;->makeSocketFactory([Ljavax/net/ssl/KeyManager;[Ljavax/net/ssl/TrustManager;)Ljavax/net/ssl/SSLSocketFactory;
+Landroid/net/SSLCertificateSocketFactory;->mAlpnProtocols:[B
+Landroid/net/SSLCertificateSocketFactory;->mChannelIdPrivateKey:Ljava/security/PrivateKey;
+Landroid/net/SSLCertificateSocketFactory;->mHandshakeTimeoutMillis:I
+Landroid/net/SSLCertificateSocketFactory;->mInsecureFactory:Ljavax/net/ssl/SSLSocketFactory;
+Landroid/net/SSLCertificateSocketFactory;->mKeyManagers:[Ljavax/net/ssl/KeyManager;
+Landroid/net/SSLCertificateSocketFactory;->mNpnProtocols:[B
+Landroid/net/SSLCertificateSocketFactory;->mSecureFactory:Ljavax/net/ssl/SSLSocketFactory;
+Landroid/net/SSLCertificateSocketFactory;->mSecure:Z
+Landroid/net/SSLCertificateSocketFactory;->mSessionCache:Lcom/android/org/conscrypt/SSLClientSessionCache;
+Landroid/net/SSLCertificateSocketFactory;->mTrustManagers:[Ljavax/net/ssl/TrustManager;
+Landroid/net/SSLCertificateSocketFactory;->setAlpnProtocols([[B)V
+Landroid/net/SSLCertificateSocketFactory;->setChannelIdPrivateKey(Ljava/security/PrivateKey;)V
+Landroid/net/SSLCertificateSocketFactory;->setSoWriteTimeout(Ljava/net/Socket;I)V
+Landroid/net/SSLCertificateSocketFactory;->TAG:Ljava/lang/String;
+Landroid/net/SSLCertificateSocketFactory;->toLengthPrefixedList([[[B)[B
+Landroid/net/SSLCertificateSocketFactory;->verifyHostname(Ljava/net/Socket;Ljava/lang/String;)V
Landroid/net/TrafficStats;->getStatsService()Landroid/net/INetworkStatsService;
+Landroid/net/TrafficStats;->setThreadStatsTagBackup()V
+Landroid/net/TrafficStats;->setThreadStatsTagRestore()V
+Landroid/net/TrafficStats;->setThreadStatsUid(I)V
+Landroid/net/WifiKey;-><init>(Ljava/lang/String;Ljava/lang/String;)V
Landroid/net/wifi/p2p/WifiP2pGroup;->getNetworkId()I
Landroid/net/wifi/p2p/WifiP2pGroupList;->getGroupList()Ljava/util/Collection;
Landroid/net/wifi/p2p/WifiP2pManager;->deletePersistentGroup(Landroid/net/wifi/p2p/WifiP2pManager$Channel;ILandroid/net/wifi/p2p/WifiP2pManager$ActionListener;)V
Landroid/net/wifi/p2p/WifiP2pManager;->requestPersistentGroupInfo(Landroid/net/wifi/p2p/WifiP2pManager$Channel;Landroid/net/wifi/p2p/WifiP2pManager$PersistentGroupInfoListener;)V
Landroid/net/wifi/p2p/WifiP2pManager;->setDeviceName(Landroid/net/wifi/p2p/WifiP2pManager$Channel;Ljava/lang/String;Landroid/net/wifi/p2p/WifiP2pManager$ActionListener;)V
+Landroid/net/wifi/RttManager;->getRttCapabilities()Landroid/net/wifi/RttManager$RttCapabilities;
+Landroid/net/wifi/RttManager$RttParams;-><init>()V
+Landroid/net/wifi/RttManager;->startRanging([Landroid/net/wifi/RttManager$RttParams;Landroid/net/wifi/RttManager$RttListener;)V
Landroid/net/wifi/WifiConfiguration;->apBand:I
Landroid/net/wifi/WifiConfiguration;->apChannel:I
Landroid/net/wifi/WifiConfiguration;->hasNoInternetAccess()Z
+Landroid/net/wifi/WifiConfiguration;->isEphemeral()Z
+Landroid/net/wifi/WifiConfiguration;->isNoInternetAccessExpected()Z
Landroid/net/wifi/WifiConfiguration;->mIpConfiguration:Landroid/net/IpConfiguration;
Landroid/net/wifi/WifiConfiguration;->validatedInternetAccess:Z
Landroid/net/wifi/WifiManager;->connect(ILandroid/net/wifi/WifiManager$ActionListener;)V
Landroid/net/wifi/WifiManager;->connect(Landroid/net/wifi/WifiConfiguration;Landroid/net/wifi/WifiManager$ActionListener;)V
Landroid/net/wifi/WifiManager;->EXTRA_WIFI_AP_STATE:Ljava/lang/String;
Landroid/net/wifi/WifiManager;->forget(ILandroid/net/wifi/WifiManager$ActionListener;)V
+Landroid/net/wifi/WifiManager;->getPrivilegedConfiguredNetworks()Ljava/util/List;
Landroid/net/wifi/WifiManager;->getWifiApConfiguration()Landroid/net/wifi/WifiConfiguration;
Landroid/net/wifi/WifiManager;->getWifiApState()I
Landroid/net/wifi/WifiManager;->isDualBandSupported()Z
Landroid/net/wifi/WifiManager;->isWifiApEnabled()Z
+Landroid/net/wifi/WifiManager;->isWifiScannerSupported()Z
Landroid/net/wifi/WifiManager;->mService:Landroid/net/wifi/IWifiManager;
Landroid/net/wifi/WifiManager;->save(Landroid/net/wifi/WifiConfiguration;Landroid/net/wifi/WifiManager$ActionListener;)V
Landroid/net/wifi/WifiManager;->setWifiApConfiguration(Landroid/net/wifi/WifiConfiguration;)Z
+Landroid/net/wifi/WifiManager;->startScan(Landroid/os/WorkSource;)Z
Landroid/net/wifi/WifiManager;->WIFI_AP_STATE_CHANGED_ACTION:Ljava/lang/String;
Landroid/net/wifi/WifiManager;->WIFI_AP_STATE_DISABLED:I
Landroid/net/wifi/WifiManager;->WIFI_AP_STATE_DISABLING:I
Landroid/net/wifi/WifiManager;->WIFI_AP_STATE_ENABLED:I
Landroid/net/wifi/WifiManager;->WIFI_AP_STATE_ENABLING:I
Landroid/net/wifi/WifiManager;->WIFI_AP_STATE_FAILED:I
+Landroid/net/wifi/WifiScanner;->getScanResults()Z
+Landroid/net/wifi/WifiScanner$ScanData;->getResults()[Landroid/net/wifi/ScanResult;
+Landroid/net/wifi/WifiScanner$ScanSettings;-><init>()V
+Landroid/net/wifi/WifiScanner;->startBackgroundScan(Landroid/net/wifi/WifiScanner$ScanSettings;Landroid/net/wifi/WifiScanner$ScanListener;)V
+Landroid/net/wifi/WifiScanner;->startScan(Landroid/net/wifi/WifiScanner$ScanSettings;Landroid/net/wifi/WifiScanner$ScanListener;Landroid/os/WorkSource;)V
+Landroid/net/wifi/WifiScanner;->startScan(Landroid/net/wifi/WifiScanner$ScanSettings;Landroid/net/wifi/WifiScanner$ScanListener;)V
+Landroid/net/wifi/WifiScanner;->stopBackgroundScan(Landroid/net/wifi/WifiScanner$ScanListener;)V
+Landroid/nfc/NfcAdapter;->addNfcUnlockHandler(Landroid/nfc/NfcAdapter$NfcUnlockHandler;[Ljava/lang/String;)Z
+Landroid/nfc/NfcAdapter;->disable()Z
+Landroid/nfc/NfcAdapter;->enable()Z
Landroid/nfc/NfcAdapter;->getDefaultAdapter()Landroid/nfc/NfcAdapter;
+Landroid/nfc/NfcAdapter;->removeNfcUnlockHandler(Landroid/nfc/NfcAdapter$NfcUnlockHandler;)Z
Landroid/nfc/NfcAdapter;->setNdefPushMessageCallback(Landroid/nfc/NfcAdapter$CreateNdefMessageCallback;Landroid/app/Activity;I)V
+Landroid/nfc/NfcAdapter;->setNdefPushMessage(Landroid/nfc/NdefMessage;Landroid/app/Activity;I)V
Landroid/opengl/GLSurfaceView$EglHelper;->mEglContext:Ljavax/microedition/khronos/egl/EGLContext;
Landroid/opengl/GLSurfaceView$GLThread;->mEglHelper:Landroid/opengl/GLSurfaceView$EglHelper;
Landroid/opengl/GLSurfaceView;->mGLThread:Landroid/opengl/GLSurfaceView$GLThread;
@@ -543,7 +750,11 @@
Landroid/os/UpdateEngine;->applyPayload(Ljava/lang/String;JJ[Ljava/lang/String;)V
Landroid/os/UpdateEngine;->bind(Landroid/os/UpdateEngineCallback;Landroid/os/Handler;)Z
Landroid/os/UpdateEngine;->bind(Landroid/os/UpdateEngineCallback;)Z
+Landroid/os/UpdateEngineCallback;-><init>()V
+Landroid/os/UpdateEngineCallback;->onPayloadApplicationComplete(I)V
+Landroid/os/UpdateEngineCallback;->onStatusUpdate(IF)V
Landroid/os/UpdateEngine;->cancel()V
+Landroid/os/UpdateEngine;-><init>()V
Landroid/os/UpdateEngine;->resetStatus()V
Landroid/os/UpdateLock;->acquire()V
Landroid/os/UpdateLock;->isHeld()Z
@@ -567,10 +778,12 @@
Landroid/os/UserManager;->getUserHandle()I
Landroid/os/UserManager;->getUserHandle(I)I
Landroid/os/UserManager;->getUserInfo(I)Landroid/content/pm/UserInfo;
+Landroid/os/UserManager;->getUserRestrictionSource(Ljava/lang/String;Landroid/os/UserHandle;)I
Landroid/os/UserManager;->getUserSerialNumber(I)I
Landroid/os/UserManager;->getUsers()Ljava/util/List;
Landroid/os/UserManager;->hasBaseUserRestriction(Ljava/lang/String;Landroid/os/UserHandle;)Z
Landroid/os/UserManager;->isLinkedUser()Z
+Landroid/os/UserManager;->isManagedProfile()Z
Landroid/os/UserManager;->isUserUnlocked(I)Z
Landroid/os/WorkSource;->add(ILjava/lang/String;)Z
Landroid/os/WorkSource;->add(I)Z
@@ -645,10 +858,39 @@
Landroid/service/media/IMediaBrowserServiceCallbacks;->onLoadChildren(Ljava/lang/String;Landroid/content/pm/ParceledListSlice;)V
Landroid/service/media/IMediaBrowserServiceCallbacks;->onLoadChildrenWithOptions(Ljava/lang/String;Landroid/content/pm/ParceledListSlice;Landroid/os/Bundle;)V
Landroid/service/media/MediaBrowserService;->KEY_MEDIA_ITEM:Ljava/lang/String;
+Landroid/service/media/MediaBrowserService$Result;->mFlags:I
Landroid/service/notification/NotificationListenerService;->registerAsSystemService(Landroid/content/Context;Landroid/content/ComponentName;I)V
Landroid/service/notification/NotificationListenerService;->unregisterAsSystemService()V
+Landroid/service/persistentdata/PersistentDataBlockManager;->getMaximumDataBlockSize()J
+Landroid/service/persistentdata/PersistentDataBlockManager;->read()[B
+Landroid/service/persistentdata/PersistentDataBlockManager;->write([B)I
+Landroid/service/resolver/ResolverRankerService;-><init>()V
+Landroid/service/resolver/ResolverTarget;->getChooserScore()F
+Landroid/service/resolver/ResolverTarget;->getLaunchScore()F
+Landroid/service/resolver/ResolverTarget;->getRecencyScore()F
+Landroid/service/resolver/ResolverTarget;->getSelectProbability()F
+Landroid/service/resolver/ResolverTarget;->getTimeSpentScore()F
+Landroid/service/resolver/ResolverTarget;->setSelectProbability(F)V
+Landroid/service/trust/TrustAgentService;-><init>()V
+Landroid/service/voice/VoiceInteractionService;->isKeyphraseAndLocaleSupportedForHotword(Ljava/lang/String;Ljava/util/Locale;)Z
Landroid/service/wallpaper/WallpaperService$Engine;->setFixedSizeAllowed(Z)V
Landroid/speech/tts/TextToSpeech;->getCurrentEngine()Ljava/lang/String;
+Landroid/telecom/AudioState;->getRoute()I
+Landroid/telecom/AudioState;->getSupportedRouteMask()I
+Landroid/telecom/AudioState;->isMuted()Z
+Landroid/telecom/Call;->addListener(Landroid/telecom/Call$Listener;)V
+Landroid/telecom/Call$Listener;-><init>()V
+Landroid/telecom/Call;->removeListener(Landroid/telecom/Call$Listener;)V
+Landroid/telecom/Phone;->addListener(Landroid/telecom/Phone$Listener;)V
+Landroid/telecom/Phone;->getAudioState()Landroid/telecom/AudioState;
+Landroid/telecom/Phone;->getCallAudioState()Landroid/telecom/CallAudioState;
+Landroid/telecom/Phone;->getCalls()Ljava/util/List;
+Landroid/telecom/Phone$Listener;-><init>()V
+Landroid/telecom/Phone;->removeListener(Landroid/telecom/Phone$Listener;)V
+Landroid/telecom/Phone;->setAudioRoute(I)V
+Landroid/telecom/Phone;->setMuted(Z)V
+Landroid/telecom/TelecomManager;->endCall()Z
+Landroid/telecom/TelecomManager;->EXTRA_IS_HANDOVER:Ljava/lang/String;
Landroid/telephony/PhoneStateListener;->mSubId:Ljava/lang/Integer;
Landroid/telephony/ServiceState;->newFromBundle(Landroid/os/Bundle;)Landroid/telephony/ServiceState;
Landroid/telephony/SignalStrength;->getCdmaLevel()I
@@ -661,10 +903,12 @@
Landroid/telephony/SmsMessage;->mWrappedSmsMessage:Lcom/android/internal/telephony/SmsMessageBase;
Landroid/telephony/SubscriptionManager;->getDefaultSmsPhoneId()I
Landroid/telephony/SubscriptionManager;->getPhoneId(I)I
+Landroid/telephony/SubscriptionManager;->getSlotIndex(I)I
Landroid/telephony/SubscriptionManager;->getSubId(I)[I
Landroid/telephony/SubscriptionManager;->setDefaultSmsSubId(I)V
Landroid/telephony/TelephonyManager;->checkCarrierPrivilegesForPackage(Ljava/lang/String;)I
Landroid/telephony/TelephonyManager;->from(Landroid/content/Context;)Landroid/telephony/TelephonyManager;
+Landroid/telephony/TelephonyManager;->getCarrierPackageNamesForIntent(Landroid/content/Intent;)Ljava/util/List;
Landroid/telephony/TelephonyManager;->getCurrentPhoneType()I
Landroid/telephony/TelephonyManager;->getCurrentPhoneType(I)I
Landroid/telephony/TelephonyManager;->getDataEnabled(I)Z
@@ -695,6 +939,36 @@
Landroid/text/SpannableStringBuilder;->mSpanFlags:[I
Landroid/text/SpannableStringBuilder;->mSpans:[Ljava/lang/Object;
Landroid/text/SpannableStringBuilder;->mSpanStarts:[I
+Landroid/text/SpannableStringInternal;->charAt(I)C
+Landroid/text/SpannableStringInternal;->checkRange(Ljava/lang/String;II)V
+Landroid/text/SpannableStringInternal;->COLUMNS:I
+Landroid/text/SpannableStringInternal;->copySpans(Landroid/text/SpannableStringInternal;II)V
+Landroid/text/SpannableStringInternal;->copySpans(Landroid/text/Spanned;II)V
+Landroid/text/SpannableStringInternal;->EMPTY:[Ljava/lang/Object;
+Landroid/text/SpannableStringInternal;->END:I
+Landroid/text/SpannableStringInternal;->FLAGS:I
+Landroid/text/SpannableStringInternal;->getChars(II[CI)V
+Landroid/text/SpannableStringInternal;->getSpanEnd(Ljava/lang/Object;)I
+Landroid/text/SpannableStringInternal;->getSpanFlags(Ljava/lang/Object;)I
+Landroid/text/SpannableStringInternal;->getSpans(IILjava/lang/Class;)[Ljava/lang/Object;
+Landroid/text/SpannableStringInternal;->getSpanStart(Ljava/lang/Object;)I
+Landroid/text/SpannableStringInternal;-><init>(Ljava/lang/CharSequence;II)V
+Landroid/text/SpannableStringInternal;->isIndexFollowsNextLine(I)Z
+Landroid/text/SpannableStringInternal;->isOutOfCopyRange(IIII)Z
+Landroid/text/SpannableStringInternal;->length()I
+Landroid/text/SpannableStringInternal;->mSpanCount:I
+Landroid/text/SpannableStringInternal;->mSpanData:[I
+Landroid/text/SpannableStringInternal;->mSpans:[Ljava/lang/Object;
+Landroid/text/SpannableStringInternal;->mText:Ljava/lang/String;
+Landroid/text/SpannableStringInternal;->nextSpanTransition(IILjava/lang/Class;)I
+Landroid/text/SpannableStringInternal;->region(II)Ljava/lang/String;
+Landroid/text/SpannableStringInternal;->removeSpan(Ljava/lang/Object;)V
+Landroid/text/SpannableStringInternal;->sendSpanAdded(Ljava/lang/Object;II)V
+Landroid/text/SpannableStringInternal;->sendSpanChanged(Ljava/lang/Object;IIII)V
+Landroid/text/SpannableStringInternal;->sendSpanRemoved(Ljava/lang/Object;II)V
+Landroid/text/SpannableStringInternal;->setSpan(Ljava/lang/Object;III)V
+Landroid/text/SpannableStringInternal;->setSpan(Ljava/lang/Object;IIIZ)V
+Landroid/text/SpannableStringInternal;->START:I
Landroid/text/StaticLayout;->mColumns:I
Landroid/text/StaticLayout;->mLineCount:I
Landroid/text/StaticLayout;->mLines:[I
@@ -736,6 +1010,8 @@
Landroid/view/IWindowManager;->setStrictModeVisualIndicatorPreference(Ljava/lang/String;)V
Landroid/view/IWindowManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/view/IWindowManager;
Landroid/view/IWindowSession$Stub$Proxy;->relayout(Landroid/view/IWindow;ILandroid/view/WindowManager$LayoutParams;IIIILandroid/graphics/Rect;Landroid/graphics/Rect;Landroid/graphics/Rect;Landroid/graphics/Rect;Landroid/graphics/Rect;Landroid/graphics/Rect;Landroid/graphics/Rect;Landroid/view/DisplayCutout$ParcelableWrapper;Landroid/util/MergedConfiguration;Landroid/view/Surface;)I
+Landroid/view/LayoutInflater;->createViewFromTag(Landroid/view/View;Ljava/lang/String;Landroid/content/Context;Landroid/util/AttributeSet;)Landroid/view/View;
+Landroid/view/LayoutInflater;->createViewFromTag(Landroid/view/View;Ljava/lang/String;Landroid/content/Context;Landroid/util/AttributeSet;Z)Landroid/view/View;
Landroid/view/LayoutInflater;->mConstructorArgs:[Ljava/lang/Object;
Landroid/view/LayoutInflater;->mFactory2:Landroid/view/LayoutInflater$Factory2;
Landroid/view/LayoutInflater;->mFactory:Landroid/view/LayoutInflater$Factory;
@@ -1113,6 +1389,9 @@
Lcom/android/org/conscrypt/OpenSSLSocketImpl;->setAlpnProtocols([B)V
Ldalvik/system/BaseDexClassLoader;->getLdLibraryPath()Ljava/lang/String;
Ldalvik/system/BaseDexClassLoader;->pathList:Ldalvik/system/DexPathList;
+Ldalvik/system/BlockGuard;->getThreadPolicy()Ldalvik/system/BlockGuard$Policy;
+Ldalvik/system/BlockGuard$Policy;->onNetwork()V
+Ldalvik/system/CloseGuard;->close()V
Ldalvik/system/CloseGuard;->get()Ldalvik/system/CloseGuard;
Ldalvik/system/CloseGuard;->open(Ljava/lang/String;)V
Ldalvik/system/CloseGuard;->warnIfOpen()V
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index aca8d48..0bc510a 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -6362,8 +6362,6 @@
} else {
writer.print(prefix); writer.println("No AutofillManager");
}
-
- ResourcesManager.getInstance().dump(prefix, writer);
}
/**
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index 5ee7ede..4626cb2 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -348,4 +348,9 @@
* Returns is the caller has the same uid as the Recents component
*/
public abstract boolean isCallerRecents(int callingUid);
+
+ /**
+ * Whether an UID is active or idle.
+ */
+ public abstract boolean isUidActive(int uid);
}
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 3d088ff..5a63319 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -113,6 +113,7 @@
import android.util.EventLog;
import android.util.Log;
import android.util.LogPrinter;
+import android.util.MergedConfiguration;
import android.util.Pair;
import android.util.PrintWriterPrinter;
import android.util.Slog;
@@ -166,9 +167,9 @@
import java.lang.reflect.Method;
import java.net.InetAddress;
import java.text.DateFormat;
-import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
@@ -204,7 +205,7 @@
private static final boolean DEBUG_SERVICE = false;
public static final boolean DEBUG_MEMORY_TRIM = false;
private static final boolean DEBUG_PROVIDER = false;
- private static final boolean DEBUG_ORDER = false;
+ public static final boolean DEBUG_ORDER = false;
private static final long MIN_TIME_BETWEEN_GCS = 5*1000;
private static final int SQLITE_MEM_RELEASED_EVENT_LOG_TAG = 75003;
private static final int LOG_AM_ON_PAUSE_CALLED = 30021;
@@ -222,7 +223,7 @@
private static final boolean REPORT_TO_ACTIVITY = true;
// Maximum number of recent tokens to maintain for debugging purposes
- private static final int MAX_RECENT_TOKENS = 10;
+ private static final int MAX_DESTROYED_ACTIVITIES = 10;
/**
* Denotes an invalid sequence number corresponding to a process state change.
@@ -256,7 +257,7 @@
final H mH = new H();
final Executor mExecutor = new HandlerExecutor(mH);
final ArrayMap<IBinder, ActivityClientRecord> mActivities = new ArrayMap<>();
- final ArrayDeque<Integer> mRecentTokens = new ArrayDeque<>();
+ final ArrayList<DestroyedActivityInfo> mRecentDestroyedActivities = new ArrayList<>();
// List of new activities (via ActivityRecord.nextIdle) that should
// be reported when next we idle.
@@ -339,6 +340,26 @@
}
}
+ /**
+ * TODO(b/71506345): Remove this once bug is resolved.
+ */
+ private static final class DestroyedActivityInfo {
+ private final Integer mToken;
+ private final String mReason;
+ private final long mTime;
+
+ DestroyedActivityInfo(Integer token, String reason) {
+ mToken = token;
+ mReason = reason;
+ mTime = System.currentTimeMillis();
+ }
+
+ void dump(PrintWriter pw, String prefix) {
+ pw.println(prefix + "[token:" + mToken + " | time:" + mTime + " | reason:" + mReason
+ + "]");
+ }
+ }
+
// The lock of mProviderMap protects the following variables.
final ArrayMap<ProviderKey, ProviderClientRecord> mProviderMap
= new ArrayMap<ProviderKey, ProviderClientRecord>();
@@ -398,7 +419,6 @@
boolean startsNotResumed;
public final boolean isForward;
int pendingConfigChanges;
- boolean onlyLocalRequest;
Window mPendingRemoveWindow;
WindowManager mPendingRemoveWindowManager;
@@ -520,7 +540,6 @@
sb.append(", startsNotResumed=").append(startsNotResumed);
sb.append(", isForward=").append(isForward);
sb.append(", pendingConfigChanges=").append(pendingConfigChanges);
- sb.append(", onlyLocalRequest=").append(onlyLocalRequest);
sb.append(", preserveWindow=").append(mPreserveWindow);
if (activity != null) {
sb.append(", Activity{");
@@ -765,15 +784,6 @@
sendMessage(H.SLEEPING, token, sleeping ? 1 : 0);
}
- @Override
- public final void scheduleRelaunchActivity(IBinder token,
- List<ResultInfo> pendingResults, List<ReferrerIntent> pendingNewIntents,
- int configChanges, boolean notResumed, Configuration config,
- Configuration overrideConfig, boolean preserveWindow) {
- requestRelaunchActivity(token, pendingResults, pendingNewIntents,
- configChanges, notResumed, config, overrideConfig, true, preserveWindow);
- }
-
public final void scheduleReceiver(Intent intent, ActivityInfo info,
CompatibilityInfo compatInfo, int resultCode, String data, Bundle extras,
boolean sync, int sendingUser, int processState) {
@@ -1531,7 +1541,6 @@
public static final int UNBIND_SERVICE = 122;
public static final int DUMP_SERVICE = 123;
public static final int LOW_MEMORY = 124;
- public static final int RELAUNCH_ACTIVITY = 126;
public static final int PROFILER_CONTROL = 127;
public static final int CREATE_BACKUP_AGENT = 128;
public static final int DESTROY_BACKUP_AGENT = 129;
@@ -1577,7 +1586,6 @@
case UNBIND_SERVICE: return "UNBIND_SERVICE";
case DUMP_SERVICE: return "DUMP_SERVICE";
case LOW_MEMORY: return "LOW_MEMORY";
- case RELAUNCH_ACTIVITY: return "RELAUNCH_ACTIVITY";
case PROFILER_CONTROL: return "PROFILER_CONTROL";
case CREATE_BACKUP_AGENT: return "CREATE_BACKUP_AGENT";
case DESTROY_BACKUP_AGENT: return "DESTROY_BACKUP_AGENT";
@@ -1611,12 +1619,6 @@
public void handleMessage(Message msg) {
if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + codeToString(msg.what));
switch (msg.what) {
- case RELAUNCH_ACTIVITY: {
- Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityRestart");
- ActivityClientRecord r = (ActivityClientRecord)msg.obj;
- handleRelaunchActivity(r);
- Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
- } break;
case BIND_APPLICATION:
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "bindApplication");
AppBindData data = (AppBindData)msg.obj;
@@ -2182,14 +2184,28 @@
@Override
public void dump(PrintWriter pw, String prefix) {
- pw.println(prefix + "mActivities:");
+ pw.println(prefix + "Activities:");
- for (ArrayMap.Entry<IBinder, ActivityClientRecord> entry : mActivities.entrySet()) {
- pw.println(prefix + " [token:" + entry.getKey().hashCode() + " record:"
- + entry.getValue().toString() + "]");
+ if (!mActivities.isEmpty()) {
+ final Iterator<Map.Entry<IBinder, ActivityClientRecord>> activitiesIterator =
+ mActivities.entrySet().iterator();
+
+ while (activitiesIterator.hasNext()) {
+ final ArrayMap.Entry<IBinder, ActivityClientRecord> entry =
+ activitiesIterator.next();
+ pw.println(prefix + " [token:" + entry.getKey().hashCode() + " record:"
+ + entry.getValue().toString() + "]");
+ }
}
- pw.println(prefix + "mRecentTokens:" + mRecentTokens);
+ if (!mRecentDestroyedActivities.isEmpty()) {
+ pw.println(prefix + "Recent destroyed activities:");
+ for (int i = 0, size = mRecentDestroyedActivities.size(); i < size; i++) {
+ final DestroyedActivityInfo info = mRecentDestroyedActivities.get(i);
+ pw.print(prefix);
+ info.dump(pw, " ");
+ }
+ }
}
public static void dumpMemInfoTable(PrintWriter pw, Debug.MemoryInfo memInfo, boolean checkin,
@@ -2876,11 +2892,6 @@
r.setState(ON_CREATE);
mActivities.put(r.token, r);
- mRecentTokens.push(r.token.hashCode());
-
- if (mRecentTokens.size() > MAX_RECENT_TOKENS) {
- mRecentTokens.removeLast();
- }
} catch (SuperNotCalledException e) {
throw e;
@@ -3726,20 +3737,6 @@
}
r.activity.performResume(r.startsNotResumed);
- synchronized (mResourcesManager) {
- // If there is a pending local relaunch that was requested when the activity was
- // paused, it will put the activity into paused state when it finally happens.
- // Since the activity resumed before being relaunched, we don't want that to
- // happen, so we need to clear the request to relaunch paused.
- for (int i = mRelaunchingActivities.size() - 1; i >= 0; i--) {
- final ActivityClientRecord relaunching = mRelaunchingActivities.get(i);
- if (relaunching.token == r.token
- && relaunching.onlyLocalRequest && relaunching.startsNotResumed) {
- relaunching.startsNotResumed = false;
- }
- }
- }
-
EventLog.writeEvent(LOG_AM_ON_RESUME_CALLED, UserHandle.myUserId(),
r.activity.getComponentName().getClassName(), reason);
@@ -3888,14 +3885,12 @@
}
}
- if (!r.onlyLocalRequest) {
- r.nextIdle = mNewActivities;
- mNewActivities = r;
- if (localLOGV) Slog.v(
- TAG, "Scheduling idle handler for " + r);
- Looper.myQueue().addIdleHandler(new Idler());
+ r.nextIdle = mNewActivities;
+ mNewActivities = r;
+ if (localLOGV) {
+ Slog.v(TAG, "Scheduling idle handler for " + r);
}
- r.onlyLocalRequest = false;
+ Looper.myQueue().addIdleHandler(new Idler());
} else {
// If an exception was thrown when trying to resume, then
// just end this activity.
@@ -4453,7 +4448,7 @@
/** Core implementation of activity destroy call. */
ActivityClientRecord performDestroyActivity(IBinder token, boolean finishing,
- int configChanges, boolean getNonConfigInstance) {
+ int configChanges, boolean getNonConfigInstance, String reason) {
ActivityClientRecord r = mActivities.get(token);
Class<? extends Activity> activityClass = null;
if (localLOGV) Slog.v(TAG, "Performing finish of " + r);
@@ -4505,6 +4500,12 @@
r.setState(ON_DESTROY);
}
mActivities.remove(token);
+ mRecentDestroyedActivities.add(0, new DestroyedActivityInfo(token.hashCode(), reason));
+
+ final int recentDestroyedActivitiesSize = mRecentDestroyedActivities.size();
+ if (recentDestroyedActivitiesSize > MAX_DESTROYED_ACTIVITIES) {
+ mRecentDestroyedActivities.remove(recentDestroyedActivitiesSize - 1);
+ }
StrictMode.decrementExpectedActivityCount(activityClass);
return r;
}
@@ -4516,9 +4517,9 @@
@Override
public void handleDestroyActivity(IBinder token, boolean finishing, int configChanges,
- boolean getNonConfigInstance) {
+ boolean getNonConfigInstance, String reason) {
ActivityClientRecord r = performDestroyActivity(token, finishing,
- configChanges, getNonConfigInstance);
+ configChanges, getNonConfigInstance, reason);
if (r != null) {
cleanUpPendingRemoveWindows(r, finishing);
WindowManager wm = r.activity.getWindowManager();
@@ -4586,15 +4587,12 @@
mSomeActivitiesChanged = true;
}
- /**
- * @param preserveWindow Whether the activity should try to reuse the window it created,
- * including the decor view after the relaunch.
- */
- public final void requestRelaunchActivity(IBinder token,
+ @Override
+ public ActivityClientRecord prepareRelaunchActivity(IBinder token,
List<ResultInfo> pendingResults, List<ReferrerIntent> pendingNewIntents,
- int configChanges, boolean notResumed, Configuration config,
- Configuration overrideConfig, boolean fromServer, boolean preserveWindow) {
+ int configChanges, MergedConfiguration config, boolean preserveWindow) {
ActivityClientRecord target = null;
+ boolean scheduleRelaunch = false;
synchronized (mResourcesManager) {
for (int i=0; i<mRelaunchingActivities.size(); i++) {
@@ -4616,57 +4614,31 @@
r.pendingIntents = pendingNewIntents;
}
}
-
- // For each relaunch request, activity manager expects an answer
- if (!r.onlyLocalRequest && fromServer) {
- try {
- ActivityManager.getService().activityRelaunched(token);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
break;
}
}
if (target == null) {
- if (DEBUG_ORDER) Slog.d(TAG, "requestRelaunchActivity: target is null, fromServer:"
- + fromServer);
+ if (DEBUG_ORDER) Slog.d(TAG, "requestRelaunchActivity: target is null");
target = new ActivityClientRecord();
target.token = token;
target.pendingResults = pendingResults;
target.pendingIntents = pendingNewIntents;
target.mPreserveWindow = preserveWindow;
- if (!fromServer) {
- final ActivityClientRecord existing = mActivities.get(token);
- if (DEBUG_ORDER) Slog.d(TAG, "requestRelaunchActivity: " + existing);
- if (existing != null) {
- if (DEBUG_ORDER) Slog.d(TAG, "requestRelaunchActivity: paused= "
- + existing.paused);;
- target.startsNotResumed = existing.paused;
- target.overrideConfig = existing.overrideConfig;
- }
- target.onlyLocalRequest = true;
- }
mRelaunchingActivities.add(target);
- sendMessage(H.RELAUNCH_ACTIVITY, target);
+ scheduleRelaunch = true;
}
-
- if (fromServer) {
- target.startsNotResumed = notResumed;
- target.onlyLocalRequest = false;
- }
- if (config != null) {
- target.createdConfig = config;
- }
- if (overrideConfig != null) {
- target.overrideConfig = overrideConfig;
- }
+ target.createdConfig = config.getGlobalConfiguration();
+ target.overrideConfig = config.getOverrideConfiguration();
target.pendingConfigChanges |= configChanges;
}
+
+ return scheduleRelaunch ? target : null;
}
- private void handleRelaunchActivity(ActivityClientRecord tmp) {
+ @Override
+ public void handleRelaunchActivity(ActivityClientRecord tmp,
+ PendingTransactionActions pendingActions) {
// If we are getting ready to gc after going to the background, well
// we are back active so skip it.
unscheduleGcIdler();
@@ -4735,18 +4707,10 @@
ActivityClientRecord r = mActivities.get(tmp.token);
if (DEBUG_CONFIGURATION) Slog.v(TAG, "Handling relaunch of " + r);
if (r == null) {
- if (!tmp.onlyLocalRequest) {
- try {
- ActivityManager.getService().activityRelaunched(tmp.token);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
return;
}
r.activity.mConfigChangeFlags |= configChanges;
- r.onlyLocalRequest = tmp.onlyLocalRequest;
r.mPreserveWindow = tmp.mPreserveWindow;
r.activity.mChangingConfigurations = true;
@@ -4763,9 +4727,9 @@
// preserved by the server, so we want to notify it that we are preparing to replace
// everything
try {
- if (r.mPreserveWindow || r.onlyLocalRequest) {
+ if (r.mPreserveWindow) {
WindowManagerGlobal.getWindowSession().prepareToReplaceWindows(
- r.token, !r.onlyLocalRequest);
+ r.token, true /* childrenOnly */);
}
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
@@ -4780,7 +4744,7 @@
callActivityOnStop(r, true /* saveState */, "handleRelaunchActivity");
}
- handleDestroyActivity(r.token, false, configChanges, true);
+ handleDestroyActivity(r.token, false, configChanges, true, "handleRelaunchActivity");
r.activity = null;
r.window = null;
@@ -4804,24 +4768,22 @@
r.startsNotResumed = tmp.startsNotResumed;
r.overrideConfig = tmp.overrideConfig;
- // TODO(lifecycler): Move relaunch to lifecycler.
- PendingTransactionActions pendingActions = new PendingTransactionActions();
handleLaunchActivity(r, pendingActions);
- handleStartActivity(r, pendingActions);
- handleResumeActivity(r.token, false /* clearHide */, r.isForward, "relaunch");
- if (r.startsNotResumed) {
- performPauseActivity(r, false /* finished */, "relaunch", pendingActions);
- }
+ // Only report a successful relaunch to WindowManager.
+ pendingActions.setReportRelaunchToWindowManager(true);
+ }
- if (!tmp.onlyLocalRequest) {
- try {
- ActivityManager.getService().activityRelaunched(r.token);
- if (r.window != null) {
- r.window.reportActivityRelaunched();
- }
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ @Override
+ public void reportRelaunch(IBinder token, PendingTransactionActions pendingActions) {
+ try {
+ ActivityManager.getService().activityRelaunched(token);
+ final ActivityClientRecord r = mActivities.get(token);
+ if (pendingActions.shouldReportRelaunchToWindowManager() && r != null
+ && r.window != null) {
+ r.window.reportActivityRelaunched();
}
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
}
}
@@ -5890,21 +5852,23 @@
// Preload fonts resources
FontsContract.setApplicationContextForResources(appContext);
- try {
- final ApplicationInfo info =
- getPackageManager().getApplicationInfo(
- data.appInfo.packageName,
- PackageManager.GET_META_DATA /*flags*/,
- UserHandle.myUserId());
- if (info.metaData != null) {
- final int preloadedFontsResource = info.metaData.getInt(
- ApplicationInfo.METADATA_PRELOADED_FONTS, 0);
- if (preloadedFontsResource != 0) {
- data.info.getResources().preloadFonts(preloadedFontsResource);
+ if (!Process.isIsolated()) {
+ try {
+ final ApplicationInfo info =
+ getPackageManager().getApplicationInfo(
+ data.appInfo.packageName,
+ PackageManager.GET_META_DATA /*flags*/,
+ UserHandle.myUserId());
+ if (info.metaData != null) {
+ final int preloadedFontsResource = info.metaData.getInt(
+ ApplicationInfo.METADATA_PRELOADED_FONTS, 0);
+ if (preloadedFontsResource != 0) {
+ data.info.getResources().preloadFonts(preloadedFontsResource);
+ }
}
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
}
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
}
}
diff --git a/core/java/android/app/ClientTransactionHandler.java b/core/java/android/app/ClientTransactionHandler.java
index 5b61fdf..310965e 100644
--- a/core/java/android/app/ClientTransactionHandler.java
+++ b/core/java/android/app/ClientTransactionHandler.java
@@ -21,6 +21,7 @@
import android.content.res.CompatibilityInfo;
import android.content.res.Configuration;
import android.os.IBinder;
+import android.util.MergedConfiguration;
import com.android.internal.content.ReferrerIntent;
@@ -60,7 +61,7 @@
/** Destroy the activity. */
public abstract void handleDestroyActivity(IBinder token, boolean finishing, int configChanges,
- boolean getNonConfigInstance);
+ boolean getNonConfigInstance, String reason);
/** Pause the activity. */
public abstract void handlePauseActivity(IBinder token, boolean finished, boolean userLeaving,
@@ -124,6 +125,39 @@
public abstract ActivityThread.ActivityClientRecord getActivityClient(IBinder token);
/**
+ * Prepare activity relaunch to update internal bookkeeping. This is used to track multiple
+ * relaunch and config update requests.
+ * @param token Activity token.
+ * @param pendingResults Activity results to be delivered.
+ * @param pendingNewIntents New intent messages to be delivered.
+ * @param configChanges Mask of configuration changes that have occurred.
+ * @param config New configuration applied to the activity.
+ * @param preserveWindow Whether the activity should try to reuse the window it created,
+ * including the decor view after the relaunch.
+ * @return An initialized instance of {@link ActivityThread.ActivityClientRecord} to use during
+ * relaunch, or {@code null} if relaunch cancelled.
+ */
+ public abstract ActivityThread.ActivityClientRecord prepareRelaunchActivity(IBinder token,
+ List<ResultInfo> pendingResults, List<ReferrerIntent> pendingNewIntents,
+ int configChanges, MergedConfiguration config, boolean preserveWindow);
+
+ /**
+ * Perform activity relaunch.
+ * @param r Activity client record prepared for relaunch.
+ * @param pendingActions Pending actions to be used on later stages of activity transaction.
+ * */
+ public abstract void handleRelaunchActivity(ActivityThread.ActivityClientRecord r,
+ PendingTransactionActions pendingActions);
+
+ /**
+ * Report that relaunch request was handled.
+ * @param token Target activity token.
+ * @param pendingActions Pending actions initialized on earlier stages of activity transaction.
+ * Used to check if we should report relaunch to WM.
+ * */
+ public abstract void reportRelaunch(IBinder token, PendingTransactionActions pendingActions);
+
+ /**
* Debugging output.
* @param pw {@link PrintWriter} to write logs to.
* @param prefix Prefix to prepend to output.
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 4a9b2bc..a1ba13d 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -409,6 +409,7 @@
return sp;
}
+ @GuardedBy("ContextImpl.class")
private ArrayMap<File, SharedPreferencesImpl> getSharedPreferencesCacheLocked() {
if (sSharedPrefsCache == null) {
sSharedPrefsCache = new ArrayMap<>();
diff --git a/core/java/android/app/Dialog.java b/core/java/android/app/Dialog.java
index 2b648ea..eb26026 100644
--- a/core/java/android/app/Dialog.java
+++ b/core/java/android/app/Dialog.java
@@ -609,18 +609,19 @@
/**
* A key was pressed down.
+ * <p>
+ * If the focused view didn't want this event, this method is called.
+ * <p>
+ * Default implementation consumes {@link KeyEvent#KEYCODE_BACK KEYCODE_BACK}
+ * and, as of {@link android.os.Build.VERSION_CODES#P P}, {@link KeyEvent#KEYCODE_ESCAPE
+ * KEYCODE_ESCAPE} to later handle them in {@link #onKeyUp}.
*
- * <p>If the focused view didn't want this event, this method is called.
- *
- * <p>The default implementation consumed the KEYCODE_BACK to later
- * handle it in {@link #onKeyUp}.
- *
* @see #onKeyUp
* @see android.view.KeyEvent
*/
@Override
public boolean onKeyDown(int keyCode, @NonNull KeyEvent event) {
- if (keyCode == KeyEvent.KEYCODE_BACK) {
+ if (keyCode == KeyEvent.KEYCODE_BACK || keyCode == KeyEvent.KEYCODE_ESCAPE) {
event.startTracking();
return true;
}
@@ -640,16 +641,18 @@
/**
* A key was released.
- *
- * <p>The default implementation handles KEYCODE_BACK to close the
- * dialog.
+ * <p>
+ * Default implementation consumes {@link KeyEvent#KEYCODE_BACK KEYCODE_BACK}
+ * and, as of {@link android.os.Build.VERSION_CODES#P P}, {@link KeyEvent#KEYCODE_ESCAPE
+ * KEYCODE_ESCAPE} to close the dialog.
*
* @see #onKeyDown
- * @see KeyEvent
+ * @see android.view.KeyEvent
*/
@Override
public boolean onKeyUp(int keyCode, @NonNull KeyEvent event) {
- if (keyCode == KeyEvent.KEYCODE_BACK && event.isTracking()
+ if ((keyCode == KeyEvent.KEYCODE_BACK || keyCode == KeyEvent.KEYCODE_ESCAPE)
+ && event.isTracking()
&& !event.isCanceled()) {
onBackPressed();
return true;
diff --git a/core/java/android/app/IApplicationThread.aidl b/core/java/android/app/IApplicationThread.aidl
index 9e99a78..ae9b83e 100644
--- a/core/java/android/app/IApplicationThread.aidl
+++ b/core/java/android/app/IApplicationThread.aidl
@@ -83,9 +83,6 @@
int resultCode, in String data, in Bundle extras, boolean ordered,
boolean sticky, int sendingUser, int processState);
void scheduleLowMemory();
- void scheduleRelaunchActivity(IBinder token, in List<ResultInfo> pendingResults,
- in List<ReferrerIntent> pendingNewIntents, int configChanges, boolean notResumed,
- in Configuration config, in Configuration overrideConfig, boolean preserveWindow);
void scheduleSleeping(IBinder token, boolean sleeping);
void profilerControl(boolean start, in ProfilerInfo profilerInfo, int profileType);
void setSchedulingGroup(int group);
diff --git a/core/java/android/app/LocalActivityManager.java b/core/java/android/app/LocalActivityManager.java
index 1d34595..e297719 100644
--- a/core/java/android/app/LocalActivityManager.java
+++ b/core/java/android/app/LocalActivityManager.java
@@ -380,7 +380,7 @@
}
if (localLOGV) Log.v(TAG, r.id + ": destroying");
mActivityThread.performDestroyActivity(r, finish, 0 /* configChanges */,
- false /* getNonConfigInstance */);
+ false /* getNonConfigInstance */, "LocalActivityManager::performDestroy");
r.activity = null;
r.window = null;
if (finish) {
@@ -645,7 +645,7 @@
LocalActivityRecord r = mActivityArray.get(i);
if (localLOGV) Log.v(TAG, r.id + ": destroying");
mActivityThread.performDestroyActivity(r, finishing, 0 /* configChanges */,
- false /* getNonConfigInstance */);
+ false /* getNonConfigInstance */, "LocalActivityManager::dispatchDestroy");
}
mActivities.clear();
mActivityArray.clear();
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index f4836b7..c805658 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -4616,10 +4616,15 @@
if (N>MAX_ACTION_BUTTONS) N=MAX_ACTION_BUTTONS;
for (int i=0; i<N; i++) {
Action action = mActions.get(i);
- validRemoteInput |= hasValidRemoteInput(action);
+ boolean actionHasValidInput = hasValidRemoteInput(action);
+ validRemoteInput |= actionHasValidInput;
final RemoteViews button = generateActionButton(action, emphazisedMode,
i % 2 != 0, p.ambient);
+ if (actionHasValidInput) {
+ // Clear the drawable
+ button.setInt(R.id.action0, "setBackgroundResource", 0);
+ }
big.addView(R.id.actions, button);
}
} else {
diff --git a/core/java/android/app/ResourcesManager.java b/core/java/android/app/ResourcesManager.java
index b96e028..fb11272 100644
--- a/core/java/android/app/ResourcesManager.java
+++ b/core/java/android/app/ResourcesManager.java
@@ -21,7 +21,6 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.pm.ActivityInfo;
-import android.content.res.ApkAssets;
import android.content.res.AssetManager;
import android.content.res.CompatResources;
import android.content.res.CompatibilityInfo;
@@ -35,7 +34,6 @@
import android.util.ArrayMap;
import android.util.DisplayMetrics;
import android.util.Log;
-import android.util.LruCache;
import android.util.Pair;
import android.util.Slog;
import android.view.Display;
@@ -43,13 +41,9 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.ArrayUtils;
-import com.android.internal.util.IndentingPrintWriter;
-import java.io.IOException;
-import java.io.PrintWriter;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
-import java.util.Collection;
import java.util.Objects;
import java.util.WeakHashMap;
import java.util.function.Predicate;
@@ -65,7 +59,12 @@
* Predicate that returns true if a WeakReference is gc'ed.
*/
private static final Predicate<WeakReference<Resources>> sEmptyReferencePredicate =
- weakRef -> weakRef == null || weakRef.get() == null;
+ new Predicate<WeakReference<Resources>>() {
+ @Override
+ public boolean test(WeakReference<Resources> weakRef) {
+ return weakRef == null || weakRef.get() == null;
+ }
+ };
/**
* The global compatibility settings.
@@ -90,48 +89,6 @@
*/
private final ArrayList<WeakReference<Resources>> mResourceReferences = new ArrayList<>();
- private static class ApkKey {
- public final String path;
- public final boolean sharedLib;
- public final boolean overlay;
-
- ApkKey(String path, boolean sharedLib, boolean overlay) {
- this.path = path;
- this.sharedLib = sharedLib;
- this.overlay = overlay;
- }
-
- @Override
- public int hashCode() {
- int result = 1;
- result = 31 * result + this.path.hashCode();
- result = 31 * result + Boolean.hashCode(this.sharedLib);
- result = 31 * result + Boolean.hashCode(this.overlay);
- return result;
- }
-
- @Override
- public boolean equals(Object obj) {
- if (!(obj instanceof ApkKey)) {
- return false;
- }
- ApkKey other = (ApkKey) obj;
- return this.path.equals(other.path) && this.sharedLib == other.sharedLib
- && this.overlay == other.overlay;
- }
- }
-
- /**
- * The ApkAssets we are caching and intend to hold strong references to.
- */
- private final LruCache<ApkKey, ApkAssets> mLoadedApkAssets = new LruCache<>(15);
-
- /**
- * The ApkAssets that are being referenced in the wild that we can reuse, even if they aren't
- * in our LRU cache. Bonus resources :)
- */
- private final ArrayMap<ApkKey, WeakReference<ApkAssets>> mCachedApkAssets = new ArrayMap<>();
-
/**
* Resources and base configuration override associated with an Activity.
*/
@@ -303,41 +260,6 @@
}
}
- private @NonNull ApkAssets loadApkAssets(String path, boolean sharedLib, boolean overlay)
- throws IOException {
- final ApkKey newKey = new ApkKey(path, sharedLib, overlay);
- ApkAssets apkAssets = mLoadedApkAssets.get(newKey);
- if (apkAssets != null) {
- return apkAssets;
- }
-
- // Optimistically check if this ApkAssets exists somewhere else.
- final WeakReference<ApkAssets> apkAssetsRef = mCachedApkAssets.get(newKey);
- if (apkAssetsRef != null) {
- apkAssets = apkAssetsRef.get();
- if (apkAssets != null) {
- mLoadedApkAssets.put(newKey, apkAssets);
- return apkAssets;
- } else {
- // Clean up the reference.
- mCachedApkAssets.remove(newKey);
- }
- }
-
- // We must load this from disk.
- if (overlay) {
- final String idmapPath = "/data/resource-cache/"
- + path.substring(1).replace('/', '@')
- + "@idmap";
- apkAssets = ApkAssets.loadOverlayFromPath(idmapPath, false /*system*/);
- } else {
- apkAssets = ApkAssets.loadFromPath(path, false /*system*/, sharedLib);
- }
- mLoadedApkAssets.put(newKey, apkAssets);
- mCachedApkAssets.put(newKey, new WeakReference<>(apkAssets));
- return apkAssets;
- }
-
/**
* Creates an AssetManager from the paths within the ResourcesKey.
*
@@ -348,15 +270,13 @@
*/
@VisibleForTesting
protected @Nullable AssetManager createAssetManager(@NonNull final ResourcesKey key) {
- final ArrayList<ApkAssets> apkAssets = new ArrayList<>();
+ AssetManager assets = new AssetManager();
// resDir can be null if the 'android' package is creating a new Resources object.
// This is fine, since each AssetManager automatically loads the 'android' package
// already.
if (key.mResDir != null) {
- try {
- apkAssets.add(loadApkAssets(key.mResDir, false /*sharedLib*/, false /*overlay*/));
- } catch (IOException e) {
+ if (assets.addAssetPath(key.mResDir) == 0) {
Log.e(TAG, "failed to add asset path " + key.mResDir);
return null;
}
@@ -364,10 +284,7 @@
if (key.mSplitResDirs != null) {
for (final String splitResDir : key.mSplitResDirs) {
- try {
- apkAssets.add(loadApkAssets(splitResDir, false /*sharedLib*/,
- false /*overlay*/));
- } catch (IOException e) {
+ if (assets.addAssetPath(splitResDir) == 0) {
Log.e(TAG, "failed to add split asset path " + splitResDir);
return null;
}
@@ -376,13 +293,7 @@
if (key.mOverlayDirs != null) {
for (final String idmapPath : key.mOverlayDirs) {
- try {
- apkAssets.add(loadApkAssets(idmapPath, false /*sharedLib*/, true /*overlay*/));
- } catch (IOException e) {
- Log.w(TAG, "failed to add overlay path " + idmapPath);
-
- // continue.
- }
+ assets.addOverlayPath(idmapPath);
}
}
@@ -391,77 +302,16 @@
if (libDir.endsWith(".apk")) {
// Avoid opening files we know do not have resources,
// like code-only .jar files.
- try {
- apkAssets.add(loadApkAssets(libDir, true /*sharedLib*/, false /*overlay*/));
- } catch (IOException e) {
+ if (assets.addAssetPathAsSharedLibrary(libDir) == 0) {
Log.w(TAG, "Asset path '" + libDir +
"' does not exist or contains no resources.");
-
- // continue.
}
}
}
}
-
- AssetManager assets = new AssetManager();
- assets.setApkAssets(apkAssets.toArray(new ApkAssets[apkAssets.size()]),
- false /*invalidateCaches*/);
return assets;
}
- private static <T> int countLiveReferences(Collection<WeakReference<T>> collection) {
- int count = 0;
- for (WeakReference<T> ref : collection) {
- final T value = ref != null ? ref.get() : null;
- if (value != null) {
- count++;
- }
- }
- return count;
- }
-
- /**
- * @hide
- */
- public void dump(String prefix, PrintWriter printWriter) {
- synchronized (this) {
- IndentingPrintWriter pw = new IndentingPrintWriter(printWriter, " ");
- for (int i = 0; i < prefix.length() / 2; i++) {
- pw.increaseIndent();
- }
-
- pw.println("ResourcesManager:");
- pw.increaseIndent();
- pw.print("cached apks: total=");
- pw.print(mLoadedApkAssets.size());
- pw.print(" created=");
- pw.print(mLoadedApkAssets.createCount());
- pw.print(" evicted=");
- pw.print(mLoadedApkAssets.evictionCount());
- pw.print(" hit=");
- pw.print(mLoadedApkAssets.hitCount());
- pw.print(" miss=");
- pw.print(mLoadedApkAssets.missCount());
- pw.print(" max=");
- pw.print(mLoadedApkAssets.maxSize());
- pw.println();
-
- pw.print("total apks: ");
- pw.println(countLiveReferences(mCachedApkAssets.values()));
-
- pw.print("resources: ");
-
- int references = countLiveReferences(mResourceReferences);
- for (ActivityResources activityResources : mActivityResourceReferences.values()) {
- references += countLiveReferences(activityResources.activityResources);
- }
- pw.println(references);
-
- pw.print("resource impls: ");
- pw.println(countLiveReferences(mResourceImpls.values()));
- }
- }
-
private Configuration generateConfig(@NonNull ResourcesKey key, @NonNull DisplayMetrics dm) {
Configuration config;
final boolean isDefaultDisplay = (key.mDisplayId == Display.DEFAULT_DISPLAY);
@@ -780,16 +630,28 @@
// We will create the ResourcesImpl object outside of holding this lock.
}
+ }
- // If we're here, we didn't find a suitable ResourcesImpl to use, so create one now.
- ResourcesImpl resourcesImpl = createResourcesImpl(key);
- if (resourcesImpl == null) {
- return null;
+ // If we're here, we didn't find a suitable ResourcesImpl to use, so create one now.
+ ResourcesImpl resourcesImpl = createResourcesImpl(key);
+ if (resourcesImpl == null) {
+ return null;
+ }
+
+ synchronized (this) {
+ ResourcesImpl existingResourcesImpl = findResourcesImplForKeyLocked(key);
+ if (existingResourcesImpl != null) {
+ if (DEBUG) {
+ Slog.d(TAG, "- got beat! existing impl=" + existingResourcesImpl
+ + " new impl=" + resourcesImpl);
+ }
+ resourcesImpl.getAssets().close();
+ resourcesImpl = existingResourcesImpl;
+ } else {
+ // Add this ResourcesImpl to the cache.
+ mResourceImpls.put(key, new WeakReference<>(resourcesImpl));
}
- // Add this ResourcesImpl to the cache.
- mResourceImpls.put(key, new WeakReference<>(resourcesImpl));
-
final Resources resources;
if (activityToken != null) {
resources = getOrCreateResourcesForActivityLocked(activityToken, classLoader,
diff --git a/core/java/android/app/Service.java b/core/java/android/app/Service.java
index 256c479..ea0fd75 100644
--- a/core/java/android/app/Service.java
+++ b/core/java/android/app/Service.java
@@ -471,14 +471,6 @@
* {@link #onStart} and returns either {@link #START_STICKY}
* or {@link #START_STICKY_COMPATIBILITY}.
*
- * <p>If you need your application to run on platform versions prior to API
- * level 5, you can use the following model to handle the older {@link #onStart}
- * callback in that case. The <code>handleCommand</code> method is implemented by
- * you as appropriate:
- *
- * {@sample development/samples/ApiDemos/src/com/example/android/apis/app/ForegroundService.java
- * start_compatibility}
- *
* <p class="caution">Note that the system calls this on your
* service's main thread. A service's main thread is the same
* thread where UI operations take place for Activities running in the
@@ -687,6 +679,10 @@
* {@link #startService(Intent)} first to tell the system it should keep the service running,
* and then use this method to tell it to keep it running harder.</p>
*
+ * <p>Apps targeting API {@link android.os.Build.VERSION_CODES#P} or later must request
+ * the permission {@link android.Manifest.permission#FOREGROUND_SERVICE} in order to use
+ * this API.</p>
+ *
* @param id The identifier for this notification as per
* {@link NotificationManager#notify(int, Notification)
* NotificationManager.notify(int, Notification)}; must not be 0.
diff --git a/core/java/android/app/StatsManager.java b/core/java/android/app/StatsManager.java
index c525c89..c2c91c2 100644
--- a/core/java/android/app/StatsManager.java
+++ b/core/java/android/app/StatsManager.java
@@ -30,20 +30,27 @@
* @hide
*/
@SystemApi
-public final class StatsManager extends android.util.StatsManager { // TODO: Remove the extends.
+public final class StatsManager {
IStatsManager mService;
private static final String TAG = "StatsManager";
+ private static final boolean DEBUG = false;
- /** Long extra of uid that added the relevant stats config. */
- public static final String EXTRA_STATS_CONFIG_UID =
- "android.app.extra.STATS_CONFIG_UID";
- /** Long extra of the relevant stats config's configKey. */
- public static final String EXTRA_STATS_CONFIG_KEY =
- "android.app.extra.STATS_CONFIG_KEY";
- /** Long extra of the relevant statsd_config.proto's Subscription.id. */
+ /**
+ * Long extra of uid that added the relevant stats config.
+ */
+ public static final String EXTRA_STATS_CONFIG_UID = "android.app.extra.STATS_CONFIG_UID";
+ /**
+ * Long extra of the relevant stats config's configKey.
+ */
+ public static final String EXTRA_STATS_CONFIG_KEY = "android.app.extra.STATS_CONFIG_KEY";
+ /**
+ * Long extra of the relevant statsd_config.proto's Subscription.id.
+ */
public static final String EXTRA_STATS_SUBSCRIPTION_ID =
"android.app.extra.STATS_SUBSCRIPTION_ID";
- /** Long extra of the relevant statsd_config.proto's Subscription.rule_id. */
+ /**
+ * Long extra of the relevant statsd_config.proto's Subscription.rule_id.
+ */
public static final String EXTRA_STATS_SUBSCRIPTION_RULE_ID =
"android.app.extra.STATS_SUBSCRIPTION_RULE_ID";
/**
@@ -68,28 +75,34 @@
}
/**
+ * Temporary. Will be deleted.
+ */
+ @RequiresPermission(Manifest.permission.DUMP)
+ public boolean addConfiguration(long configKey, byte[] config, String a, String b) {
+ return addConfiguration(configKey, config);
+ }
+
+ /**
* Clients can send a configuration and simultaneously registers the name of a broadcast
* receiver that listens for when it should request data.
*
* @param configKey An arbitrary integer that allows clients to track the configuration.
* @param config Wire-encoded StatsDConfig proto that specifies metrics (and all
* dependencies eg, conditions and matchers).
- * @param pkg The package name to receive the broadcast.
- * @param cls The name of the class that receives the broadcast.
* @return true if successful
*/
@RequiresPermission(Manifest.permission.DUMP)
- public boolean addConfiguration(long configKey, byte[] config, String pkg, String cls) {
+ public boolean addConfiguration(long configKey, byte[] config) {
synchronized (this) {
try {
IStatsManager service = getIStatsManagerLocked();
if (service == null) {
- Slog.d(TAG, "Failed to find statsd when adding configuration");
+ if (DEBUG) Slog.d(TAG, "Failed to find statsd when adding configuration");
return false;
}
- return service.addConfiguration(configKey, config, pkg, cls);
+ return service.addConfiguration(configKey, config);
} catch (RemoteException e) {
- Slog.d(TAG, "Failed to connect to statsd when adding configuration");
+ if (DEBUG) Slog.d(TAG, "Failed to connect to statsd when adding configuration");
return false;
}
}
@@ -107,12 +120,12 @@
try {
IStatsManager service = getIStatsManagerLocked();
if (service == null) {
- Slog.d(TAG, "Failed to find statsd when removing configuration");
+ if (DEBUG) Slog.d(TAG, "Failed to find statsd when removing configuration");
return false;
}
return service.removeConfiguration(configKey);
} catch (RemoteException e) {
- Slog.d(TAG, "Failed to connect to statsd when removing configuration");
+ if (DEBUG) Slog.d(TAG, "Failed to connect to statsd when removing configuration");
return false;
}
}
@@ -121,38 +134,34 @@
/**
* Set the PendingIntent to be used when broadcasting subscriber information to the given
* subscriberId within the given config.
- *
* <p>
* Suppose that the calling uid has added a config with key configKey, and that in this config
* it is specified that when a particular anomaly is detected, a broadcast should be sent to
* a BroadcastSubscriber with id subscriberId. This function links the given pendingIntent with
* that subscriberId (for that config), so that this pendingIntent is used to send the broadcast
* when the anomaly is detected.
- *
* <p>
* When statsd sends the broadcast, the PendingIntent will used to send an intent with
* information of
- * {@link #EXTRA_STATS_CONFIG_UID},
- * {@link #EXTRA_STATS_CONFIG_KEY},
- * {@link #EXTRA_STATS_SUBSCRIPTION_ID},
- * {@link #EXTRA_STATS_SUBSCRIPTION_RULE_ID}, and
- * {@link #EXTRA_STATS_DIMENSIONS_VALUE}.
- *
+ * {@link #EXTRA_STATS_CONFIG_UID},
+ * {@link #EXTRA_STATS_CONFIG_KEY},
+ * {@link #EXTRA_STATS_SUBSCRIPTION_ID},
+ * {@link #EXTRA_STATS_SUBSCRIPTION_RULE_ID}, and
+ * {@link #EXTRA_STATS_DIMENSIONS_VALUE}.
* <p>
* This function can only be called by the owner (uid) of the config. It must be called each
* time statsd starts. The config must have been added first (via addConfiguration()).
*
- * @param configKey The integer naming the config to which this subscriber is attached.
- * @param subscriberId ID of the subscriber, as used in the config.
+ * @param configKey The integer naming the config to which this subscriber is attached.
+ * @param subscriberId ID of the subscriber, as used in the config.
* @param pendingIntent the PendingIntent to use when broadcasting info to the subscriber
* associated with the given subscriberId. May be null, in which case
* it undoes any previous setting of this subscriberId.
* @return true if successful
*/
@RequiresPermission(Manifest.permission.DUMP)
- public boolean setBroadcastSubscriber(long configKey,
- long subscriberId,
- PendingIntent pendingIntent) {
+ public boolean setBroadcastSubscriber(
+ long configKey, long subscriberId, PendingIntent pendingIntent) {
synchronized (this) {
try {
IStatsManager service = getIStatsManagerLocked();
@@ -175,6 +184,44 @@
}
/**
+ * Registers the operation that is called to retrieve the metrics data. This must be called
+ * each time statsd starts. The config must have been added first (via addConfiguration(),
+ * although addConfiguration could have been called on a previous boot). This operation allows
+ * statsd to send metrics data whenever statsd determines that the metrics in memory are
+ * approaching the memory limits. The fetch operation should call {@link #getData} to fetch the
+ * data, which also deletes the retrieved metrics from statsd's memory.
+ *
+ * @param configKey The integer naming the config to which this operation is attached.
+ * @param pendingIntent the PendingIntent to use when broadcasting info to the subscriber
+ * associated with the given subscriberId. May be null, in which case
+ * it removes any associated pending intent with this configKey.
+ * @return true if successful
+ */
+ @RequiresPermission(Manifest.permission.DUMP)
+ public boolean setDataFetchOperation(long configKey, PendingIntent pendingIntent) {
+ synchronized (this) {
+ try {
+ IStatsManager service = getIStatsManagerLocked();
+ if (service == null) {
+ Slog.d(TAG, "Failed to find statsd when registering data listener.");
+ return false;
+ }
+ if (pendingIntent == null) {
+ return service.removeDataFetchOperation(configKey);
+ } else {
+ // Extracts IIntentSender from the PendingIntent and turns it into an IBinder.
+ IBinder intentSender = pendingIntent.getTarget().asBinder();
+ return service.setDataFetchOperation(configKey, intentSender);
+ }
+
+ } catch (RemoteException e) {
+ Slog.d(TAG, "Failed to connect to statsd when registering data listener.");
+ return false;
+ }
+ }
+ }
+
+ /**
* Clients can request data with a binder call. This getter is destructive and also clears
* the retrieved metrics from statsd memory.
*
@@ -187,12 +234,12 @@
try {
IStatsManager service = getIStatsManagerLocked();
if (service == null) {
- Slog.d(TAG, "Failed to find statsd when getting data");
+ if (DEBUG) Slog.d(TAG, "Failed to find statsd when getting data");
return null;
}
return service.getData(configKey);
} catch (RemoteException e) {
- Slog.d(TAG, "Failed to connecto statsd when getting data");
+ if (DEBUG) Slog.d(TAG, "Failed to connecto statsd when getting data");
return null;
}
}
@@ -211,12 +258,12 @@
try {
IStatsManager service = getIStatsManagerLocked();
if (service == null) {
- Slog.d(TAG, "Failed to find statsd when getting metadata");
+ if (DEBUG) Slog.d(TAG, "Failed to find statsd when getting metadata");
return null;
}
return service.getMetadata();
} catch (RemoteException e) {
- Slog.d(TAG, "Failed to connecto statsd when getting metadata");
+ if (DEBUG) Slog.d(TAG, "Failed to connecto statsd when getting metadata");
return null;
}
}
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index 87f32b2..aa52cde 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -38,7 +38,6 @@
import android.content.Context;
import android.content.IRestrictionsManager;
import android.content.RestrictionsManager;
-import android.content.pm.ApplicationInfo;
import android.content.pm.CrossProfileApps;
import android.content.pm.ICrossProfileApps;
import android.content.pm.IShortcutService;
@@ -90,7 +89,6 @@
import android.net.lowpan.LowpanManager;
import android.net.nsd.INsdManager;
import android.net.nsd.NsdManager;
-import android.net.wifi.IRttManager;
import android.net.wifi.IWifiManager;
import android.net.wifi.IWifiScanner;
import android.net.wifi.RttManager;
@@ -117,7 +115,6 @@
import android.os.IUserManager;
import android.os.IncidentManager;
import android.os.PowerManager;
-import android.os.Process;
import android.os.RecoverySystem;
import android.os.ServiceManager;
import android.os.ServiceManager.ServiceNotFoundException;
@@ -639,13 +636,13 @@
registerService(Context.WIFI_RTT_SERVICE, RttManager.class,
new CachedServiceFetcher<RttManager>() {
- @Override
- public RttManager createService(ContextImpl ctx) throws ServiceNotFoundException {
- IBinder b = ServiceManager.getServiceOrThrow(Context.WIFI_RTT_SERVICE);
- IRttManager service = IRttManager.Stub.asInterface(b);
- return new RttManager(ctx.getOuterContext(), service,
- ConnectivityThread.getInstanceLooper());
- }});
+ @Override
+ public RttManager createService(ContextImpl ctx) throws ServiceNotFoundException {
+ IBinder b = ServiceManager.getServiceOrThrow(Context.WIFI_RTT_RANGING_SERVICE);
+ IWifiRttManager service = IWifiRttManager.Stub.asInterface(b);
+ return new RttManager(ctx.getOuterContext(),
+ new WifiRttManager(ctx.getOuterContext(), service));
+ }});
registerService(Context.WIFI_RTT_RANGING_SERVICE, WifiRttManager.class,
new CachedServiceFetcher<WifiRttManager>() {
diff --git a/core/java/android/app/admin/SecurityLog.java b/core/java/android/app/admin/SecurityLog.java
index 08effd9..202b894 100644
--- a/core/java/android/app/admin/SecurityLog.java
+++ b/core/java/android/app/admin/SecurityLog.java
@@ -77,6 +77,7 @@
TAG_KEY_DESTRUCTION,
TAG_CERT_AUTHORITY_INSTALLED,
TAG_CERT_AUTHORITY_REMOVED,
+ TAG_CRYPTO_SELF_TEST_COMPLETED,
})
public @interface SecurityLogTag {}
@@ -400,6 +401,14 @@
SecurityLogTags.SECURITY_USER_RESTRICTION_REMOVED;
/**
+ * Indicates that cryptographic functionality self test has completed. The log entry contains an
+ * {@code Integer} payload, indicating the result of the test (0 if the test failed, 1 if
+ * succeeded) and accessible via {@link SecurityEvent#getData()}.
+ */
+ public static final int TAG_CRYPTO_SELF_TEST_COMPLETED =
+ SecurityLogTags.SECURITY_CRYPTO_SELF_TEST_COMPLETED;
+
+ /**
* Event severity level indicating that the event corresponds to normal workflow.
*/
public static final int LEVEL_INFO = 1;
@@ -529,6 +538,7 @@
case TAG_USER_RESTRICTION_REMOVED:
return LEVEL_INFO;
case TAG_CERT_AUTHORITY_REMOVED:
+ case TAG_CRYPTO_SELF_TEST_COMPLETED:
return getSuccess() ? LEVEL_INFO : LEVEL_ERROR;
case TAG_CERT_AUTHORITY_INSTALLED:
case TAG_KEYGUARD_DISMISS_AUTH_ATTEMPT:
diff --git a/core/java/android/app/admin/SecurityLogTags.logtags b/core/java/android/app/admin/SecurityLogTags.logtags
index be62678..b64b7e3 100644
--- a/core/java/android/app/admin/SecurityLogTags.logtags
+++ b/core/java/android/app/admin/SecurityLogTags.logtags
@@ -34,4 +34,5 @@
210027 security_user_restriction_added (package|3),(admin_user|1),(restriction|3)
210028 security_user_restriction_removed (package|3),(admin_user|1),(restriction|3)
210029 security_cert_authority_installed (success|1),(subject|3)
-210030 security_cert_authority_removed (success|1),(subject|3)
\ No newline at end of file
+210030 security_cert_authority_removed (success|1),(subject|3)
+210031 security_crypto_self_test_completed (success|1)
\ No newline at end of file
diff --git a/core/java/android/app/servertransaction/ActivityRelaunchItem.java b/core/java/android/app/servertransaction/ActivityRelaunchItem.java
new file mode 100644
index 0000000..d8a7463
--- /dev/null
+++ b/core/java/android/app/servertransaction/ActivityRelaunchItem.java
@@ -0,0 +1,176 @@
+/*
+ * 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.app.servertransaction;
+
+import static android.app.ActivityThread.DEBUG_ORDER;
+
+import android.app.ActivityThread;
+import android.app.ClientTransactionHandler;
+import android.app.ResultInfo;
+import android.os.IBinder;
+import android.os.Parcel;
+import android.os.Trace;
+import android.util.MergedConfiguration;
+import android.util.Slog;
+
+import com.android.internal.content.ReferrerIntent;
+
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * Activity relaunch callback.
+ * @hide
+ */
+public class ActivityRelaunchItem extends ClientTransactionItem {
+
+ private static final String TAG = "ActivityRelaunchItem";
+
+ private List<ResultInfo> mPendingResults;
+ private List<ReferrerIntent> mPendingNewIntents;
+ private int mConfigChanges;
+ private MergedConfiguration mConfig;
+ private boolean mPreserveWindow;
+
+ /**
+ * A record that was properly configured for relaunch. Execution will be cancelled if not
+ * initialized after {@link #preExecute(ClientTransactionHandler, IBinder)}.
+ */
+ private ActivityThread.ActivityClientRecord mActivityClientRecord;
+
+ @Override
+ public void preExecute(ClientTransactionHandler client, IBinder token) {
+ mActivityClientRecord = client.prepareRelaunchActivity(token, mPendingResults,
+ mPendingNewIntents, mConfigChanges, mConfig, mPreserveWindow);
+ }
+
+ @Override
+ public void execute(ClientTransactionHandler client, IBinder token,
+ PendingTransactionActions pendingActions) {
+ if (mActivityClientRecord == null) {
+ if (DEBUG_ORDER) Slog.d(TAG, "Activity relaunch cancelled");
+ return;
+ }
+ Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityRestart");
+ client.handleRelaunchActivity(mActivityClientRecord, pendingActions);
+ Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
+ }
+
+ @Override
+ public void postExecute(ClientTransactionHandler client, IBinder token,
+ PendingTransactionActions pendingActions) {
+ client.reportRelaunch(token, pendingActions);
+ }
+
+ // ObjectPoolItem implementation
+
+ private ActivityRelaunchItem() {}
+
+ /** Obtain an instance initialized with provided params. */
+ public static ActivityRelaunchItem obtain(List<ResultInfo> pendingResults,
+ List<ReferrerIntent> pendingNewIntents, int configChanges, MergedConfiguration config,
+ boolean preserveWindow) {
+ ActivityRelaunchItem instance = ObjectPool.obtain(ActivityRelaunchItem.class);
+ if (instance == null) {
+ instance = new ActivityRelaunchItem();
+ }
+ instance.mPendingResults = pendingResults;
+ instance.mPendingNewIntents = pendingNewIntents;
+ instance.mConfigChanges = configChanges;
+ instance.mConfig = config;
+ instance.mPreserveWindow = preserveWindow;
+
+ return instance;
+ }
+
+ @Override
+ public void recycle() {
+ mPendingResults = null;
+ mPendingNewIntents = null;
+ mConfigChanges = 0;
+ mConfig = null;
+ mPreserveWindow = false;
+ mActivityClientRecord = null;
+ ObjectPool.recycle(this);
+ }
+
+
+ // Parcelable implementation
+
+ /** Write to Parcel. */
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeTypedList(mPendingResults, flags);
+ dest.writeTypedList(mPendingNewIntents, flags);
+ dest.writeInt(mConfigChanges);
+ dest.writeTypedObject(mConfig, flags);
+ dest.writeBoolean(mPreserveWindow);
+ }
+
+ /** Read from Parcel. */
+ private ActivityRelaunchItem(Parcel in) {
+ mPendingResults = in.createTypedArrayList(ResultInfo.CREATOR);
+ mPendingNewIntents = in.createTypedArrayList(ReferrerIntent.CREATOR);
+ mConfigChanges = in.readInt();
+ mConfig = in.readTypedObject(MergedConfiguration.CREATOR);
+ mPreserveWindow = in.readBoolean();
+ }
+
+ public static final Creator<ActivityRelaunchItem> CREATOR =
+ new Creator<ActivityRelaunchItem>() {
+ public ActivityRelaunchItem createFromParcel(Parcel in) {
+ return new ActivityRelaunchItem(in);
+ }
+
+ public ActivityRelaunchItem[] newArray(int size) {
+ return new ActivityRelaunchItem[size];
+ }
+ };
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ final ActivityRelaunchItem other = (ActivityRelaunchItem) o;
+ return Objects.equals(mPendingResults, other.mPendingResults)
+ && Objects.equals(mPendingNewIntents, other.mPendingNewIntents)
+ && mConfigChanges == other.mConfigChanges && Objects.equals(mConfig, other.mConfig)
+ && mPreserveWindow == other.mPreserveWindow;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = 17;
+ result = 31 * result + Objects.hashCode(mPendingResults);
+ result = 31 * result + Objects.hashCode(mPendingNewIntents);
+ result = 31 * result + mConfigChanges;
+ result = 31 * result + Objects.hashCode(mConfig);
+ result = 31 * result + (mPreserveWindow ? 1 : 0);
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ return "ActivityRelaunchItem{pendingResults=" + mPendingResults
+ + ",pendingNewIntents=" + mPendingNewIntents + ",configChanges=" + mConfigChanges
+ + ",config=" + mConfig + ",preserveWindow" + mPreserveWindow + "}";
+ }
+}
diff --git a/core/java/android/app/servertransaction/DestroyActivityItem.java b/core/java/android/app/servertransaction/DestroyActivityItem.java
index cbcf6c7..48a79f7 100644
--- a/core/java/android/app/servertransaction/DestroyActivityItem.java
+++ b/core/java/android/app/servertransaction/DestroyActivityItem.java
@@ -37,7 +37,7 @@
PendingTransactionActions pendingActions) {
Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "activityDestroy");
client.handleDestroyActivity(token, mFinished, mConfigChanges,
- false /* getNonConfigInstance */);
+ false /* getNonConfigInstance */, getDescription());
Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
}
diff --git a/core/java/android/app/servertransaction/PendingTransactionActions.java b/core/java/android/app/servertransaction/PendingTransactionActions.java
index 073d28c..af7b7a2 100644
--- a/core/java/android/app/servertransaction/PendingTransactionActions.java
+++ b/core/java/android/app/servertransaction/PendingTransactionActions.java
@@ -44,6 +44,7 @@
private boolean mCallOnPostCreate;
private Bundle mOldState;
private StopInfo mStopInfo;
+ private boolean mReportRelaunchToWM;
public PendingTransactionActions() {
clear();
@@ -91,6 +92,24 @@
mStopInfo = stopInfo;
}
+ /**
+ * Check if we should report an activity relaunch to WindowManager. We report back for every
+ * relaunch request to ActivityManager, but only for those that were actually finished to we
+ * report to WindowManager.
+ */
+ public boolean shouldReportRelaunchToWindowManager() {
+ return mReportRelaunchToWM;
+ }
+
+ /**
+ * Set if we should report an activity relaunch to WindowManager. We report back for every
+ * relaunch request to ActivityManager, but only for those that were actually finished we report
+ * to WindowManager.
+ */
+ public void setReportRelaunchToWindowManager(boolean reportToWm) {
+ mReportRelaunchToWM = reportToWm;
+ }
+
/** Reports to server about activity stop. */
public static class StopInfo implements Runnable {
private static final String TAG = "ActivityStopInfo";
diff --git a/core/java/android/app/servertransaction/TransactionExecutor.java b/core/java/android/app/servertransaction/TransactionExecutor.java
index 78b393a..840fef8 100644
--- a/core/java/android/app/servertransaction/TransactionExecutor.java
+++ b/core/java/android/app/servertransaction/TransactionExecutor.java
@@ -194,7 +194,9 @@
break;
case ON_DESTROY:
mTransactionHandler.handleDestroyActivity(r.token, false /* finishing */,
- 0 /* configChanges */, false /* getNonConfigInstance */);
+ 0 /* configChanges */, false /* getNonConfigInstance */,
+ "performLifecycleSequence. cycling to:"
+ + mLifecycleSequence.get(size - 1));
break;
case ON_RESTART:
mTransactionHandler.performRestartActivity(r.token, false /* start */);
diff --git a/core/java/android/app/usage/UsageEvents.java b/core/java/android/app/usage/UsageEvents.java
index edb992b..6b573e9 100644
--- a/core/java/android/app/usage/UsageEvents.java
+++ b/core/java/android/app/usage/UsageEvents.java
@@ -16,6 +16,7 @@
package android.app.usage;
import android.annotation.IntDef;
+import android.annotation.SystemApi;
import android.content.res.Configuration;
import android.os.Parcel;
import android.os.Parcelable;
@@ -104,12 +105,14 @@
* An event type denoting that a notification was viewed by the user.
* @hide
*/
+ @SystemApi
public static final int NOTIFICATION_SEEN = 10;
/**
* An event type denoting a change in App Standby Bucket.
* @hide
*/
+ @SystemApi
public static final int STANDBY_BUCKET_CHANGED = 11;
/** @hide */
@@ -257,6 +260,17 @@
return mShortcutId;
}
+ /**
+ * Returns the standby bucket of the app, if the event is of type
+ * {@link #STANDBY_BUCKET_CHANGED}, otherwise returns 0.
+ * @return the standby bucket associated with the event.
+ * @hide
+ */
+ @SystemApi
+ public int getStandbyBucket() {
+ return mBucket;
+ }
+
/** @hide */
public Event getObfuscatedIfInstantApp() {
if ((mFlags & FLAG_IS_PACKAGE_INSTANT_APP) == 0) {
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index b85a319..15f3777 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -560,6 +560,7 @@
*
* @param resId Resource id for the CharSequence text
*/
+ @NonNull
public final CharSequence getText(@StringRes int resId) {
return getResources().getText(resId);
}
@@ -616,12 +617,11 @@
* @param id The desired resource identifier, as generated by the aapt
* tool. This integer encodes the package, type, and resource
* entry. The value 0 is an invalid identifier.
- * @return An object that can be used to draw this resource, or
- * {@code null} if the resource could not be resolved.
+ * @return An object that can be used to draw this resource.
* @throws android.content.res.Resources.NotFoundException if the given ID
* does not exist.
*/
- @Nullable
+ @NonNull
public final Drawable getDrawable(@DrawableRes int id) {
return getResources().getDrawable(id, getTheme());
}
@@ -633,12 +633,11 @@
* @param id The desired resource identifier, as generated by the aapt
* tool. This integer encodes the package, type, and resource
* entry. The value 0 is an invalid identifier.
- * @return A color state list, or {@code null} if the resource could not be
- * resolved.
+ * @return A color state list.
* @throws android.content.res.Resources.NotFoundException if the given ID
* does not exist.
*/
- @Nullable
+ @NonNull
public final ColorStateList getColorStateList(@ColorRes int id) {
return getResources().getColorStateList(id, getTheme());
}
@@ -3539,6 +3538,7 @@
* @hide
*/
@SystemApi
+ @Deprecated
public static final String WIFI_RTT_SERVICE = "rttmanager";
/**
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index fa73e3c..12d4079 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -2399,6 +2399,26 @@
*/
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_CONFIGURATION_CHANGED = "android.intent.action.CONFIGURATION_CHANGED";
+
+ /**
+ * Broadcast Action: The current device {@link android.content.res.Configuration} has changed
+ * such that the device may be eligible for the installation of additional configuration splits.
+ * Configuration properties that can trigger this broadcast include locale and display density.
+ *
+ * <p class="note">
+ * Unlike {@link #ACTION_CONFIGURATION_CHANGED}, you <em>can</em> receive this through
+ * components declared in manifests. However, the receiver <em>must</em> hold the
+ * {@link android.Manifest.permission#INSTALL_PACKAGES} permission.
+ *
+ * <p class="note">
+ * This is a protected intent that can only be sent by the system.
+ *
+ * @hide
+ */
+ @SystemApi
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String ACTION_SPLIT_CONFIGURATION_CHANGED =
+ "android.intent.action.SPLIT_CONFIGURATION_CHANGED";
/**
* Broadcast Action: The current device's locale has changed.
*
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 377942a..dda4167 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -54,7 +54,6 @@
import android.content.pm.split.DefaultSplitAssetLoader;
import android.content.pm.split.SplitAssetDependencyLoader;
import android.content.pm.split.SplitAssetLoader;
-import android.content.res.ApkAssets;
import android.content.res.AssetManager;
import android.content.res.Configuration;
import android.content.res.Resources;
@@ -1287,6 +1286,7 @@
*/
@Deprecated
public Package parseMonolithicPackage(File apkFile, int flags) throws PackageParserException {
+ final AssetManager assets = newConfiguredAssetManager();
final PackageLite lite = parseMonolithicPackageLite(apkFile, flags);
if (mOnlyCoreApps) {
if (!lite.coreApp) {
@@ -1295,9 +1295,8 @@
}
}
- final SplitAssetLoader assetLoader = new DefaultSplitAssetLoader(lite, flags);
try {
- final Package pkg = parseBaseApk(apkFile, assetLoader.getBaseAssetManager(), flags);
+ final Package pkg = parseBaseApk(apkFile, assets, flags);
pkg.setCodePath(apkFile.getCanonicalPath());
pkg.setUse32bitAbi(lite.use32bitAbi);
return pkg;
@@ -1305,10 +1304,28 @@
throw new PackageParserException(INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION,
"Failed to get path: " + apkFile, e);
} finally {
- IoUtils.closeQuietly(assetLoader);
+ IoUtils.closeQuietly(assets);
}
}
+ private static int loadApkIntoAssetManager(AssetManager assets, String apkPath, int flags)
+ throws PackageParserException {
+ if ((flags & PARSE_MUST_BE_APK) != 0 && !isApkPath(apkPath)) {
+ throw new PackageParserException(INSTALL_PARSE_FAILED_NOT_APK,
+ "Invalid package file: " + apkPath);
+ }
+
+ // The AssetManager guarantees uniqueness for asset paths, so if this asset path
+ // already exists in the AssetManager, addAssetPath will only return the cookie
+ // assigned to it.
+ int cookie = assets.addAssetPath(apkPath);
+ if (cookie == 0) {
+ throw new PackageParserException(INSTALL_PARSE_FAILED_BAD_MANIFEST,
+ "Failed adding asset path: " + apkPath);
+ }
+ return cookie;
+ }
+
private Package parseBaseApk(File apkFile, AssetManager assets, int flags)
throws PackageParserException {
final String apkPath = apkFile.getAbsolutePath();
@@ -1324,15 +1341,13 @@
if (DEBUG_JAR) Slog.d(TAG, "Scanning base APK: " + apkPath);
+ final int cookie = loadApkIntoAssetManager(assets, apkPath, flags);
+
+ Resources res = null;
XmlResourceParser parser = null;
try {
- final int cookie = assets.findCookieForPath(apkPath);
- if (cookie == 0) {
- throw new PackageParserException(INSTALL_PARSE_FAILED_BAD_MANIFEST,
- "Failed adding asset path: " + apkPath);
- }
+ res = new Resources(assets, mMetrics, null);
parser = assets.openXmlResourceParser(cookie, ANDROID_MANIFEST_FILENAME);
- final Resources res = new Resources(assets, mMetrics, null);
final String[] outError = new String[1];
final Package pkg = parseBaseApk(apkPath, res, parser, flags, outError);
@@ -1367,18 +1382,15 @@
if (DEBUG_JAR) Slog.d(TAG, "Scanning split APK: " + apkPath);
+ final int cookie = loadApkIntoAssetManager(assets, apkPath, flags);
+
final Resources res;
XmlResourceParser parser = null;
try {
- // This must always succeed, as the path has been added to the AssetManager before.
- final int cookie = assets.findCookieForPath(apkPath);
- if (cookie == 0) {
- throw new PackageParserException(INSTALL_PARSE_FAILED_BAD_MANIFEST,
- "Failed adding asset path: " + apkPath);
- }
-
- parser = assets.openXmlResourceParser(cookie, ANDROID_MANIFEST_FILENAME);
res = new Resources(assets, mMetrics, null);
+ assets.setConfiguration(0, 0, null, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ Build.VERSION.RESOURCES_SDK_INT);
+ parser = assets.openXmlResourceParser(cookie, ANDROID_MANIFEST_FILENAME);
final String[] outError = new String[1];
pkg = parseSplitApk(pkg, res, parser, flags, splitIndex, outError);
@@ -1580,19 +1592,21 @@
int flags) throws PackageParserException {
final String apkPath = fd != null ? debugPathName : apkFile.getAbsolutePath();
+ AssetManager assets = null;
XmlResourceParser parser = null;
try {
- final ApkAssets apkAssets;
- try {
- apkAssets = fd != null
- ? ApkAssets.loadFromFd(fd, debugPathName, false, false)
- : ApkAssets.loadFromPath(apkPath);
- } catch (IOException e) {
+ assets = newConfiguredAssetManager();
+ int cookie = fd != null
+ ? assets.addAssetFd(fd, debugPathName) : assets.addAssetPath(apkPath);
+ if (cookie == 0) {
throw new PackageParserException(INSTALL_PARSE_FAILED_NOT_APK,
"Failed to parse " + apkPath);
}
- parser = apkAssets.openXml(ANDROID_MANIFEST_FILENAME);
+ final DisplayMetrics metrics = new DisplayMetrics();
+ metrics.setToDefaults();
+
+ parser = assets.openXmlResourceParser(cookie, ANDROID_MANIFEST_FILENAME);
final SigningDetails signingDetails;
if ((flags & PARSE_COLLECT_CERTIFICATES) != 0) {
@@ -1619,7 +1633,7 @@
"Failed to parse " + apkPath, e);
} finally {
IoUtils.closeQuietly(parser);
- // TODO(b/72056911): Implement and call close() on ApkAssets.
+ IoUtils.closeQuietly(assets);
}
}
diff --git a/core/java/android/content/pm/VerifierDeviceIdentity.java b/core/java/android/content/pm/VerifierDeviceIdentity.java
index a8cdb6a..90be6f31 100644
--- a/core/java/android/content/pm/VerifierDeviceIdentity.java
+++ b/core/java/android/content/pm/VerifierDeviceIdentity.java
@@ -19,6 +19,8 @@
import android.os.Parcel;
import android.os.Parcelable;
+import com.android.internal.annotations.VisibleForTesting;
+
import java.io.UnsupportedEncodingException;
import java.security.SecureRandom;
import java.util.Random;
@@ -86,6 +88,7 @@
* @return verifier device identity based on the input from the provided
* random number generator
*/
+ @VisibleForTesting
static VerifierDeviceIdentity generate(Random rng) {
long identity = rng.nextLong();
return new VerifierDeviceIdentity(identity);
diff --git a/core/java/android/content/pm/permission/RuntimePermissionPresenter.java b/core/java/android/content/pm/permission/RuntimePermissionPresenter.java
index 02d0a6d..79bc9a3 100644
--- a/core/java/android/content/pm/permission/RuntimePermissionPresenter.java
+++ b/core/java/android/content/pm/permission/RuntimePermissionPresenter.java
@@ -274,6 +274,7 @@
}
}
+ @GuardedBy("mLock")
private void scheduleNextMessageIfNeededLocked() {
if (mBound && mRemoteInstance != null && !mPendingWork.isEmpty()) {
Message nextMessage = mPendingWork.remove(0);
diff --git a/core/java/android/content/pm/split/DefaultSplitAssetLoader.java b/core/java/android/content/pm/split/DefaultSplitAssetLoader.java
index 9e3a8f4..99eb470 100644
--- a/core/java/android/content/pm/split/DefaultSplitAssetLoader.java
+++ b/core/java/android/content/pm/split/DefaultSplitAssetLoader.java
@@ -15,13 +15,10 @@
*/
package android.content.pm.split;
-import static android.content.pm.PackageManager.INSTALL_FAILED_INVALID_APK;
+import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_BAD_MANIFEST;
import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_NOT_APK;
import android.content.pm.PackageParser;
-import android.content.pm.PackageParser.PackageParserException;
-import android.content.pm.PackageParser.ParseFlags;
-import android.content.res.ApkAssets;
import android.content.res.AssetManager;
import android.os.Build;
@@ -29,8 +26,6 @@
import libcore.io.IoUtils;
-import java.io.IOException;
-
/**
* Loads the base and split APKs into a single AssetManager.
* @hide
@@ -38,66 +33,68 @@
public class DefaultSplitAssetLoader implements SplitAssetLoader {
private final String mBaseCodePath;
private final String[] mSplitCodePaths;
- private final @ParseFlags int mFlags;
+ private final int mFlags;
+
private AssetManager mCachedAssetManager;
- public DefaultSplitAssetLoader(PackageParser.PackageLite pkg, @ParseFlags int flags) {
+ public DefaultSplitAssetLoader(PackageParser.PackageLite pkg, int flags) {
mBaseCodePath = pkg.baseCodePath;
mSplitCodePaths = pkg.splitCodePaths;
mFlags = flags;
}
- private static ApkAssets loadApkAssets(String path, @ParseFlags int flags)
- throws PackageParserException {
- if ((flags & PackageParser.PARSE_MUST_BE_APK) != 0 && !PackageParser.isApkPath(path)) {
- throw new PackageParserException(INSTALL_PARSE_FAILED_NOT_APK,
- "Invalid package file: " + path);
+ private static void loadApkIntoAssetManager(AssetManager assets, String apkPath, int flags)
+ throws PackageParser.PackageParserException {
+ if ((flags & PackageParser.PARSE_MUST_BE_APK) != 0 && !PackageParser.isApkPath(apkPath)) {
+ throw new PackageParser.PackageParserException(INSTALL_PARSE_FAILED_NOT_APK,
+ "Invalid package file: " + apkPath);
}
- try {
- return ApkAssets.loadFromPath(path);
- } catch (IOException e) {
- throw new PackageParserException(INSTALL_FAILED_INVALID_APK,
- "Failed to load APK at path " + path, e);
+ if (assets.addAssetPath(apkPath) == 0) {
+ throw new PackageParser.PackageParserException(
+ INSTALL_PARSE_FAILED_BAD_MANIFEST,
+ "Failed adding asset path: " + apkPath);
}
}
@Override
- public AssetManager getBaseAssetManager() throws PackageParserException {
+ public AssetManager getBaseAssetManager() throws PackageParser.PackageParserException {
if (mCachedAssetManager != null) {
return mCachedAssetManager;
}
- ApkAssets[] apkAssets = new ApkAssets[(mSplitCodePaths != null
- ? mSplitCodePaths.length : 0) + 1];
+ AssetManager assets = new AssetManager();
+ try {
+ assets.setConfiguration(0, 0, null, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ Build.VERSION.RESOURCES_SDK_INT);
+ loadApkIntoAssetManager(assets, mBaseCodePath, mFlags);
- // Load the base.
- int splitIdx = 0;
- apkAssets[splitIdx++] = loadApkAssets(mBaseCodePath, mFlags);
+ if (!ArrayUtils.isEmpty(mSplitCodePaths)) {
+ for (String apkPath : mSplitCodePaths) {
+ loadApkIntoAssetManager(assets, apkPath, mFlags);
+ }
+ }
- // Load any splits.
- if (!ArrayUtils.isEmpty(mSplitCodePaths)) {
- for (String apkPath : mSplitCodePaths) {
- apkAssets[splitIdx++] = loadApkAssets(apkPath, mFlags);
+ mCachedAssetManager = assets;
+ assets = null;
+ return mCachedAssetManager;
+ } finally {
+ if (assets != null) {
+ IoUtils.closeQuietly(assets);
}
}
-
- AssetManager assets = new AssetManager();
- assets.setConfiguration(0, 0, null, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- Build.VERSION.RESOURCES_SDK_INT);
- assets.setApkAssets(apkAssets, false /*invalidateCaches*/);
-
- mCachedAssetManager = assets;
- return mCachedAssetManager;
}
@Override
- public AssetManager getSplitAssetManager(int splitIdx) throws PackageParserException {
+ public AssetManager getSplitAssetManager(int splitIdx)
+ throws PackageParser.PackageParserException {
return getBaseAssetManager();
}
@Override
public void close() throws Exception {
- IoUtils.closeQuietly(mCachedAssetManager);
+ if (mCachedAssetManager != null) {
+ IoUtils.closeQuietly(mCachedAssetManager);
+ }
}
}
diff --git a/core/java/android/content/pm/split/SplitAssetDependencyLoader.java b/core/java/android/content/pm/split/SplitAssetDependencyLoader.java
index 58eaabf..16023f0 100644
--- a/core/java/android/content/pm/split/SplitAssetDependencyLoader.java
+++ b/core/java/android/content/pm/split/SplitAssetDependencyLoader.java
@@ -15,21 +15,17 @@
*/
package android.content.pm.split;
+import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_BAD_MANIFEST;
import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_NOT_APK;
import android.annotation.NonNull;
-import android.content.pm.PackageManager;
import android.content.pm.PackageParser;
-import android.content.pm.PackageParser.PackageParserException;
-import android.content.pm.PackageParser.ParseFlags;
-import android.content.res.ApkAssets;
import android.content.res.AssetManager;
import android.os.Build;
import android.util.SparseArray;
import libcore.io.IoUtils;
-import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
@@ -38,15 +34,17 @@
* is to be used when an application opts-in to isolated split loading.
* @hide
*/
-public class SplitAssetDependencyLoader extends SplitDependencyLoader<PackageParserException>
+public class SplitAssetDependencyLoader
+ extends SplitDependencyLoader<PackageParser.PackageParserException>
implements SplitAssetLoader {
private final String[] mSplitPaths;
- private final @ParseFlags int mFlags;
- private final ApkAssets[][] mCachedSplitApks;
- private final AssetManager[] mCachedAssetManagers;
+ private final int mFlags;
+
+ private String[][] mCachedPaths;
+ private AssetManager[] mCachedAssetManagers;
public SplitAssetDependencyLoader(PackageParser.PackageLite pkg,
- SparseArray<int[]> dependencies, @ParseFlags int flags) {
+ SparseArray<int[]> dependencies, int flags) {
super(dependencies);
// The base is inserted into index 0, so we need to shift all the splits by 1.
@@ -55,7 +53,7 @@
System.arraycopy(pkg.splitCodePaths, 0, mSplitPaths, 1, pkg.splitCodePaths.length);
mFlags = flags;
- mCachedSplitApks = new ApkAssets[mSplitPaths.length][];
+ mCachedPaths = new String[mSplitPaths.length][];
mCachedAssetManagers = new AssetManager[mSplitPaths.length];
}
@@ -64,60 +62,58 @@
return mCachedAssetManagers[splitIdx] != null;
}
- private static ApkAssets loadApkAssets(String path, @ParseFlags int flags)
- throws PackageParserException {
- if ((flags & PackageParser.PARSE_MUST_BE_APK) != 0 && !PackageParser.isApkPath(path)) {
- throw new PackageParserException(INSTALL_PARSE_FAILED_NOT_APK,
- "Invalid package file: " + path);
- }
-
- try {
- return ApkAssets.loadFromPath(path);
- } catch (IOException e) {
- throw new PackageParserException(PackageManager.INSTALL_FAILED_INVALID_APK,
- "Failed to load APK at path " + path, e);
- }
- }
-
- private static AssetManager createAssetManagerWithAssets(ApkAssets[] apkAssets) {
+ private static AssetManager createAssetManagerWithPaths(String[] assetPaths, int flags)
+ throws PackageParser.PackageParserException {
final AssetManager assets = new AssetManager();
- assets.setConfiguration(0, 0, null, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- Build.VERSION.RESOURCES_SDK_INT);
- assets.setApkAssets(apkAssets, false /*invalidateCaches*/);
- return assets;
+ try {
+ assets.setConfiguration(0, 0, null, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ Build.VERSION.RESOURCES_SDK_INT);
+
+ for (String assetPath : assetPaths) {
+ if ((flags & PackageParser.PARSE_MUST_BE_APK) != 0 &&
+ !PackageParser.isApkPath(assetPath)) {
+ throw new PackageParser.PackageParserException(INSTALL_PARSE_FAILED_NOT_APK,
+ "Invalid package file: " + assetPath);
+ }
+
+ if (assets.addAssetPath(assetPath) == 0) {
+ throw new PackageParser.PackageParserException(
+ INSTALL_PARSE_FAILED_BAD_MANIFEST,
+ "Failed adding asset path: " + assetPath);
+ }
+ }
+ return assets;
+ } catch (Throwable e) {
+ IoUtils.closeQuietly(assets);
+ throw e;
+ }
}
@Override
protected void constructSplit(int splitIdx, @NonNull int[] configSplitIndices,
- int parentSplitIdx) throws PackageParserException {
- final ArrayList<ApkAssets> assets = new ArrayList<>();
-
- // Include parent ApkAssets.
+ int parentSplitIdx) throws PackageParser.PackageParserException {
+ final ArrayList<String> assetPaths = new ArrayList<>();
if (parentSplitIdx >= 0) {
- Collections.addAll(assets, mCachedSplitApks[parentSplitIdx]);
+ Collections.addAll(assetPaths, mCachedPaths[parentSplitIdx]);
}
- // Include this ApkAssets.
- assets.add(loadApkAssets(mSplitPaths[splitIdx], mFlags));
-
- // Load and include all config splits for this feature.
+ assetPaths.add(mSplitPaths[splitIdx]);
for (int configSplitIdx : configSplitIndices) {
- assets.add(loadApkAssets(mSplitPaths[configSplitIdx], mFlags));
+ assetPaths.add(mSplitPaths[configSplitIdx]);
}
-
- // Cache the results.
- mCachedSplitApks[splitIdx] = assets.toArray(new ApkAssets[assets.size()]);
- mCachedAssetManagers[splitIdx] = createAssetManagerWithAssets(mCachedSplitApks[splitIdx]);
+ mCachedPaths[splitIdx] = assetPaths.toArray(new String[assetPaths.size()]);
+ mCachedAssetManagers[splitIdx] = createAssetManagerWithPaths(mCachedPaths[splitIdx],
+ mFlags);
}
@Override
- public AssetManager getBaseAssetManager() throws PackageParserException {
+ public AssetManager getBaseAssetManager() throws PackageParser.PackageParserException {
loadDependenciesForSplit(0);
return mCachedAssetManagers[0];
}
@Override
- public AssetManager getSplitAssetManager(int idx) throws PackageParserException {
+ public AssetManager getSplitAssetManager(int idx) throws PackageParser.PackageParserException {
// Since we insert the base at position 0, and PackageParser keeps splits separate from
// the base, we need to adjust the index.
loadDependenciesForSplit(idx + 1);
diff --git a/core/java/android/content/res/ApkAssets.java b/core/java/android/content/res/ApkAssets.java
deleted file mode 100644
index fd664bc..0000000
--- a/core/java/android/content/res/ApkAssets.java
+++ /dev/null
@@ -1,190 +0,0 @@
-/*
- * 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.content.res;
-
-import android.annotation.NonNull;
-
-import com.android.internal.annotations.GuardedBy;
-import com.android.internal.util.Preconditions;
-
-import java.io.FileDescriptor;
-import java.io.IOException;
-
-/**
- * The loaded, immutable, in-memory representation of an APK.
- *
- * The main implementation is native C++ and there is very little API surface exposed here. The APK
- * is mainly accessed via {@link AssetManager}.
- *
- * Since the ApkAssets instance is immutable, it can be reused and shared across AssetManagers,
- * making the creation of AssetManagers very cheap.
- * @hide
- */
-public final class ApkAssets {
- @GuardedBy("this") private final long mNativePtr;
- @GuardedBy("this") private StringBlock mStringBlock;
-
- /**
- * Creates a new ApkAssets instance from the given path on disk.
- *
- * @param path The path to an APK on disk.
- * @return a new instance of ApkAssets.
- * @throws IOException if a disk I/O error or parsing error occurred.
- */
- public static @NonNull ApkAssets loadFromPath(@NonNull String path) throws IOException {
- return new ApkAssets(path, false /*system*/, false /*forceSharedLib*/, false /*overlay*/);
- }
-
- /**
- * Creates a new ApkAssets instance from the given path on disk.
- *
- * @param path The path to an APK on disk.
- * @param system When true, the APK is loaded as a system APK (framework).
- * @return a new instance of ApkAssets.
- * @throws IOException if a disk I/O error or parsing error occurred.
- */
- public static @NonNull ApkAssets loadFromPath(@NonNull String path, boolean system)
- throws IOException {
- return new ApkAssets(path, system, false /*forceSharedLib*/, false /*overlay*/);
- }
-
- /**
- * Creates a new ApkAssets instance from the given path on disk.
- *
- * @param path The path to an APK on disk.
- * @param system When true, the APK is loaded as a system APK (framework).
- * @param forceSharedLibrary When true, any packages within the APK with package ID 0x7f are
- * loaded as a shared library.
- * @return a new instance of ApkAssets.
- * @throws IOException if a disk I/O error or parsing error occurred.
- */
- public static @NonNull ApkAssets loadFromPath(@NonNull String path, boolean system,
- boolean forceSharedLibrary) throws IOException {
- return new ApkAssets(path, system, forceSharedLibrary, false /*overlay*/);
- }
-
- /**
- * Creates a new ApkAssets instance from the given file descriptor. Not for use by applications.
- *
- * Performs a dup of the underlying fd, so you must take care of still closing
- * the FileDescriptor yourself (and can do that whenever you want).
- *
- * @param fd The FileDescriptor of an open, readable APK.
- * @param friendlyName The friendly name used to identify this ApkAssets when logging.
- * @param system When true, the APK is loaded as a system APK (framework).
- * @param forceSharedLibrary When true, any packages within the APK with package ID 0x7f are
- * loaded as a shared library.
- * @return a new instance of ApkAssets.
- * @throws IOException if a disk I/O error or parsing error occurred.
- */
- public static @NonNull ApkAssets loadFromFd(@NonNull FileDescriptor fd,
- @NonNull String friendlyName, boolean system, boolean forceSharedLibrary)
- throws IOException {
- return new ApkAssets(fd, friendlyName, system, forceSharedLibrary);
- }
-
- /**
- * Creates a new ApkAssets instance from the IDMAP at idmapPath. The overlay APK path
- * is encoded within the IDMAP.
- *
- * @param idmapPath Path to the IDMAP of an overlay APK.
- * @param system When true, the APK is loaded as a system APK (framework).
- * @return a new instance of ApkAssets.
- * @throws IOException if a disk I/O error or parsing error occurred.
- */
- public static @NonNull ApkAssets loadOverlayFromPath(@NonNull String idmapPath, boolean system)
- throws IOException {
- return new ApkAssets(idmapPath, system, false /*forceSharedLibrary*/, true /*overlay*/);
- }
-
- private ApkAssets(@NonNull String path, boolean system, boolean forceSharedLib, boolean overlay)
- throws IOException {
- Preconditions.checkNotNull(path, "path");
- mNativePtr = nativeLoad(path, system, forceSharedLib, overlay);
- mStringBlock = new StringBlock(nativeGetStringBlock(mNativePtr), true /*useSparse*/);
- }
-
- private ApkAssets(@NonNull FileDescriptor fd, @NonNull String friendlyName, boolean system,
- boolean forceSharedLib) throws IOException {
- Preconditions.checkNotNull(fd, "fd");
- Preconditions.checkNotNull(friendlyName, "friendlyName");
- mNativePtr = nativeLoadFromFd(fd, friendlyName, system, forceSharedLib);
- mStringBlock = new StringBlock(nativeGetStringBlock(mNativePtr), true /*useSparse*/);
- }
-
- @NonNull String getAssetPath() {
- synchronized (this) {
- return nativeGetAssetPath(mNativePtr);
- }
- }
-
- CharSequence getStringFromPool(int idx) {
- synchronized (this) {
- return mStringBlock.get(idx);
- }
- }
-
- /**
- * Retrieve a parser for a compiled XML file. This is associated with a single APK and
- * <em>NOT</em> a full AssetManager. This means that shared-library references will not be
- * dynamically assigned runtime package IDs.
- *
- * @param fileName The path to the file within the APK.
- * @return An XmlResourceParser.
- * @throws IOException if the file was not found or an error occurred retrieving it.
- */
- public @NonNull XmlResourceParser openXml(@NonNull String fileName) throws IOException {
- Preconditions.checkNotNull(fileName, "fileName");
- synchronized (this) {
- long nativeXmlPtr = nativeOpenXml(mNativePtr, fileName);
- try (XmlBlock block = new XmlBlock(null, nativeXmlPtr)) {
- XmlResourceParser parser = block.newParser();
- // If nativeOpenXml doesn't throw, it will always return a valid native pointer,
- // which makes newParser always return non-null. But let's be paranoid.
- if (parser == null) {
- throw new AssertionError("block.newParser() returned a null parser");
- }
- return parser;
- }
- }
- }
-
- /**
- * Returns false if the underlying APK was changed since this ApkAssets was loaded.
- */
- public boolean isUpToDate() {
- synchronized (this) {
- return nativeIsUpToDate(mNativePtr);
- }
- }
-
- @Override
- protected void finalize() throws Throwable {
- nativeDestroy(mNativePtr);
- }
-
- private static native long nativeLoad(
- @NonNull String path, boolean system, boolean forceSharedLib, boolean overlay)
- throws IOException;
- private static native long nativeLoadFromFd(@NonNull FileDescriptor fd,
- @NonNull String friendlyName, boolean system, boolean forceSharedLib)
- throws IOException;
- private static native void nativeDestroy(long ptr);
- private static native @NonNull String nativeGetAssetPath(long ptr);
- private static native long nativeGetStringBlock(long ptr);
- private static native boolean nativeIsUpToDate(long ptr);
- private static native long nativeOpenXml(long ptr, @NonNull String fileName) throws IOException;
-}
diff --git a/core/java/android/content/res/AssetManager.java b/core/java/android/content/res/AssetManager.java
index 24116b4..5f8a34d 100644
--- a/core/java/android/content/res/AssetManager.java
+++ b/core/java/android/content/res/AssetManager.java
@@ -18,11 +18,9 @@
import android.annotation.AnyRes;
import android.annotation.ArrayRes;
-import android.annotation.AttrRes;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.StringRes;
-import android.annotation.StyleRes;
import android.content.pm.ActivityInfo;
import android.content.res.Configuration.NativeConfig;
import android.os.ParcelFileDescriptor;
@@ -31,19 +29,11 @@
import android.util.TypedValue;
import com.android.internal.annotations.GuardedBy;
-import com.android.internal.util.Preconditions;
-import libcore.io.IoUtils;
-
-import java.io.BufferedReader;
-import java.io.FileInputStream;
+import java.io.FileDescriptor;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.nio.channels.FileLock;
-import java.util.ArrayList;
-import java.util.Arrays;
import java.util.HashMap;
/**
@@ -54,19 +44,7 @@
* bytes.
*/
public final class AssetManager implements AutoCloseable {
- private static final String TAG = "AssetManager";
- private static final boolean DEBUG_REFS = false;
-
- private static final String FRAMEWORK_APK_PATH = "/system/framework/framework-res.apk";
-
- private static final Object sSync = new Object();
-
- private static final ApkAssets[] sEmptyApkAssets = new ApkAssets[0];
-
- // Not private for LayoutLib's BridgeAssetManager.
- @GuardedBy("sSync") static AssetManager sSystem = null;
-
- @GuardedBy("sSync") private static ApkAssets[] sSystemApkAssets = new ApkAssets[0];
+ /* modes used when opening an asset */
/**
* Mode for {@link #open(String, int)}: no specific information about how
@@ -89,332 +67,88 @@
*/
public static final int ACCESS_BUFFER = 3;
- @GuardedBy("this") private final TypedValue mValue = new TypedValue();
- @GuardedBy("this") private final long[] mOffsets = new long[2];
+ private static final String TAG = "AssetManager";
+ private static final boolean localLOGV = false || false;
+
+ private static final boolean DEBUG_REFS = false;
+
+ private static final Object sSync = new Object();
+ /*package*/ static AssetManager sSystem = null;
- // Pointer to native implementation, stuffed inside a long.
- @GuardedBy("this") private long mObject;
+ private final TypedValue mValue = new TypedValue();
+ private final long[] mOffsets = new long[2];
+
+ // For communication with native code.
+ private long mObject;
- // The loaded asset paths.
- @GuardedBy("this") private ApkAssets[] mApkAssets;
-
- // Debug/reference counting implementation.
- @GuardedBy("this") private boolean mOpen = true;
- @GuardedBy("this") private int mNumRefs = 1;
- @GuardedBy("this") private HashMap<Long, RuntimeException> mRefStacks;
-
+ private StringBlock mStringBlocks[] = null;
+
+ private int mNumRefs = 1;
+ private boolean mOpen = true;
+ private HashMap<Long, RuntimeException> mRefStacks;
+
/**
* Create a new AssetManager containing only the basic system assets.
* Applications will not generally use this method, instead retrieving the
* appropriate asset manager with {@link Resources#getAssets}. Not for
* use by applications.
- * @hide
+ * {@hide}
*/
public AssetManager() {
- final ApkAssets[] assets;
- synchronized (sSync) {
- createSystemAssetsInZygoteLocked();
- assets = sSystemApkAssets;
- }
-
- mObject = nativeCreate();
- if (DEBUG_REFS) {
- mNumRefs = 0;
- incRefsLocked(hashCode());
- }
-
- // Always set the framework resources.
- setApkAssets(assets, false /*invalidateCaches*/);
- }
-
- /**
- * Private constructor that doesn't call ensureSystemAssets.
- * Used for the creation of system assets.
- */
- @SuppressWarnings("unused")
- private AssetManager(boolean sentinel) {
- mObject = nativeCreate();
- if (DEBUG_REFS) {
- mNumRefs = 0;
- incRefsLocked(hashCode());
- }
- }
-
- /**
- * This must be called from Zygote so that system assets are shared by all applications.
- */
- private static void createSystemAssetsInZygoteLocked() {
- if (sSystem != null) {
- return;
- }
-
- // Make sure that all IDMAPs are up to date.
- nativeVerifySystemIdmaps();
-
- try {
- ArrayList<ApkAssets> apkAssets = new ArrayList<>();
- apkAssets.add(ApkAssets.loadFromPath(FRAMEWORK_APK_PATH, true /*system*/));
- loadStaticRuntimeOverlays(apkAssets);
-
- sSystemApkAssets = apkAssets.toArray(new ApkAssets[apkAssets.size()]);
- sSystem = new AssetManager(true /*sentinel*/);
- sSystem.setApkAssets(sSystemApkAssets, false /*invalidateCaches*/);
- } catch (IOException e) {
- throw new IllegalStateException("Failed to create system AssetManager", e);
- }
- }
-
- /**
- * Loads the static runtime overlays declared in /data/resource-cache/overlays.list.
- * Throws an exception if the file is corrupt or if loading the APKs referenced by the file
- * fails. Returns quietly if the overlays.list file doesn't exist.
- * @param outApkAssets The list to fill with the loaded ApkAssets.
- */
- private static void loadStaticRuntimeOverlays(ArrayList<ApkAssets> outApkAssets)
- throws IOException {
- final FileInputStream fis;
- try {
- fis = new FileInputStream("/data/resource-cache/overlays.list");
- } catch (FileNotFoundException e) {
- // We might not have any overlays, this is fine. We catch here since ApkAssets
- // loading can also fail with the same exception, which we would want to propagate.
- Log.i(TAG, "no overlays.list file found");
- return;
- }
-
- try {
- // Acquire a lock so that any idmap scanning doesn't impact the current set.
- // The order of this try-with-resources block matters. We must release the lock, and
- // then close the file streams when exiting the block.
- try (final BufferedReader br = new BufferedReader(new InputStreamReader(fis));
- final FileLock flock = fis.getChannel().lock(0, Long.MAX_VALUE, true /*shared*/)) {
- for (String line; (line = br.readLine()) != null; ) {
- final String idmapPath = line.split(" ")[1];
- outApkAssets.add(ApkAssets.loadOverlayFromPath(idmapPath, true /*system*/));
- }
+ synchronized (this) {
+ if (DEBUG_REFS) {
+ mNumRefs = 0;
+ incRefsLocked(this.hashCode());
}
- } finally {
- // When BufferedReader is closed above, FileInputStream is closed as well. But let's be
- // paranoid.
- IoUtils.closeQuietly(fis);
+ init(false);
+ if (localLOGV) Log.v(TAG, "New asset manager: " + this);
+ ensureSystemAssets();
}
}
+ private static void ensureSystemAssets() {
+ synchronized (sSync) {
+ if (sSystem == null) {
+ AssetManager system = new AssetManager(true);
+ system.makeStringBlocks(null);
+ sSystem = system;
+ }
+ }
+ }
+
+ private AssetManager(boolean isSystem) {
+ if (DEBUG_REFS) {
+ synchronized (this) {
+ mNumRefs = 0;
+ incRefsLocked(this.hashCode());
+ }
+ }
+ init(true);
+ if (localLOGV) Log.v(TAG, "New asset manager: " + this);
+ }
+
/**
* Return a global shared asset manager that provides access to only
* system assets (no application assets).
- * @hide
+ * {@hide}
*/
public static AssetManager getSystem() {
- synchronized (sSync) {
- createSystemAssetsInZygoteLocked();
- return sSystem;
- }
+ ensureSystemAssets();
+ return sSystem;
}
/**
* Close this asset manager.
*/
- @Override
public void close() {
- synchronized (this) {
- if (!mOpen) {
- return;
- }
-
- mOpen = false;
- decRefsLocked(hashCode());
- }
- }
-
- /**
- * Changes the asset paths in this AssetManager. This replaces the {@link #addAssetPath(String)}
- * family of methods.
- *
- * @param apkAssets The new set of paths.
- * @param invalidateCaches Whether to invalidate any caches. This should almost always be true.
- * Set this to false if you are appending new resources
- * (not new configurations).
- * @hide
- */
- public void setApkAssets(@NonNull ApkAssets[] apkAssets, boolean invalidateCaches) {
- Preconditions.checkNotNull(apkAssets, "apkAssets");
-
- // Copy the apkAssets, but prepend the system assets (framework + overlays).
- final ApkAssets[] newApkAssets = new ApkAssets[apkAssets.length + sSystemApkAssets.length];
- System.arraycopy(sSystemApkAssets, 0, newApkAssets, 0, sSystemApkAssets.length);
- System.arraycopy(apkAssets, 0, newApkAssets, sSystemApkAssets.length, apkAssets.length);
-
- synchronized (this) {
- ensureOpenLocked();
- mApkAssets = newApkAssets;
- nativeSetApkAssets(mObject, mApkAssets, invalidateCaches);
- if (invalidateCaches) {
- // Invalidate all caches.
- invalidateCachesLocked(-1);
- }
- }
- }
-
- /**
- * Invalidates the caches in this AssetManager according to the bitmask `diff`.
- *
- * @param diff The bitmask of changes generated by {@link Configuration#diff(Configuration)}.
- * @see ActivityInfo.Config
- */
- private void invalidateCachesLocked(int diff) {
- // TODO(adamlesinski): Currently there are no caches to invalidate in Java code.
- }
-
- /**
- * Returns the set of ApkAssets loaded by this AssetManager. If the AssetManager is closed, this
- * returns a 0-length array.
- * @hide
- */
- public @NonNull ApkAssets[] getApkAssets() {
- synchronized (this) {
+ synchronized(this) {
+ //System.out.println("Release: num=" + mNumRefs
+ // + ", released=" + mReleased);
if (mOpen) {
- return mApkAssets;
+ mOpen = false;
+ decRefsLocked(this.hashCode());
}
}
- return sEmptyApkAssets;
- }
-
- /**
- * Returns a cookie for use with the other APIs of AssetManager.
- * @return 0 if the path was not found, otherwise a positive integer cookie representing
- * this path in the AssetManager.
- * @hide
- */
- public int findCookieForPath(@NonNull String path) {
- Preconditions.checkNotNull(path, "path");
- synchronized (this) {
- ensureValidLocked();
- final int count = mApkAssets.length;
- for (int i = 0; i < count; i++) {
- if (path.equals(mApkAssets[i].getAssetPath())) {
- return i + 1;
- }
- }
- }
- return 0;
- }
-
- /**
- * @deprecated Use {@link #setApkAssets(ApkAssets[], boolean)}
- * @hide
- */
- @Deprecated
- public int addAssetPath(String path) {
- return addAssetPathInternal(path, false /*overlay*/, false /*appAsLib*/);
- }
-
- /**
- * @deprecated Use {@link #setApkAssets(ApkAssets[], boolean)}
- * @hide
- */
- @Deprecated
- public int addAssetPathAsSharedLibrary(String path) {
- return addAssetPathInternal(path, false /*overlay*/, true /*appAsLib*/);
- }
-
- /**
- * @deprecated Use {@link #setApkAssets(ApkAssets[], boolean)}
- * @hide
- */
- @Deprecated
- public int addOverlayPath(String path) {
- return addAssetPathInternal(path, true /*overlay*/, false /*appAsLib*/);
- }
-
- private int addAssetPathInternal(String path, boolean overlay, boolean appAsLib) {
- Preconditions.checkNotNull(path, "path");
- synchronized (this) {
- ensureOpenLocked();
- final int count = mApkAssets.length;
- for (int i = 0; i < count; i++) {
- if (mApkAssets[i].getAssetPath().equals(path)) {
- return i + 1;
- }
- }
-
- final ApkAssets assets;
- try {
- if (overlay) {
- // TODO(b/70343104): This hardcoded path will be removed once
- // addAssetPathInternal is deleted.
- final String idmapPath = "/data/resource-cache/"
- + path.substring(1).replace('/', '@')
- + "@idmap";
- assets = ApkAssets.loadOverlayFromPath(idmapPath, false /*system*/);
- } else {
- assets = ApkAssets.loadFromPath(path, false /*system*/, appAsLib);
- }
- } catch (IOException e) {
- return 0;
- }
-
- final ApkAssets[] newApkAssets = Arrays.copyOf(mApkAssets, count + 1);
- newApkAssets[count] = assets;
- setApkAssets(newApkAssets, true);
- return count + 1;
- }
- }
-
- /**
- * Ensures that the native implementation has not been destroyed.
- * The AssetManager may have been closed, but references to it still exist
- * and therefore the native implementation is not destroyed.
- */
- private void ensureValidLocked() {
- if (mObject == 0) {
- throw new RuntimeException("AssetManager has been destroyed");
- }
- }
-
- /**
- * Ensures that the AssetManager has not been explicitly closed. If this method passes,
- * then this implies that ensureValidLocked() also passes.
- */
- private void ensureOpenLocked() {
- // If mOpen is true, this implies that mObject != 0.
- if (!mOpen) {
- throw new RuntimeException("AssetManager has been closed");
- }
- }
-
- /**
- * Populates {@code outValue} with the data associated a particular
- * resource identifier for the current configuration.
- *
- * @param resId the resource identifier to load
- * @param densityDpi the density bucket for which to load the resource
- * @param outValue the typed value in which to put the data
- * @param resolveRefs {@code true} to resolve references, {@code false}
- * to leave them unresolved
- * @return {@code true} if the data was loaded into {@code outValue},
- * {@code false} otherwise
- */
- boolean getResourceValue(@AnyRes int resId, int densityDpi, @NonNull TypedValue outValue,
- boolean resolveRefs) {
- Preconditions.checkNotNull(outValue, "outValue");
- synchronized (this) {
- ensureValidLocked();
- final int cookie = nativeGetResourceValue(
- mObject, resId, (short) densityDpi, outValue, resolveRefs);
- if (cookie <= 0) {
- return false;
- }
-
- // Convert the changing configurations flags populated by native code.
- outValue.changingConfigurations = ActivityInfo.activityInfoConfigNativeToJava(
- outValue.changingConfigurations);
-
- if (outValue.type == TypedValue.TYPE_STRING) {
- outValue.string = mApkAssets[cookie - 1].getStringFromPool(outValue.data);
- }
- return true;
- }
}
/**
@@ -424,7 +158,8 @@
* @param resId the resource identifier to load
* @return the string value, or {@code null}
*/
- @Nullable CharSequence getResourceText(@StringRes int resId) {
+ @Nullable
+ final CharSequence getResourceText(@StringRes int resId) {
synchronized (this) {
final TypedValue outValue = mValue;
if (getResourceValue(resId, 0, outValue, true)) {
@@ -439,15 +174,15 @@
* identifier for the current configuration.
*
* @param resId the resource identifier to load
- * @param bagEntryId the index into the bag to load
+ * @param bagEntryId
* @return the string value, or {@code null}
*/
- @Nullable CharSequence getResourceBagText(@StringRes int resId, int bagEntryId) {
+ @Nullable
+ final CharSequence getResourceBagText(@StringRes int resId, int bagEntryId) {
synchronized (this) {
- ensureValidLocked();
final TypedValue outValue = mValue;
- final int cookie = nativeGetResourceBagValue(mObject, resId, bagEntryId, outValue);
- if (cookie <= 0) {
+ final int block = loadResourceBagValue(resId, bagEntryId, outValue, true);
+ if (block < 0) {
return null;
}
@@ -456,49 +191,12 @@
outValue.changingConfigurations);
if (outValue.type == TypedValue.TYPE_STRING) {
- return mApkAssets[cookie - 1].getStringFromPool(outValue.data);
+ return mStringBlocks[block].get(outValue.data);
}
return outValue.coerceToString();
}
}
- int getResourceArraySize(@ArrayRes int resId) {
- synchronized (this) {
- ensureValidLocked();
- return nativeGetResourceArraySize(mObject, resId);
- }
- }
-
- /**
- * Populates `outData` with array elements of `resId`. `outData` is normally
- * used with
- * {@link TypedArray}.
- *
- * Each logical element in `outData` is {@link TypedArray#STYLE_NUM_ENTRIES}
- * long,
- * with the indices of the data representing the type, value, asset cookie,
- * resource ID,
- * configuration change mask, and density of the element.
- *
- * @param resId The resource ID of an array resource.
- * @param outData The array to populate with data.
- * @return The length of the array.
- *
- * @see TypedArray#STYLE_TYPE
- * @see TypedArray#STYLE_DATA
- * @see TypedArray#STYLE_ASSET_COOKIE
- * @see TypedArray#STYLE_RESOURCE_ID
- * @see TypedArray#STYLE_CHANGING_CONFIGURATIONS
- * @see TypedArray#STYLE_DENSITY
- */
- int getResourceArray(@ArrayRes int resId, @NonNull int[] outData) {
- Preconditions.checkNotNull(outData, "outData");
- synchronized (this) {
- ensureValidLocked();
- return nativeGetResourceArray(mObject, resId, outData);
- }
- }
-
/**
* Retrieves the string array associated with a particular resource
* identifier for the current configuration.
@@ -506,10 +204,39 @@
* @param resId the resource identifier of the string array
* @return the string array, or {@code null}
*/
- @Nullable String[] getResourceStringArray(@ArrayRes int resId) {
+ @Nullable
+ final String[] getResourceStringArray(@ArrayRes int resId) {
+ return getArrayStringResource(resId);
+ }
+
+ /**
+ * Populates {@code outValue} with the data associated a particular
+ * resource identifier for the current configuration.
+ *
+ * @param resId the resource identifier to load
+ * @param densityDpi the density bucket for which to load the resource
+ * @param outValue the typed value in which to put the data
+ * @param resolveRefs {@code true} to resolve references, {@code false}
+ * to leave them unresolved
+ * @return {@code true} if the data was loaded into {@code outValue},
+ * {@code false} otherwise
+ */
+ final boolean getResourceValue(@AnyRes int resId, int densityDpi, @NonNull TypedValue outValue,
+ boolean resolveRefs) {
synchronized (this) {
- ensureValidLocked();
- return nativeGetResourceStringArray(mObject, resId);
+ final int block = loadResourceValue(resId, (short) densityDpi, outValue, resolveRefs);
+ if (block < 0) {
+ return false;
+ }
+
+ // Convert the changing configurations flags populated by native code.
+ outValue.changingConfigurations = ActivityInfo.activityInfoConfigNativeToJava(
+ outValue.changingConfigurations);
+
+ if (outValue.type == TypedValue.TYPE_STRING) {
+ outValue.string = mStringBlocks[block].get(outValue.data);
+ }
+ return true;
}
}
@@ -519,48 +246,26 @@
*
* @param resId the resource id of the string array
*/
- @Nullable CharSequence[] getResourceTextArray(@ArrayRes int resId) {
+ final @Nullable CharSequence[] getResourceTextArray(@ArrayRes int resId) {
synchronized (this) {
- ensureValidLocked();
- final int[] rawInfoArray = nativeGetResourceStringArrayInfo(mObject, resId);
+ final int[] rawInfoArray = getArrayStringInfo(resId);
if (rawInfoArray == null) {
return null;
}
-
final int rawInfoArrayLen = rawInfoArray.length;
final int infoArrayLen = rawInfoArrayLen / 2;
+ int block;
+ int index;
final CharSequence[] retArray = new CharSequence[infoArrayLen];
for (int i = 0, j = 0; i < rawInfoArrayLen; i = i + 2, j++) {
- int cookie = rawInfoArray[i];
- int index = rawInfoArray[i + 1];
- retArray[j] = (index >= 0 && cookie > 0)
- ? mApkAssets[cookie - 1].getStringFromPool(index) : null;
+ block = rawInfoArray[i];
+ index = rawInfoArray[i + 1];
+ retArray[j] = index >= 0 ? mStringBlocks[block].get(index) : null;
}
return retArray;
}
}
- @Nullable int[] getResourceIntArray(@ArrayRes int resId) {
- synchronized (this) {
- ensureValidLocked();
- return nativeGetResourceIntArray(mObject, resId);
- }
- }
-
- /**
- * Get the attributes for a style resource. These are the <item>
- * elements in
- * a <style> resource.
- * @param resId The resource ID of the style
- * @return An array of attribute IDs.
- */
- @AttrRes int[] getStyleAttributes(@StyleRes int resId) {
- synchronized (this) {
- ensureValidLocked();
- return nativeGetStyleAttributes(mObject, resId);
- }
- }
-
/**
* Populates {@code outValue} with the data associated with a particular
* resource identifier for the current configuration. Resolves theme
@@ -574,88 +279,73 @@
* @return {@code true} if the data was loaded into {@code outValue},
* {@code false} otherwise
*/
- boolean getThemeValue(long theme, @AnyRes int resId, @NonNull TypedValue outValue,
+ final boolean getThemeValue(long theme, @AnyRes int resId, @NonNull TypedValue outValue,
boolean resolveRefs) {
- Preconditions.checkNotNull(outValue, "outValue");
+ final int block = loadThemeAttributeValue(theme, resId, outValue, resolveRefs);
+ if (block < 0) {
+ return false;
+ }
+
+ // Convert the changing configurations flags populated by native code.
+ outValue.changingConfigurations = ActivityInfo.activityInfoConfigNativeToJava(
+ outValue.changingConfigurations);
+
+ if (outValue.type == TypedValue.TYPE_STRING) {
+ final StringBlock[] blocks = ensureStringBlocks();
+ outValue.string = blocks[block].get(outValue.data);
+ }
+ return true;
+ }
+
+ /**
+ * Ensures the string blocks are loaded.
+ *
+ * @return the string blocks
+ */
+ @NonNull
+ final StringBlock[] ensureStringBlocks() {
synchronized (this) {
- ensureValidLocked();
- final int cookie = nativeThemeGetAttributeValue(mObject, theme, resId, outValue,
- resolveRefs);
- if (cookie <= 0) {
- return false;
+ if (mStringBlocks == null) {
+ makeStringBlocks(sSystem.mStringBlocks);
}
+ return mStringBlocks;
+ }
+ }
- // Convert the changing configurations flags populated by native code.
- outValue.changingConfigurations = ActivityInfo.activityInfoConfigNativeToJava(
- outValue.changingConfigurations);
-
- if (outValue.type == TypedValue.TYPE_STRING) {
- outValue.string = mApkAssets[cookie - 1].getStringFromPool(outValue.data);
+ /*package*/ final void makeStringBlocks(StringBlock[] seed) {
+ final int seedNum = (seed != null) ? seed.length : 0;
+ final int num = getStringBlockCount();
+ mStringBlocks = new StringBlock[num];
+ if (localLOGV) Log.v(TAG, "Making string blocks for " + this
+ + ": " + num);
+ for (int i=0; i<num; i++) {
+ if (i < seedNum) {
+ mStringBlocks[i] = seed[i];
+ } else {
+ mStringBlocks[i] = new StringBlock(getNativeStringBlock(i), true);
}
- return true;
}
}
- void dumpTheme(long theme, int priority, String tag, String prefix) {
+ /*package*/ final CharSequence getPooledStringForCookie(int cookie, int id) {
synchronized (this) {
- ensureValidLocked();
- nativeThemeDump(mObject, theme, priority, tag, prefix);
+ // Cookies map to string blocks starting at 1.
+ return mStringBlocks[cookie - 1].get(id);
}
}
- @Nullable String getResourceName(@AnyRes int resId) {
- synchronized (this) {
- ensureValidLocked();
- return nativeGetResourceName(mObject, resId);
- }
- }
-
- @Nullable String getResourcePackageName(@AnyRes int resId) {
- synchronized (this) {
- ensureValidLocked();
- return nativeGetResourcePackageName(mObject, resId);
- }
- }
-
- @Nullable String getResourceTypeName(@AnyRes int resId) {
- synchronized (this) {
- ensureValidLocked();
- return nativeGetResourceTypeName(mObject, resId);
- }
- }
-
- @Nullable String getResourceEntryName(@AnyRes int resId) {
- synchronized (this) {
- ensureValidLocked();
- return nativeGetResourceEntryName(mObject, resId);
- }
- }
-
- @AnyRes int getResourceIdentifier(@NonNull String name, @Nullable String defType,
- @Nullable String defPackage) {
- synchronized (this) {
- ensureValidLocked();
- // name is checked in JNI.
- return nativeGetResourceIdentifier(mObject, name, defType, defPackage);
- }
- }
-
- CharSequence getPooledStringForCookie(int cookie, int id) {
- // Cookies map to ApkAssets starting at 1.
- return getApkAssets()[cookie - 1].getStringFromPool(id);
- }
-
/**
* Open an asset using ACCESS_STREAMING mode. This provides access to
* files that have been bundled with an application as assets -- that is,
* files placed in to the "assets" directory.
*
- * @param fileName The name of the asset to open. This name can be hierarchical.
+ * @param fileName The name of the asset to open. This name can be
+ * hierarchical.
*
* @see #open(String, int)
* @see #list
*/
- public @NonNull InputStream open(@NonNull String fileName) throws IOException {
+ public final InputStream open(String fileName) throws IOException {
return open(fileName, ACCESS_STREAMING);
}
@@ -665,7 +355,8 @@
* with an application as assets -- that is, files placed in to the
* "assets" directory.
*
- * @param fileName The name of the asset to open. This name can be hierarchical.
+ * @param fileName The name of the asset to open. This name can be
+ * hierarchical.
* @param accessMode Desired access mode for retrieving the data.
*
* @see #ACCESS_UNKNOWN
@@ -675,40 +366,34 @@
* @see #open(String)
* @see #list
*/
- public @NonNull InputStream open(@NonNull String fileName, int accessMode) throws IOException {
- Preconditions.checkNotNull(fileName, "fileName");
+ public final InputStream open(String fileName, int accessMode)
+ throws IOException {
synchronized (this) {
- ensureOpenLocked();
- final long asset = nativeOpenAsset(mObject, fileName, accessMode);
- if (asset == 0) {
- throw new FileNotFoundException("Asset file: " + fileName);
+ if (!mOpen) {
+ throw new RuntimeException("Assetmanager has been closed");
}
- final AssetInputStream assetInputStream = new AssetInputStream(asset);
- incRefsLocked(assetInputStream.hashCode());
- return assetInputStream;
+ long asset = openAsset(fileName, accessMode);
+ if (asset != 0) {
+ AssetInputStream res = new AssetInputStream(asset);
+ incRefsLocked(res.hashCode());
+ return res;
+ }
}
+ throw new FileNotFoundException("Asset file: " + fileName);
}
- /**
- * Open an uncompressed asset by mmapping it and returning an {@link AssetFileDescriptor}.
- * This provides access to files that have been bundled with an application as assets -- that
- * is, files placed in to the "assets" directory.
- *
- * The asset must be uncompressed, or an exception will be thrown.
- *
- * @param fileName The name of the asset to open. This name can be hierarchical.
- * @return An open AssetFileDescriptor.
- */
- public @NonNull AssetFileDescriptor openFd(@NonNull String fileName) throws IOException {
- Preconditions.checkNotNull(fileName, "fileName");
+ public final AssetFileDescriptor openFd(String fileName)
+ throws IOException {
synchronized (this) {
- ensureOpenLocked();
- final ParcelFileDescriptor pfd = nativeOpenAssetFd(mObject, fileName, mOffsets);
- if (pfd == null) {
- throw new FileNotFoundException("Asset file: " + fileName);
+ if (!mOpen) {
+ throw new RuntimeException("Assetmanager has been closed");
}
- return new AssetFileDescriptor(pfd, mOffsets[0], mOffsets[1]);
+ ParcelFileDescriptor pfd = openAssetFd(fileName, mOffsets);
+ if (pfd != null) {
+ return new AssetFileDescriptor(pfd, mOffsets[0], mOffsets[1]);
+ }
}
+ throw new FileNotFoundException("Asset file: " + fileName);
}
/**
@@ -723,121 +408,90 @@
*
* @see #open
*/
- public @Nullable String[] list(@NonNull String path) throws IOException {
- Preconditions.checkNotNull(path, "path");
- synchronized (this) {
- ensureValidLocked();
- return nativeList(mObject, path);
- }
- }
+ public native final String[] list(String path)
+ throws IOException;
/**
+ * {@hide}
* Open a non-asset file as an asset using ACCESS_STREAMING mode. This
* provides direct access to all of the files included in an application
* package (not only its assets). Applications should not normally use
* this.
- *
- * @param fileName Name of the asset to retrieve.
- *
+ *
* @see #open(String)
- * @hide
*/
- public @NonNull InputStream openNonAsset(@NonNull String fileName) throws IOException {
+ public final InputStream openNonAsset(String fileName) throws IOException {
return openNonAsset(0, fileName, ACCESS_STREAMING);
}
/**
+ * {@hide}
* Open a non-asset file as an asset using a specific access mode. This
* provides direct access to all of the files included in an application
* package (not only its assets). Applications should not normally use
* this.
- *
- * @param fileName Name of the asset to retrieve.
- * @param accessMode Desired access mode for retrieving the data.
- *
- * @see #ACCESS_UNKNOWN
- * @see #ACCESS_STREAMING
- * @see #ACCESS_RANDOM
- * @see #ACCESS_BUFFER
+ *
* @see #open(String, int)
- * @hide
*/
- public @NonNull InputStream openNonAsset(@NonNull String fileName, int accessMode)
- throws IOException {
+ public final InputStream openNonAsset(String fileName, int accessMode)
+ throws IOException {
return openNonAsset(0, fileName, accessMode);
}
/**
+ * {@hide}
* Open a non-asset in a specified package. Not for use by applications.
- *
+ *
* @param cookie Identifier of the package to be opened.
* @param fileName Name of the asset to retrieve.
- * @hide
*/
- public @NonNull InputStream openNonAsset(int cookie, @NonNull String fileName)
- throws IOException {
+ public final InputStream openNonAsset(int cookie, String fileName)
+ throws IOException {
return openNonAsset(cookie, fileName, ACCESS_STREAMING);
}
/**
+ * {@hide}
* Open a non-asset in a specified package. Not for use by applications.
- *
+ *
* @param cookie Identifier of the package to be opened.
* @param fileName Name of the asset to retrieve.
* @param accessMode Desired access mode for retrieving the data.
- * @hide
*/
- public @NonNull InputStream openNonAsset(int cookie, @NonNull String fileName, int accessMode)
- throws IOException {
- Preconditions.checkNotNull(fileName, "fileName");
+ public final InputStream openNonAsset(int cookie, String fileName, int accessMode)
+ throws IOException {
synchronized (this) {
- ensureOpenLocked();
- final long asset = nativeOpenNonAsset(mObject, cookie, fileName, accessMode);
- if (asset == 0) {
- throw new FileNotFoundException("Asset absolute file: " + fileName);
+ if (!mOpen) {
+ throw new RuntimeException("Assetmanager has been closed");
}
- final AssetInputStream assetInputStream = new AssetInputStream(asset);
- incRefsLocked(assetInputStream.hashCode());
- return assetInputStream;
+ long asset = openNonAssetNative(cookie, fileName, accessMode);
+ if (asset != 0) {
+ AssetInputStream res = new AssetInputStream(asset);
+ incRefsLocked(res.hashCode());
+ return res;
+ }
}
+ throw new FileNotFoundException("Asset absolute file: " + fileName);
}
- /**
- * Open a non-asset as an asset by mmapping it and returning an {@link AssetFileDescriptor}.
- * This provides direct access to all of the files included in an application
- * package (not only its assets). Applications should not normally use this.
- *
- * The asset must not be compressed, or an exception will be thrown.
- *
- * @param fileName Name of the asset to retrieve.
- */
- public @NonNull AssetFileDescriptor openNonAssetFd(@NonNull String fileName)
+ public final AssetFileDescriptor openNonAssetFd(String fileName)
throws IOException {
return openNonAssetFd(0, fileName);
}
-
- /**
- * Open a non-asset as an asset by mmapping it and returning an {@link AssetFileDescriptor}.
- * This provides direct access to all of the files included in an application
- * package (not only its assets). Applications should not normally use this.
- *
- * The asset must not be compressed, or an exception will be thrown.
- *
- * @param cookie Identifier of the package to be opened.
- * @param fileName Name of the asset to retrieve.
- */
- public @NonNull AssetFileDescriptor openNonAssetFd(int cookie, @NonNull String fileName)
- throws IOException {
- Preconditions.checkNotNull(fileName, "fileName");
+
+ public final AssetFileDescriptor openNonAssetFd(int cookie,
+ String fileName) throws IOException {
synchronized (this) {
- ensureOpenLocked();
- final ParcelFileDescriptor pfd =
- nativeOpenNonAssetFd(mObject, cookie, fileName, mOffsets);
- if (pfd == null) {
- throw new FileNotFoundException("Asset absolute file: " + fileName);
+ if (!mOpen) {
+ throw new RuntimeException("Assetmanager has been closed");
}
- return new AssetFileDescriptor(pfd, mOffsets[0], mOffsets[1]);
+ ParcelFileDescriptor pfd = openNonAssetFdNative(cookie,
+ fileName, mOffsets);
+ if (pfd != null) {
+ return new AssetFileDescriptor(pfd, mOffsets[0], mOffsets[1]);
+ }
}
+ throw new FileNotFoundException("Asset absolute file: " + fileName);
}
/**
@@ -845,7 +499,7 @@
*
* @param fileName The name of the file to retrieve.
*/
- public @NonNull XmlResourceParser openXmlResourceParser(@NonNull String fileName)
+ public final XmlResourceParser openXmlResourceParser(String fileName)
throws IOException {
return openXmlResourceParser(0, fileName);
}
@@ -856,265 +510,270 @@
* @param cookie Identifier of the package to be opened.
* @param fileName The name of the file to retrieve.
*/
- public @NonNull XmlResourceParser openXmlResourceParser(int cookie, @NonNull String fileName)
- throws IOException {
- try (XmlBlock block = openXmlBlockAsset(cookie, fileName)) {
- XmlResourceParser parser = block.newParser();
- // If openXmlBlockAsset doesn't throw, it will always return an XmlBlock object with
- // a valid native pointer, which makes newParser always return non-null. But let's
- // be paranoid.
- if (parser == null) {
- throw new AssertionError("block.newParser() returned a null parser");
- }
- return parser;
- }
+ public final XmlResourceParser openXmlResourceParser(int cookie,
+ String fileName) throws IOException {
+ XmlBlock block = openXmlBlockAsset(cookie, fileName);
+ XmlResourceParser rp = block.newParser();
+ block.close();
+ return rp;
}
/**
- * Retrieve a non-asset as a compiled XML file. Not for use by applications.
+ * {@hide}
+ * Retrieve a non-asset as a compiled XML file. Not for use by
+ * applications.
*
* @param fileName The name of the file to retrieve.
- * @hide
*/
- @NonNull XmlBlock openXmlBlockAsset(@NonNull String fileName) throws IOException {
+ /*package*/ final XmlBlock openXmlBlockAsset(String fileName)
+ throws IOException {
return openXmlBlockAsset(0, fileName);
}
/**
+ * {@hide}
* Retrieve a non-asset as a compiled XML file. Not for use by
* applications.
*
* @param cookie Identifier of the package to be opened.
* @param fileName Name of the asset to retrieve.
- * @hide
*/
- @NonNull XmlBlock openXmlBlockAsset(int cookie, @NonNull String fileName) throws IOException {
- Preconditions.checkNotNull(fileName, "fileName");
+ /*package*/ final XmlBlock openXmlBlockAsset(int cookie, String fileName)
+ throws IOException {
synchronized (this) {
- ensureOpenLocked();
- final long xmlBlock = nativeOpenXmlAsset(mObject, cookie, fileName);
- if (xmlBlock == 0) {
- throw new FileNotFoundException("Asset XML file: " + fileName);
+ if (!mOpen) {
+ throw new RuntimeException("Assetmanager has been closed");
}
- final XmlBlock block = new XmlBlock(this, xmlBlock);
- incRefsLocked(block.hashCode());
- return block;
+ long xmlBlock = openXmlAssetNative(cookie, fileName);
+ if (xmlBlock != 0) {
+ XmlBlock res = new XmlBlock(this, xmlBlock);
+ incRefsLocked(res.hashCode());
+ return res;
+ }
}
+ throw new FileNotFoundException("Asset XML file: " + fileName);
}
- void xmlBlockGone(int id) {
+ /*package*/ void xmlBlockGone(int id) {
synchronized (this) {
decRefsLocked(id);
}
}
- void applyStyle(long themePtr, @AttrRes int defStyleAttr, @StyleRes int defStyleRes,
- @Nullable XmlBlock.Parser parser, @NonNull int[] inAttrs, long outValuesAddress,
- long outIndicesAddress) {
- Preconditions.checkNotNull(inAttrs, "inAttrs");
+ /*package*/ final long createTheme() {
synchronized (this) {
- // Need to synchronize on AssetManager because we will be accessing
- // the native implementation of AssetManager.
- ensureValidLocked();
- nativeApplyStyle(mObject, themePtr, defStyleAttr, defStyleRes,
- parser != null ? parser.mParseState : 0, inAttrs, outValuesAddress,
- outIndicesAddress);
+ if (!mOpen) {
+ throw new RuntimeException("Assetmanager has been closed");
+ }
+ long res = newTheme();
+ incRefsLocked(res);
+ return res;
}
}
- boolean resolveAttrs(long themePtr, @AttrRes int defStyleAttr, @StyleRes int defStyleRes,
- @Nullable int[] inValues, @NonNull int[] inAttrs, @NonNull int[] outValues,
- @NonNull int[] outIndices) {
- Preconditions.checkNotNull(inAttrs, "inAttrs");
- Preconditions.checkNotNull(outValues, "outValues");
- Preconditions.checkNotNull(outIndices, "outIndices");
+ /*package*/ final void releaseTheme(long theme) {
synchronized (this) {
- // Need to synchronize on AssetManager because we will be accessing
- // the native implementation of AssetManager.
- ensureValidLocked();
- return nativeResolveAttrs(mObject,
- themePtr, defStyleAttr, defStyleRes, inValues, inAttrs, outValues, outIndices);
+ deleteTheme(theme);
+ decRefsLocked(theme);
}
}
- boolean retrieveAttributes(@NonNull XmlBlock.Parser parser, @NonNull int[] inAttrs,
- @NonNull int[] outValues, @NonNull int[] outIndices) {
- Preconditions.checkNotNull(parser, "parser");
- Preconditions.checkNotNull(inAttrs, "inAttrs");
- Preconditions.checkNotNull(outValues, "outValues");
- Preconditions.checkNotNull(outIndices, "outIndices");
- synchronized (this) {
- // Need to synchronize on AssetManager because we will be accessing
- // the native implementation of AssetManager.
- ensureValidLocked();
- return nativeRetrieveAttributes(
- mObject, parser.mParseState, inAttrs, outValues, outIndices);
- }
- }
-
- long createTheme() {
- synchronized (this) {
- ensureValidLocked();
- long themePtr = nativeThemeCreate(mObject);
- incRefsLocked(themePtr);
- return themePtr;
- }
- }
-
- void releaseTheme(long themePtr) {
- synchronized (this) {
- nativeThemeDestroy(themePtr);
- decRefsLocked(themePtr);
- }
- }
-
- void applyStyleToTheme(long themePtr, @StyleRes int resId, boolean force) {
- synchronized (this) {
- // Need to synchronize on AssetManager because we will be accessing
- // the native implementation of AssetManager.
- ensureValidLocked();
- nativeThemeApplyStyle(mObject, themePtr, resId, force);
- }
- }
-
- @Override
protected void finalize() throws Throwable {
- if (DEBUG_REFS && mNumRefs != 0) {
- Log.w(TAG, "AssetManager " + this + " finalized with non-zero refs: " + mNumRefs);
- if (mRefStacks != null) {
- for (RuntimeException e : mRefStacks.values()) {
- Log.w(TAG, "Reference from here", e);
+ try {
+ if (DEBUG_REFS && mNumRefs != 0) {
+ Log.w(TAG, "AssetManager " + this
+ + " finalized with non-zero refs: " + mNumRefs);
+ if (mRefStacks != null) {
+ for (RuntimeException e : mRefStacks.values()) {
+ Log.w(TAG, "Reference from here", e);
+ }
}
}
- }
-
- if (mObject != 0) {
- nativeDestroy(mObject);
+ destroy();
+ } finally {
+ super.finalize();
}
}
-
- /* No Locking is needed for AssetInputStream because an AssetInputStream is not-thread
- safe and it does not rely on AssetManager once it has been created. It completely owns the
- underlying Asset. */
+
public final class AssetInputStream extends InputStream {
- private long mAssetNativePtr;
- private long mLength;
- private long mMarkPos;
-
/**
* @hide
*/
public final int getAssetInt() {
throw new UnsupportedOperationException();
}
-
/**
* @hide
*/
public final long getNativeAsset() {
- return mAssetNativePtr;
+ return mAsset;
}
-
- private AssetInputStream(long assetNativePtr) {
- mAssetNativePtr = assetNativePtr;
- mLength = nativeAssetGetLength(assetNativePtr);
+ private AssetInputStream(long asset)
+ {
+ mAsset = asset;
+ mLength = getAssetLength(asset);
}
-
- @Override
public final int read() throws IOException {
- ensureOpen();
- return nativeAssetReadChar(mAssetNativePtr);
+ return readAssetChar(mAsset);
}
-
- @Override
- public final int read(@NonNull byte[] b) throws IOException {
- ensureOpen();
- Preconditions.checkNotNull(b, "b");
- return nativeAssetRead(mAssetNativePtr, b, 0, b.length);
- }
-
- @Override
- public final int read(@NonNull byte[] b, int off, int len) throws IOException {
- ensureOpen();
- Preconditions.checkNotNull(b, "b");
- return nativeAssetRead(mAssetNativePtr, b, off, len);
- }
-
- @Override
- public final long skip(long n) throws IOException {
- ensureOpen();
- long pos = nativeAssetSeek(mAssetNativePtr, 0, 0);
- if ((pos + n) > mLength) {
- n = mLength - pos;
- }
- if (n > 0) {
- nativeAssetSeek(mAssetNativePtr, n, 0);
- }
- return n;
- }
-
- @Override
- public final int available() throws IOException {
- ensureOpen();
- final long len = nativeAssetGetRemainingLength(mAssetNativePtr);
- return len > Integer.MAX_VALUE ? Integer.MAX_VALUE : (int) len;
- }
-
- @Override
public final boolean markSupported() {
return true;
}
-
- @Override
- public final void mark(int readlimit) {
- ensureOpen();
- mMarkPos = nativeAssetSeek(mAssetNativePtr, 0, 0);
+ public final int available() throws IOException {
+ long len = getAssetRemainingLength(mAsset);
+ return len > Integer.MAX_VALUE ? Integer.MAX_VALUE : (int)len;
}
-
- @Override
- public final void reset() throws IOException {
- ensureOpen();
- nativeAssetSeek(mAssetNativePtr, mMarkPos, -1);
- }
-
- @Override
public final void close() throws IOException {
- if (mAssetNativePtr != 0) {
- nativeAssetDestroy(mAssetNativePtr);
- mAssetNativePtr = 0;
-
- synchronized (AssetManager.this) {
+ synchronized (AssetManager.this) {
+ if (mAsset != 0) {
+ destroyAsset(mAsset);
+ mAsset = 0;
decRefsLocked(hashCode());
}
}
}
+ public final void mark(int readlimit) {
+ mMarkPos = seekAsset(mAsset, 0, 0);
+ }
+ public final void reset() throws IOException {
+ seekAsset(mAsset, mMarkPos, -1);
+ }
+ public final int read(byte[] b) throws IOException {
+ return readAsset(mAsset, b, 0, b.length);
+ }
+ public final int read(byte[] b, int off, int len) throws IOException {
+ return readAsset(mAsset, b, off, len);
+ }
+ public final long skip(long n) throws IOException {
+ long pos = seekAsset(mAsset, 0, 0);
+ if ((pos+n) > mLength) {
+ n = mLength-pos;
+ }
+ if (n > 0) {
+ seekAsset(mAsset, n, 0);
+ }
+ return n;
+ }
- @Override
- protected void finalize() throws Throwable {
+ protected void finalize() throws Throwable
+ {
close();
}
- private void ensureOpen() {
- if (mAssetNativePtr == 0) {
- throw new IllegalStateException("AssetInputStream is closed");
- }
+ private long mAsset;
+ private long mLength;
+ private long mMarkPos;
+ }
+
+ /**
+ * Add an additional set of assets to the asset manager. This can be
+ * either a directory or ZIP file. Not for use by applications. Returns
+ * the cookie of the added asset, or 0 on failure.
+ * {@hide}
+ */
+ public final int addAssetPath(String path) {
+ return addAssetPathInternal(path, false);
+ }
+
+ /**
+ * Add an application assets to the asset manager and loading it as shared library.
+ * This can be either a directory or ZIP file. Not for use by applications. Returns
+ * the cookie of the added asset, or 0 on failure.
+ * {@hide}
+ */
+ public final int addAssetPathAsSharedLibrary(String path) {
+ return addAssetPathInternal(path, true);
+ }
+
+ private final int addAssetPathInternal(String path, boolean appAsLib) {
+ synchronized (this) {
+ int res = addAssetPathNative(path, appAsLib);
+ makeStringBlocks(mStringBlocks);
+ return res;
}
}
+ private native final int addAssetPathNative(String path, boolean appAsLib);
+
+ /**
+ * Add an additional set of assets to the asset manager from an already open
+ * FileDescriptor. Not for use by applications.
+ * This does not give full AssetManager functionality for these assets,
+ * since the origin of the file is not known for purposes of sharing,
+ * overlay resolution, and other features. However it does allow you
+ * to do simple access to the contents of the given fd as an apk file.
+ * Performs a dup of the underlying fd, so you must take care of still closing
+ * the FileDescriptor yourself (and can do that whenever you want).
+ * Returns the cookie of the added asset, or 0 on failure.
+ * {@hide}
+ */
+ public int addAssetFd(FileDescriptor fd, String debugPathName) {
+ return addAssetFdInternal(fd, debugPathName, false);
+ }
+
+ private int addAssetFdInternal(FileDescriptor fd, String debugPathName,
+ boolean appAsLib) {
+ synchronized (this) {
+ int res = addAssetFdNative(fd, debugPathName, appAsLib);
+ makeStringBlocks(mStringBlocks);
+ return res;
+ }
+ }
+
+ private native int addAssetFdNative(FileDescriptor fd, String debugPathName,
+ boolean appAsLib);
+
+ /**
+ * Add a set of assets to overlay an already added set of assets.
+ *
+ * This is only intended for application resources. System wide resources
+ * are handled before any Java code is executed.
+ *
+ * {@hide}
+ */
+
+ public final int addOverlayPath(String idmapPath) {
+ synchronized (this) {
+ int res = addOverlayPathNative(idmapPath);
+ makeStringBlocks(mStringBlocks);
+ return res;
+ }
+ }
+
+ /**
+ * See addOverlayPath.
+ *
+ * {@hide}
+ */
+ public native final int addOverlayPathNative(String idmapPath);
+
+ /**
+ * Add multiple sets of assets to the asset manager at once. See
+ * {@link #addAssetPath(String)} for more information. Returns array of
+ * cookies for each added asset with 0 indicating failure, or null if
+ * the input array of paths is null.
+ * {@hide}
+ */
+ public final int[] addAssetPaths(String[] paths) {
+ if (paths == null) {
+ return null;
+ }
+
+ int[] cookies = new int[paths.length];
+ for (int i = 0; i < paths.length; i++) {
+ cookies[i] = addAssetPath(paths[i]);
+ }
+
+ return cookies;
+ }
+
/**
* Determine whether the state in this asset manager is up-to-date with
* the files on the filesystem. If false is returned, you need to
* instantiate a new AssetManager class to see the new data.
- * @hide
+ * {@hide}
*/
- public boolean isUpToDate() {
- for (ApkAssets apkAssets : getApkAssets()) {
- if (!apkAssets.isUpToDate()) {
- return false;
- }
- }
- return true;
- }
+ public native final boolean isUpToDate();
/**
* Get the locales that this asset manager contains data for.
@@ -1127,12 +786,7 @@
* are of the form {@code ll_CC} where {@code ll} is a two letter language code,
* and {@code CC} is a two letter country code.
*/
- public String[] getLocales() {
- synchronized (this) {
- ensureValidLocked();
- return nativeGetLocales(mObject, false /*excludeSystem*/);
- }
- }
+ public native final String[] getLocales();
/**
* Same as getLocales(), except that locales that are only provided by the system (i.e. those
@@ -1142,57 +796,132 @@
* assets support Cherokee and French, getLocales() would return
* [Cherokee, English, French, German], while getNonSystemLocales() would return
* [Cherokee, French].
- * @hide
+ * {@hide}
*/
- public String[] getNonSystemLocales() {
- synchronized (this) {
- ensureValidLocked();
- return nativeGetLocales(mObject, true /*excludeSystem*/);
- }
- }
+ public native final String[] getNonSystemLocales();
+
+ /** {@hide} */
+ public native final Configuration[] getSizeConfigurations();
/**
- * @hide
- */
- Configuration[] getSizeConfigurations() {
- synchronized (this) {
- ensureValidLocked();
- return nativeGetSizeConfigurations(mObject);
- }
- }
-
- /**
- * Change the configuration used when retrieving resources. Not for use by
+ * Change the configuation used when retrieving resources. Not for use by
* applications.
- * @hide
+ * {@hide}
*/
- public void setConfiguration(int mcc, int mnc, @Nullable String locale, int orientation,
- int touchscreen, int density, int keyboard, int keyboardHidden, int navigation,
- int screenWidth, int screenHeight, int smallestScreenWidthDp, int screenWidthDp,
- int screenHeightDp, int screenLayout, int uiMode, int colorMode, int majorVersion) {
- synchronized (this) {
- ensureValidLocked();
- nativeSetConfiguration(mObject, mcc, mnc, locale, orientation, touchscreen, density,
- keyboard, keyboardHidden, navigation, screenWidth, screenHeight,
- smallestScreenWidthDp, screenWidthDp, screenHeightDp, screenLayout, uiMode,
- colorMode, majorVersion);
- }
- }
+ public native final void setConfiguration(int mcc, int mnc, String locale,
+ int orientation, int touchscreen, int density, int keyboard,
+ int keyboardHidden, int navigation, int screenWidth, int screenHeight,
+ int smallestScreenWidthDp, int screenWidthDp, int screenHeightDp,
+ int screenLayout, int uiMode, int colorMode, int majorVersion);
/**
- * @hide
+ * Retrieve the resource identifier for the given resource name.
*/
- public SparseArray<String> getAssignedPackageIdentifiers() {
- synchronized (this) {
- ensureValidLocked();
- return nativeGetAssignedPackageIdentifiers(mObject);
- }
- }
+ /*package*/ native final int getResourceIdentifier(String name,
+ String defType,
+ String defPackage);
- private void incRefsLocked(long id) {
+ /*package*/ native final String getResourceName(int resid);
+ /*package*/ native final String getResourcePackageName(int resid);
+ /*package*/ native final String getResourceTypeName(int resid);
+ /*package*/ native final String getResourceEntryName(int resid);
+
+ private native final long openAsset(String fileName, int accessMode);
+ private final native ParcelFileDescriptor openAssetFd(String fileName,
+ long[] outOffsets) throws IOException;
+ private native final long openNonAssetNative(int cookie, String fileName,
+ int accessMode);
+ private native ParcelFileDescriptor openNonAssetFdNative(int cookie,
+ String fileName, long[] outOffsets) throws IOException;
+ private native final void destroyAsset(long asset);
+ private native final int readAssetChar(long asset);
+ private native final int readAsset(long asset, byte[] b, int off, int len);
+ private native final long seekAsset(long asset, long offset, int whence);
+ private native final long getAssetLength(long asset);
+ private native final long getAssetRemainingLength(long asset);
+
+ /** Returns true if the resource was found, filling in mRetStringBlock and
+ * mRetData. */
+ private native final int loadResourceValue(int ident, short density, TypedValue outValue,
+ boolean resolve);
+ /** Returns true if the resource was found, filling in mRetStringBlock and
+ * mRetData. */
+ private native final int loadResourceBagValue(int ident, int bagEntryId, TypedValue outValue,
+ boolean resolve);
+ /*package*/ static final int STYLE_NUM_ENTRIES = 6;
+ /*package*/ static final int STYLE_TYPE = 0;
+ /*package*/ static final int STYLE_DATA = 1;
+ /*package*/ static final int STYLE_ASSET_COOKIE = 2;
+ /*package*/ static final int STYLE_RESOURCE_ID = 3;
+
+ /* Offset within typed data array for native changingConfigurations. */
+ static final int STYLE_CHANGING_CONFIGURATIONS = 4;
+
+ /*package*/ static final int STYLE_DENSITY = 5;
+ /*package*/ native static final void applyStyle(long theme,
+ int defStyleAttr, int defStyleRes, long xmlParser,
+ int[] inAttrs, int length, long outValuesAddress, long outIndicesAddress);
+ /*package*/ native static final boolean resolveAttrs(long theme,
+ int defStyleAttr, int defStyleRes, int[] inValues,
+ int[] inAttrs, int[] outValues, int[] outIndices);
+ /*package*/ native final boolean retrieveAttributes(
+ long xmlParser, int[] inAttrs, int[] outValues, int[] outIndices);
+ /*package*/ native final int getArraySize(int resource);
+ /*package*/ native final int retrieveArray(int resource, int[] outValues);
+ private native final int getStringBlockCount();
+ private native final long getNativeStringBlock(int block);
+
+ /**
+ * {@hide}
+ */
+ public native final String getCookieName(int cookie);
+
+ /**
+ * {@hide}
+ */
+ public native final SparseArray<String> getAssignedPackageIdentifiers();
+
+ /**
+ * {@hide}
+ */
+ public native static final int getGlobalAssetCount();
+
+ /**
+ * {@hide}
+ */
+ public native static final String getAssetAllocations();
+
+ /**
+ * {@hide}
+ */
+ public native static final int getGlobalAssetManagerCount();
+
+ private native final long newTheme();
+ private native final void deleteTheme(long theme);
+ /*package*/ native static final void applyThemeStyle(long theme, int styleRes, boolean force);
+ /*package*/ native static final void copyTheme(long dest, long source);
+ /*package*/ native static final void clearTheme(long theme);
+ /*package*/ native static final int loadThemeAttributeValue(long theme, int ident,
+ TypedValue outValue,
+ boolean resolve);
+ /*package*/ native static final void dumpTheme(long theme, int priority, String tag, String prefix);
+ /*package*/ native static final @NativeConfig int getThemeChangingConfigurations(long theme);
+
+ private native final long openXmlAssetNative(int cookie, String fileName);
+
+ private native final String[] getArrayStringResource(int arrayRes);
+ private native final int[] getArrayStringInfo(int arrayRes);
+ /*package*/ native final int[] getArrayIntResource(int arrayRes);
+ /*package*/ native final int[] getStyleAttributes(int themeRes);
+
+ private native final void init(boolean isSystem);
+ private native final void destroy();
+
+ @GuardedBy("this")
+ private final void incRefsLocked(long id) {
if (DEBUG_REFS) {
if (mRefStacks == null) {
- mRefStacks = new HashMap<>();
+ mRefStacks = new HashMap<Long, RuntimeException>();
}
RuntimeException ex = new RuntimeException();
ex.fillInStackTrace();
@@ -1201,117 +930,16 @@
mNumRefs++;
}
- private void decRefsLocked(long id) {
+ @GuardedBy("this")
+ private final void decRefsLocked(long id) {
if (DEBUG_REFS && mRefStacks != null) {
mRefStacks.remove(id);
}
mNumRefs--;
- if (mNumRefs == 0 && mObject != 0) {
- nativeDestroy(mObject);
- mObject = 0;
- mApkAssets = sEmptyApkAssets;
+ //System.out.println("Dec streams: mNumRefs=" + mNumRefs
+ // + " mReleased=" + mReleased);
+ if (mNumRefs == 0) {
+ destroy();
}
}
-
- // AssetManager setup native methods.
- private static native long nativeCreate();
- private static native void nativeDestroy(long ptr);
- private static native void nativeSetApkAssets(long ptr, @NonNull ApkAssets[] apkAssets,
- boolean invalidateCaches);
- private static native void nativeSetConfiguration(long ptr, int mcc, int mnc,
- @Nullable String locale, int orientation, int touchscreen, int density, int keyboard,
- int keyboardHidden, int navigation, int screenWidth, int screenHeight,
- int smallestScreenWidthDp, int screenWidthDp, int screenHeightDp, int screenLayout,
- int uiMode, int colorMode, int majorVersion);
- private static native @NonNull SparseArray<String> nativeGetAssignedPackageIdentifiers(
- long ptr);
-
- // File native methods.
- private static native @Nullable String[] nativeList(long ptr, @NonNull String path)
- throws IOException;
- private static native long nativeOpenAsset(long ptr, @NonNull String fileName, int accessMode);
- private static native @Nullable ParcelFileDescriptor nativeOpenAssetFd(long ptr,
- @NonNull String fileName, long[] outOffsets) throws IOException;
- private static native long nativeOpenNonAsset(long ptr, int cookie, @NonNull String fileName,
- int accessMode);
- private static native @Nullable ParcelFileDescriptor nativeOpenNonAssetFd(long ptr, int cookie,
- @NonNull String fileName, @NonNull long[] outOffsets) throws IOException;
- private static native long nativeOpenXmlAsset(long ptr, int cookie, @NonNull String fileName);
-
- // Primitive resource native methods.
- private static native int nativeGetResourceValue(long ptr, @AnyRes int resId, short density,
- @NonNull TypedValue outValue, boolean resolveReferences);
- private static native int nativeGetResourceBagValue(long ptr, @AnyRes int resId, int bagEntryId,
- @NonNull TypedValue outValue);
-
- private static native @Nullable @AttrRes int[] nativeGetStyleAttributes(long ptr,
- @StyleRes int resId);
- private static native @Nullable String[] nativeGetResourceStringArray(long ptr,
- @ArrayRes int resId);
- private static native @Nullable int[] nativeGetResourceStringArrayInfo(long ptr,
- @ArrayRes int resId);
- private static native @Nullable int[] nativeGetResourceIntArray(long ptr, @ArrayRes int resId);
- private static native int nativeGetResourceArraySize(long ptr, @ArrayRes int resId);
- private static native int nativeGetResourceArray(long ptr, @ArrayRes int resId,
- @NonNull int[] outValues);
-
- // Resource name/ID native methods.
- private static native @AnyRes int nativeGetResourceIdentifier(long ptr, @NonNull String name,
- @Nullable String defType, @Nullable String defPackage);
- private static native @Nullable String nativeGetResourceName(long ptr, @AnyRes int resid);
- private static native @Nullable String nativeGetResourcePackageName(long ptr,
- @AnyRes int resid);
- private static native @Nullable String nativeGetResourceTypeName(long ptr, @AnyRes int resid);
- private static native @Nullable String nativeGetResourceEntryName(long ptr, @AnyRes int resid);
- private static native @Nullable String[] nativeGetLocales(long ptr, boolean excludeSystem);
- private static native @Nullable Configuration[] nativeGetSizeConfigurations(long ptr);
-
- // Style attribute retrieval native methods.
- private static native void nativeApplyStyle(long ptr, long themePtr, @AttrRes int defStyleAttr,
- @StyleRes int defStyleRes, long xmlParserPtr, @NonNull int[] inAttrs,
- long outValuesAddress, long outIndicesAddress);
- private static native boolean nativeResolveAttrs(long ptr, long themePtr,
- @AttrRes int defStyleAttr, @StyleRes int defStyleRes, @Nullable int[] inValues,
- @NonNull int[] inAttrs, @NonNull int[] outValues, @NonNull int[] outIndices);
- private static native boolean nativeRetrieveAttributes(long ptr, long xmlParserPtr,
- @NonNull int[] inAttrs, @NonNull int[] outValues, @NonNull int[] outIndices);
-
- // Theme related native methods
- private static native long nativeThemeCreate(long ptr);
- private static native void nativeThemeDestroy(long themePtr);
- private static native void nativeThemeApplyStyle(long ptr, long themePtr, @StyleRes int resId,
- boolean force);
- static native void nativeThemeCopy(long destThemePtr, long sourceThemePtr);
- static native void nativeThemeClear(long themePtr);
- private static native int nativeThemeGetAttributeValue(long ptr, long themePtr,
- @AttrRes int resId, @NonNull TypedValue outValue, boolean resolve);
- private static native void nativeThemeDump(long ptr, long themePtr, int priority, String tag,
- String prefix);
- static native @NativeConfig int nativeThemeGetChangingConfigurations(long themePtr);
-
- // AssetInputStream related native methods.
- private static native void nativeAssetDestroy(long assetPtr);
- private static native int nativeAssetReadChar(long assetPtr);
- private static native int nativeAssetRead(long assetPtr, byte[] b, int off, int len);
- private static native long nativeAssetSeek(long assetPtr, long offset, int whence);
- private static native long nativeAssetGetLength(long assetPtr);
- private static native long nativeAssetGetRemainingLength(long assetPtr);
-
- private static native void nativeVerifySystemIdmaps();
-
- // Global debug native methods.
- /**
- * @hide
- */
- public static native int getGlobalAssetCount();
-
- /**
- * @hide
- */
- public static native String getAssetAllocations();
-
- /**
- * @hide
- */
- public static native int getGlobalAssetManagerCount();
}
diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java
index 8f58891..ad85e71 100644
--- a/core/java/android/content/res/Resources.java
+++ b/core/java/android/content/res/Resources.java
@@ -590,7 +590,7 @@
*/
@NonNull
public int[] getIntArray(@ArrayRes int id) throws NotFoundException {
- int[] res = mResourcesImpl.getAssets().getResourceIntArray(id);
+ int[] res = mResourcesImpl.getAssets().getArrayIntResource(id);
if (res != null) {
return res;
}
@@ -613,13 +613,13 @@
@NonNull
public TypedArray obtainTypedArray(@ArrayRes int id) throws NotFoundException {
final ResourcesImpl impl = mResourcesImpl;
- int len = impl.getAssets().getResourceArraySize(id);
+ int len = impl.getAssets().getArraySize(id);
if (len < 0) {
throw new NotFoundException("Array resource ID #0x" + Integer.toHexString(id));
}
TypedArray array = TypedArray.obtain(this, len);
- array.mLength = impl.getAssets().getResourceArray(id, array.mData);
+ array.mLength = impl.getAssets().retrieveArray(id, array.mData);
array.mIndices[0] = 0;
return array;
@@ -867,8 +867,9 @@
* @param theme The theme used to style the drawable attributes, may be {@code null}.
* @return Drawable An object that can be used to draw this resource.
* @throws NotFoundException Throws NotFoundException if the given ID does
- * not exist.
+ * not exist, or cannot be decoded.
*/
+ @NonNull
public Drawable getDrawableForDensity(@DrawableRes int id, int density, @Nullable Theme theme) {
final TypedValue value = obtainTempTypedValue();
try {
@@ -980,7 +981,7 @@
* or multiple colors that can be selected based on a state.
* @deprecated Use {@link #getColorStateList(int, Theme)} instead.
*/
- @Nullable
+ @NonNull
@Deprecated
public ColorStateList getColorStateList(@ColorRes int id) throws NotFoundException {
final ColorStateList csl = getColorStateList(id, null);
@@ -1011,7 +1012,7 @@
* @return A themed ColorStateList object containing either a single solid
* color or multiple colors that can be selected based on a state.
*/
- @Nullable
+ @NonNull
public ColorStateList getColorStateList(@ColorRes int id, @Nullable Theme theme)
throws NotFoundException {
final TypedValue value = obtainTempTypedValue();
@@ -1024,7 +1025,7 @@
}
}
- @Nullable
+ @NonNull
ColorStateList loadColorStateList(@NonNull TypedValue value, int id, @Nullable Theme theme)
throws NotFoundException {
return mResourcesImpl.loadColorStateList(this, value, id, theme);
@@ -1033,7 +1034,7 @@
/**
* @hide
*/
- @Nullable
+ @NonNull
public ComplexColor loadComplexColor(@NonNull TypedValue value, int id, @Nullable Theme theme) {
return mResourcesImpl.loadComplexColor(this, value, id, theme);
}
@@ -1139,6 +1140,7 @@
*
* @see #getXml
*/
+ @NonNull
public XmlResourceParser getLayout(@LayoutRes int id) throws NotFoundException {
return loadXmlResourceParser(id, "layout");
}
@@ -1163,6 +1165,7 @@
*
* @see #getXml
*/
+ @NonNull
public XmlResourceParser getAnimation(@AnimatorRes @AnimRes int id) throws NotFoundException {
return loadXmlResourceParser(id, "anim");
}
@@ -1188,6 +1191,7 @@
*
* @see android.util.AttributeSet
*/
+ @NonNull
public XmlResourceParser getXml(@XmlRes int id) throws NotFoundException {
return loadXmlResourceParser(id, "xml");
}
@@ -1203,8 +1207,8 @@
* @return InputStream Access to the resource data.
*
* @throws NotFoundException Throws NotFoundException if the given ID does not exist.
- *
*/
+ @NonNull
public InputStream openRawResource(@RawRes int id) throws NotFoundException {
final TypedValue value = obtainTempTypedValue();
try {
@@ -1261,6 +1265,7 @@
*
* @throws NotFoundException Throws NotFoundException if the given ID does not exist.
*/
+ @NonNull
public InputStream openRawResource(@RawRes int id, TypedValue value)
throws NotFoundException {
return mResourcesImpl.openRawResource(id, value);
@@ -1789,7 +1794,8 @@
// out the attributes from the XML file (applying type information
// contained in the resources and such).
XmlBlock.Parser parser = (XmlBlock.Parser)set;
- mResourcesImpl.getAssets().retrieveAttributes(parser, attrs, array.mData, array.mIndices);
+ mResourcesImpl.getAssets().retrieveAttributes(parser.mParseState, attrs,
+ array.mData, array.mIndices);
array.mXml = parser;
diff --git a/core/java/android/content/res/ResourcesImpl.java b/core/java/android/content/res/ResourcesImpl.java
index 80e3860..91dd7ee 100644
--- a/core/java/android/content/res/ResourcesImpl.java
+++ b/core/java/android/content/res/ResourcesImpl.java
@@ -27,11 +27,9 @@
import android.annotation.StyleableRes;
import android.content.pm.ActivityInfo;
import android.content.pm.ActivityInfo.Config;
-import android.content.res.AssetManager.AssetInputStream;
import android.content.res.Configuration.NativeConfig;
import android.content.res.Resources.NotFoundException;
import android.graphics.Bitmap;
-import android.graphics.ImageDecoder;
import android.graphics.Typeface;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
@@ -170,6 +168,7 @@
mDisplayAdjustments = displayAdjustments;
mConfiguration.setToDefaults();
updateConfiguration(config, metrics, displayAdjustments.getCompatibilityInfo());
+ mAssets.ensureStringBlocks();
}
public DisplayAdjustments getDisplayAdjustments() {
@@ -544,7 +543,7 @@
}
}
- @Nullable
+ @NonNull
Drawable loadDrawable(@NonNull Resources wrapper, @NonNull TypedValue value, int id,
int density, @Nullable Resources.Theme theme)
throws NotFoundException {
@@ -628,7 +627,7 @@
} else if (isColorDrawable) {
dr = new ColorDrawable(value.data);
} else {
- dr = loadDrawableForCookie(wrapper, value, id, density, null);
+ dr = loadDrawableForCookie(wrapper, value, id, density);
}
// DrawableContainer' constant state has drawables instances. In order to leave the
// constant state intact in the cache, we need to create a new DrawableContainer after
@@ -755,8 +754,9 @@
/**
* Loads a drawable from XML or resources stream.
*/
+ @NonNull
private Drawable loadDrawableForCookie(@NonNull Resources wrapper, @NonNull TypedValue value,
- int id, int density, @Nullable Resources.Theme theme) {
+ int id, int density) {
if (value.string == null) {
throw new NotFoundException("Resource \"" + getResourceName(id) + "\" ("
+ Integer.toHexString(id) + ") is not a Drawable (color or path): " + value);
@@ -775,22 +775,23 @@
}
}
- // For prelaod tracing.
+ // For preload tracing.
long startTime = 0;
int startBitmapCount = 0;
long startBitmapSize = 0;
- int startDrwableCount = 0;
+ int startDrawableCount = 0;
if (TRACE_FOR_DETAILED_PRELOAD) {
startTime = System.nanoTime();
startBitmapCount = Bitmap.sPreloadTracingNumInstantiatedBitmaps;
startBitmapSize = Bitmap.sPreloadTracingTotalBitmapsSize;
- startDrwableCount = sPreloadTracingNumLoadedDrawables;
+ startDrawableCount = sPreloadTracingNumLoadedDrawables;
}
if (DEBUG_LOAD) {
Log.v(TAG, "Loading drawable for cookie " + value.assetCookie + ": " + file);
}
+
final Drawable dr;
Trace.traceBegin(Trace.TRACE_TAG_RESOURCES, file);
@@ -805,18 +806,13 @@
if (file.endsWith(".xml")) {
final XmlResourceParser rp = loadXmlResourceParser(
file, id, value.assetCookie, "drawable");
- dr = Drawable.createFromXmlForDensity(wrapper, rp, density, theme);
+ dr = Drawable.createFromXmlForDensity(wrapper, rp, density, null);
rp.close();
} else {
final InputStream is = mAssets.openNonAsset(
value.assetCookie, file, AssetManager.ACCESS_STREAMING);
- AssetInputStream ais = (AssetInputStream) is;
- // ImageDecoder will close the input stream.
- ImageDecoder.Source src = new ImageDecoder.AssetInputStreamSource(ais,
- wrapper, value);
- dr = ImageDecoder.decodeDrawable(src, (decoder, info, s) -> {
- decoder.setAllocator(ImageDecoder.ALLOCATOR_SOFTWARE);
- });
+ dr = Drawable.createFromResourceStream(wrapper, value, is, file, null);
+ is.close();
}
} finally {
stack.pop();
@@ -840,7 +836,7 @@
final long loadedBitmapSize =
Bitmap.sPreloadTracingTotalBitmapsSize - startBitmapSize;
final int loadedDrawables =
- sPreloadTracingNumLoadedDrawables - startDrwableCount;
+ sPreloadTracingNumLoadedDrawables - startDrawableCount;
sPreloadTracingNumLoadedDrawables++;
@@ -916,6 +912,7 @@
* first try to load CSL from the cache. If not found, try to get from the constant state.
* Last, parse the XML and generate the CSL.
*/
+ @NonNull
private ComplexColor loadComplexColorFromName(Resources wrapper, Resources.Theme theme,
TypedValue value, int id) {
final long key = (((long) value.assetCookie) << 32) | value.data;
@@ -935,17 +932,15 @@
complexColor = loadComplexColorForCookie(wrapper, value, id, theme);
}
- if (complexColor != null) {
- complexColor.setBaseChangingConfigurations(value.changingConfigurations);
+ complexColor.setBaseChangingConfigurations(value.changingConfigurations);
- if (mPreloading) {
- if (verifyPreloadConfig(complexColor.getChangingConfigurations(),
- 0, value.resourceId, "color")) {
- sPreloadedComplexColors.put(key, complexColor.getConstantState());
- }
- } else {
- cache.put(key, theme, complexColor.getConstantState());
+ if (mPreloading) {
+ if (verifyPreloadConfig(complexColor.getChangingConfigurations(),
+ 0, value.resourceId, "color")) {
+ sPreloadedComplexColors.put(key, complexColor.getConstantState());
}
+ } else {
+ cache.put(key, theme, complexColor.getConstantState());
}
return complexColor;
}
@@ -991,7 +986,7 @@
return complexColor;
}
- @Nullable
+ @NonNull
ColorStateList loadColorStateList(Resources wrapper, TypedValue value, int id,
Resources.Theme theme)
throws NotFoundException {
@@ -1051,7 +1046,7 @@
*
* @return a ComplexColor (GradientColor or ColorStateList) based on the XML file content.
*/
- @Nullable
+ @NonNull
private ComplexColor loadComplexColorForCookie(Resources wrapper, TypedValue value, int id,
Resources.Theme theme) {
if (value.string == null) {
@@ -1280,7 +1275,8 @@
void applyStyle(int resId, boolean force) {
synchronized (mKey) {
- mAssets.applyStyleToTheme(mTheme, resId, force);
+ AssetManager.applyThemeStyle(mTheme, resId, force);
+
mThemeResId = resId;
mKey.append(resId, force);
}
@@ -1289,7 +1285,7 @@
void setTo(ThemeImpl other) {
synchronized (mKey) {
synchronized (other.mKey) {
- AssetManager.nativeThemeCopy(mTheme, other.mTheme);
+ AssetManager.copyTheme(mTheme, other.mTheme);
mThemeResId = other.mThemeResId;
mKey.setTo(other.getKey());
@@ -1312,10 +1308,12 @@
// out the attributes from the XML file (applying type information
// contained in the resources and such).
final XmlBlock.Parser parser = (XmlBlock.Parser) set;
- mAssets.applyStyle(mTheme, defStyleAttr, defStyleRes, parser, attrs,
- array.mDataAddress, array.mIndicesAddress);
+ AssetManager.applyStyle(mTheme, defStyleAttr, defStyleRes,
+ parser != null ? parser.mParseState : 0,
+ attrs, attrs.length, array.mDataAddress, array.mIndicesAddress);
array.mTheme = wrapper;
array.mXml = parser;
+
return array;
}
}
@@ -1332,7 +1330,7 @@
}
final TypedArray array = TypedArray.obtain(wrapper.getResources(), len);
- mAssets.resolveAttrs(mTheme, 0, 0, values, attrs, array.mData, array.mIndices);
+ AssetManager.resolveAttrs(mTheme, 0, 0, values, attrs, array.mData, array.mIndices);
array.mTheme = wrapper;
array.mXml = null;
return array;
@@ -1352,14 +1350,14 @@
@Config int getChangingConfigurations() {
synchronized (mKey) {
final @NativeConfig int nativeChangingConfig =
- AssetManager.nativeThemeGetChangingConfigurations(mTheme);
+ AssetManager.getThemeChangingConfigurations(mTheme);
return ActivityInfo.activityInfoConfigNativeToJava(nativeChangingConfig);
}
}
public void dump(int priority, String tag, String prefix) {
synchronized (mKey) {
- mAssets.dumpTheme(mTheme, priority, tag, prefix);
+ AssetManager.dumpTheme(mTheme, priority, tag, prefix);
}
}
@@ -1388,13 +1386,13 @@
*/
void rebase() {
synchronized (mKey) {
- AssetManager.nativeThemeClear(mTheme);
+ AssetManager.clearTheme(mTheme);
// Reapply the same styles in the same order.
for (int i = 0; i < mKey.mCount; i++) {
final int resId = mKey.mResId[i];
final boolean force = mKey.mForce[i];
- mAssets.applyStyleToTheme(mTheme, resId, force);
+ AssetManager.applyThemeStyle(mTheme, resId, force);
}
}
}
diff --git a/core/java/android/content/res/TypedArray.java b/core/java/android/content/res/TypedArray.java
index cbb3c6d..f33c751 100644
--- a/core/java/android/content/res/TypedArray.java
+++ b/core/java/android/content/res/TypedArray.java
@@ -61,15 +61,6 @@
return attrs;
}
- // STYLE_ prefixed constants are offsets within the typed data array.
- static final int STYLE_NUM_ENTRIES = 6;
- static final int STYLE_TYPE = 0;
- static final int STYLE_DATA = 1;
- static final int STYLE_ASSET_COOKIE = 2;
- static final int STYLE_RESOURCE_ID = 3;
- static final int STYLE_CHANGING_CONFIGURATIONS = 4;
- static final int STYLE_DENSITY = 5;
-
private final Resources mResources;
private DisplayMetrics mMetrics;
private AssetManager mAssets;
@@ -87,7 +78,7 @@
private void resize(int len) {
mLength = len;
- final int dataLen = len * STYLE_NUM_ENTRIES;
+ final int dataLen = len * AssetManager.STYLE_NUM_ENTRIES;
final int indicesLen = len + 1;
final VMRuntime runtime = VMRuntime.getRuntime();
if (mDataAddress == 0 || mData.length < dataLen) {
@@ -175,9 +166,9 @@
throw new RuntimeException("Cannot make calls to a recycled instance!");
}
- index *= STYLE_NUM_ENTRIES;
+ index *= AssetManager.STYLE_NUM_ENTRIES;
final int[] data = mData;
- final int type = data[index + STYLE_TYPE];
+ final int type = data[index+AssetManager.STYLE_TYPE];
if (type == TypedValue.TYPE_NULL) {
return null;
} else if (type == TypedValue.TYPE_STRING) {
@@ -212,9 +203,9 @@
throw new RuntimeException("Cannot make calls to a recycled instance!");
}
- index *= STYLE_NUM_ENTRIES;
+ index *= AssetManager.STYLE_NUM_ENTRIES;
final int[] data = mData;
- final int type = data[index + STYLE_TYPE];
+ final int type = data[index+AssetManager.STYLE_TYPE];
if (type == TypedValue.TYPE_NULL) {
return null;
} else if (type == TypedValue.TYPE_STRING) {
@@ -251,13 +242,14 @@
throw new RuntimeException("Cannot make calls to a recycled instance!");
}
- index *= STYLE_NUM_ENTRIES;
+ index *= AssetManager.STYLE_NUM_ENTRIES;
final int[] data = mData;
- final int type = data[index + STYLE_TYPE];
+ final int type = data[index+AssetManager.STYLE_TYPE];
if (type == TypedValue.TYPE_STRING) {
- final int cookie = data[index + STYLE_ASSET_COOKIE];
+ final int cookie = data[index+AssetManager.STYLE_ASSET_COOKIE];
if (cookie < 0) {
- return mXml.getPooledString(data[index + STYLE_DATA]).toString();
+ return mXml.getPooledString(
+ data[index+AssetManager.STYLE_DATA]).toString();
}
}
return null;
@@ -282,11 +274,11 @@
throw new RuntimeException("Cannot make calls to a recycled instance!");
}
- index *= STYLE_NUM_ENTRIES;
+ index *= AssetManager.STYLE_NUM_ENTRIES;
final int[] data = mData;
- final int type = data[index + STYLE_TYPE];
+ final int type = data[index+AssetManager.STYLE_TYPE];
final @Config int changingConfigs = ActivityInfo.activityInfoConfigNativeToJava(
- data[index + STYLE_CHANGING_CONFIGURATIONS]);
+ data[index + AssetManager.STYLE_CHANGING_CONFIGURATIONS]);
if ((changingConfigs & ~allowedChangingConfigs) != 0) {
return null;
}
@@ -328,14 +320,14 @@
throw new RuntimeException("Cannot make calls to a recycled instance!");
}
- index *= STYLE_NUM_ENTRIES;
+ index *= AssetManager.STYLE_NUM_ENTRIES;
final int[] data = mData;
- final int type = data[index + STYLE_TYPE];
+ final int type = data[index+AssetManager.STYLE_TYPE];
if (type == TypedValue.TYPE_NULL) {
return defValue;
} else if (type >= TypedValue.TYPE_FIRST_INT
&& type <= TypedValue.TYPE_LAST_INT) {
- return data[index + STYLE_DATA] != 0;
+ return data[index+AssetManager.STYLE_DATA] != 0;
}
final TypedValue v = mValue;
@@ -367,14 +359,14 @@
throw new RuntimeException("Cannot make calls to a recycled instance!");
}
- index *= STYLE_NUM_ENTRIES;
+ index *= AssetManager.STYLE_NUM_ENTRIES;
final int[] data = mData;
- final int type = data[index + STYLE_TYPE];
+ final int type = data[index+AssetManager.STYLE_TYPE];
if (type == TypedValue.TYPE_NULL) {
return defValue;
} else if (type >= TypedValue.TYPE_FIRST_INT
&& type <= TypedValue.TYPE_LAST_INT) {
- return data[index + STYLE_DATA];
+ return data[index+AssetManager.STYLE_DATA];
}
final TypedValue v = mValue;
@@ -404,16 +396,16 @@
throw new RuntimeException("Cannot make calls to a recycled instance!");
}
- index *= STYLE_NUM_ENTRIES;
+ index *= AssetManager.STYLE_NUM_ENTRIES;
final int[] data = mData;
- final int type = data[index + STYLE_TYPE];
+ final int type = data[index+AssetManager.STYLE_TYPE];
if (type == TypedValue.TYPE_NULL) {
return defValue;
} else if (type == TypedValue.TYPE_FLOAT) {
- return Float.intBitsToFloat(data[index + STYLE_DATA]);
+ return Float.intBitsToFloat(data[index+AssetManager.STYLE_DATA]);
} else if (type >= TypedValue.TYPE_FIRST_INT
&& type <= TypedValue.TYPE_LAST_INT) {
- return data[index + STYLE_DATA];
+ return data[index+AssetManager.STYLE_DATA];
}
final TypedValue v = mValue;
@@ -454,15 +446,15 @@
}
final int attrIndex = index;
- index *= STYLE_NUM_ENTRIES;
+ index *= AssetManager.STYLE_NUM_ENTRIES;
final int[] data = mData;
- final int type = data[index + STYLE_TYPE];
+ final int type = data[index+AssetManager.STYLE_TYPE];
if (type == TypedValue.TYPE_NULL) {
return defValue;
} else if (type >= TypedValue.TYPE_FIRST_INT
&& type <= TypedValue.TYPE_LAST_INT) {
- return data[index + STYLE_DATA];
+ return data[index+AssetManager.STYLE_DATA];
} else if (type == TypedValue.TYPE_STRING) {
final TypedValue value = mValue;
if (getValueAt(index, value)) {
@@ -506,7 +498,7 @@
}
final TypedValue value = mValue;
- if (getValueAt(index * STYLE_NUM_ENTRIES, value)) {
+ if (getValueAt(index*AssetManager.STYLE_NUM_ENTRIES, value)) {
if (value.type == TypedValue.TYPE_ATTRIBUTE) {
throw new UnsupportedOperationException(
"Failed to resolve attribute at index " + index + ": " + value);
@@ -541,7 +533,7 @@
}
final TypedValue value = mValue;
- if (getValueAt(index * STYLE_NUM_ENTRIES, value)) {
+ if (getValueAt(index*AssetManager.STYLE_NUM_ENTRIES, value)) {
if (value.type == TypedValue.TYPE_ATTRIBUTE) {
throw new UnsupportedOperationException(
"Failed to resolve attribute at index " + index + ": " + value);
@@ -572,15 +564,15 @@
}
final int attrIndex = index;
- index *= STYLE_NUM_ENTRIES;
+ index *= AssetManager.STYLE_NUM_ENTRIES;
final int[] data = mData;
- final int type = data[index + STYLE_TYPE];
+ final int type = data[index+AssetManager.STYLE_TYPE];
if (type == TypedValue.TYPE_NULL) {
return defValue;
} else if (type >= TypedValue.TYPE_FIRST_INT
&& type <= TypedValue.TYPE_LAST_INT) {
- return data[index + STYLE_DATA];
+ return data[index+AssetManager.STYLE_DATA];
} else if (type == TypedValue.TYPE_ATTRIBUTE) {
final TypedValue value = mValue;
getValueAt(index, value);
@@ -620,14 +612,15 @@
}
final int attrIndex = index;
- index *= STYLE_NUM_ENTRIES;
+ index *= AssetManager.STYLE_NUM_ENTRIES;
final int[] data = mData;
- final int type = data[index + STYLE_TYPE];
+ final int type = data[index+AssetManager.STYLE_TYPE];
if (type == TypedValue.TYPE_NULL) {
return defValue;
} else if (type == TypedValue.TYPE_DIMENSION) {
- return TypedValue.complexToDimension(data[index + STYLE_DATA], mMetrics);
+ return TypedValue.complexToDimension(
+ data[index + AssetManager.STYLE_DATA], mMetrics);
} else if (type == TypedValue.TYPE_ATTRIBUTE) {
final TypedValue value = mValue;
getValueAt(index, value);
@@ -668,14 +661,15 @@
}
final int attrIndex = index;
- index *= STYLE_NUM_ENTRIES;
+ index *= AssetManager.STYLE_NUM_ENTRIES;
final int[] data = mData;
- final int type = data[index + STYLE_TYPE];
+ final int type = data[index+AssetManager.STYLE_TYPE];
if (type == TypedValue.TYPE_NULL) {
return defValue;
} else if (type == TypedValue.TYPE_DIMENSION) {
- return TypedValue.complexToDimensionPixelOffset(data[index + STYLE_DATA], mMetrics);
+ return TypedValue.complexToDimensionPixelOffset(
+ data[index + AssetManager.STYLE_DATA], mMetrics);
} else if (type == TypedValue.TYPE_ATTRIBUTE) {
final TypedValue value = mValue;
getValueAt(index, value);
@@ -717,14 +711,15 @@
}
final int attrIndex = index;
- index *= STYLE_NUM_ENTRIES;
+ index *= AssetManager.STYLE_NUM_ENTRIES;
final int[] data = mData;
- final int type = data[index + STYLE_TYPE];
+ final int type = data[index+AssetManager.STYLE_TYPE];
if (type == TypedValue.TYPE_NULL) {
return defValue;
} else if (type == TypedValue.TYPE_DIMENSION) {
- return TypedValue.complexToDimensionPixelSize(data[index + STYLE_DATA], mMetrics);
+ return TypedValue.complexToDimensionPixelSize(
+ data[index+AssetManager.STYLE_DATA], mMetrics);
} else if (type == TypedValue.TYPE_ATTRIBUTE) {
final TypedValue value = mValue;
getValueAt(index, value);
@@ -760,15 +755,16 @@
}
final int attrIndex = index;
- index *= STYLE_NUM_ENTRIES;
+ index *= AssetManager.STYLE_NUM_ENTRIES;
final int[] data = mData;
- final int type = data[index + STYLE_TYPE];
+ final int type = data[index+AssetManager.STYLE_TYPE];
if (type >= TypedValue.TYPE_FIRST_INT
&& type <= TypedValue.TYPE_LAST_INT) {
- return data[index + STYLE_DATA];
+ return data[index+AssetManager.STYLE_DATA];
} else if (type == TypedValue.TYPE_DIMENSION) {
- return TypedValue.complexToDimensionPixelSize(data[index + STYLE_DATA], mMetrics);
+ return TypedValue.complexToDimensionPixelSize(
+ data[index+AssetManager.STYLE_DATA], mMetrics);
} else if (type == TypedValue.TYPE_ATTRIBUTE) {
final TypedValue value = mValue;
getValueAt(index, value);
@@ -799,14 +795,15 @@
throw new RuntimeException("Cannot make calls to a recycled instance!");
}
- index *= STYLE_NUM_ENTRIES;
+ index *= AssetManager.STYLE_NUM_ENTRIES;
final int[] data = mData;
- final int type = data[index + STYLE_TYPE];
+ final int type = data[index+AssetManager.STYLE_TYPE];
if (type >= TypedValue.TYPE_FIRST_INT
&& type <= TypedValue.TYPE_LAST_INT) {
- return data[index + STYLE_DATA];
+ return data[index+AssetManager.STYLE_DATA];
} else if (type == TypedValue.TYPE_DIMENSION) {
- return TypedValue.complexToDimensionPixelSize(data[index + STYLE_DATA], mMetrics);
+ return TypedValue.complexToDimensionPixelSize(
+ data[index + AssetManager.STYLE_DATA], mMetrics);
}
return defValue;
@@ -836,14 +833,15 @@
}
final int attrIndex = index;
- index *= STYLE_NUM_ENTRIES;
+ index *= AssetManager.STYLE_NUM_ENTRIES;
final int[] data = mData;
- final int type = data[index + STYLE_TYPE];
+ final int type = data[index+AssetManager.STYLE_TYPE];
if (type == TypedValue.TYPE_NULL) {
return defValue;
} else if (type == TypedValue.TYPE_FRACTION) {
- return TypedValue.complexToFraction(data[index + STYLE_DATA], base, pbase);
+ return TypedValue.complexToFraction(
+ data[index+AssetManager.STYLE_DATA], base, pbase);
} else if (type == TypedValue.TYPE_ATTRIBUTE) {
final TypedValue value = mValue;
getValueAt(index, value);
@@ -876,10 +874,10 @@
throw new RuntimeException("Cannot make calls to a recycled instance!");
}
- index *= STYLE_NUM_ENTRIES;
+ index *= AssetManager.STYLE_NUM_ENTRIES;
final int[] data = mData;
- if (data[index + STYLE_TYPE] != TypedValue.TYPE_NULL) {
- final int resid = data[index + STYLE_RESOURCE_ID];
+ if (data[index+AssetManager.STYLE_TYPE] != TypedValue.TYPE_NULL) {
+ final int resid = data[index+AssetManager.STYLE_RESOURCE_ID];
if (resid != 0) {
return resid;
}
@@ -904,10 +902,10 @@
throw new RuntimeException("Cannot make calls to a recycled instance!");
}
- index *= STYLE_NUM_ENTRIES;
+ index *= AssetManager.STYLE_NUM_ENTRIES;
final int[] data = mData;
- if (data[index + STYLE_TYPE] == TypedValue.TYPE_ATTRIBUTE) {
- return data[index + STYLE_DATA];
+ if (data[index + AssetManager.STYLE_TYPE] == TypedValue.TYPE_ATTRIBUTE) {
+ return data[index + AssetManager.STYLE_DATA];
}
return defValue;
}
@@ -941,7 +939,7 @@
}
final TypedValue value = mValue;
- if (getValueAt(index * STYLE_NUM_ENTRIES, value)) {
+ if (getValueAt(index*AssetManager.STYLE_NUM_ENTRIES, value)) {
if (value.type == TypedValue.TYPE_ATTRIBUTE) {
throw new UnsupportedOperationException(
"Failed to resolve attribute at index " + index + ": " + value);
@@ -977,7 +975,7 @@
}
final TypedValue value = mValue;
- if (getValueAt(index * STYLE_NUM_ENTRIES, value)) {
+ if (getValueAt(index*AssetManager.STYLE_NUM_ENTRIES, value)) {
if (value.type == TypedValue.TYPE_ATTRIBUTE) {
throw new UnsupportedOperationException(
"Failed to resolve attribute at index " + index + ": " + value);
@@ -1008,7 +1006,7 @@
}
final TypedValue value = mValue;
- if (getValueAt(index * STYLE_NUM_ENTRIES, value)) {
+ if (getValueAt(index*AssetManager.STYLE_NUM_ENTRIES, value)) {
return mResources.getTextArray(value.resourceId);
}
return null;
@@ -1029,7 +1027,7 @@
throw new RuntimeException("Cannot make calls to a recycled instance!");
}
- return getValueAt(index * STYLE_NUM_ENTRIES, outValue);
+ return getValueAt(index*AssetManager.STYLE_NUM_ENTRIES, outValue);
}
/**
@@ -1045,8 +1043,8 @@
throw new RuntimeException("Cannot make calls to a recycled instance!");
}
- index *= STYLE_NUM_ENTRIES;
- return mData[index + STYLE_TYPE];
+ index *= AssetManager.STYLE_NUM_ENTRIES;
+ return mData[index + AssetManager.STYLE_TYPE];
}
/**
@@ -1065,9 +1063,9 @@
throw new RuntimeException("Cannot make calls to a recycled instance!");
}
- index *= STYLE_NUM_ENTRIES;
+ index *= AssetManager.STYLE_NUM_ENTRIES;
final int[] data = mData;
- final int type = data[index + STYLE_TYPE];
+ final int type = data[index+AssetManager.STYLE_TYPE];
return type != TypedValue.TYPE_NULL;
}
@@ -1086,11 +1084,11 @@
throw new RuntimeException("Cannot make calls to a recycled instance!");
}
- index *= STYLE_NUM_ENTRIES;
+ index *= AssetManager.STYLE_NUM_ENTRIES;
final int[] data = mData;
- final int type = data[index + STYLE_TYPE];
+ final int type = data[index+AssetManager.STYLE_TYPE];
return type != TypedValue.TYPE_NULL
- || data[index + STYLE_DATA] == TypedValue.DATA_NULL_EMPTY;
+ || data[index+AssetManager.STYLE_DATA] == TypedValue.DATA_NULL_EMPTY;
}
/**
@@ -1111,7 +1109,7 @@
}
final TypedValue value = mValue;
- if (getValueAt(index * STYLE_NUM_ENTRIES, value)) {
+ if (getValueAt(index*AssetManager.STYLE_NUM_ENTRIES, value)) {
return value;
}
return null;
@@ -1183,16 +1181,16 @@
final int[] data = mData;
final int N = length();
for (int i = 0; i < N; i++) {
- final int index = i * STYLE_NUM_ENTRIES;
- if (data[index + STYLE_TYPE] != TypedValue.TYPE_ATTRIBUTE) {
+ final int index = i * AssetManager.STYLE_NUM_ENTRIES;
+ if (data[index + AssetManager.STYLE_TYPE] != TypedValue.TYPE_ATTRIBUTE) {
// Not an attribute, ignore.
continue;
}
// Null the entry so that we can safely call getZzz().
- data[index + STYLE_TYPE] = TypedValue.TYPE_NULL;
+ data[index + AssetManager.STYLE_TYPE] = TypedValue.TYPE_NULL;
- final int attr = data[index + STYLE_DATA];
+ final int attr = data[index + AssetManager.STYLE_DATA];
if (attr == 0) {
// Useless data, ignore.
continue;
@@ -1233,44 +1231,45 @@
final int[] data = mData;
final int N = length();
for (int i = 0; i < N; i++) {
- final int index = i * STYLE_NUM_ENTRIES;
- final int type = data[index + STYLE_TYPE];
+ final int index = i * AssetManager.STYLE_NUM_ENTRIES;
+ final int type = data[index + AssetManager.STYLE_TYPE];
if (type == TypedValue.TYPE_NULL) {
continue;
}
changingConfig |= ActivityInfo.activityInfoConfigNativeToJava(
- data[index + STYLE_CHANGING_CONFIGURATIONS]);
+ data[index + AssetManager.STYLE_CHANGING_CONFIGURATIONS]);
}
return changingConfig;
}
private boolean getValueAt(int index, TypedValue outValue) {
final int[] data = mData;
- final int type = data[index + STYLE_TYPE];
+ final int type = data[index+AssetManager.STYLE_TYPE];
if (type == TypedValue.TYPE_NULL) {
return false;
}
outValue.type = type;
- outValue.data = data[index + STYLE_DATA];
- outValue.assetCookie = data[index + STYLE_ASSET_COOKIE];
- outValue.resourceId = data[index + STYLE_RESOURCE_ID];
+ outValue.data = data[index+AssetManager.STYLE_DATA];
+ outValue.assetCookie = data[index+AssetManager.STYLE_ASSET_COOKIE];
+ outValue.resourceId = data[index+AssetManager.STYLE_RESOURCE_ID];
outValue.changingConfigurations = ActivityInfo.activityInfoConfigNativeToJava(
- data[index + STYLE_CHANGING_CONFIGURATIONS]);
- outValue.density = data[index + STYLE_DENSITY];
+ data[index + AssetManager.STYLE_CHANGING_CONFIGURATIONS]);
+ outValue.density = data[index+AssetManager.STYLE_DENSITY];
outValue.string = (type == TypedValue.TYPE_STRING) ? loadStringValueAt(index) : null;
return true;
}
private CharSequence loadStringValueAt(int index) {
final int[] data = mData;
- final int cookie = data[index + STYLE_ASSET_COOKIE];
+ final int cookie = data[index+AssetManager.STYLE_ASSET_COOKIE];
if (cookie < 0) {
if (mXml != null) {
- return mXml.getPooledString(data[index + STYLE_DATA]);
+ return mXml.getPooledString(
+ data[index+AssetManager.STYLE_DATA]);
}
return null;
}
- return mAssets.getPooledStringForCookie(cookie, data[index + STYLE_DATA]);
+ return mAssets.getPooledStringForCookie(cookie, data[index+AssetManager.STYLE_DATA]);
}
/** @hide */
diff --git a/core/java/android/content/res/XmlBlock.java b/core/java/android/content/res/XmlBlock.java
index d4ccffb..e6b95741 100644
--- a/core/java/android/content/res/XmlBlock.java
+++ b/core/java/android/content/res/XmlBlock.java
@@ -16,7 +16,6 @@
package android.content.res;
-import android.annotation.Nullable;
import android.util.TypedValue;
import com.android.internal.util.XmlUtils;
@@ -34,7 +33,7 @@
*
* {@hide}
*/
-final class XmlBlock implements AutoCloseable {
+final class XmlBlock {
private static final boolean DEBUG=false;
public XmlBlock(byte[] data) {
@@ -49,7 +48,6 @@
mStrings = new StringBlock(nativeGetStringBlock(mNative), false);
}
- @Override
public void close() {
synchronized (this) {
if (mOpen) {
@@ -480,13 +478,13 @@
* are doing! The given native object must exist for the entire lifetime
* of this newly creating XmlBlock.
*/
- XmlBlock(@Nullable AssetManager assets, long xmlBlock) {
+ XmlBlock(AssetManager assets, long xmlBlock) {
mAssets = assets;
mNative = xmlBlock;
mStrings = new StringBlock(nativeGetStringBlock(xmlBlock), false);
}
- private @Nullable final AssetManager mAssets;
+ private final AssetManager mAssets;
private final long mNative;
/*package*/ final StringBlock mStrings;
private boolean mOpen = true;
diff --git a/core/java/android/content/res/XmlResourceParser.java b/core/java/android/content/res/XmlResourceParser.java
index 6be9b9e..86f4ba6 100644
--- a/core/java/android/content/res/XmlResourceParser.java
+++ b/core/java/android/content/res/XmlResourceParser.java
@@ -27,6 +27,8 @@
* it is done reading the resource.
*/
public interface XmlResourceParser extends XmlPullParser, AttributeSet, AutoCloseable {
+ String getAttributeNamespace (int index);
+
/**
* Close this parser. Calls on the interface are no longer valid after this call.
*/
diff --git a/core/java/android/database/sqlite/SQLiteConnectionPool.java b/core/java/android/database/sqlite/SQLiteConnectionPool.java
index b211700..dc60612 100644
--- a/core/java/android/database/sqlite/SQLiteConnectionPool.java
+++ b/core/java/android/database/sqlite/SQLiteConnectionPool.java
@@ -422,6 +422,7 @@
}
// Can't throw.
+ @GuardedBy("mLock")
private boolean recycleConnectionLocked(SQLiteConnection connection,
AcquiredConnectionStatus status) {
if (status == AcquiredConnectionStatus.RECONFIGURE) {
@@ -531,6 +532,7 @@
}
// Can't throw.
+ @GuardedBy("mLock")
private void closeAvailableConnectionsAndLogExceptionsLocked() {
closeAvailableNonPrimaryConnectionsAndLogExceptionsLocked();
@@ -541,6 +543,7 @@
}
// Can't throw.
+ @GuardedBy("mLock")
private boolean closeAvailableConnectionLocked(int connectionId) {
final int count = mAvailableNonPrimaryConnections.size();
for (int i = count - 1; i >= 0; i--) {
@@ -562,6 +565,7 @@
}
// Can't throw.
+ @GuardedBy("mLock")
private void closeAvailableNonPrimaryConnectionsAndLogExceptionsLocked() {
final int count = mAvailableNonPrimaryConnections.size();
for (int i = 0; i < count; i++) {
@@ -581,6 +585,7 @@
}
// Can't throw.
+ @GuardedBy("mLock")
private void closeExcessConnectionsAndLogExceptionsLocked() {
int availableCount = mAvailableNonPrimaryConnections.size();
while (availableCount-- > mMaxConnectionPoolSize - 1) {
@@ -591,6 +596,7 @@
}
// Can't throw.
+ @GuardedBy("mLock")
private void closeConnectionAndLogExceptionsLocked(SQLiteConnection connection) {
try {
connection.close(); // might throw
@@ -609,6 +615,7 @@
}
// Can't throw.
+ @GuardedBy("mLock")
private void reconfigureAllConnectionsLocked() {
if (mAvailablePrimaryConnection != null) {
try {
@@ -776,6 +783,7 @@
}
// Can't throw.
+ @GuardedBy("mLock")
private void cancelConnectionWaiterLocked(ConnectionWaiter waiter) {
if (waiter.mAssignedConnection != null || waiter.mException != null) {
// Waiter is done waiting but has not woken up yet.
@@ -848,6 +856,7 @@
}
// Can't throw.
+ @GuardedBy("mLock")
private void wakeConnectionWaitersLocked() {
// Unpark all waiters that have requests that we can fulfill.
// This method is designed to not throw runtime exceptions, although we might send
@@ -910,6 +919,7 @@
}
// Might throw.
+ @GuardedBy("mLock")
private SQLiteConnection tryAcquirePrimaryConnectionLocked(int connectionFlags) {
// If the primary connection is available, acquire it now.
SQLiteConnection connection = mAvailablePrimaryConnection;
@@ -935,6 +945,7 @@
}
// Might throw.
+ @GuardedBy("mLock")
private SQLiteConnection tryAcquireNonPrimaryConnectionLocked(
String sql, int connectionFlags) {
// Try to acquire the next connection in the queue.
@@ -974,6 +985,7 @@
}
// Might throw.
+ @GuardedBy("mLock")
private void finishAcquireConnectionLocked(SQLiteConnection connection, int connectionFlags) {
try {
final boolean readOnly = (connectionFlags & CONNECTION_FLAG_READ_ONLY) != 0;
diff --git a/core/java/android/hardware/display/AmbientBrightnessDayStats.java b/core/java/android/hardware/display/AmbientBrightnessDayStats.java
index 00f3c36..1aa2557 100644
--- a/core/java/android/hardware/display/AmbientBrightnessDayStats.java
+++ b/core/java/android/hardware/display/AmbientBrightnessDayStats.java
@@ -47,6 +47,11 @@
private final float[] mStats;
/**
+ * Initialize day stats from the given state. The time spent in each of the bucket is
+ * initialized to 0.
+ *
+ * @param localDate The date for which stats are being tracked
+ * @param bucketBoundaries Bucket boundaries used from creating the buckets from
* @hide
*/
public AmbientBrightnessDayStats(@NonNull LocalDate localDate,
@@ -55,6 +60,11 @@
}
/**
+ * Initialize day stats from the given state
+ *
+ * @param localDate The date for which stats are being tracked
+ * @param bucketBoundaries Bucket boundaries used from creating the buckets from
+ * @param stats Time spent in each of the buckets (in seconds)
* @hide
*/
public AmbientBrightnessDayStats(@NonNull LocalDate localDate,
@@ -81,14 +91,26 @@
mStats = stats;
}
+ /**
+ * @return The {@link LocalDate} for which brightness stats are being tracked.
+ */
public LocalDate getLocalDate() {
return mLocalDate;
}
+ /**
+ * @return Aggregated stats of time spent (in seconds) in various buckets.
+ */
public float[] getStats() {
return mStats;
}
+ /**
+ * Returns the bucket boundaries (in lux) used for creating buckets. For eg., if the bucket
+ * boundaries array is {b1, b2, b3}, the buckets will be [b1, b2), [b2, b3), [b3, inf).
+ *
+ * @return The list of bucket boundaries.
+ */
public float[] getBucketBoundaries() {
return mBucketBoundaries;
}
@@ -169,7 +191,14 @@
dest.writeFloatArray(mStats);
}
- /** @hide */
+ /**
+ * Updates the stats by incrementing the time spent for the appropriate bucket based on ambient
+ * brightness reading.
+ *
+ * @param ambientBrightness Ambient brightness reading (in lux)
+ * @param durationSec Time spent with the given reading (in seconds)
+ * @hide
+ */
public void log(float ambientBrightness, float durationSec) {
int bucketIndex = getBucketIndex(ambientBrightness);
if (bucketIndex >= 0) {
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index a817f33..017674f 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -1818,9 +1818,9 @@
}
/**
- * Called when the input method window has been shown to the user, after
- * previously not being visible. This is done after all of the UI setup
- * for the window has occurred (creating its views etc).
+ * Called immediately before the input method window is shown to the user.
+ * You could override this to prepare for the window to be shown
+ * (update view structure etc).
*/
public void onWindowShown() {
// Intentionally empty
diff --git a/core/java/android/net/IpSecManager.java b/core/java/android/net/IpSecManager.java
index 24a078f..b609847 100644
--- a/core/java/android/net/IpSecManager.java
+++ b/core/java/android/net/IpSecManager.java
@@ -19,6 +19,7 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
+import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.annotation.TestApi;
@@ -761,6 +762,7 @@
* @hide
*/
@SystemApi
+ @RequiresPermission(android.Manifest.permission.NETWORK_STACK)
public IpSecTunnelInterface createIpSecTunnelInterface(@NonNull InetAddress localAddress,
@NonNull InetAddress remoteAddress, @NonNull Network underlyingNetwork)
throws ResourceUnavailableException, IOException {
@@ -780,6 +782,7 @@
* @hide
*/
@SystemApi
+ @RequiresPermission(android.Manifest.permission.NETWORK_STACK)
public void applyTunnelModeTransform(IpSecTunnelInterface tunnel, int direction,
IpSecTransform transform) throws IOException {
try {
diff --git a/core/java/android/net/IpSecTransform.java b/core/java/android/net/IpSecTransform.java
index 0829b4a..38759a9 100644
--- a/core/java/android/net/IpSecTransform.java
+++ b/core/java/android/net/IpSecTransform.java
@@ -21,6 +21,7 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
+import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.content.Context;
import android.os.Binder;
@@ -266,6 +267,10 @@
* @hide
*/
@SystemApi
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.NETWORK_STACK,
+ android.Manifest.permission.PACKET_KEEPALIVE_OFFLOAD
+ })
public void startNattKeepalive(@NonNull NattKeepaliveCallback userCallback,
int intervalSeconds, @NonNull Handler handler) throws IOException {
checkNotNull(userCallback);
@@ -305,6 +310,10 @@
* @hide
*/
@SystemApi
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.NETWORK_STACK,
+ android.Manifest.permission.PACKET_KEEPALIVE_OFFLOAD
+ })
public void stopNattKeepalive() {
synchronized (mKeepaliveCallback) {
if (mKeepalive == null) {
@@ -449,6 +458,7 @@
* @hide
*/
@SystemApi
+ @RequiresPermission(android.Manifest.permission.NETWORK_STACK)
public IpSecTransform buildTunnelModeTransform(
@NonNull InetAddress sourceAddress,
@NonNull IpSecManager.SecurityParameterIndex spi)
diff --git a/core/java/android/os/BatteryManager.java b/core/java/android/os/BatteryManager.java
index a734719..8b4f02e 100644
--- a/core/java/android/os/BatteryManager.java
+++ b/core/java/android/os/BatteryManager.java
@@ -323,4 +323,16 @@
public long getLongProperty(int id) {
return queryProperty(id);
}
+
+ /**
+ * Return true if the plugType given is wired
+ * @param plugType {@link #BATTERY_PLUGGED_AC}, {@link #BATTERY_PLUGGED_USB},
+ * or {@link #BATTERY_PLUGGED_WIRELESS}
+ *
+ * @return true if plugType is wired
+ * @hide
+ */
+ public static boolean isPlugWired(int plugType) {
+ return plugType == BATTERY_PLUGGED_USB || plugType == BATTERY_PLUGGED_AC;
+ }
}
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index fd0e5ae..5d7cf1e 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -1537,6 +1537,7 @@
public static final int STATE2_BLUETOOTH_ON_FLAG = 1<<22;
public static final int STATE2_CAMERA_FLAG = 1<<21;
public static final int STATE2_BLUETOOTH_SCAN_FLAG = 1 << 20;
+ public static final int STATE2_CELLULAR_HIGH_TX_POWER_FLAG = 1 << 19;
public static final int MOST_INTERESTING_STATES2 =
STATE2_POWER_SAVE_FLAG | STATE2_WIFI_ON_FLAG | STATE2_DEVICE_IDLE_MASK
@@ -2353,9 +2354,11 @@
WIFI_SUPPL_STATE_NAMES, WIFI_SUPPL_STATE_SHORT_NAMES),
new BitDescription(HistoryItem.STATE2_CAMERA_FLAG, "camera", "ca"),
new BitDescription(HistoryItem.STATE2_BLUETOOTH_SCAN_FLAG, "ble_scan", "bles"),
+ new BitDescription(HistoryItem.STATE2_CELLULAR_HIGH_TX_POWER_FLAG,
+ "cellular_high_tx_power", "Chtp"),
new BitDescription(HistoryItem.STATE2_GPS_SIGNAL_QUALITY_MASK,
HistoryItem.STATE2_GPS_SIGNAL_QUALITY_SHIFT, "gps_signal_quality", "Gss",
- new String[] { "poor", "good"}, new String[] { "poor", "good"}),
+ new String[] { "poor", "good"}, new String[] { "poor", "good"})
};
public static final String[] HISTORY_EVENT_NAMES = new String[] {
diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java
index 48f5684..fc78861 100644
--- a/core/java/android/os/Build.java
+++ b/core/java/android/os/Build.java
@@ -894,6 +894,14 @@
/**
* P.
+ *
+ * <p>Applications targeting this or a later release will get these
+ * new changes in behavior:</p>
+ * <ul>
+ * <li>{@link android.app.Service#startForeground Service.startForeground} requires
+ * that apps hold the permission
+ * {@link android.Manifest.permission#FOREGROUND_SERVICE}.</li>
+ * </ul>
*/
public static final int P = CUR_DEVELOPMENT; // STOPSHIP Replace with the real version.
}
diff --git a/core/java/android/os/IStatsCompanionService.aidl b/core/java/android/os/IStatsCompanionService.aidl
index 8a27700..eae5217 100644
--- a/core/java/android/os/IStatsCompanionService.aidl
+++ b/core/java/android/os/IStatsCompanionService.aidl
@@ -55,9 +55,8 @@
/** Pull the specified data. Results will be sent to statsd when complete. */
StatsLogEventWrapper[] pullData(int pullCode);
- /** Send a broadcast to the specified pkg and class that it should getData now. */
- // TODO: Rename this and use a pending intent instead.
- oneway void sendBroadcast(String pkg, String cls);
+ /** Send a broadcast to the specified PendingIntent's as IBinder that it should getData now. */
+ oneway void sendDataBroadcast(in IBinder intentSender);
/**
* Requests StatsCompanionService to send a broadcast using the given intentSender
diff --git a/core/java/android/os/IStatsManager.aidl b/core/java/android/os/IStatsManager.aidl
index 679b49d..682a24f1 100644
--- a/core/java/android/os/IStatsManager.aidl
+++ b/core/java/android/os/IStatsManager.aidl
@@ -81,12 +81,26 @@
/**
* Sets a configuration with the specified config key and subscribes to updates for this
* configuration key. Broadcasts will be sent if this configuration needs to be collected.
- * The configuration must be a wire-encoded StatsdConfig. The caller specifies the name of the
- * package and class that should receive these broadcasts.
+ * The configuration must be a wire-encoded StatsDConfig. The receiver for this data is
+ * registered in a separate function.
*
* Returns if this configuration was correctly registered.
*/
- boolean addConfiguration(in long configKey, in byte[] config, in String pkg, in String cls);
+ boolean addConfiguration(in long configKey, in byte[] config);
+
+ /**
+ * Registers the given pending intent for this config key. This intent is invoked when the
+ * memory consumed by the metrics for this configuration approach the pre-defined limits. There
+ * can be at most one listener per config key.
+ *
+ * Returns if this listener was correctly registered.
+ */
+ boolean setDataFetchOperation(long configKey, in IBinder intentSender);
+
+ /**
+ * Removes the data fetch operation for the specified configuration.
+ */
+ boolean removeDataFetchOperation(long configKey);
/**
* Removes the configuration with the matching config key. No-op if this config key does not
diff --git a/core/java/android/os/VintfObject.java b/core/java/android/os/VintfObject.java
index 12a495b..fb22194 100644
--- a/core/java/android/os/VintfObject.java
+++ b/core/java/android/os/VintfObject.java
@@ -80,4 +80,11 @@
* ("28", ["libjpeg.so", "libbase.so"])]
*/
public static native Map<String, String[]> getVndkSnapshots();
+
+ /**
+ * @return target FCM version, a number specified in the device manifest
+ * indicating the FCM version that the device manifest implements. Null if
+ * device manifest doesn't specify this number (for legacy devices).
+ */
+ public static native Long getTargetFrameworkCompatibilityMatrixVersion();
}
diff --git a/core/java/android/os/ZygoteProcess.java b/core/java/android/os/ZygoteProcess.java
index 670f794..4a97640 100644
--- a/core/java/android/os/ZygoteProcess.java
+++ b/core/java/android/os/ZygoteProcess.java
@@ -61,18 +61,27 @@
/**
* The name of the socket used to communicate with the primary zygote.
*/
- private final String mSocket;
+ private final LocalSocketAddress mSocket;
/**
* The name of the secondary (alternate ABI) zygote socket.
*/
- private final String mSecondarySocket;
+ private final LocalSocketAddress mSecondarySocket;
public ZygoteProcess(String primarySocket, String secondarySocket) {
+ this(new LocalSocketAddress(primarySocket, LocalSocketAddress.Namespace.RESERVED),
+ new LocalSocketAddress(secondarySocket, LocalSocketAddress.Namespace.RESERVED));
+ }
+
+ public ZygoteProcess(LocalSocketAddress primarySocket, LocalSocketAddress secondarySocket) {
mSocket = primarySocket;
mSecondarySocket = secondarySocket;
}
+ public LocalSocketAddress getPrimarySocketAddress() {
+ return mSocket;
+ }
+
/**
* State for communicating with the zygote process.
*/
@@ -92,14 +101,13 @@
this.abiList = abiList;
}
- public static ZygoteState connect(String socketAddress) throws IOException {
+ public static ZygoteState connect(LocalSocketAddress address) throws IOException {
DataInputStream zygoteInputStream = null;
BufferedWriter zygoteWriter = null;
final LocalSocket zygoteSocket = new LocalSocket();
try {
- zygoteSocket.connect(new LocalSocketAddress(socketAddress,
- LocalSocketAddress.Namespace.RESERVED));
+ zygoteSocket.connect(address);
zygoteInputStream = new DataInputStream(zygoteSocket.getInputStream());
@@ -115,8 +123,8 @@
}
String abiListString = getAbiList(zygoteWriter, zygoteInputStream);
- Log.i("Zygote", "Process: zygote socket " + socketAddress + " opened, supported ABIS: "
- + abiListString);
+ Log.i("Zygote", "Process: zygote socket " + address.getNamespace() + "/"
+ + address.getName() + " opened, supported ABIS: " + abiListString);
return new ZygoteState(zygoteSocket, zygoteInputStream, zygoteWriter,
Arrays.asList(abiListString.split(",")));
@@ -514,9 +522,19 @@
* @param socketName The name of the socket to connect to.
*/
public static void waitForConnectionToZygote(String socketName) {
+ final LocalSocketAddress address =
+ new LocalSocketAddress(socketName, LocalSocketAddress.Namespace.RESERVED);
+ waitForConnectionToZygote(address);
+ }
+
+ /**
+ * Try connecting to the Zygote over and over again until we hit a time-out.
+ * @param address The name of the socket to connect to.
+ */
+ public static void waitForConnectionToZygote(LocalSocketAddress address) {
for (int n = 20; n >= 0; n--) {
try {
- final ZygoteState zs = ZygoteState.connect(socketName);
+ final ZygoteState zs = ZygoteState.connect(address);
zs.close();
return;
} catch (IOException ioe) {
@@ -529,6 +547,6 @@
} catch (InterruptedException ie) {
}
}
- Slog.wtf(LOG_TAG, "Failed to connect to Zygote through socket " + socketName);
+ Slog.wtf(LOG_TAG, "Failed to connect to Zygote through socket " + address.getName());
}
}
diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java
index b7b2b2d..422e36b 100644
--- a/core/java/android/service/notification/NotificationListenerService.java
+++ b/core/java/android/service/notification/NotificationListenerService.java
@@ -1343,6 +1343,7 @@
/**
* @hide
*/
+ @GuardedBy("mLock")
public final void applyUpdateLocked(NotificationRankingUpdate update) {
mRankingMap = new RankingMap(update);
}
diff --git a/core/java/android/text/OWNERS b/core/java/android/text/OWNERS
index 0f85e1f..9f2182e 100644
--- a/core/java/android/text/OWNERS
+++ b/core/java/android/text/OWNERS
@@ -1,4 +1,3 @@
siyamed@google.com
nona@google.com
clarabayarri@google.com
-toki@google.com
diff --git a/core/java/android/transition/ArcMotion.java b/core/java/android/transition/ArcMotion.java
index da14834..172c837 100644
--- a/core/java/android/transition/ArcMotion.java
+++ b/core/java/android/transition/ArcMotion.java
@@ -216,7 +216,13 @@
boolean isMovingUpwards = startY > endY;
- if ((Math.abs(deltaX) < Math.abs(deltaY))) {
+ if (deltaY == 0) {
+ ex = dx;
+ ey = dy + (Math.abs(deltaX) * 0.5f * mMinimumHorizontalTangent);
+ } else if (deltaX == 0) {
+ ex = dx + (Math.abs(deltaY) * 0.5f * mMinimumVerticalTangent);
+ ey = dy;
+ } else if ((Math.abs(deltaX) < Math.abs(deltaY))) {
// Similar triangles bfa and bde mean that (ab/fb = eb/bd)
// Therefore, eb = ab * bd / fb
// ab = hypotenuse
@@ -254,7 +260,7 @@
float maximumArcDist2 = midDist2 * mMaximumTangent * mMaximumTangent;
float newArcDistance2 = 0;
- if (arcDist2 < minimumArcDist2) {
+ if (arcDist2 != 0 && arcDist2 < minimumArcDist2) {
newArcDistance2 = minimumArcDist2;
} else if (arcDist2 > maximumArcDist2) {
newArcDistance2 = maximumArcDist2;
diff --git a/core/java/android/util/AttributeSet.java b/core/java/android/util/AttributeSet.java
index eb8c168..7f327c7 100644
--- a/core/java/android/util/AttributeSet.java
+++ b/core/java/android/util/AttributeSet.java
@@ -17,6 +17,8 @@
package android.util;
+import org.xmlpull.v1.XmlPullParser;
+
/**
* A collection of attributes, as found associated with a tag in an XML
* document. Often you will not want to use this interface directly, instead
@@ -54,18 +56,42 @@
* compiled XML resource that is not available in a normal XML file, such
* as {@link #getAttributeNameResource(int)} which returns the resource
* identifier associated with a particular XML attribute name.
+ *
+ * @see XmlPullParser
*/
public interface AttributeSet {
/**
* Returns the number of attributes available in the set.
- *
+ *
+ * <p>See also {@link XmlPullParser#getAttributeCount XmlPullParser.getAttributeCount()},
+ * which this method corresponds to when parsing a compiled XML file.</p>
+ *
* @return A positive integer, or 0 if the set is empty.
*/
public int getAttributeCount();
/**
+ * Returns the namespace of the specified attribute.
+ *
+ * <p>See also {@link XmlPullParser#getAttributeNamespace XmlPullParser.getAttributeNamespace()},
+ * which this method corresponds to when parsing a compiled XML file.</p>
+ *
+ * @param index Index of the desired attribute, 0...count-1.
+ *
+ * @return A String containing the namespace of the attribute, or null if th
+ * attribute cannot be found.
+ */
+ default String getAttributeNamespace (int index) {
+ // This is a new method since the first interface definition, so add stub impl.
+ return null;
+ }
+
+ /**
* Returns the name of the specified attribute.
- *
+ *
+ * <p>See also {@link XmlPullParser#getAttributeName XmlPullParser.getAttributeName()},
+ * which this method corresponds to when parsing a compiled XML file.</p>
+ *
* @param index Index of the desired attribute, 0...count-1.
*
* @return A String containing the name of the attribute, or null if the
diff --git a/core/java/android/util/ByteStringUtils.java b/core/java/android/util/ByteStringUtils.java
index 333208d..f6460ad 100644
--- a/core/java/android/util/ByteStringUtils.java
+++ b/core/java/android/util/ByteStringUtils.java
@@ -22,61 +22,63 @@
* @hide
*/
public final class ByteStringUtils {
- private final static char[] HEX_ARRAY = "0123456789ABCDEF".toCharArray();
+ private static final char[] HEX_LOWERCASE_ARRAY = "0123456789abcdef".toCharArray();
+ private static final char[] HEX_UPPERCASE_ARRAY = "0123456789ABCDEF".toCharArray();
- private ByteStringUtils() {
+ private ByteStringUtils() {
/* hide constructor */
- }
-
- /**
- * Returns the hex encoded string representation of bytes.
- * @param bytes Byte array to encode.
- * @return Hex encoded string representation of bytes.
- */
- public static String toHexString(byte[] bytes) {
- if (bytes == null || bytes.length == 0 || bytes.length % 2 != 0) {
- return null;
}
- final int byteLength = bytes.length;
- final int charCount = 2 * byteLength;
- final char[] chars = new char[charCount];
+ /**
+ * Returns the hex encoded string representation of bytes.
+ * @param bytes Byte array to encode.
+ * @return Hex encoded string representation of bytes.
+ */
+ public static String toHexString(byte[] bytes) {
+ if (bytes == null || bytes.length == 0 || bytes.length % 2 != 0) {
+ return null;
+ }
- for (int i = 0; i < byteLength; i++) {
- final int byteHex = bytes[i] & 0xFF;
- chars[i * 2] = HEX_ARRAY[byteHex >>> 4];
- chars[i * 2 + 1] = HEX_ARRAY[byteHex & 0x0F];
- }
- return new String(chars);
- }
+ final int byteLength = bytes.length;
+ final int charCount = 2 * byteLength;
+ final char[] chars = new char[charCount];
- /**
- * Returns the decoded byte array representation of str.
- * @param str Hex encoded string to decode.
- * @return Decoded byte array representation of str.
- */
- public static byte[] fromHexToByteArray(String str) {
- if (str == null || str.length() == 0 || str.length() % 2 != 0) {
- return null;
+ for (int i = 0; i < byteLength; i++) {
+ final int byteHex = bytes[i] & 0xFF;
+ chars[i * 2] = HEX_UPPERCASE_ARRAY[byteHex >>> 4];
+ chars[i * 2 + 1] = HEX_UPPERCASE_ARRAY[byteHex & 0x0F];
+ }
+ return new String(chars);
}
- final char[] chars = str.toCharArray();
- final int charLength = chars.length;
- final byte[] bytes = new byte[charLength / 2];
+ /**
+ * Returns the decoded byte array representation of str.
+ * @param str Hex encoded string to decode.
+ * @return Decoded byte array representation of str.
+ */
+ public static byte[] fromHexToByteArray(String str) {
+ if (str == null || str.length() == 0 || str.length() % 2 != 0) {
+ return null;
+ }
- for (int i = 0; i < bytes.length; i++) {
- bytes[i] =
- (byte)(((getIndex(chars[i * 2]) << 4) & 0xF0) | (getIndex(chars[i * 2 + 1]) & 0x0F));
- }
- return bytes;
- }
+ final char[] chars = str.toCharArray();
+ final int charLength = chars.length;
+ final byte[] bytes = new byte[charLength / 2];
- private static int getIndex(char c) {
- for (int i = 0; i < HEX_ARRAY.length; i++) {
- if (HEX_ARRAY[i] == c) {
- return i;
- }
+ for (int i = 0; i < bytes.length; i++) {
+ bytes[i] =
+ (byte) (((getIndex(chars[i * 2]) << 4) & 0xF0)
+ | (getIndex(chars[i * 2 + 1]) & 0x0F));
+ }
+ return bytes;
}
- return -1;
- }
+
+ private static int getIndex(char c) {
+ for (int i = 0; i < HEX_UPPERCASE_ARRAY.length; i++) {
+ if (HEX_UPPERCASE_ARRAY[i] == c || HEX_LOWERCASE_ARRAY[i] == c) {
+ return i;
+ }
+ }
+ return -1;
+ }
}
diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java
index 410cdc6..1ead0b4 100644
--- a/core/java/android/util/FeatureFlagUtils.java
+++ b/core/java/android/util/FeatureFlagUtils.java
@@ -37,13 +37,13 @@
private static final Map<String, String> DEFAULT_FLAGS;
static {
DEFAULT_FLAGS = new HashMap<>();
- DEFAULT_FLAGS.put("device_info_v2", "true");
DEFAULT_FLAGS.put("settings_connected_device_v2", "true");
DEFAULT_FLAGS.put("settings_battery_v2", "true");
DEFAULT_FLAGS.put("settings_battery_display_app_list", "false");
DEFAULT_FLAGS.put("settings_zone_picker_v2", "true");
- DEFAULT_FLAGS.put("settings_about_phone_v2", "false");
+ DEFAULT_FLAGS.put("settings_about_phone_v2", "true");
DEFAULT_FLAGS.put("settings_bluetooth_while_driving", "false");
+ DEFAULT_FLAGS.put("settings_data_usage_v2", "false");
}
/**
diff --git a/core/java/android/util/StatsManager.java b/core/java/android/util/StatsManager.java
deleted file mode 100644
index 51fb18a..0000000
--- a/core/java/android/util/StatsManager.java
+++ /dev/null
@@ -1,239 +0,0 @@
-/*
- * 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.util;
-
-import android.Manifest;
-import android.annotation.RequiresPermission;
-import android.os.IBinder;
-import android.os.IStatsManager;
-import android.os.RemoteException;
-import android.os.ServiceManager;
-
-
-/*
- *
- *
- *
- *
- * THIS ENTIRE FILE IS ONLY TEMPORARY TO PREVENT BREAKAGES OF DEPENDENCIES ON OLD APIS.
- * The new StatsManager is to be found in android.app.StatsManager.
- * TODO: Delete this file!
- *
- *
- *
- *
- */
-
-
-/**
- * API for StatsD clients to send configurations and retrieve data.
- *
- * @hide
- */
-public class StatsManager {
- IStatsManager mService;
- private static final String TAG = "StatsManager";
-
- /**
- * Constructor for StatsManagerClient.
- *
- * @hide
- */
- public StatsManager() {
- }
-
- /**
- * Temporary to prevent build failures. Will be deleted.
- */
- @RequiresPermission(Manifest.permission.DUMP)
- public boolean addConfiguration(String configKey, byte[] config, String pkg, String cls) {
- synchronized (this) {
- try {
- IStatsManager service = getIStatsManagerLocked();
- if (service == null) {
- Slog.d(TAG, "Failed to find statsd when adding configuration");
- return false;
- }
- return service.addConfiguration(Long.parseLong(configKey), config, pkg, cls);
- } catch (RemoteException e) {
- Slog.d(TAG, "Failed to connect to statsd when adding configuration");
- return false;
- }
- }
- }
-
- /**
- * Clients can send a configuration and simultaneously registers the name of a broadcast
- * receiver that listens for when it should request data.
- *
- * @param configKey An arbitrary integer that allows clients to track the configuration.
- * @param config Wire-encoded StatsDConfig proto that specifies metrics (and all
- * dependencies eg, conditions and matchers).
- * @param pkg The package name to receive the broadcast.
- * @param cls The name of the class that receives the broadcast.
- * @return true if successful
- */
- @RequiresPermission(Manifest.permission.DUMP)
- public boolean addConfiguration(long configKey, byte[] config, String pkg, String cls) {
- synchronized (this) {
- try {
- IStatsManager service = getIStatsManagerLocked();
- if (service == null) {
- Slog.d(TAG, "Failed to find statsd when adding configuration");
- return false;
- }
- return service.addConfiguration(configKey, config, pkg, cls);
- } catch (RemoteException e) {
- Slog.d(TAG, "Failed to connect to statsd when adding configuration");
- return false;
- }
- }
- }
-
- /**
- * Temporary to prevent build failures. Will be deleted.
- */
- @RequiresPermission(Manifest.permission.DUMP)
- public boolean removeConfiguration(String configKey) {
- // To prevent breakages of old dependencies.
- synchronized (this) {
- try {
- IStatsManager service = getIStatsManagerLocked();
- if (service == null) {
- Slog.d(TAG, "Failed to find statsd when removing configuration");
- return false;
- }
- return service.removeConfiguration(Long.parseLong(configKey));
- } catch (RemoteException e) {
- Slog.d(TAG, "Failed to connect to statsd when removing configuration");
- return false;
- }
- }
- }
-
- /**
- * Remove a configuration from logging.
- *
- * @param configKey Configuration key to remove.
- * @return true if successful
- */
- @RequiresPermission(Manifest.permission.DUMP)
- public boolean removeConfiguration(long configKey) {
- synchronized (this) {
- try {
- IStatsManager service = getIStatsManagerLocked();
- if (service == null) {
- Slog.d(TAG, "Failed to find statsd when removing configuration");
- return false;
- }
- return service.removeConfiguration(configKey);
- } catch (RemoteException e) {
- Slog.d(TAG, "Failed to connect to statsd when removing configuration");
- return false;
- }
- }
- }
-
- /**
- * Temporary to prevent build failures. Will be deleted.
- */
- @RequiresPermission(Manifest.permission.DUMP)
- public byte[] getData(String configKey) {
- // TODO: remove this and all other methods with String-based config keys.
- // To prevent build breakages of dependencies.
- synchronized (this) {
- try {
- IStatsManager service = getIStatsManagerLocked();
- if (service == null) {
- Slog.d(TAG, "Failed to find statsd when getting data");
- return null;
- }
- return service.getData(Long.parseLong(configKey));
- } catch (RemoteException e) {
- Slog.d(TAG, "Failed to connecto statsd when getting data");
- return null;
- }
- }
- }
-
- /**
- * Clients can request data with a binder call. This getter is destructive and also clears
- * the retrieved metrics from statsd memory.
- *
- * @param configKey Configuration key to retrieve data from.
- * @return Serialized ConfigMetricsReportList proto. Returns null on failure.
- */
- @RequiresPermission(Manifest.permission.DUMP)
- public byte[] getData(long configKey) {
- synchronized (this) {
- try {
- IStatsManager service = getIStatsManagerLocked();
- if (service == null) {
- Slog.d(TAG, "Failed to find statsd when getting data");
- return null;
- }
- return service.getData(configKey);
- } catch (RemoteException e) {
- Slog.d(TAG, "Failed to connecto statsd when getting data");
- return null;
- }
- }
- }
-
- /**
- * Clients can request metadata for statsd. Will contain stats across all configurations but not
- * the actual metrics themselves (metrics must be collected via {@link #getData(String)}.
- * This getter is not destructive and will not reset any metrics/counters.
- *
- * @return Serialized StatsdStatsReport proto. Returns null on failure.
- */
- @RequiresPermission(Manifest.permission.DUMP)
- public byte[] getMetadata() {
- synchronized (this) {
- try {
- IStatsManager service = getIStatsManagerLocked();
- if (service == null) {
- Slog.d(TAG, "Failed to find statsd when getting metadata");
- return null;
- }
- return service.getMetadata();
- } catch (RemoteException e) {
- Slog.d(TAG, "Failed to connecto statsd when getting metadata");
- return null;
- }
- }
- }
-
- private class StatsdDeathRecipient implements IBinder.DeathRecipient {
- @Override
- public void binderDied() {
- synchronized (this) {
- mService = null;
- }
- }
- }
-
- private IStatsManager getIStatsManagerLocked() throws RemoteException {
- if (mService != null) {
- return mService;
- }
- mService = IStatsManager.Stub.asInterface(ServiceManager.getService("stats"));
- if (mService != null) {
- mService.asBinder().linkToDeath(new StatsdDeathRecipient(), 0);
- }
- return mService;
- }
-}
diff --git a/core/java/android/util/XmlPullAttributes.java b/core/java/android/util/XmlPullAttributes.java
index 6c8bb39..cb35eb5 100644
--- a/core/java/android/util/XmlPullAttributes.java
+++ b/core/java/android/util/XmlPullAttributes.java
@@ -34,6 +34,10 @@
return mParser.getAttributeCount();
}
+ public String getAttributeNamespace (int index) {
+ return mParser.getAttributeNamespace(index);
+ }
+
public String getAttributeName(int index) {
return mParser.getAttributeName(index);
}
diff --git a/core/java/android/view/DisplayInfo.java b/core/java/android/view/DisplayInfo.java
index 37e9815..7251b71 100644
--- a/core/java/android/view/DisplayInfo.java
+++ b/core/java/android/view/DisplayInfo.java
@@ -20,6 +20,7 @@
import static android.view.DisplayInfoProto.APP_WIDTH;
import static android.view.DisplayInfoProto.LOGICAL_HEIGHT;
import static android.view.DisplayInfoProto.LOGICAL_WIDTH;
+import static android.view.DisplayInfoProto.NAME;
import android.content.res.CompatibilityInfo;
import android.content.res.Configuration;
@@ -685,6 +686,7 @@
protoOutputStream.write(LOGICAL_HEIGHT, logicalHeight);
protoOutputStream.write(APP_WIDTH, appWidth);
protoOutputStream.write(APP_HEIGHT, appHeight);
+ protoOutputStream.write(NAME, name);
protoOutputStream.end(token);
}
diff --git a/core/java/android/view/IRecentsAnimationRunner.aidl b/core/java/android/view/IRecentsAnimationRunner.aidl
index ea6226b..69973e6 100644
--- a/core/java/android/view/IRecentsAnimationRunner.aidl
+++ b/core/java/android/view/IRecentsAnimationRunner.aidl
@@ -16,6 +16,7 @@
package android.view;
+import android.graphics.Rect;
import android.view.RemoteAnimationTarget;
import android.view.IRecentsAnimationController;
@@ -28,15 +29,26 @@
oneway interface IRecentsAnimationRunner {
/**
- * Called when the system is ready for the handler to start animating all the visible tasks.
+ * Deprecated, to be removed once Launcher updates
*/
void onAnimationStart(in IRecentsAnimationController controller,
- in RemoteAnimationTarget[] apps);
+ in RemoteAnimationTarget[] apps) = 0;
/**
* Called when the system needs to cancel the current animation. This can be due to the
* wallpaper not drawing in time, or the handler not finishing the animation within a predefined
* amount of time.
*/
- void onAnimationCanceled();
+ void onAnimationCanceled() = 1;
+
+ /**
+ * Called when the system is ready for the handler to start animating all the visible tasks.
+ *
+ * @param homeContentInsets The current home app content insets
+ * @param minimizedHomeBounds Specifies the bounds of the minimized home app, will be
+ * {@code null} if the device is not currently in split screen
+ */
+ void onAnimationStart_New(in IRecentsAnimationController controller,
+ in RemoteAnimationTarget[] apps, in Rect homeContentInsets,
+ in Rect minimizedHomeBounds) = 2;
}
diff --git a/core/java/android/view/RemoteAnimationTarget.java b/core/java/android/view/RemoteAnimationTarget.java
index c28c389..facf575 100644
--- a/core/java/android/view/RemoteAnimationTarget.java
+++ b/core/java/android/view/RemoteAnimationTarget.java
@@ -79,6 +79,11 @@
public final Rect clipRect;
/**
+ * The insets of the main app window.
+ */
+ public final Rect contentInsets;
+
+ /**
* The index of the element in the tree in prefix order. This should be used for z-layering
* to preserve original z-layer order in the hierarchy tree assuming no "boosting" needs to
* happen.
@@ -105,13 +110,14 @@
public final WindowConfiguration windowConfiguration;
public RemoteAnimationTarget(int taskId, int mode, SurfaceControl leash, boolean isTranslucent,
- Rect clipRect, int prefixOrderIndex, Point position, Rect sourceContainerBounds,
- WindowConfiguration windowConfig) {
+ Rect clipRect, Rect contentInsets, int prefixOrderIndex, Point position,
+ Rect sourceContainerBounds, WindowConfiguration windowConfig) {
this.mode = mode;
this.taskId = taskId;
this.leash = leash;
this.isTranslucent = isTranslucent;
this.clipRect = new Rect(clipRect);
+ this.contentInsets = new Rect(contentInsets);
this.prefixOrderIndex = prefixOrderIndex;
this.position = new Point(position);
this.sourceContainerBounds = new Rect(sourceContainerBounds);
@@ -124,6 +130,7 @@
leash = in.readParcelable(null);
isTranslucent = in.readBoolean();
clipRect = in.readParcelable(null);
+ contentInsets = in.readParcelable(null);
prefixOrderIndex = in.readInt();
position = in.readParcelable(null);
sourceContainerBounds = in.readParcelable(null);
@@ -142,6 +149,7 @@
dest.writeParcelable(leash, 0 /* flags */);
dest.writeBoolean(isTranslucent);
dest.writeParcelable(clipRect, 0 /* flags */);
+ dest.writeParcelable(contentInsets, 0 /* flags */);
dest.writeInt(prefixOrderIndex);
dest.writeParcelable(position, 0 /* flags */);
dest.writeParcelable(sourceContainerBounds, 0 /* flags */);
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index c460a33..5de25ba 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -907,6 +907,13 @@
*/
private static boolean sThrowOnInvalidFloatProperties;
+ /**
+ * Prior to P, {@code #startDragAndDrop} accepts a builder which produces an empty drag shadow.
+ * Currently zero size SurfaceControl cannot be created thus we create a dummy 1x1 surface
+ * instead.
+ */
+ private static boolean sAcceptZeroSizeDragShadow;
+
/** @hide */
@IntDef({NOT_FOCUSABLE, FOCUSABLE, FOCUSABLE_AUTO})
@Retention(RetentionPolicy.SOURCE)
@@ -4799,6 +4806,7 @@
Canvas.sCompatibilityRestore = targetSdkVersion < Build.VERSION_CODES.M;
Canvas.sCompatibilitySetBitmap = targetSdkVersion < Build.VERSION_CODES.O;
+ Canvas.setCompatibilityVersion(targetSdkVersion);
// In M and newer, our widgets can pass a "hint" value in the size
// for UNSPECIFIED MeasureSpecs. This lets child views of scrolling containers
@@ -4841,6 +4849,8 @@
sAlwaysAssignFocus = targetSdkVersion < Build.VERSION_CODES.P;
+ sAcceptZeroSizeDragShadow = targetSdkVersion < Build.VERSION_CODES.P;
+
sCompatibilityDone = true;
}
}
@@ -23628,8 +23638,7 @@
* constructor variant is only useful when the {@link #onProvideShadowMetrics(Point, Point)}
* and {@link #onDrawShadow(Canvas)} methods are also overridden in order
* to supply the drag shadow's dimensions and appearance without
- * reference to any View object. If they are not overridden, then the result is an
- * invisible drag shadow.
+ * reference to any View object.
*/
public DragShadowBuilder() {
mView = new WeakReference<View>(null);
@@ -23783,6 +23792,9 @@
// Create 1x1 surface when zero surface size is specified because SurfaceControl.Builder
// does not accept zero size surface.
if (shadowSize.x == 0 || shadowSize.y == 0) {
+ if (!sAcceptZeroSizeDragShadow) {
+ throw new IllegalStateException("Drag shadow dimensions must be positive");
+ }
shadowSize.x = 1;
shadowSize.y = 1;
}
diff --git a/core/java/android/view/accessibility/AccessibilityManager.java b/core/java/android/view/accessibility/AccessibilityManager.java
index 191c270..dee267d 100644
--- a/core/java/android/view/accessibility/AccessibilityManager.java
+++ b/core/java/android/view/accessibility/AccessibilityManager.java
@@ -774,8 +774,9 @@
*/
public void removeAccessibilityServicesStateChangeListener(
@NonNull AccessibilityServicesStateChangeListener listener) {
- // Final CopyOnWriteArrayList - no lock needed.
- mServicesStateChangeListeners.remove(listener);
+ synchronized (mLock) {
+ mServicesStateChangeListeners.remove(listener);
+ }
}
/**
diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
index 23e7d61..417a725 100644
--- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java
+++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
@@ -3173,6 +3173,15 @@
*/
@Override
public void writeToParcel(Parcel parcel, int flags) {
+ writeToParcelNoRecycle(parcel, flags);
+ // Since instances of this class are fetched via synchronous i.e. blocking
+ // calls in IPCs we always recycle as soon as the instance is marshaled.
+ recycle();
+ }
+
+ /** @hide */
+ @TestApi
+ public void writeToParcelNoRecycle(Parcel parcel, int flags) {
// Write bit set of indices of fields with values differing from default
long nonDefaultFields = 0;
int fieldIndex = 0; // index of the current field
@@ -3406,10 +3415,6 @@
+ " vs " + fieldIndex);
}
}
-
- // Since instances of this class are fetched via synchronous i.e. blocking
- // calls in IPCs we always recycle as soon as the instance is marshaled.
- recycle();
}
/**
@@ -3557,7 +3562,7 @@
if (isBitSet(nonDefaultFields, fieldIndex++)) {
mContentDescription = parcel.readCharSequence();
}
- if (isBitSet(nonDefaultFields, fieldIndex++)) mPaneTitle = parcel.readString();
+ if (isBitSet(nonDefaultFields, fieldIndex++)) mPaneTitle = parcel.readCharSequence();
if (isBitSet(nonDefaultFields, fieldIndex++)) mTooltipText = parcel.readCharSequence();
if (isBitSet(nonDefaultFields, fieldIndex++)) mViewIdResourceName = parcel.readString();
diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java
index 5131a8a..8b64bad 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -619,6 +619,7 @@
/**
* @hide
*/
+ @GuardedBy("mLock")
public boolean isCompatibilityModeEnabledLocked() {
return mCompatibilityBridge != null;
}
@@ -709,6 +710,7 @@
notifyViewEntered(view, 0);
}
+ @GuardedBy("mLock")
private boolean shouldIgnoreViewEnteredLocked(@NonNull View view, int flags) {
if (isDisabledByServiceLocked()) {
if (sVerbose) {
@@ -749,6 +751,7 @@
}
/** Returns AutofillCallback if need fire EVENT_INPUT_UNAVAILABLE */
+ @GuardedBy("mLock")
private AutofillCallback notifyViewEnteredLocked(@NonNull View view, int flags) {
if (shouldIgnoreViewEnteredLocked(view, flags)) return null;
@@ -792,6 +795,7 @@
}
}
+ @GuardedBy("mLock")
void notifyViewExitedLocked(@NonNull View view) {
ensureServiceClientAddedIfNeededLocked();
@@ -893,6 +897,7 @@
}
/** Returns AutofillCallback if need fire EVENT_INPUT_UNAVAILABLE */
+ @GuardedBy("mLock")
private AutofillCallback notifyViewEnteredLocked(View view, int virtualId, Rect bounds,
int flags) {
AutofillCallback callback = null;
@@ -936,6 +941,7 @@
}
}
+ @GuardedBy("mLock")
private void notifyViewExitedLocked(@NonNull View view, int virtualId) {
ensureServiceClientAddedIfNeededLocked();
@@ -1087,6 +1093,7 @@
}
}
+ @GuardedBy("mLock")
private void commitLocked() {
if (!mEnabled && !isActiveLocked()) {
return;
@@ -1115,6 +1122,7 @@
}
}
+ @GuardedBy("mLock")
private void cancelLocked() {
if (!mEnabled && !isActiveLocked()) {
return;
@@ -1378,6 +1386,7 @@
return new AutofillId(parent.getAutofillViewId(), virtualId);
}
+ @GuardedBy("mLock")
private void startSessionLocked(@NonNull AutofillId id, @NonNull Rect bounds,
@NonNull AutofillValue value, int flags) {
if (sVerbose) {
@@ -1408,6 +1417,7 @@
}
}
+ @GuardedBy("mLock")
private void finishSessionLocked() {
if (sVerbose) Log.v(TAG, "finishSessionLocked(): " + getStateAsStringLocked());
@@ -1422,6 +1432,7 @@
resetSessionLocked();
}
+ @GuardedBy("mLock")
private void cancelSessionLocked() {
if (sVerbose) Log.v(TAG, "cancelSessionLocked(): " + getStateAsStringLocked());
@@ -1436,6 +1447,7 @@
resetSessionLocked();
}
+ @GuardedBy("mLock")
private void resetSessionLocked() {
mSessionId = NO_SESSION;
mState = STATE_UNKNOWN;
@@ -1444,6 +1456,7 @@
mSaveTriggerId = null;
}
+ @GuardedBy("mLock")
private void updateSessionLocked(AutofillId id, Rect bounds, AutofillValue value, int action,
int flags) {
if (sVerbose && action != ACTION_VIEW_EXITED) {
@@ -1478,6 +1491,7 @@
}
}
+ @GuardedBy("mLock")
private void ensureServiceClientAddedIfNeededLocked() {
if (getClient() == null) {
return;
@@ -1947,6 +1961,7 @@
pw.print(" verbose: "); pw.println(sVerbose);
}
+ @GuardedBy("mLock")
private String getStateAsStringLocked() {
switch (mState) {
case STATE_UNKNOWN:
@@ -1964,14 +1979,17 @@
}
}
+ @GuardedBy("mLock")
private boolean isActiveLocked() {
return mState == STATE_ACTIVE;
}
+ @GuardedBy("mLock")
private boolean isDisabledByServiceLocked() {
return mState == STATE_DISABLED_BY_SERVICE;
}
+ @GuardedBy("mLock")
private boolean isFinishedLocked() {
return mState == STATE_FINISHED;
}
@@ -2167,6 +2185,7 @@
AutofillValue.forText(node.getText()));
}
+ @GuardedBy("mLock")
private void updateTrackedViewsLocked() {
if (mTrackedViews != null) {
mTrackedViews.onVisibleForAutofillChangedLocked();
@@ -2311,6 +2330,7 @@
* @param id the id of the view/virtual view whose visibility changed.
* @param isVisible visible if the view is visible in the view hierarchy.
*/
+ @GuardedBy("mLock")
void notifyViewVisibilityChangedLocked(@NonNull AutofillId id, boolean isVisible) {
if (sDebug) {
Log.d(TAG, "notifyViewVisibilityChangedLocked(): id=" + id + " isVisible="
@@ -2344,6 +2364,7 @@
*
* @see AutofillClient#autofillClientIsVisibleForAutofill()
*/
+ @GuardedBy("mLock")
void onVisibleForAutofillChangedLocked() {
// The visibility of the views might have changed while the client was not be visible,
// hence update the visibility state for all views.
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index 41ceb30..9988661 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -4186,7 +4186,7 @@
primaryHorizontal,
layout.getLineTop(line),
primaryHorizontal,
- layout.getLineBottom(line) - layout.getLineBottom(line) + mHandleHeight);
+ layout.getLineBottom(line) + mHandleHeight);
}
// Take TextView's padding and scroll into account.
int textHorizontalOffset = mTextView.viewportToContentHorizontalOffset();
diff --git a/core/java/android/widget/OWNERS b/core/java/android/widget/OWNERS
index 8f0d02f..2789bae 100644
--- a/core/java/android/widget/OWNERS
+++ b/core/java/android/widget/OWNERS
@@ -1,12 +1,9 @@
per-file TextView.java = siyamed@google.com
per-file TextView.java = nona@google.com
per-file TextView.java = clarabayarri@google.com
-per-file TextView.java = toki@google.com
per-file EditText.java = siyamed@google.com
per-file EditText.java = nona@google.com
per-file EditText.java = clarabayarri@google.com
-per-file EditText.java = toki@google.com
per-file Editor.java = siyamed@google.com
per-file Editor.java = nona@google.com
per-file Editor.java = clarabayarri@google.com
-per-file Editor.java = toki@google.com
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index ecab15f..eb58b09 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -678,7 +678,8 @@
StopwatchTimer mCameraOnTimer;
int mGpsSignalQualityBin = -1;
- final StopwatchTimer[] mGpsSignalQualityTimer =
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+ protected final StopwatchTimer[] mGpsSignalQualityTimer =
new StopwatchTimer[GnssMetrics.NUM_GPS_SIGNAL_QUALITY_LEVELS];
int mPhoneSignalStrengthBin = -1;
@@ -760,6 +761,8 @@
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
protected StopwatchTimer mBluetoothScanTimer;
+ boolean mIsCellularTxPowerHigh = false;
+
int mMobileRadioPowerState = DataConnectionRealTimeInfo.DC_POWER_STATE_LOW;
long mMobileRadioActiveStartTime;
StopwatchTimer mMobileRadioActiveTimer;
@@ -3825,6 +3828,7 @@
mActiveHistoryStates2 = 0xffffffff;
}
+ @GuardedBy("this")
public void updateTimeBasesLocked(boolean unplugged, int screenState, long uptime,
long realtime) {
final boolean screenOff = !isScreenOn(screenState);
@@ -3902,6 +3906,7 @@
* This should only be called after the cpu times have been read.
* @see #scheduleRemoveIsolatedUidLocked(int, int)
*/
+ @GuardedBy("this")
public void removeIsolatedUidLocked(int isolatedUid) {
StatsLog.write(
StatsLog.ISOLATED_UID_CHANGED, mIsolatedUids.get(isolatedUid, -1),
@@ -4731,6 +4736,7 @@
return;
}
+ @GuardedBy("this")
public void noteScreenStateLocked(int state) {
state = mPretendScreenOff ? Display.STATE_OFF : state;
@@ -9647,6 +9653,7 @@
return ps;
}
+ @GuardedBy("mBsi")
public void updateUidProcessStateLocked(int procState) {
int uidRunningState;
// Make special note of Foreground Services
@@ -11211,6 +11218,9 @@
Slog.d(TAG, "Updating mobile radio stats with " + activityInfo);
}
+ // Add modem tx power to history.
+ addModemTxPowerToHistory(activityInfo);
+
// Grab a separate lock to acquire the network stats, which may do I/O.
NetworkStats delta = null;
synchronized (mModemNetworkLock) {
@@ -11385,6 +11395,44 @@
new BluetoothActivityEnergyInfo(0, 0, 0, 0, 0, 0);
/**
+ * Add modem tx power to history
+ * Device is said to be in high cellular transmit power when it has spent most of the transmit
+ * time at the highest power level.
+ * @param activityInfo
+ */
+ private void addModemTxPowerToHistory(final ModemActivityInfo activityInfo) {
+ if (activityInfo == null) {
+ return;
+ }
+ int[] txTimeMs = activityInfo.getTxTimeMillis();
+ if (txTimeMs == null || txTimeMs.length != ModemActivityInfo.TX_POWER_LEVELS) {
+ return;
+ }
+ final long elapsedRealtime = mClocks.elapsedRealtime();
+ final long uptime = mClocks.uptimeMillis();
+ int levelMaxTimeSpent = 0;
+ for (int i = 1; i < txTimeMs.length; i++) {
+ if (txTimeMs[i] > txTimeMs[levelMaxTimeSpent]) {
+ levelMaxTimeSpent = i;
+ }
+ }
+ if (levelMaxTimeSpent == ModemActivityInfo.TX_POWER_LEVELS - 1) {
+ if (!mIsCellularTxPowerHigh) {
+ mHistoryCur.states2 |= HistoryItem.STATE2_CELLULAR_HIGH_TX_POWER_FLAG;
+ addHistoryRecordLocked(elapsedRealtime, uptime);
+ mIsCellularTxPowerHigh = true;
+ }
+ return;
+ }
+ if (mIsCellularTxPowerHigh) {
+ mHistoryCur.states2 &= ~HistoryItem.STATE2_CELLULAR_HIGH_TX_POWER_FLAG;
+ addHistoryRecordLocked(elapsedRealtime, uptime);
+ mIsCellularTxPowerHigh = false;
+ }
+ return;
+ }
+
+ /**
* Distribute Bluetooth energy info and network traffic to apps.
* @param info The energy information from the bluetooth controller.
*/
@@ -11718,6 +11766,7 @@
* and we are on battery with screen off, we give more of the cpu time to those apps holding
* wakelocks. If the screen is on, we just assign the actual cpu time an app used.
*/
+ @GuardedBy("this")
public void updateCpuTimeLocked() {
if (mPowerProfile == null) {
return;
@@ -12163,6 +12212,7 @@
return false;
}
+ @GuardedBy("this")
protected void setOnBatteryLocked(final long mSecRealtime, final long mSecUptime,
final boolean onBattery, final int oldStatus, final int level, final int chargeUAh) {
boolean doWrite = false;
@@ -12339,6 +12389,7 @@
// This should probably be exposed in the API, though it's not critical
public static final int BATTERY_PLUGGED_NONE = OsProtoEnums.BATTERY_PLUGGED_NONE; // = 0
+ @GuardedBy("this")
public void setBatteryStateLocked(final int status, final int health, final int plugType,
final int level, /* not final */ int temp, final int volt, final int chargeUAh,
final int chargeFullUAh) {
@@ -13154,6 +13205,7 @@
}
}
+ @GuardedBy("this")
public void dumpConstantsLocked(PrintWriter pw) {
mConstants.dumpLocked(pw);
}
@@ -13537,6 +13589,7 @@
mCameraOnTimer.readSummaryFromParcelLocked(in);
mBluetoothScanNesting = 0;
mBluetoothScanTimer.readSummaryFromParcelLocked(in);
+ mIsCellularTxPowerHigh = false;
int NRPMS = in.readInt();
if (NRPMS > 10000) {
@@ -14473,6 +14526,7 @@
mCameraOnTimer = new StopwatchTimer(mClocks, null, -13, null, mOnBatteryTimeBase, in);
mBluetoothScanNesting = 0;
mBluetoothScanTimer = new StopwatchTimer(mClocks, null, -14, null, mOnBatteryTimeBase, in);
+ mIsCellularTxPowerHigh = false;
mDischargeUnplugLevel = in.readInt();
mDischargePlugLevel = in.readInt();
mDischargeCurrentLevel = in.readInt();
diff --git a/core/java/com/android/internal/os/FuseAppLoop.java b/core/java/com/android/internal/os/FuseAppLoop.java
index 088e726..12405eb 100644
--- a/core/java/com/android/internal/os/FuseAppLoop.java
+++ b/core/java/com/android/internal/os/FuseAppLoop.java
@@ -283,6 +283,7 @@
return -OsConstants.EBADF;
}
+ @GuardedBy("mLock")
private CallbackEntry getCallbackEntryOrThrowLocked(long inode) throws ErrnoException {
final CallbackEntry entry = mCallbackMap.get(checkInode(inode));
if (entry == null) {
@@ -291,12 +292,14 @@
return entry;
}
+ @GuardedBy("mLock")
private void recycleLocked(Args args) {
if (mArgsPool.size() < ARGS_POOL_SIZE) {
mArgsPool.add(args);
}
}
+ @GuardedBy("mLock")
private void replySimpleLocked(long unique, int result) {
if (mInstance != 0) {
native_replySimple(mInstance, unique, result);
diff --git a/core/java/com/android/internal/statusbar/IStatusBar.aidl b/core/java/com/android/internal/statusbar/IStatusBar.aidl
index f70d3c2..221bf88 100644
--- a/core/java/com/android/internal/statusbar/IStatusBar.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBar.aidl
@@ -35,7 +35,7 @@
void animateCollapsePanels();
void togglePanel();
- void showChargingAnimation(int batteryLevel);
+ void showWirelessChargingAnimation(int batteryLevel);
/**
* Notifies the status bar of a System UI visibility flag change.
diff --git a/core/java/com/android/internal/view/menu/MenuBuilder.java b/core/java/com/android/internal/view/menu/MenuBuilder.java
index b53459e..67dc81a 100644
--- a/core/java/com/android/internal/view/menu/MenuBuilder.java
+++ b/core/java/com/android/internal/view/menu/MenuBuilder.java
@@ -551,6 +551,7 @@
mPreventDispatchingItemsChanged = true;
clear();
clearHeader();
+ mPresenters.clear();
mPreventDispatchingItemsChanged = false;
mItemsChangedWhileDispatchPrevented = false;
onItemsChanged(true);
diff --git a/core/java/com/android/internal/widget/VerifyCredentialResponse.java b/core/java/com/android/internal/widget/VerifyCredentialResponse.java
index ad6020c..7d1c706 100644
--- a/core/java/com/android/internal/widget/VerifyCredentialResponse.java
+++ b/core/java/com/android/internal/widget/VerifyCredentialResponse.java
@@ -98,6 +98,8 @@
if (mPayload != null) {
dest.writeInt(mPayload.length);
dest.writeByteArray(mPayload);
+ } else {
+ dest.writeInt(0);
}
}
}
diff --git a/core/java/com/android/internal/widget/ViewPager.java b/core/java/com/android/internal/widget/ViewPager.java
index d5b6def..64bdc6e 100644
--- a/core/java/com/android/internal/widget/ViewPager.java
+++ b/core/java/com/android/internal/widget/ViewPager.java
@@ -31,6 +31,7 @@
import android.util.AttributeSet;
import android.util.Log;
import android.util.MathUtils;
+import android.view.AbsSavedState;
import android.view.FocusFinder;
import android.view.Gravity;
import android.view.KeyEvent;
@@ -1198,16 +1199,12 @@
* state, in which case it should implement a subclass of this which
* contains that state.
*/
- public static class SavedState extends BaseSavedState {
+ public static class SavedState extends AbsSavedState {
int position;
Parcelable adapterState;
ClassLoader loader;
- public SavedState(Parcel source) {
- super(source);
- }
-
- public SavedState(Parcelable superState) {
+ public SavedState(@NonNull Parcelable superState) {
super(superState);
}
@@ -1225,10 +1222,15 @@
+ " position=" + position + "}";
}
- public static final Creator<SavedState> CREATOR = new Creator<SavedState>() {
+ public static final Creator<SavedState> CREATOR = new ClassLoaderCreator<SavedState>() {
+ @Override
+ public SavedState createFromParcel(Parcel in, ClassLoader loader) {
+ return new SavedState(in, loader);
+ }
+
@Override
public SavedState createFromParcel(Parcel in) {
- return new SavedState(in);
+ return new SavedState(in, null);
}
@Override
public SavedState[] newArray(int size) {
@@ -1237,7 +1239,7 @@
};
SavedState(Parcel in, ClassLoader loader) {
- super(in);
+ super(in, loader);
if (loader == null) {
loader = getClass().getClassLoader();
}
diff --git a/core/java/com/android/server/BootReceiver.java b/core/java/com/android/server/BootReceiver.java
index efd98e2..95534e2 100644
--- a/core/java/com/android/server/BootReceiver.java
+++ b/core/java/com/android/server/BootReceiver.java
@@ -72,6 +72,7 @@
SystemProperties.getInt("ro.debuggable", 0) == 1 ? 98304 : 65536;
private static final File TOMBSTONE_DIR = new File("/data/tombstones");
+ private static final String TAG_TOMBSTONE = "SYSTEM_TOMBSTONE";
// The pre-froyo package and class of the system updater, which
// ran in the system process. We need to remove its packages here
@@ -265,7 +266,7 @@
File file = new File(TOMBSTONE_DIR, path);
if (file.isFile()) {
addFileToDropBox(db, timestamps, headers, file.getPath(), LOG_SIZE,
- "SYSTEM_TOMBSTONE");
+ TAG_TOMBSTONE);
}
} catch (IOException e) {
Slog.e(TAG, "Can't log tombstone", e);
@@ -299,9 +300,20 @@
timestamps.put(filename, fileTime);
+
+ String fileContents = FileUtils.readTextFile(file, maxSize, "[[TRUNCATED]]\n");
+ String text = headers + fileContents + footers;
+ // Create an additional report for system server native crashes, with a special tag.
+ if (tag.equals(TAG_TOMBSTONE) && fileContents.contains(">>> system_server <<<")) {
+ addTextToDropBox(db, "system_server_native_crash", text, filename, maxSize);
+ }
+ addTextToDropBox(db, tag, text, filename, maxSize);
+ }
+
+ private static void addTextToDropBox(DropBoxManager db, String tag, String text,
+ String filename, int maxSize) {
Slog.i(TAG, "Copying " + filename + " to DropBox (" + tag + ")");
- db.addText(tag, headers + FileUtils.readTextFile(file, maxSize, "[[TRUNCATED]]\n") +
- footers);
+ db.addText(tag, text);
EventLog.writeEvent(DropboxLogTags.DROPBOX_FILE_COPY, filename, maxSize, tag);
}
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index 78a3e13..33f80ce 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -110,8 +110,8 @@
"android_util_AssetManager.cpp",
"android_util_Binder.cpp",
"android_util_EventLog.cpp",
- "android_util_Log.cpp",
"android_util_MemoryIntArray.cpp",
+ "android_util_Log.cpp",
"android_util_PathParser.cpp",
"android_util_Process.cpp",
"android_util_StringBlock.cpp",
@@ -191,7 +191,6 @@
"android_backup_FileBackupHelperBase.cpp",
"android_backup_BackupHelperDispatcher.cpp",
"android_app_backup_FullBackup.cpp",
- "android_content_res_ApkAssets.cpp",
"android_content_res_ObbScanner.cpp",
"android_content_res_Configuration.cpp",
"android_animation_PropertyValuesHolder.cpp",
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 4a032c4..d202173 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -123,7 +123,6 @@
extern int register_android_util_PathParser(JNIEnv* env);
extern int register_android_content_StringBlock(JNIEnv* env);
extern int register_android_content_XmlBlock(JNIEnv* env);
-extern int register_android_content_res_ApkAssets(JNIEnv* env);
extern int register_android_graphics_Canvas(JNIEnv* env);
extern int register_android_graphics_CanvasProperty(JNIEnv* env);
extern int register_android_graphics_ColorFilter(JNIEnv* env);
@@ -1347,7 +1346,6 @@
REG_JNI(register_android_content_AssetManager),
REG_JNI(register_android_content_StringBlock),
REG_JNI(register_android_content_XmlBlock),
- REG_JNI(register_android_content_res_ApkAssets),
REG_JNI(register_android_text_AndroidCharacter),
REG_JNI(register_android_text_Hyphenator),
REG_JNI(register_android_text_MeasuredParagraph),
diff --git a/core/jni/android/graphics/FontFamily.cpp b/core/jni/android/graphics/FontFamily.cpp
index f6223fa..48aef4a 100644
--- a/core/jni/android/graphics/FontFamily.cpp
+++ b/core/jni/android/graphics/FontFamily.cpp
@@ -28,7 +28,7 @@
#include <nativehelper/ScopedUtfChars.h>
#include <android_runtime/AndroidRuntime.h>
#include <android_runtime/android_util_AssetManager.h>
-#include <androidfw/AssetManager2.h>
+#include <androidfw/AssetManager.h>
#include "Utils.h"
#include "FontUtils.h"
@@ -90,7 +90,7 @@
}
static bool addSkTypeface(NativeFamilyBuilder* builder, sk_sp<SkData>&& data, int ttcIndex,
- jint givenWeight, jint givenItalic) {
+ jint weight, jint italic) {
uirenderer::FatVector<SkFontArguments::Axis, 2> skiaAxes;
for (const auto& axis : builder->axes) {
skiaAxes.emplace_back(SkFontArguments::Axis{axis.axisTag, axis.value});
@@ -114,27 +114,15 @@
std::shared_ptr<minikin::MinikinFont> minikinFont =
std::make_shared<MinikinFontSkia>(std::move(face), fontPtr, fontSize, ttcIndex,
builder->axes);
+ minikin::Font::Builder fontBuilder(minikinFont);
- int weight = givenWeight;
- bool italic = givenItalic == 1;
- if (givenWeight == RESOLVE_BY_FONT_TABLE || givenItalic == RESOLVE_BY_FONT_TABLE) {
- int os2Weight;
- bool os2Italic;
- if (!minikin::FontFamily::analyzeStyle(minikinFont, &os2Weight, &os2Italic)) {
- ALOGE("analyzeStyle failed. Using default style");
- os2Weight = 400;
- os2Italic = false;
- }
- if (givenWeight == RESOLVE_BY_FONT_TABLE) {
- weight = os2Weight;
- }
- if (givenItalic == RESOLVE_BY_FONT_TABLE) {
- italic = os2Italic;
- }
+ if (weight != RESOLVE_BY_FONT_TABLE) {
+ fontBuilder.setWeight(weight);
}
-
- builder->fonts.push_back(minikin::Font(minikinFont,
- minikin::FontStyle(weight, static_cast<minikin::FontStyle::Slant>(italic))));
+ if (italic != RESOLVE_BY_FONT_TABLE) {
+ fontBuilder.setSlant(static_cast<minikin::FontStyle::Slant>(italic != 0));
+ }
+ builder->fonts.push_back(fontBuilder.build());
builder->axes.clear();
return true;
}
@@ -217,8 +205,7 @@
NPE_CHECK_RETURN_ZERO(env, jpath);
NativeFamilyBuilder* builder = reinterpret_cast<NativeFamilyBuilder*>(builderPtr);
-
- Guarded<AssetManager2>* mgr = AssetManagerForJavaObject(env, jassetMgr);
+ AssetManager* mgr = assetManagerForJavaObject(env, jassetMgr);
if (NULL == mgr) {
builder->axes.clear();
return false;
@@ -230,33 +217,27 @@
return false;
}
- std::unique_ptr<Asset> asset;
- {
- ScopedLock<AssetManager2> locked_mgr(*mgr);
- if (isAsset) {
- asset = locked_mgr->Open(str.c_str(), Asset::ACCESS_BUFFER);
- } else if (cookie > 0) {
- // Valid java cookies are 1-based, but AssetManager cookies are 0-based.
- asset = locked_mgr->OpenNonAsset(str.c_str(), static_cast<ApkAssetsCookie>(cookie - 1),
- Asset::ACCESS_BUFFER);
- } else {
- asset = locked_mgr->OpenNonAsset(str.c_str(), Asset::ACCESS_BUFFER);
- }
+ Asset* asset;
+ if (isAsset) {
+ asset = mgr->open(str.c_str(), Asset::ACCESS_BUFFER);
+ } else {
+ asset = cookie ? mgr->openNonAsset(static_cast<int32_t>(cookie), str.c_str(),
+ Asset::ACCESS_BUFFER) : mgr->openNonAsset(str.c_str(), Asset::ACCESS_BUFFER);
}
- if (nullptr == asset) {
+ if (NULL == asset) {
builder->axes.clear();
return false;
}
const void* buf = asset->getBuffer(false);
if (NULL == buf) {
+ delete asset;
builder->axes.clear();
return false;
}
- sk_sp<SkData> data(SkData::MakeWithProc(buf, asset->getLength(), releaseAsset,
- asset.release()));
+ sk_sp<SkData> data(SkData::MakeWithProc(buf, asset->getLength(), releaseAsset, asset));
return addSkTypeface(builder, std::move(data), ttcIndex, weight, isItalic);
}
diff --git a/core/jni/android/graphics/Paint.cpp b/core/jni/android/graphics/Paint.cpp
index 115d0d5..482d028 100644
--- a/core/jni/android/graphics/Paint.cpp
+++ b/core/jni/android/graphics/Paint.cpp
@@ -576,7 +576,7 @@
minikin::FakedFont baseFont = typeface->fFontCollection->baseFontFaked(typeface->fStyle);
float saveSkewX = paint->getTextSkewX();
bool savefakeBold = paint->isFakeBoldText();
- MinikinFontSkia::populateSkPaint(paint, baseFont.font, baseFont.fakery);
+ MinikinFontSkia::populateSkPaint(paint, baseFont.font->typeface().get(), baseFont.fakery);
SkScalar spacing = paint->getFontMetrics(metrics);
// The populateSkPaint call may have changed fake bold / text skew
// because we want to measure with those effects applied, so now
diff --git a/core/jni/android_app_NativeActivity.cpp b/core/jni/android_app_NativeActivity.cpp
index 49a24a3..09e37e1 100644
--- a/core/jni/android_app_NativeActivity.cpp
+++ b/core/jni/android_app_NativeActivity.cpp
@@ -361,7 +361,7 @@
code->sdkVersion = sdkVersion;
code->javaAssetManager = env->NewGlobalRef(jAssetMgr);
- code->assetManager = NdkAssetManagerForJavaObject(env, jAssetMgr);
+ code->assetManager = assetManagerForJavaObject(env, jAssetMgr);
if (obbDir != NULL) {
dirStr = env->GetStringUTFChars(obbDir, NULL);
diff --git a/core/jni/android_content_res_ApkAssets.cpp b/core/jni/android_content_res_ApkAssets.cpp
deleted file mode 100644
index c0f151b..0000000
--- a/core/jni/android_content_res_ApkAssets.cpp
+++ /dev/null
@@ -1,150 +0,0 @@
-/*
- * 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.
- */
-
-#include "android-base/macros.h"
-#include "android-base/stringprintf.h"
-#include "android-base/unique_fd.h"
-#include "androidfw/ApkAssets.h"
-#include "utils/misc.h"
-
-#include "core_jni_helpers.h"
-#include "jni.h"
-#include "nativehelper/ScopedUtfChars.h"
-
-using ::android::base::unique_fd;
-
-namespace android {
-
-static jlong NativeLoad(JNIEnv* env, jclass /*clazz*/, jstring java_path, jboolean system,
- jboolean force_shared_lib, jboolean overlay) {
- ScopedUtfChars path(env, java_path);
- if (path.c_str() == nullptr) {
- return 0;
- }
-
- std::unique_ptr<const ApkAssets> apk_assets;
- if (overlay) {
- apk_assets = ApkAssets::LoadOverlay(path.c_str(), system);
- } else if (force_shared_lib) {
- apk_assets = ApkAssets::LoadAsSharedLibrary(path.c_str(), system);
- } else {
- apk_assets = ApkAssets::Load(path.c_str(), system);
- }
-
- if (apk_assets == nullptr) {
- std::string error_msg = base::StringPrintf("Failed to load asset path %s", path.c_str());
- jniThrowException(env, "java/io/IOException", error_msg.c_str());
- return 0;
- }
- return reinterpret_cast<jlong>(apk_assets.release());
-}
-
-static jlong NativeLoadFromFd(JNIEnv* env, jclass /*clazz*/, jobject file_descriptor,
- jstring friendly_name, jboolean system, jboolean force_shared_lib) {
- ScopedUtfChars friendly_name_utf8(env, friendly_name);
- if (friendly_name_utf8.c_str() == nullptr) {
- return 0;
- }
-
- int fd = jniGetFDFromFileDescriptor(env, file_descriptor);
- if (fd < 0) {
- jniThrowException(env, "java/lang/IllegalArgumentException", "Bad FileDescriptor");
- return 0;
- }
-
- unique_fd dup_fd(::dup(fd));
- if (dup_fd < 0) {
- jniThrowIOException(env, errno);
- return 0;
- }
-
- std::unique_ptr<const ApkAssets> apk_assets = ApkAssets::LoadFromFd(std::move(dup_fd),
- friendly_name_utf8.c_str(),
- system, force_shared_lib);
- if (apk_assets == nullptr) {
- std::string error_msg = base::StringPrintf("Failed to load asset path %s from fd %d",
- friendly_name_utf8.c_str(), dup_fd.get());
- jniThrowException(env, "java/io/IOException", error_msg.c_str());
- return 0;
- }
- return reinterpret_cast<jlong>(apk_assets.release());
-}
-
-static void NativeDestroy(JNIEnv* /*env*/, jclass /*clazz*/, jlong ptr) {
- delete reinterpret_cast<ApkAssets*>(ptr);
-}
-
-static jstring NativeGetAssetPath(JNIEnv* env, jclass /*clazz*/, jlong ptr) {
- const ApkAssets* apk_assets = reinterpret_cast<const ApkAssets*>(ptr);
- return env->NewStringUTF(apk_assets->GetPath().c_str());
-}
-
-static jlong NativeGetStringBlock(JNIEnv* /*env*/, jclass /*clazz*/, jlong ptr) {
- const ApkAssets* apk_assets = reinterpret_cast<const ApkAssets*>(ptr);
- return reinterpret_cast<jlong>(apk_assets->GetLoadedArsc()->GetStringPool());
-}
-
-static jboolean NativeIsUpToDate(JNIEnv* /*env*/, jclass /*clazz*/, jlong ptr) {
- const ApkAssets* apk_assets = reinterpret_cast<const ApkAssets*>(ptr);
- (void)apk_assets;
- return JNI_TRUE;
-}
-
-static jlong NativeOpenXml(JNIEnv* env, jclass /*clazz*/, jlong ptr, jstring file_name) {
- ScopedUtfChars path_utf8(env, file_name);
- if (path_utf8.c_str() == nullptr) {
- return 0;
- }
-
- const ApkAssets* apk_assets = reinterpret_cast<const ApkAssets*>(ptr);
- std::unique_ptr<Asset> asset = apk_assets->Open(path_utf8.c_str(),
- Asset::AccessMode::ACCESS_RANDOM);
- if (asset == nullptr) {
- jniThrowException(env, "java/io/FileNotFoundException", path_utf8.c_str());
- return 0;
- }
-
- // DynamicRefTable is only needed when looking up resource references. Opening an XML file
- // directly from an ApkAssets has no notion of proper resource references.
- std::unique_ptr<ResXMLTree> xml_tree = util::make_unique<ResXMLTree>(nullptr /*dynamicRefTable*/);
- status_t err = xml_tree->setTo(asset->getBuffer(true), asset->getLength(), true);
- asset.reset();
-
- if (err != NO_ERROR) {
- jniThrowException(env, "java/io/FileNotFoundException", "Corrupt XML binary file");
- return 0;
- }
- return reinterpret_cast<jlong>(xml_tree.release());
-}
-
-// JNI registration.
-static const JNINativeMethod gApkAssetsMethods[] = {
- {"nativeLoad", "(Ljava/lang/String;ZZZ)J", (void*)NativeLoad},
- {"nativeLoadFromFd", "(Ljava/io/FileDescriptor;Ljava/lang/String;ZZ)J",
- (void*)NativeLoadFromFd},
- {"nativeDestroy", "(J)V", (void*)NativeDestroy},
- {"nativeGetAssetPath", "(J)Ljava/lang/String;", (void*)NativeGetAssetPath},
- {"nativeGetStringBlock", "(J)J", (void*)NativeGetStringBlock},
- {"nativeIsUpToDate", "(J)Z", (void*)NativeIsUpToDate},
- {"nativeOpenXml", "(JLjava/lang/String;)J", (void*)NativeOpenXml},
-};
-
-int register_android_content_res_ApkAssets(JNIEnv* env) {
- return RegisterMethodsOrDie(env, "android/content/res/ApkAssets", gApkAssetsMethods,
- arraysize(gApkAssetsMethods));
-}
-
-} // namespace android
diff --git a/core/jni/android_graphics_Canvas.cpp b/core/jni/android_graphics_Canvas.cpp
index 6b961f5..06de5da 100644
--- a/core/jni/android_graphics_Canvas.cpp
+++ b/core/jni/android_graphics_Canvas.cpp
@@ -573,6 +573,11 @@
minikin::Layout::purgeCaches();
}
+static void setCompatibilityVersion(JNIEnv* env, jobject, jint apiLevel) {
+ Canvas::setCompatibilityVersion(apiLevel);
+}
+
+
}; // namespace CanvasJNI
static const JNINativeMethod gMethods[] = {
@@ -580,6 +585,7 @@
{"nInitRaster", "(Landroid/graphics/Bitmap;)J", (void*) CanvasJNI::initRaster},
{"nFreeCaches", "()V", (void*) CanvasJNI::freeCaches},
{"nFreeTextLayoutCaches", "()V", (void*) CanvasJNI::freeTextLayoutCaches},
+ {"nSetCompatibilityVersion", "(I)V", (void*) CanvasJNI::setCompatibilityVersion},
// ------------ @FastNative ----------------
{"nSetBitmap", "(JLandroid/graphics/Bitmap;)V", (void*) CanvasJNI::setBitmap},
diff --git a/core/jni/android_os_VintfObject.cpp b/core/jni/android_os_VintfObject.cpp
index 1659168..e8ef349 100644
--- a/core/jni/android_os_VintfObject.cpp
+++ b/core/jni/android_os_VintfObject.cpp
@@ -32,10 +32,13 @@
static jclass gHashMapClazz;
static jmethodID gHashMapInit;
static jmethodID gHashMapPut;
+static jclass gLongClazz;
+static jmethodID gLongValueOf;
namespace android {
using vintf::HalManifest;
+using vintf::Level;
using vintf::SchemaType;
using vintf::VintfObject;
using vintf::XmlConverter;
@@ -154,6 +157,14 @@
return jMap;
}
+static jobject android_os_VintfObject_getTargetFrameworkCompatibilityMatrixVersion(JNIEnv* env, jclass) {
+ std::shared_ptr<const HalManifest> manifest = VintfObject::GetDeviceHalManifest();
+ if (manifest == nullptr || manifest->level() == Level::UNSPECIFIED) {
+ return nullptr;
+ }
+ return env->CallStaticObjectMethod(gLongClazz, gLongValueOf, static_cast<jlong>(manifest->level()));
+}
+
// ----------------------------------------------------------------------------
static const JNINativeMethod gVintfObjectMethods[] = {
@@ -163,6 +174,7 @@
{"getHalNamesAndVersions", "()[Ljava/lang/String;", (void*)android_os_VintfObject_getHalNamesAndVersions},
{"getSepolicyVersion", "()Ljava/lang/String;", (void*)android_os_VintfObject_getSepolicyVersion},
{"getVndkSnapshots", "()Ljava/util/Map;", (void*)android_os_VintfObject_getVndkSnapshots},
+ {"getTargetFrameworkCompatibilityMatrixVersion", "()Ljava/lang/Long;", (void*)android_os_VintfObject_getTargetFrameworkCompatibilityMatrixVersion},
};
const char* const kVintfObjectPathName = "android/os/VintfObject";
@@ -175,6 +187,8 @@
gHashMapInit = GetMethodIDOrDie(env, gHashMapClazz, "<init>", "()V");
gHashMapPut = GetMethodIDOrDie(env, gHashMapClazz,
"put", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
+ gLongClazz = MakeGlobalRefOrDie(env, FindClassOrDie(env, "java/lang/Long"));
+ gLongValueOf = GetStaticMethodIDOrDie(env, gLongClazz, "valueOf", "(J)Ljava/lang/Long;");
return RegisterMethodsOrDie(env, kVintfObjectPathName, gVintfObjectMethods,
NELEM(gVintfObjectMethods));
diff --git a/core/jni/android_util_AssetManager.cpp b/core/jni/android_util_AssetManager.cpp
index c623ca6..683b4c4 100644
--- a/core/jni/android_util_AssetManager.cpp
+++ b/core/jni/android_util_AssetManager.cpp
@@ -1,1441 +1,1851 @@
-/*
- * Copyright 2006, 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.
- */
+/* //device/libs/android_runtime/android_util_AssetManager.cpp
+**
+** Copyright 2006, 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 "asset"
+#include <android_runtime/android_util_AssetManager.h>
+
#include <inttypes.h>
#include <linux/capability.h>
#include <stdio.h>
-#include <sys/stat.h>
-#include <sys/system_properties.h>
#include <sys/types.h>
#include <sys/wait.h>
+#include <sys/stat.h>
+#include <sys/system_properties.h>
#include <private/android_filesystem_config.h> // for AID_SYSTEM
-#include "android-base/logging.h"
-#include "android-base/properties.h"
-#include "android-base/stringprintf.h"
-#include "android_runtime/android_util_AssetManager.h"
-#include "android_runtime/AndroidRuntime.h"
-#include "android_util_Binder.h"
#include "androidfw/Asset.h"
#include "androidfw/AssetManager.h"
-#include "androidfw/AssetManager2.h"
#include "androidfw/AttributeResolution.h"
-#include "androidfw/MutexGuard.h"
#include "androidfw/ResourceTypes.h"
+#include "android_runtime/AndroidRuntime.h"
+#include "android_util_Binder.h"
#include "core_jni_helpers.h"
#include "jni.h"
-#include "nativehelper/JNIHelp.h"
-#include "nativehelper/ScopedPrimitiveArray.h"
-#include "nativehelper/ScopedStringChars.h"
-#include "nativehelper/ScopedUtfChars.h"
+#include <nativehelper/JNIHelp.h>
+#include <nativehelper/ScopedStringChars.h>
+#include <nativehelper/ScopedUtfChars.h>
#include "utils/Log.h"
-#include "utils/String8.h"
#include "utils/misc.h"
+#include "utils/String8.h"
extern "C" int capget(cap_user_header_t hdrp, cap_user_data_t datap);
extern "C" int capset(cap_user_header_t hdrp, const cap_user_data_t datap);
-using ::android::base::StringPrintf;
namespace android {
+static const bool kThrowOnBadId = false;
+
// ----------------------------------------------------------------------------
-static struct typedvalue_offsets_t {
- jfieldID mType;
- jfieldID mData;
- jfieldID mString;
- jfieldID mAssetCookie;
- jfieldID mResourceId;
- jfieldID mChangingConfigurations;
- jfieldID mDensity;
+static struct typedvalue_offsets_t
+{
+ jfieldID mType;
+ jfieldID mData;
+ jfieldID mString;
+ jfieldID mAssetCookie;
+ jfieldID mResourceId;
+ jfieldID mChangingConfigurations;
+ jfieldID mDensity;
} gTypedValueOffsets;
-static struct assetfiledescriptor_offsets_t {
- jfieldID mFd;
- jfieldID mStartOffset;
- jfieldID mLength;
+static struct assetfiledescriptor_offsets_t
+{
+ jfieldID mFd;
+ jfieldID mStartOffset;
+ jfieldID mLength;
} gAssetFileDescriptorOffsets;
-static struct assetmanager_offsets_t {
- jfieldID mObject;
+static struct assetmanager_offsets_t
+{
+ jfieldID mObject;
} gAssetManagerOffsets;
-static struct {
- jfieldID native_ptr;
-} gApkAssetsFields;
-
-static struct sparsearray_offsets_t {
- jclass classObject;
- jmethodID constructor;
- jmethodID put;
+static struct sparsearray_offsets_t
+{
+ jclass classObject;
+ jmethodID constructor;
+ jmethodID put;
} gSparseArrayOffsets;
-static struct configuration_offsets_t {
- jclass classObject;
- jmethodID constructor;
- jfieldID mSmallestScreenWidthDpOffset;
- jfieldID mScreenWidthDpOffset;
- jfieldID mScreenHeightDpOffset;
+static struct configuration_offsets_t
+{
+ jclass classObject;
+ jmethodID constructor;
+ jfieldID mSmallestScreenWidthDpOffset;
+ jfieldID mScreenWidthDpOffset;
+ jfieldID mScreenHeightDpOffset;
} gConfigurationOffsets;
-jclass g_stringClass = nullptr;
+jclass g_stringClass = NULL;
// ----------------------------------------------------------------------------
-// Java asset cookies have 0 as an invalid cookie, but TypedArray expects < 0.
-constexpr inline static jint ApkAssetsCookieToJavaCookie(ApkAssetsCookie cookie) {
- return cookie != kInvalidCookie ? static_cast<jint>(cookie + 1) : -1;
-}
+static jint copyValue(JNIEnv* env, jobject outValue, const ResTable* table,
+ const Res_value& value, uint32_t ref, ssize_t block,
+ uint32_t typeSpecFlags, ResTable_config* config = NULL);
-constexpr inline static ApkAssetsCookie JavaCookieToApkAssetsCookie(jint cookie) {
- return cookie > 0 ? static_cast<ApkAssetsCookie>(cookie - 1) : kInvalidCookie;
+jint copyValue(JNIEnv* env, jobject outValue, const ResTable* table,
+ const Res_value& value, uint32_t ref, ssize_t block,
+ uint32_t typeSpecFlags, ResTable_config* config)
+{
+ env->SetIntField(outValue, gTypedValueOffsets.mType, value.dataType);
+ env->SetIntField(outValue, gTypedValueOffsets.mAssetCookie,
+ static_cast<jint>(table->getTableCookie(block)));
+ env->SetIntField(outValue, gTypedValueOffsets.mData, value.data);
+ env->SetObjectField(outValue, gTypedValueOffsets.mString, NULL);
+ env->SetIntField(outValue, gTypedValueOffsets.mResourceId, ref);
+ env->SetIntField(outValue, gTypedValueOffsets.mChangingConfigurations,
+ typeSpecFlags);
+ if (config != NULL) {
+ env->SetIntField(outValue, gTypedValueOffsets.mDensity, config->density);
+ }
+ return block;
}
// This is called by zygote (running as user root) as part of preloadResources.
-static void NativeVerifySystemIdmaps(JNIEnv* /*env*/, jclass /*clazz*/) {
- switch (pid_t pid = fork()) {
- case -1:
- PLOG(ERROR) << "failed to fork for idmap";
- break;
+static void verifySystemIdmaps()
+{
+ pid_t pid;
+ char system_id[10];
- // child
- case 0: {
- struct __user_cap_header_struct capheader;
- struct __user_cap_data_struct capdata;
+ snprintf(system_id, sizeof(system_id), "%d", AID_SYSTEM);
- memset(&capheader, 0, sizeof(capheader));
- memset(&capdata, 0, sizeof(capdata));
+ switch (pid = fork()) {
+ case -1:
+ ALOGE("failed to fork for idmap: %s", strerror(errno));
+ break;
+ case 0: // child
+ {
+ struct __user_cap_header_struct capheader;
+ struct __user_cap_data_struct capdata;
- capheader.version = _LINUX_CAPABILITY_VERSION;
- capheader.pid = 0;
+ memset(&capheader, 0, sizeof(capheader));
+ memset(&capdata, 0, sizeof(capdata));
- if (capget(&capheader, &capdata) != 0) {
- PLOG(ERROR) << "capget";
- exit(1);
- }
+ capheader.version = _LINUX_CAPABILITY_VERSION;
+ capheader.pid = 0;
- capdata.effective = capdata.permitted;
- if (capset(&capheader, &capdata) != 0) {
- PLOG(ERROR) << "capset";
- exit(1);
- }
+ if (capget(&capheader, &capdata) != 0) {
+ ALOGE("capget: %s\n", strerror(errno));
+ exit(1);
+ }
- if (setgid(AID_SYSTEM) != 0) {
- PLOG(ERROR) << "setgid";
- exit(1);
- }
+ capdata.effective = capdata.permitted;
+ if (capset(&capheader, &capdata) != 0) {
+ ALOGE("capset: %s\n", strerror(errno));
+ exit(1);
+ }
- if (setuid(AID_SYSTEM) != 0) {
- PLOG(ERROR) << "setuid";
- exit(1);
- }
+ if (setgid(AID_SYSTEM) != 0) {
+ ALOGE("setgid: %s\n", strerror(errno));
+ exit(1);
+ }
- // Generic idmap parameters
- const char* argv[8];
- int argc = 0;
- struct stat st;
+ if (setuid(AID_SYSTEM) != 0) {
+ ALOGE("setuid: %s\n", strerror(errno));
+ exit(1);
+ }
- memset(argv, 0, sizeof(argv));
- argv[argc++] = AssetManager::IDMAP_BIN;
- argv[argc++] = "--scan";
- argv[argc++] = AssetManager::TARGET_PACKAGE_NAME;
- argv[argc++] = AssetManager::TARGET_APK_PATH;
- argv[argc++] = AssetManager::IDMAP_DIR;
+ // Generic idmap parameters
+ const char* argv[8];
+ int argc = 0;
+ struct stat st;
- // Directories to scan for overlays: if OVERLAY_THEME_DIR_PROPERTY is defined,
- // use OVERLAY_DIR/<value of OVERLAY_THEME_DIR_PROPERTY> in addition to OVERLAY_DIR.
- std::string overlay_theme_path = base::GetProperty(AssetManager::OVERLAY_THEME_DIR_PROPERTY,
- "");
- if (!overlay_theme_path.empty()) {
- overlay_theme_path = std::string(AssetManager::OVERLAY_DIR) + "/" + overlay_theme_path;
- if (stat(overlay_theme_path.c_str(), &st) == 0) {
- argv[argc++] = overlay_theme_path.c_str();
- }
- }
+ memset(argv, NULL, sizeof(argv));
+ argv[argc++] = AssetManager::IDMAP_BIN;
+ argv[argc++] = "--scan";
+ argv[argc++] = AssetManager::TARGET_PACKAGE_NAME;
+ argv[argc++] = AssetManager::TARGET_APK_PATH;
+ argv[argc++] = AssetManager::IDMAP_DIR;
- if (stat(AssetManager::OVERLAY_DIR, &st) == 0) {
- argv[argc++] = AssetManager::OVERLAY_DIR;
- }
+ // Directories to scan for overlays: if OVERLAY_THEME_DIR_PROPERTY is defined,
+ // use OVERLAY_DIR/<value of OVERLAY_THEME_DIR_PROPERTY> in addition to OVERLAY_DIR.
+ char subdir[PROP_VALUE_MAX];
+ int len = __system_property_get(AssetManager::OVERLAY_THEME_DIR_PROPERTY, subdir);
+ if (len > 0) {
+ String8 overlayPath = String8(AssetManager::OVERLAY_DIR) + "/" + subdir;
+ if (stat(overlayPath.string(), &st) == 0) {
+ argv[argc++] = overlayPath.string();
+ }
+ }
+ if (stat(AssetManager::OVERLAY_DIR, &st) == 0) {
+ argv[argc++] = AssetManager::OVERLAY_DIR;
+ }
- if (stat(AssetManager::PRODUCT_OVERLAY_DIR, &st) == 0) {
- argv[argc++] = AssetManager::PRODUCT_OVERLAY_DIR;
- }
+ if (stat(AssetManager::PRODUCT_OVERLAY_DIR, &st) == 0) {
+ argv[argc++] = AssetManager::PRODUCT_OVERLAY_DIR;
+ }
- // Finally, invoke idmap (if any overlay directory exists)
- if (argc > 5) {
- execv(AssetManager::IDMAP_BIN, (char* const*)argv);
- PLOG(ERROR) << "failed to execv for idmap";
- exit(1); // should never get here
- } else {
- exit(0);
- }
- } break;
-
- // parent
- default:
- waitpid(pid, nullptr, 0);
- break;
- }
+ // Finally, invoke idmap (if any overlay directory exists)
+ if (argc > 5) {
+ execv(AssetManager::IDMAP_BIN, (char* const*)argv);
+ ALOGE("failed to execv for idmap: %s", strerror(errno));
+ exit(1); // should never get here
+ } else {
+ exit(0);
+ }
+ }
+ break;
+ default: // parent
+ waitpid(pid, NULL, 0);
+ break;
+ }
}
-static jint CopyValue(JNIEnv* env, ApkAssetsCookie cookie, const Res_value& value, uint32_t ref,
- uint32_t type_spec_flags, ResTable_config* config, jobject out_typed_value) {
- env->SetIntField(out_typed_value, gTypedValueOffsets.mType, value.dataType);
- env->SetIntField(out_typed_value, gTypedValueOffsets.mAssetCookie,
- ApkAssetsCookieToJavaCookie(cookie));
- env->SetIntField(out_typed_value, gTypedValueOffsets.mData, value.data);
- env->SetObjectField(out_typed_value, gTypedValueOffsets.mString, nullptr);
- env->SetIntField(out_typed_value, gTypedValueOffsets.mResourceId, ref);
- env->SetIntField(out_typed_value, gTypedValueOffsets.mChangingConfigurations, type_spec_flags);
- if (config != nullptr) {
- env->SetIntField(out_typed_value, gTypedValueOffsets.mDensity, config->density);
- }
- return static_cast<jint>(ApkAssetsCookieToJavaCookie(cookie));
-}
// ----------------------------------------------------------------------------
-// Let the opaque type AAssetManager refer to a guarded AssetManager2 instance.
-struct GuardedAssetManager : public ::AAssetManager {
- Guarded<AssetManager2> guarded_assetmanager;
-};
-
-::AAssetManager* NdkAssetManagerForJavaObject(JNIEnv* env, jobject jassetmanager) {
- jlong assetmanager_handle = env->GetLongField(jassetmanager, gAssetManagerOffsets.mObject);
- ::AAssetManager* am = reinterpret_cast<::AAssetManager*>(assetmanager_handle);
- if (am == nullptr) {
+// this guy is exported to other jni routines
+AssetManager* assetManagerForJavaObject(JNIEnv* env, jobject obj)
+{
+ jlong amHandle = env->GetLongField(obj, gAssetManagerOffsets.mObject);
+ AssetManager* am = reinterpret_cast<AssetManager*>(amHandle);
+ if (am != NULL) {
+ return am;
+ }
jniThrowException(env, "java/lang/IllegalStateException", "AssetManager has been finalized!");
- return nullptr;
- }
- return am;
+ return NULL;
}
-Guarded<AssetManager2>* AssetManagerForNdkAssetManager(::AAssetManager* assetmanager) {
- if (assetmanager == nullptr) {
- return nullptr;
- }
- return &reinterpret_cast<GuardedAssetManager*>(assetmanager)->guarded_assetmanager;
-}
-
-Guarded<AssetManager2>* AssetManagerForJavaObject(JNIEnv* env, jobject jassetmanager) {
- return AssetManagerForNdkAssetManager(NdkAssetManagerForJavaObject(env, jassetmanager));
-}
-
-static Guarded<AssetManager2>& AssetManagerFromLong(jlong ptr) {
- return *AssetManagerForNdkAssetManager(reinterpret_cast<AAssetManager*>(ptr));
-}
-
-static jobject ReturnParcelFileDescriptor(JNIEnv* env, std::unique_ptr<Asset> asset,
- jlongArray out_offsets) {
- off64_t start_offset, length;
- int fd = asset->openFileDescriptor(&start_offset, &length);
- asset.reset();
-
- if (fd < 0) {
- jniThrowException(env, "java/io/FileNotFoundException",
- "This file can not be opened as a file descriptor; it is probably "
- "compressed");
- return nullptr;
- }
-
- jlong* offsets = reinterpret_cast<jlong*>(env->GetPrimitiveArrayCritical(out_offsets, 0));
- if (offsets == nullptr) {
- close(fd);
- return nullptr;
- }
-
- offsets[0] = start_offset;
- offsets[1] = length;
-
- env->ReleasePrimitiveArrayCritical(out_offsets, offsets, 0);
-
- jobject file_desc = jniCreateFileDescriptor(env, fd);
- if (file_desc == nullptr) {
- close(fd);
- return nullptr;
- }
- return newParcelFileDescriptor(env, file_desc);
-}
-
-static jint NativeGetGlobalAssetCount(JNIEnv* /*env*/, jobject /*clazz*/) {
- return Asset::getGlobalCount();
-}
-
-static jobject NativeGetAssetAllocations(JNIEnv* env, jobject /*clazz*/) {
- String8 alloc = Asset::getAssetAllocations();
- if (alloc.length() <= 0) {
- return nullptr;
- }
- return env->NewStringUTF(alloc.string());
-}
-
-static jint NativeGetGlobalAssetManagerCount(JNIEnv* /*env*/, jobject /*clazz*/) {
- // TODO(adamlesinski): Switch to AssetManager2.
- return AssetManager::getGlobalCount();
-}
-
-static jlong NativeCreate(JNIEnv* /*env*/, jclass /*clazz*/) {
- // AssetManager2 needs to be protected by a lock. To avoid cache misses, we allocate the lock and
- // AssetManager2 in a contiguous block (GuardedAssetManager).
- return reinterpret_cast<jlong>(new GuardedAssetManager());
-}
-
-static void NativeDestroy(JNIEnv* /*env*/, jclass /*clazz*/, jlong ptr) {
- delete reinterpret_cast<GuardedAssetManager*>(ptr);
-}
-
-static void NativeSetApkAssets(JNIEnv* env, jclass /*clazz*/, jlong ptr,
- jobjectArray apk_assets_array, jboolean invalidate_caches) {
- const jsize apk_assets_len = env->GetArrayLength(apk_assets_array);
- std::vector<const ApkAssets*> apk_assets;
- apk_assets.reserve(apk_assets_len);
- for (jsize i = 0; i < apk_assets_len; i++) {
- jobject obj = env->GetObjectArrayElement(apk_assets_array, i);
- if (obj == nullptr) {
- std::string msg = StringPrintf("ApkAssets at index %d is null", i);
- jniThrowNullPointerException(env, msg.c_str());
- return;
+static jlong android_content_AssetManager_openAsset(JNIEnv* env, jobject clazz,
+ jstring fileName, jint mode)
+{
+ AssetManager* am = assetManagerForJavaObject(env, clazz);
+ if (am == NULL) {
+ return 0;
}
- jlong apk_assets_native_ptr = env->GetLongField(obj, gApkAssetsFields.native_ptr);
- if (env->ExceptionCheck()) {
- return;
- }
- apk_assets.push_back(reinterpret_cast<const ApkAssets*>(apk_assets_native_ptr));
- }
+ ALOGV("openAsset in %p (Java object %p)\n", am, clazz);
- ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
- assetmanager->SetApkAssets(apk_assets, invalidate_caches);
-}
-
-static void NativeSetConfiguration(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint mcc, jint mnc,
- jstring locale, jint orientation, jint touchscreen, jint density,
- jint keyboard, jint keyboard_hidden, jint navigation,
- jint screen_width, jint screen_height,
- jint smallest_screen_width_dp, jint screen_width_dp,
- jint screen_height_dp, jint screen_layout, jint ui_mode,
- jint color_mode, jint major_version) {
- ResTable_config configuration;
- memset(&configuration, 0, sizeof(configuration));
- configuration.mcc = static_cast<uint16_t>(mcc);
- configuration.mnc = static_cast<uint16_t>(mnc);
- configuration.orientation = static_cast<uint8_t>(orientation);
- configuration.touchscreen = static_cast<uint8_t>(touchscreen);
- configuration.density = static_cast<uint16_t>(density);
- configuration.keyboard = static_cast<uint8_t>(keyboard);
- configuration.inputFlags = static_cast<uint8_t>(keyboard_hidden);
- configuration.navigation = static_cast<uint8_t>(navigation);
- configuration.screenWidth = static_cast<uint16_t>(screen_width);
- configuration.screenHeight = static_cast<uint16_t>(screen_height);
- configuration.smallestScreenWidthDp = static_cast<uint16_t>(smallest_screen_width_dp);
- configuration.screenWidthDp = static_cast<uint16_t>(screen_width_dp);
- configuration.screenHeightDp = static_cast<uint16_t>(screen_height_dp);
- configuration.screenLayout = static_cast<uint8_t>(screen_layout);
- configuration.uiMode = static_cast<uint8_t>(ui_mode);
- configuration.colorMode = static_cast<uint8_t>(color_mode);
- configuration.sdkVersion = static_cast<uint16_t>(major_version);
-
- if (locale != nullptr) {
- ScopedUtfChars locale_utf8(env, locale);
- CHECK(locale_utf8.c_str() != nullptr);
- configuration.setBcp47Locale(locale_utf8.c_str());
- }
-
- // Constants duplicated from Java class android.content.res.Configuration.
- static const jint kScreenLayoutRoundMask = 0x300;
- static const jint kScreenLayoutRoundShift = 8;
-
- // In Java, we use a 32bit integer for screenLayout, while we only use an 8bit integer
- // in C++. We must extract the round qualifier out of the Java screenLayout and put it
- // into screenLayout2.
- configuration.screenLayout2 =
- static_cast<uint8_t>((screen_layout & kScreenLayoutRoundMask) >> kScreenLayoutRoundShift);
-
- ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
- assetmanager->SetConfiguration(configuration);
-}
-
-static jobject NativeGetAssignedPackageIdentifiers(JNIEnv* env, jclass /*clazz*/, jlong ptr) {
- ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
-
- jobject sparse_array =
- env->NewObject(gSparseArrayOffsets.classObject, gSparseArrayOffsets.constructor);
-
- if (sparse_array == nullptr) {
- // An exception is pending.
- return nullptr;
- }
-
- assetmanager->ForEachPackage([&](const std::string& package_name, uint8_t package_id) {
- jstring jpackage_name = env->NewStringUTF(package_name.c_str());
- if (jpackage_name == nullptr) {
- // An exception is pending.
- return;
+ ScopedUtfChars fileName8(env, fileName);
+ if (fileName8.c_str() == NULL) {
+ jniThrowException(env, "java/lang/IllegalArgumentException", "Empty file name");
+ return -1;
}
- env->CallVoidMethod(sparse_array, gSparseArrayOffsets.put, static_cast<jint>(package_id),
- jpackage_name);
- });
- return sparse_array;
+ if (mode != Asset::ACCESS_UNKNOWN && mode != Asset::ACCESS_RANDOM
+ && mode != Asset::ACCESS_STREAMING && mode != Asset::ACCESS_BUFFER) {
+ jniThrowException(env, "java/lang/IllegalArgumentException", "Bad access mode");
+ return -1;
+ }
+
+ Asset* a = am->open(fileName8.c_str(), (Asset::AccessMode)mode);
+
+ if (a == NULL) {
+ jniThrowException(env, "java/io/FileNotFoundException", fileName8.c_str());
+ return -1;
+ }
+
+ //printf("Created Asset Stream: %p\n", a);
+
+ return reinterpret_cast<jlong>(a);
}
-static jobjectArray NativeList(JNIEnv* env, jclass /*clazz*/, jlong ptr, jstring path) {
- ScopedUtfChars path_utf8(env, path);
- if (path_utf8.c_str() == nullptr) {
- // This will throw NPE.
- return nullptr;
- }
+static jobject returnParcelFileDescriptor(JNIEnv* env, Asset* a, jlongArray outOffsets)
+{
+ off64_t startOffset, length;
+ int fd = a->openFileDescriptor(&startOffset, &length);
+ delete a;
- std::vector<std::string> all_file_paths;
- {
- StringPiece normalized_path = path_utf8.c_str();
- if (normalized_path.data()[0] == '/') {
- normalized_path = normalized_path.substr(1);
+ if (fd < 0) {
+ jniThrowException(env, "java/io/FileNotFoundException",
+ "This file can not be opened as a file descriptor; it is probably compressed");
+ return NULL;
}
- std::string root_path = StringPrintf("assets/%s", normalized_path.data());
- ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
- for (const ApkAssets* assets : assetmanager->GetApkAssets()) {
- assets->ForEachFile(root_path, [&](const StringPiece& file_path, FileType type) {
- if (type == FileType::kFileTypeRegular) {
- all_file_paths.push_back(file_path.to_string());
+
+ jlong* offsets = (jlong*)env->GetPrimitiveArrayCritical(outOffsets, 0);
+ if (offsets == NULL) {
+ close(fd);
+ return NULL;
+ }
+
+ offsets[0] = startOffset;
+ offsets[1] = length;
+
+ env->ReleasePrimitiveArrayCritical(outOffsets, offsets, 0);
+
+ jobject fileDesc = jniCreateFileDescriptor(env, fd);
+ if (fileDesc == NULL) {
+ close(fd);
+ return NULL;
+ }
+
+ return newParcelFileDescriptor(env, fileDesc);
+}
+
+static jobject android_content_AssetManager_openAssetFd(JNIEnv* env, jobject clazz,
+ jstring fileName, jlongArray outOffsets)
+{
+ AssetManager* am = assetManagerForJavaObject(env, clazz);
+ if (am == NULL) {
+ return NULL;
+ }
+
+ ALOGV("openAssetFd in %p (Java object %p)\n", am, clazz);
+
+ ScopedUtfChars fileName8(env, fileName);
+ if (fileName8.c_str() == NULL) {
+ return NULL;
+ }
+
+ Asset* a = am->open(fileName8.c_str(), Asset::ACCESS_RANDOM);
+
+ if (a == NULL) {
+ jniThrowException(env, "java/io/FileNotFoundException", fileName8.c_str());
+ return NULL;
+ }
+
+ //printf("Created Asset Stream: %p\n", a);
+
+ return returnParcelFileDescriptor(env, a, outOffsets);
+}
+
+static jlong android_content_AssetManager_openNonAssetNative(JNIEnv* env, jobject clazz,
+ jint cookie,
+ jstring fileName,
+ jint mode)
+{
+ AssetManager* am = assetManagerForJavaObject(env, clazz);
+ if (am == NULL) {
+ return 0;
+ }
+
+ ALOGV("openNonAssetNative in %p (Java object %p)\n", am, clazz);
+
+ ScopedUtfChars fileName8(env, fileName);
+ if (fileName8.c_str() == NULL) {
+ return -1;
+ }
+
+ if (mode != Asset::ACCESS_UNKNOWN && mode != Asset::ACCESS_RANDOM
+ && mode != Asset::ACCESS_STREAMING && mode != Asset::ACCESS_BUFFER) {
+ jniThrowException(env, "java/lang/IllegalArgumentException", "Bad access mode");
+ return -1;
+ }
+
+ Asset* a = cookie
+ ? am->openNonAsset(static_cast<int32_t>(cookie), fileName8.c_str(),
+ (Asset::AccessMode)mode)
+ : am->openNonAsset(fileName8.c_str(), (Asset::AccessMode)mode);
+
+ if (a == NULL) {
+ jniThrowException(env, "java/io/FileNotFoundException", fileName8.c_str());
+ return -1;
+ }
+
+ //printf("Created Asset Stream: %p\n", a);
+
+ return reinterpret_cast<jlong>(a);
+}
+
+static jobject android_content_AssetManager_openNonAssetFdNative(JNIEnv* env, jobject clazz,
+ jint cookie,
+ jstring fileName,
+ jlongArray outOffsets)
+{
+ AssetManager* am = assetManagerForJavaObject(env, clazz);
+ if (am == NULL) {
+ return NULL;
+ }
+
+ ALOGV("openNonAssetFd in %p (Java object %p)\n", am, clazz);
+
+ ScopedUtfChars fileName8(env, fileName);
+ if (fileName8.c_str() == NULL) {
+ return NULL;
+ }
+
+ Asset* a = cookie
+ ? am->openNonAsset(static_cast<int32_t>(cookie), fileName8.c_str(), Asset::ACCESS_RANDOM)
+ : am->openNonAsset(fileName8.c_str(), Asset::ACCESS_RANDOM);
+
+ if (a == NULL) {
+ jniThrowException(env, "java/io/FileNotFoundException", fileName8.c_str());
+ return NULL;
+ }
+
+ //printf("Created Asset Stream: %p\n", a);
+
+ return returnParcelFileDescriptor(env, a, outOffsets);
+}
+
+static jobjectArray android_content_AssetManager_list(JNIEnv* env, jobject clazz,
+ jstring fileName)
+{
+ AssetManager* am = assetManagerForJavaObject(env, clazz);
+ if (am == NULL) {
+ return NULL;
+ }
+
+ ScopedUtfChars fileName8(env, fileName);
+ if (fileName8.c_str() == NULL) {
+ return NULL;
+ }
+
+ AssetDir* dir = am->openDir(fileName8.c_str());
+
+ if (dir == NULL) {
+ jniThrowException(env, "java/io/FileNotFoundException", fileName8.c_str());
+ return NULL;
+ }
+
+ size_t N = dir->getFileCount();
+
+ jobjectArray array = env->NewObjectArray(dir->getFileCount(),
+ g_stringClass, NULL);
+ if (array == NULL) {
+ delete dir;
+ return NULL;
+ }
+
+ for (size_t i=0; i<N; i++) {
+ const String8& name = dir->getFileName(i);
+ jstring str = env->NewStringUTF(name.string());
+ if (str == NULL) {
+ delete dir;
+ return NULL;
}
- });
- }
- }
-
- jobjectArray array = env->NewObjectArray(all_file_paths.size(), g_stringClass, nullptr);
- if (array == nullptr) {
- return nullptr;
- }
-
- jsize index = 0;
- for (const std::string& file_path : all_file_paths) {
- jstring java_string = env->NewStringUTF(file_path.c_str());
-
- // Check for errors creating the strings (if malformed or no memory).
- if (env->ExceptionCheck()) {
- return nullptr;
+ env->SetObjectArrayElement(array, i, str);
+ env->DeleteLocalRef(str);
}
- env->SetObjectArrayElement(array, index++, java_string);
+ delete dir;
- // If we have a large amount of string in our array, we might overflow the
- // local reference table of the VM.
- env->DeleteLocalRef(java_string);
- }
- return array;
+ return array;
}
-static jlong NativeOpenAsset(JNIEnv* env, jclass /*clazz*/, jlong ptr, jstring asset_path,
- jint access_mode) {
- ScopedUtfChars asset_path_utf8(env, asset_path);
- if (asset_path_utf8.c_str() == nullptr) {
- // This will throw NPE.
- return 0;
- }
+static void android_content_AssetManager_destroyAsset(JNIEnv* env, jobject clazz,
+ jlong assetHandle)
+{
+ Asset* a = reinterpret_cast<Asset*>(assetHandle);
- if (access_mode != Asset::ACCESS_UNKNOWN && access_mode != Asset::ACCESS_RANDOM &&
- access_mode != Asset::ACCESS_STREAMING && access_mode != Asset::ACCESS_BUFFER) {
- jniThrowException(env, "java/lang/IllegalArgumentException", "Bad access mode");
- return 0;
- }
+ //printf("Destroying Asset Stream: %p\n", a);
- ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
- std::unique_ptr<Asset> asset =
- assetmanager->Open(asset_path_utf8.c_str(), static_cast<Asset::AccessMode>(access_mode));
- if (!asset) {
- jniThrowException(env, "java/io/FileNotFoundException", asset_path_utf8.c_str());
- return 0;
- }
- return reinterpret_cast<jlong>(asset.release());
-}
-
-static jobject NativeOpenAssetFd(JNIEnv* env, jclass /*clazz*/, jlong ptr, jstring asset_path,
- jlongArray out_offsets) {
- ScopedUtfChars asset_path_utf8(env, asset_path);
- if (asset_path_utf8.c_str() == nullptr) {
- // This will throw NPE.
- return nullptr;
- }
-
- ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
- std::unique_ptr<Asset> asset = assetmanager->Open(asset_path_utf8.c_str(), Asset::ACCESS_RANDOM);
- if (!asset) {
- jniThrowException(env, "java/io/FileNotFoundException", asset_path_utf8.c_str());
- return nullptr;
- }
- return ReturnParcelFileDescriptor(env, std::move(asset), out_offsets);
-}
-
-static jlong NativeOpenNonAsset(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint jcookie,
- jstring asset_path, jint access_mode) {
- ApkAssetsCookie cookie = JavaCookieToApkAssetsCookie(jcookie);
- ScopedUtfChars asset_path_utf8(env, asset_path);
- if (asset_path_utf8.c_str() == nullptr) {
- // This will throw NPE.
- return 0;
- }
-
- if (access_mode != Asset::ACCESS_UNKNOWN && access_mode != Asset::ACCESS_RANDOM &&
- access_mode != Asset::ACCESS_STREAMING && access_mode != Asset::ACCESS_BUFFER) {
- jniThrowException(env, "java/lang/IllegalArgumentException", "Bad access mode");
- return 0;
- }
-
- ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
- std::unique_ptr<Asset> asset;
- if (cookie != kInvalidCookie) {
- asset = assetmanager->OpenNonAsset(asset_path_utf8.c_str(), cookie,
- static_cast<Asset::AccessMode>(access_mode));
- } else {
- asset = assetmanager->OpenNonAsset(asset_path_utf8.c_str(),
- static_cast<Asset::AccessMode>(access_mode));
- }
-
- if (!asset) {
- jniThrowException(env, "java/io/FileNotFoundException", asset_path_utf8.c_str());
- return 0;
- }
- return reinterpret_cast<jlong>(asset.release());
-}
-
-static jobject NativeOpenNonAssetFd(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint jcookie,
- jstring asset_path, jlongArray out_offsets) {
- ApkAssetsCookie cookie = JavaCookieToApkAssetsCookie(jcookie);
- ScopedUtfChars asset_path_utf8(env, asset_path);
- if (asset_path_utf8.c_str() == nullptr) {
- // This will throw NPE.
- return nullptr;
- }
-
- ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
- std::unique_ptr<Asset> asset;
- if (cookie != kInvalidCookie) {
- asset = assetmanager->OpenNonAsset(asset_path_utf8.c_str(), cookie, Asset::ACCESS_RANDOM);
- } else {
- asset = assetmanager->OpenNonAsset(asset_path_utf8.c_str(), Asset::ACCESS_RANDOM);
- }
-
- if (!asset) {
- jniThrowException(env, "java/io/FileNotFoundException", asset_path_utf8.c_str());
- return nullptr;
- }
- return ReturnParcelFileDescriptor(env, std::move(asset), out_offsets);
-}
-
-static jlong NativeOpenXmlAsset(JNIEnv* env, jobject /*clazz*/, jlong ptr, jint jcookie,
- jstring asset_path) {
- ApkAssetsCookie cookie = JavaCookieToApkAssetsCookie(jcookie);
- ScopedUtfChars asset_path_utf8(env, asset_path);
- if (asset_path_utf8.c_str() == nullptr) {
- // This will throw NPE.
- return 0;
- }
-
- ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
- std::unique_ptr<Asset> asset;
- if (cookie != kInvalidCookie) {
- asset = assetmanager->OpenNonAsset(asset_path_utf8.c_str(), cookie, Asset::ACCESS_RANDOM);
- } else {
- asset = assetmanager->OpenNonAsset(asset_path_utf8.c_str(), Asset::ACCESS_RANDOM, &cookie);
- }
-
- if (!asset) {
- jniThrowException(env, "java/io/FileNotFoundException", asset_path_utf8.c_str());
- return 0;
- }
-
- // May be nullptr.
- const DynamicRefTable* dynamic_ref_table = assetmanager->GetDynamicRefTableForCookie(cookie);
-
- std::unique_ptr<ResXMLTree> xml_tree = util::make_unique<ResXMLTree>(dynamic_ref_table);
- status_t err = xml_tree->setTo(asset->getBuffer(true), asset->getLength(), true);
- asset.reset();
-
- if (err != NO_ERROR) {
- jniThrowException(env, "java/io/FileNotFoundException", "Corrupt XML binary file");
- return 0;
- }
- return reinterpret_cast<jlong>(xml_tree.release());
-}
-
-static jint NativeGetResourceValue(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid,
- jshort density, jobject typed_value,
- jboolean resolve_references) {
- ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
- Res_value value;
- ResTable_config selected_config;
- uint32_t flags;
- ApkAssetsCookie cookie =
- assetmanager->GetResource(static_cast<uint32_t>(resid), false /*may_be_bag*/,
- static_cast<uint16_t>(density), &value, &selected_config, &flags);
- if (cookie == kInvalidCookie) {
- return ApkAssetsCookieToJavaCookie(kInvalidCookie);
- }
-
- uint32_t ref = static_cast<uint32_t>(resid);
- if (resolve_references) {
- cookie = assetmanager->ResolveReference(cookie, &value, &selected_config, &flags, &ref);
- if (cookie == kInvalidCookie) {
- return ApkAssetsCookieToJavaCookie(kInvalidCookie);
- }
- }
- return CopyValue(env, cookie, value, ref, flags, &selected_config, typed_value);
-}
-
-static jint NativeGetResourceBagValue(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid,
- jint bag_entry_id, jobject typed_value) {
- ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
- const ResolvedBag* bag = assetmanager->GetBag(static_cast<uint32_t>(resid));
- if (bag == nullptr) {
- return ApkAssetsCookieToJavaCookie(kInvalidCookie);
- }
-
- uint32_t type_spec_flags = bag->type_spec_flags;
- ApkAssetsCookie cookie = kInvalidCookie;
- const Res_value* bag_value = nullptr;
- for (const ResolvedBag::Entry& entry : bag) {
- if (entry.key == static_cast<uint32_t>(bag_entry_id)) {
- cookie = entry.cookie;
- bag_value = &entry.value;
-
- // Keep searching (the old implementation did that).
- }
- }
-
- if (cookie == kInvalidCookie) {
- return ApkAssetsCookieToJavaCookie(kInvalidCookie);
- }
-
- Res_value value = *bag_value;
- uint32_t ref = static_cast<uint32_t>(resid);
- ResTable_config selected_config;
- cookie = assetmanager->ResolveReference(cookie, &value, &selected_config, &type_spec_flags, &ref);
- if (cookie == kInvalidCookie) {
- return ApkAssetsCookieToJavaCookie(kInvalidCookie);
- }
- return CopyValue(env, cookie, value, ref, type_spec_flags, nullptr, typed_value);
-}
-
-static jintArray NativeGetStyleAttributes(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid) {
- ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
- const ResolvedBag* bag = assetmanager->GetBag(static_cast<uint32_t>(resid));
- if (bag == nullptr) {
- return nullptr;
- }
-
- jintArray array = env->NewIntArray(bag->entry_count);
- if (env->ExceptionCheck()) {
- return nullptr;
- }
-
- for (uint32_t i = 0; i < bag->entry_count; i++) {
- jint attr_resid = bag->entries[i].key;
- env->SetIntArrayRegion(array, i, 1, &attr_resid);
- }
- return array;
-}
-
-static jobjectArray NativeGetResourceStringArray(JNIEnv* env, jclass /*clazz*/, jlong ptr,
- jint resid) {
- ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
- const ResolvedBag* bag = assetmanager->GetBag(static_cast<uint32_t>(resid));
- if (bag == nullptr) {
- return nullptr;
- }
-
- jobjectArray array = env->NewObjectArray(bag->entry_count, g_stringClass, nullptr);
- if (array == nullptr) {
- return nullptr;
- }
-
- for (uint32_t i = 0; i < bag->entry_count; i++) {
- const ResolvedBag::Entry& entry = bag->entries[i];
-
- // Resolve any references to their final value.
- Res_value value = entry.value;
- ResTable_config selected_config;
- uint32_t flags;
- uint32_t ref;
- ApkAssetsCookie cookie =
- assetmanager->ResolveReference(entry.cookie, &value, &selected_config, &flags, &ref);
- if (cookie == kInvalidCookie) {
- return nullptr;
+ if (a == NULL) {
+ jniThrowNullPointerException(env, "asset");
+ return;
}
- if (value.dataType == Res_value::TYPE_STRING) {
- const ApkAssets* apk_assets = assetmanager->GetApkAssets()[cookie];
- const ResStringPool* pool = apk_assets->GetLoadedArsc()->GetStringPool();
-
- jstring java_string = nullptr;
- size_t str_len;
- const char* str_utf8 = pool->string8At(value.data, &str_len);
- if (str_utf8 != nullptr) {
- java_string = env->NewStringUTF(str_utf8);
- } else {
- const char16_t* str_utf16 = pool->stringAt(value.data, &str_len);
- java_string = env->NewString(reinterpret_cast<const jchar*>(str_utf16), str_len);
- }
-
- // Check for errors creating the strings (if malformed or no memory).
- if (env->ExceptionCheck()) {
- return nullptr;
- }
-
- env->SetObjectArrayElement(array, i, java_string);
-
- // If we have a large amount of string in our array, we might overflow the
- // local reference table of the VM.
- env->DeleteLocalRef(java_string);
- }
- }
- return array;
+ delete a;
}
-static jintArray NativeGetResourceStringArrayInfo(JNIEnv* env, jclass /*clazz*/, jlong ptr,
- jint resid) {
- ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
- const ResolvedBag* bag = assetmanager->GetBag(static_cast<uint32_t>(resid));
- if (bag == nullptr) {
- return nullptr;
- }
+static jint android_content_AssetManager_readAssetChar(JNIEnv* env, jobject clazz,
+ jlong assetHandle)
+{
+ Asset* a = reinterpret_cast<Asset*>(assetHandle);
- jintArray array = env->NewIntArray(bag->entry_count * 2);
- if (array == nullptr) {
- return nullptr;
- }
-
- jint* buffer = reinterpret_cast<jint*>(env->GetPrimitiveArrayCritical(array, nullptr));
- if (buffer == nullptr) {
- return nullptr;
- }
-
- for (size_t i = 0; i < bag->entry_count; i++) {
- const ResolvedBag::Entry& entry = bag->entries[i];
- Res_value value = entry.value;
- ResTable_config selected_config;
- uint32_t flags;
- uint32_t ref;
- ApkAssetsCookie cookie =
- assetmanager->ResolveReference(entry.cookie, &value, &selected_config, &flags, &ref);
- if (cookie == kInvalidCookie) {
- env->ReleasePrimitiveArrayCritical(array, buffer, JNI_ABORT);
- return nullptr;
+ if (a == NULL) {
+ jniThrowNullPointerException(env, "asset");
+ return -1;
}
- jint string_index = -1;
- if (value.dataType == Res_value::TYPE_STRING) {
- string_index = static_cast<jint>(value.data);
- }
-
- buffer[i * 2] = ApkAssetsCookieToJavaCookie(cookie);
- buffer[(i * 2) + 1] = string_index;
- }
- env->ReleasePrimitiveArrayCritical(array, buffer, 0);
- return array;
+ uint8_t b;
+ ssize_t res = a->read(&b, 1);
+ return res == 1 ? b : -1;
}
-static jintArray NativeGetResourceIntArray(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid) {
- ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
- const ResolvedBag* bag = assetmanager->GetBag(static_cast<uint32_t>(resid));
- if (bag == nullptr) {
- return nullptr;
- }
+static jint android_content_AssetManager_readAsset(JNIEnv* env, jobject clazz,
+ jlong assetHandle, jbyteArray bArray,
+ jint off, jint len)
+{
+ Asset* a = reinterpret_cast<Asset*>(assetHandle);
- jintArray array = env->NewIntArray(bag->entry_count);
- if (array == nullptr) {
- return nullptr;
- }
-
- jint* buffer = reinterpret_cast<jint*>(env->GetPrimitiveArrayCritical(array, nullptr));
- if (buffer == nullptr) {
- return nullptr;
- }
-
- for (size_t i = 0; i < bag->entry_count; i++) {
- const ResolvedBag::Entry& entry = bag->entries[i];
- Res_value value = entry.value;
- ResTable_config selected_config;
- uint32_t flags;
- uint32_t ref;
- ApkAssetsCookie cookie =
- assetmanager->ResolveReference(entry.cookie, &value, &selected_config, &flags, &ref);
- if (cookie == kInvalidCookie) {
- env->ReleasePrimitiveArrayCritical(array, buffer, JNI_ABORT);
- return nullptr;
+ if (a == NULL || bArray == NULL) {
+ jniThrowNullPointerException(env, "asset");
+ return -1;
}
- if (value.dataType >= Res_value::TYPE_FIRST_INT && value.dataType <= Res_value::TYPE_LAST_INT) {
- buffer[i] = static_cast<jint>(value.data);
+ if (len == 0) {
+ return 0;
}
- }
- env->ReleasePrimitiveArrayCritical(array, buffer, 0);
- return array;
-}
-static jint NativeGetResourceArraySize(JNIEnv* /*env*/, jclass /*clazz*/, jlong ptr, jint resid) {
- ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
- const ResolvedBag* bag = assetmanager->GetBag(static_cast<uint32_t>(resid));
- if (bag == nullptr) {
+ jsize bLen = env->GetArrayLength(bArray);
+ if (off < 0 || off >= bLen || len < 0 || len > bLen || (off+len) > bLen) {
+ jniThrowException(env, "java/lang/IndexOutOfBoundsException", "");
+ return -1;
+ }
+
+ jbyte* b = env->GetByteArrayElements(bArray, NULL);
+ ssize_t res = a->read(b+off, len);
+ env->ReleaseByteArrayElements(bArray, b, 0);
+
+ if (res > 0) return static_cast<jint>(res);
+
+ if (res < 0) {
+ jniThrowException(env, "java/io/IOException", "");
+ }
return -1;
- }
- return static_cast<jint>(bag->entry_count);
}
-static jint NativeGetResourceArray(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid,
- jintArray out_data) {
- ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
- const ResolvedBag* bag = assetmanager->GetBag(static_cast<uint32_t>(resid));
- if (bag == nullptr) {
- return -1;
- }
+static jlong android_content_AssetManager_seekAsset(JNIEnv* env, jobject clazz,
+ jlong assetHandle,
+ jlong offset, jint whence)
+{
+ Asset* a = reinterpret_cast<Asset*>(assetHandle);
- const jsize out_data_length = env->GetArrayLength(out_data);
- if (env->ExceptionCheck()) {
- return -1;
- }
-
- if (static_cast<jsize>(bag->entry_count) > out_data_length * STYLE_NUM_ENTRIES) {
- jniThrowException(env, "java/lang/IllegalArgumentException", "Input array is not large enough");
- return -1;
- }
-
- jint* buffer = reinterpret_cast<jint*>(env->GetPrimitiveArrayCritical(out_data, nullptr));
- if (buffer == nullptr) {
- return -1;
- }
-
- jint* cursor = buffer;
- for (size_t i = 0; i < bag->entry_count; i++) {
- const ResolvedBag::Entry& entry = bag->entries[i];
- Res_value value = entry.value;
- ResTable_config selected_config;
- selected_config.density = 0;
- uint32_t flags = bag->type_spec_flags;
- uint32_t ref;
- ApkAssetsCookie cookie =
- assetmanager->ResolveReference(entry.cookie, &value, &selected_config, &flags, &ref);
- if (cookie == kInvalidCookie) {
- env->ReleasePrimitiveArrayCritical(out_data, buffer, JNI_ABORT);
- return -1;
+ if (a == NULL) {
+ jniThrowNullPointerException(env, "asset");
+ return -1;
}
- // Deal with the special @null value -- it turns back to TYPE_NULL.
- if (value.dataType == Res_value::TYPE_REFERENCE && value.data == 0) {
- value.dataType = Res_value::TYPE_NULL;
- value.data = Res_value::DATA_NULL_UNDEFINED;
+ return a->seek(
+ offset, (whence > 0) ? SEEK_END : (whence < 0 ? SEEK_SET : SEEK_CUR));
+}
+
+static jlong android_content_AssetManager_getAssetLength(JNIEnv* env, jobject clazz,
+ jlong assetHandle)
+{
+ Asset* a = reinterpret_cast<Asset*>(assetHandle);
+
+ if (a == NULL) {
+ jniThrowNullPointerException(env, "asset");
+ return -1;
}
- cursor[STYLE_TYPE] = static_cast<jint>(value.dataType);
- cursor[STYLE_DATA] = static_cast<jint>(value.data);
- cursor[STYLE_ASSET_COOKIE] = ApkAssetsCookieToJavaCookie(cookie);
- cursor[STYLE_RESOURCE_ID] = static_cast<jint>(ref);
- cursor[STYLE_CHANGING_CONFIGURATIONS] = static_cast<jint>(flags);
- cursor[STYLE_DENSITY] = static_cast<jint>(selected_config.density);
- cursor += STYLE_NUM_ENTRIES;
- }
- env->ReleasePrimitiveArrayCritical(out_data, buffer, 0);
- return static_cast<jint>(bag->entry_count);
+ return a->getLength();
}
-static jint NativeGetResourceIdentifier(JNIEnv* env, jclass /*clazz*/, jlong ptr, jstring name,
- jstring def_type, jstring def_package) {
- ScopedUtfChars name_utf8(env, name);
- if (name_utf8.c_str() == nullptr) {
- // This will throw NPE.
- return 0;
- }
+static jlong android_content_AssetManager_getAssetRemainingLength(JNIEnv* env, jobject clazz,
+ jlong assetHandle)
+{
+ Asset* a = reinterpret_cast<Asset*>(assetHandle);
- std::string type;
- if (def_type != nullptr) {
- ScopedUtfChars type_utf8(env, def_type);
- CHECK(type_utf8.c_str() != nullptr);
- type = type_utf8.c_str();
- }
-
- std::string package;
- if (def_package != nullptr) {
- ScopedUtfChars package_utf8(env, def_package);
- CHECK(package_utf8.c_str() != nullptr);
- package = package_utf8.c_str();
- }
- ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
- return static_cast<jint>(assetmanager->GetResourceId(name_utf8.c_str(), type, package));
-}
-
-static jstring NativeGetResourceName(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid) {
- ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
- AssetManager2::ResourceName name;
- if (!assetmanager->GetResourceName(static_cast<uint32_t>(resid), &name)) {
- return nullptr;
- }
-
- std::string result;
- if (name.package != nullptr) {
- result.append(name.package, name.package_len);
- }
-
- if (name.type != nullptr || name.type16 != nullptr) {
- if (!result.empty()) {
- result += ":";
+ if (a == NULL) {
+ jniThrowNullPointerException(env, "asset");
+ return -1;
}
- if (name.type != nullptr) {
- result.append(name.type, name.type_len);
- } else {
- result += util::Utf16ToUtf8(StringPiece16(name.type16, name.type_len));
- }
- }
+ return a->getRemainingLength();
+}
- if (name.entry != nullptr || name.entry16 != nullptr) {
- if (!result.empty()) {
- result += "/";
+static jint android_content_AssetManager_addAssetPath(JNIEnv* env, jobject clazz,
+ jstring path, jboolean appAsLib)
+{
+ ScopedUtfChars path8(env, path);
+ if (path8.c_str() == NULL) {
+ return 0;
}
- if (name.entry != nullptr) {
- result.append(name.entry, name.entry_len);
- } else {
- result += util::Utf16ToUtf8(StringPiece16(name.entry16, name.entry_len));
- }
- }
- return env->NewStringUTF(result.c_str());
-}
-
-static jstring NativeGetResourcePackageName(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid) {
- ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
- AssetManager2::ResourceName name;
- if (!assetmanager->GetResourceName(static_cast<uint32_t>(resid), &name)) {
- return nullptr;
- }
-
- if (name.package != nullptr) {
- return env->NewStringUTF(name.package);
- }
- return nullptr;
-}
-
-static jstring NativeGetResourceTypeName(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid) {
- ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
- AssetManager2::ResourceName name;
- if (!assetmanager->GetResourceName(static_cast<uint32_t>(resid), &name)) {
- return nullptr;
- }
-
- if (name.type != nullptr) {
- return env->NewStringUTF(name.type);
- } else if (name.type16 != nullptr) {
- return env->NewString(reinterpret_cast<const jchar*>(name.type16), name.type_len);
- }
- return nullptr;
-}
-
-static jstring NativeGetResourceEntryName(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid) {
- ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
- AssetManager2::ResourceName name;
- if (!assetmanager->GetResourceName(static_cast<uint32_t>(resid), &name)) {
- return nullptr;
- }
-
- if (name.entry != nullptr) {
- return env->NewStringUTF(name.entry);
- } else if (name.entry16 != nullptr) {
- return env->NewString(reinterpret_cast<const jchar*>(name.entry16), name.entry_len);
- }
- return nullptr;
-}
-
-static jobjectArray NativeGetLocales(JNIEnv* env, jclass /*class*/, jlong ptr,
- jboolean exclude_system) {
- ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
- std::set<std::string> locales =
- assetmanager->GetResourceLocales(exclude_system, true /*merge_equivalent_languages*/);
-
- jobjectArray array = env->NewObjectArray(locales.size(), g_stringClass, nullptr);
- if (array == nullptr) {
- return nullptr;
- }
-
- size_t idx = 0;
- for (const std::string& locale : locales) {
- jstring java_string = env->NewStringUTF(locale.c_str());
- if (java_string == nullptr) {
- return nullptr;
- }
- env->SetObjectArrayElement(array, idx++, java_string);
- env->DeleteLocalRef(java_string);
- }
- return array;
-}
-
-static jobject ConstructConfigurationObject(JNIEnv* env, const ResTable_config& config) {
- jobject result =
- env->NewObject(gConfigurationOffsets.classObject, gConfigurationOffsets.constructor);
- if (result == nullptr) {
- return nullptr;
- }
-
- env->SetIntField(result, gConfigurationOffsets.mSmallestScreenWidthDpOffset,
- config.smallestScreenWidthDp);
- env->SetIntField(result, gConfigurationOffsets.mScreenWidthDpOffset, config.screenWidthDp);
- env->SetIntField(result, gConfigurationOffsets.mScreenHeightDpOffset, config.screenHeightDp);
- return result;
-}
-
-static jobjectArray NativeGetSizeConfigurations(JNIEnv* env, jclass /*clazz*/, jlong ptr) {
- ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
- std::set<ResTable_config> configurations =
- assetmanager->GetResourceConfigurations(true /*exclude_system*/, false /*exclude_mipmap*/);
-
- jobjectArray array =
- env->NewObjectArray(configurations.size(), gConfigurationOffsets.classObject, nullptr);
- if (array == nullptr) {
- return nullptr;
- }
-
- size_t idx = 0;
- for (const ResTable_config& configuration : configurations) {
- jobject java_configuration = ConstructConfigurationObject(env, configuration);
- if (java_configuration == nullptr) {
- return nullptr;
+ AssetManager* am = assetManagerForJavaObject(env, clazz);
+ if (am == NULL) {
+ return 0;
}
- env->SetObjectArrayElement(array, idx++, java_configuration);
- env->DeleteLocalRef(java_configuration);
- }
- return array;
+ int32_t cookie;
+ bool res = am->addAssetPath(String8(path8.c_str()), &cookie, appAsLib);
+
+ return (res) ? static_cast<jint>(cookie) : 0;
}
-static void NativeApplyStyle(JNIEnv* env, jclass /*clazz*/, jlong ptr, jlong theme_ptr,
- jint def_style_attr, jint def_style_resid, jlong xml_parser_ptr,
- jintArray java_attrs, jlong out_values_ptr, jlong out_indices_ptr) {
- ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
- Theme* theme = reinterpret_cast<Theme*>(theme_ptr);
- CHECK(theme->GetAssetManager() == &(*assetmanager));
- (void) assetmanager;
+static jint android_content_AssetManager_addOverlayPath(JNIEnv* env, jobject clazz,
+ jstring idmapPath)
+{
+ ScopedUtfChars idmapPath8(env, idmapPath);
+ if (idmapPath8.c_str() == NULL) {
+ return 0;
+ }
- ResXMLParser* xml_parser = reinterpret_cast<ResXMLParser*>(xml_parser_ptr);
- uint32_t* out_values = reinterpret_cast<uint32_t*>(out_values_ptr);
- uint32_t* out_indices = reinterpret_cast<uint32_t*>(out_indices_ptr);
+ AssetManager* am = assetManagerForJavaObject(env, clazz);
+ if (am == NULL) {
+ return 0;
+ }
- jsize attrs_len = env->GetArrayLength(java_attrs);
- jint* attrs = reinterpret_cast<jint*>(env->GetPrimitiveArrayCritical(java_attrs, nullptr));
- if (attrs == nullptr) {
- return;
- }
+ int32_t cookie;
+ bool res = am->addOverlayPath(String8(idmapPath8.c_str()), &cookie);
- ApplyStyle(theme, xml_parser, static_cast<uint32_t>(def_style_attr),
- static_cast<uint32_t>(def_style_resid), reinterpret_cast<uint32_t*>(attrs), attrs_len,
- out_values, out_indices);
- env->ReleasePrimitiveArrayCritical(java_attrs, attrs, JNI_ABORT);
+ return (res) ? (jint)cookie : 0;
}
-static jboolean NativeResolveAttrs(JNIEnv* env, jclass /*clazz*/, jlong ptr, jlong theme_ptr,
- jint def_style_attr, jint def_style_resid, jintArray java_values,
- jintArray java_attrs, jintArray out_java_values,
- jintArray out_java_indices) {
- const jsize attrs_len = env->GetArrayLength(java_attrs);
- const jsize out_values_len = env->GetArrayLength(out_java_values);
- if (out_values_len < (attrs_len * STYLE_NUM_ENTRIES)) {
- jniThrowException(env, "java/lang/IndexOutOfBoundsException", "outValues too small");
- return JNI_FALSE;
- }
+static jint android_content_AssetManager_addAssetFd(JNIEnv* env, jobject clazz,
+ jobject fileDescriptor, jstring debugPathName,
+ jboolean appAsLib)
+{
+ ScopedUtfChars debugPathName8(env, debugPathName);
- jint* attrs = reinterpret_cast<jint*>(env->GetPrimitiveArrayCritical(java_attrs, nullptr));
- if (attrs == nullptr) {
- return JNI_FALSE;
- }
-
- jint* values = nullptr;
- jsize values_len = 0;
- if (java_values != nullptr) {
- values_len = env->GetArrayLength(java_values);
- values = reinterpret_cast<jint*>(env->GetPrimitiveArrayCritical(java_values, nullptr));
- if (values == nullptr) {
- env->ReleasePrimitiveArrayCritical(java_attrs, attrs, JNI_ABORT);
- return JNI_FALSE;
+ int fd = jniGetFDFromFileDescriptor(env, fileDescriptor);
+ if (fd < 0) {
+ jniThrowException(env, "java/lang/IllegalArgumentException", "Bad FileDescriptor");
+ return 0;
}
- }
- jint* out_values =
- reinterpret_cast<jint*>(env->GetPrimitiveArrayCritical(out_java_values, nullptr));
- if (out_values == nullptr) {
- env->ReleasePrimitiveArrayCritical(java_attrs, attrs, JNI_ABORT);
- if (values != nullptr) {
- env->ReleasePrimitiveArrayCritical(java_values, values, JNI_ABORT);
+ AssetManager* am = assetManagerForJavaObject(env, clazz);
+ if (am == NULL) {
+ return 0;
}
- return JNI_FALSE;
- }
- jint* out_indices = nullptr;
- if (out_java_indices != nullptr) {
- jsize out_indices_len = env->GetArrayLength(out_java_indices);
- if (out_indices_len > attrs_len) {
- out_indices =
- reinterpret_cast<jint*>(env->GetPrimitiveArrayCritical(out_java_indices, nullptr));
- if (out_indices == nullptr) {
- env->ReleasePrimitiveArrayCritical(java_attrs, attrs, JNI_ABORT);
- if (values != nullptr) {
- env->ReleasePrimitiveArrayCritical(java_values, values, JNI_ABORT);
+ int dupfd = ::dup(fd);
+ if (dupfd < 0) {
+ jniThrowIOException(env, errno);
+ return 0;
+ }
+
+ int32_t cookie;
+ bool res = am->addAssetFd(dupfd, String8(debugPathName8.c_str()), &cookie, appAsLib);
+
+ return (res) ? static_cast<jint>(cookie) : 0;
+}
+
+static jboolean android_content_AssetManager_isUpToDate(JNIEnv* env, jobject clazz)
+{
+ AssetManager* am = assetManagerForJavaObject(env, clazz);
+ if (am == NULL) {
+ return JNI_TRUE;
+ }
+ return am->isUpToDate() ? JNI_TRUE : JNI_FALSE;
+}
+
+static jobjectArray getLocales(JNIEnv* env, jobject clazz, bool includeSystemLocales)
+{
+ Vector<String8> locales;
+
+ AssetManager* am = assetManagerForJavaObject(env, clazz);
+ if (am == NULL) {
+ return NULL;
+ }
+
+ am->getLocales(&locales, includeSystemLocales);
+
+ const int N = locales.size();
+
+ jobjectArray result = env->NewObjectArray(N, g_stringClass, NULL);
+ if (result == NULL) {
+ return NULL;
+ }
+
+ for (int i=0; i<N; i++) {
+ jstring str = env->NewStringUTF(locales[i].string());
+ if (str == NULL) {
+ return NULL;
}
- env->ReleasePrimitiveArrayCritical(out_java_values, out_values, JNI_ABORT);
+ env->SetObjectArrayElement(result, i, str);
+ env->DeleteLocalRef(str);
+ }
+
+ return result;
+}
+
+static jobjectArray android_content_AssetManager_getLocales(JNIEnv* env, jobject clazz)
+{
+ return getLocales(env, clazz, true /* include system locales */);
+}
+
+static jobjectArray android_content_AssetManager_getNonSystemLocales(JNIEnv* env, jobject clazz)
+{
+ return getLocales(env, clazz, false /* don't include system locales */);
+}
+
+static jobject constructConfigurationObject(JNIEnv* env, const ResTable_config& config) {
+ jobject result = env->NewObject(gConfigurationOffsets.classObject,
+ gConfigurationOffsets.constructor);
+ if (result == NULL) {
+ return NULL;
+ }
+
+ env->SetIntField(result, gConfigurationOffsets.mSmallestScreenWidthDpOffset,
+ config.smallestScreenWidthDp);
+ env->SetIntField(result, gConfigurationOffsets.mScreenWidthDpOffset, config.screenWidthDp);
+ env->SetIntField(result, gConfigurationOffsets.mScreenHeightDpOffset, config.screenHeightDp);
+
+ return result;
+}
+
+static jobjectArray getSizeConfigurationsInternal(JNIEnv* env,
+ const Vector<ResTable_config>& configs) {
+ const int N = configs.size();
+ jobjectArray result = env->NewObjectArray(N, gConfigurationOffsets.classObject, NULL);
+ if (result == NULL) {
+ return NULL;
+ }
+
+ for (int i=0; i<N; i++) {
+ jobject config = constructConfigurationObject(env, configs[i]);
+ if (config == NULL) {
+ env->DeleteLocalRef(result);
+ return NULL;
+ }
+
+ env->SetObjectArrayElement(result, i, config);
+ env->DeleteLocalRef(config);
+ }
+
+ return result;
+}
+
+static jobjectArray android_content_AssetManager_getSizeConfigurations(JNIEnv* env, jobject clazz) {
+ AssetManager* am = assetManagerForJavaObject(env, clazz);
+ if (am == NULL) {
+ return NULL;
+ }
+
+ const ResTable& res(am->getResources());
+ Vector<ResTable_config> configs;
+ res.getConfigurations(&configs, false /* ignoreMipmap */, true /* ignoreAndroidPackage */);
+
+ return getSizeConfigurationsInternal(env, configs);
+}
+
+static void android_content_AssetManager_setConfiguration(JNIEnv* env, jobject clazz,
+ jint mcc, jint mnc,
+ jstring locale, jint orientation,
+ jint touchscreen, jint density,
+ jint keyboard, jint keyboardHidden,
+ jint navigation,
+ jint screenWidth, jint screenHeight,
+ jint smallestScreenWidthDp,
+ jint screenWidthDp, jint screenHeightDp,
+ jint screenLayout, jint uiMode,
+ jint colorMode, jint sdkVersion)
+{
+ AssetManager* am = assetManagerForJavaObject(env, clazz);
+ if (am == NULL) {
+ return;
+ }
+
+ ResTable_config config;
+ memset(&config, 0, sizeof(config));
+
+ const char* locale8 = locale != NULL ? env->GetStringUTFChars(locale, NULL) : NULL;
+
+ // Constants duplicated from Java class android.content.res.Configuration.
+ static const jint kScreenLayoutRoundMask = 0x300;
+ static const jint kScreenLayoutRoundShift = 8;
+
+ config.mcc = (uint16_t)mcc;
+ config.mnc = (uint16_t)mnc;
+ config.orientation = (uint8_t)orientation;
+ config.touchscreen = (uint8_t)touchscreen;
+ config.density = (uint16_t)density;
+ config.keyboard = (uint8_t)keyboard;
+ config.inputFlags = (uint8_t)keyboardHidden;
+ config.navigation = (uint8_t)navigation;
+ config.screenWidth = (uint16_t)screenWidth;
+ config.screenHeight = (uint16_t)screenHeight;
+ config.smallestScreenWidthDp = (uint16_t)smallestScreenWidthDp;
+ config.screenWidthDp = (uint16_t)screenWidthDp;
+ config.screenHeightDp = (uint16_t)screenHeightDp;
+ config.screenLayout = (uint8_t)screenLayout;
+ config.uiMode = (uint8_t)uiMode;
+ config.colorMode = (uint8_t)colorMode;
+ config.sdkVersion = (uint16_t)sdkVersion;
+ config.minorVersion = 0;
+
+ // In Java, we use a 32bit integer for screenLayout, while we only use an 8bit integer
+ // in C++. We must extract the round qualifier out of the Java screenLayout and put it
+ // into screenLayout2.
+ config.screenLayout2 =
+ (uint8_t)((screenLayout & kScreenLayoutRoundMask) >> kScreenLayoutRoundShift);
+
+ am->setConfiguration(config, locale8);
+
+ if (locale != NULL) env->ReleaseStringUTFChars(locale, locale8);
+}
+
+static jint android_content_AssetManager_getResourceIdentifier(JNIEnv* env, jobject clazz,
+ jstring name,
+ jstring defType,
+ jstring defPackage)
+{
+ ScopedStringChars name16(env, name);
+ if (name16.get() == NULL) {
+ return 0;
+ }
+
+ AssetManager* am = assetManagerForJavaObject(env, clazz);
+ if (am == NULL) {
+ return 0;
+ }
+
+ const char16_t* defType16 = reinterpret_cast<const char16_t*>(defType)
+ ? reinterpret_cast<const char16_t*>(env->GetStringChars(defType, NULL))
+ : NULL;
+ jsize defTypeLen = defType
+ ? env->GetStringLength(defType) : 0;
+ const char16_t* defPackage16 = reinterpret_cast<const char16_t*>(defPackage)
+ ? reinterpret_cast<const char16_t*>(env->GetStringChars(defPackage,
+ NULL))
+ : NULL;
+ jsize defPackageLen = defPackage
+ ? env->GetStringLength(defPackage) : 0;
+
+ jint ident = am->getResources().identifierForName(
+ reinterpret_cast<const char16_t*>(name16.get()), name16.size(),
+ defType16, defTypeLen, defPackage16, defPackageLen);
+
+ if (defPackage16) {
+ env->ReleaseStringChars(defPackage,
+ reinterpret_cast<const jchar*>(defPackage16));
+ }
+ if (defType16) {
+ env->ReleaseStringChars(defType,
+ reinterpret_cast<const jchar*>(defType16));
+ }
+
+ return ident;
+}
+
+static jstring android_content_AssetManager_getResourceName(JNIEnv* env, jobject clazz,
+ jint resid)
+{
+ AssetManager* am = assetManagerForJavaObject(env, clazz);
+ if (am == NULL) {
+ return NULL;
+ }
+
+ ResTable::resource_name name;
+ if (!am->getResources().getResourceName(resid, true, &name)) {
+ return NULL;
+ }
+
+ String16 str;
+ if (name.package != NULL) {
+ str.setTo(name.package, name.packageLen);
+ }
+ if (name.type8 != NULL || name.type != NULL) {
+ if (str.size() > 0) {
+ char16_t div = ':';
+ str.append(&div, 1);
+ }
+ if (name.type8 != NULL) {
+ str.append(String16(name.type8, name.typeLen));
+ } else {
+ str.append(name.type, name.typeLen);
+ }
+ }
+ if (name.name8 != NULL || name.name != NULL) {
+ if (str.size() > 0) {
+ char16_t div = '/';
+ str.append(&div, 1);
+ }
+ if (name.name8 != NULL) {
+ str.append(String16(name.name8, name.nameLen));
+ } else {
+ str.append(name.name, name.nameLen);
+ }
+ }
+
+ return env->NewString((const jchar*)str.string(), str.size());
+}
+
+static jstring android_content_AssetManager_getResourcePackageName(JNIEnv* env, jobject clazz,
+ jint resid)
+{
+ AssetManager* am = assetManagerForJavaObject(env, clazz);
+ if (am == NULL) {
+ return NULL;
+ }
+
+ ResTable::resource_name name;
+ if (!am->getResources().getResourceName(resid, true, &name)) {
+ return NULL;
+ }
+
+ if (name.package != NULL) {
+ return env->NewString((const jchar*)name.package, name.packageLen);
+ }
+
+ return NULL;
+}
+
+static jstring android_content_AssetManager_getResourceTypeName(JNIEnv* env, jobject clazz,
+ jint resid)
+{
+ AssetManager* am = assetManagerForJavaObject(env, clazz);
+ if (am == NULL) {
+ return NULL;
+ }
+
+ ResTable::resource_name name;
+ if (!am->getResources().getResourceName(resid, true, &name)) {
+ return NULL;
+ }
+
+ if (name.type8 != NULL) {
+ return env->NewStringUTF(name.type8);
+ }
+
+ if (name.type != NULL) {
+ return env->NewString((const jchar*)name.type, name.typeLen);
+ }
+
+ return NULL;
+}
+
+static jstring android_content_AssetManager_getResourceEntryName(JNIEnv* env, jobject clazz,
+ jint resid)
+{
+ AssetManager* am = assetManagerForJavaObject(env, clazz);
+ if (am == NULL) {
+ return NULL;
+ }
+
+ ResTable::resource_name name;
+ if (!am->getResources().getResourceName(resid, true, &name)) {
+ return NULL;
+ }
+
+ if (name.name8 != NULL) {
+ return env->NewStringUTF(name.name8);
+ }
+
+ if (name.name != NULL) {
+ return env->NewString((const jchar*)name.name, name.nameLen);
+ }
+
+ return NULL;
+}
+
+static jint android_content_AssetManager_loadResourceValue(JNIEnv* env, jobject clazz,
+ jint ident,
+ jshort density,
+ jobject outValue,
+ jboolean resolve)
+{
+ if (outValue == NULL) {
+ jniThrowNullPointerException(env, "outValue");
+ return 0;
+ }
+ AssetManager* am = assetManagerForJavaObject(env, clazz);
+ if (am == NULL) {
+ return 0;
+ }
+ const ResTable& res(am->getResources());
+
+ Res_value value;
+ ResTable_config config;
+ uint32_t typeSpecFlags;
+ ssize_t block = res.getResource(ident, &value, false, density, &typeSpecFlags, &config);
+ if (kThrowOnBadId) {
+ if (block == BAD_INDEX) {
+ jniThrowException(env, "java/lang/IllegalStateException", "Bad resource!");
+ return 0;
+ }
+ }
+ uint32_t ref = ident;
+ if (resolve) {
+ block = res.resolveReference(&value, block, &ref, &typeSpecFlags, &config);
+ if (kThrowOnBadId) {
+ if (block == BAD_INDEX) {
+ jniThrowException(env, "java/lang/IllegalStateException", "Bad resource!");
+ return 0;
+ }
+ }
+ }
+ if (block >= 0) {
+ return copyValue(env, outValue, &res, value, ref, block, typeSpecFlags, &config);
+ }
+
+ return static_cast<jint>(block);
+}
+
+static jint android_content_AssetManager_loadResourceBagValue(JNIEnv* env, jobject clazz,
+ jint ident, jint bagEntryId,
+ jobject outValue, jboolean resolve)
+{
+ AssetManager* am = assetManagerForJavaObject(env, clazz);
+ if (am == NULL) {
+ return 0;
+ }
+ const ResTable& res(am->getResources());
+
+ // Now lock down the resource object and start pulling stuff from it.
+ res.lock();
+
+ ssize_t block = -1;
+ Res_value value;
+
+ const ResTable::bag_entry* entry = NULL;
+ uint32_t typeSpecFlags;
+ ssize_t entryCount = res.getBagLocked(ident, &entry, &typeSpecFlags);
+
+ for (ssize_t i=0; i<entryCount; i++) {
+ if (((uint32_t)bagEntryId) == entry->map.name.ident) {
+ block = entry->stringBlock;
+ value = entry->map.value;
+ }
+ entry++;
+ }
+
+ res.unlock();
+
+ if (block < 0) {
+ return static_cast<jint>(block);
+ }
+
+ uint32_t ref = ident;
+ if (resolve) {
+ block = res.resolveReference(&value, block, &ref, &typeSpecFlags);
+ if (kThrowOnBadId) {
+ if (block == BAD_INDEX) {
+ jniThrowException(env, "java/lang/IllegalStateException", "Bad resource!");
+ return 0;
+ }
+ }
+ }
+ if (block >= 0) {
+ return copyValue(env, outValue, &res, value, ref, block, typeSpecFlags);
+ }
+
+ return static_cast<jint>(block);
+}
+
+static jint android_content_AssetManager_getStringBlockCount(JNIEnv* env, jobject clazz)
+{
+ AssetManager* am = assetManagerForJavaObject(env, clazz);
+ if (am == NULL) {
+ return 0;
+ }
+ return am->getResources().getTableCount();
+}
+
+static jlong android_content_AssetManager_getNativeStringBlock(JNIEnv* env, jobject clazz,
+ jint block)
+{
+ AssetManager* am = assetManagerForJavaObject(env, clazz);
+ if (am == NULL) {
+ return 0;
+ }
+ return reinterpret_cast<jlong>(am->getResources().getTableStringBlock(block));
+}
+
+static jstring android_content_AssetManager_getCookieName(JNIEnv* env, jobject clazz,
+ jint cookie)
+{
+ AssetManager* am = assetManagerForJavaObject(env, clazz);
+ if (am == NULL) {
+ return NULL;
+ }
+ String8 name(am->getAssetPath(static_cast<int32_t>(cookie)));
+ if (name.length() == 0) {
+ jniThrowException(env, "java/lang/IndexOutOfBoundsException", "Empty cookie name");
+ return NULL;
+ }
+ jstring str = env->NewStringUTF(name.string());
+ return str;
+}
+
+static jobject android_content_AssetManager_getAssignedPackageIdentifiers(JNIEnv* env, jobject clazz)
+{
+ AssetManager* am = assetManagerForJavaObject(env, clazz);
+ if (am == NULL) {
+ return 0;
+ }
+
+ const ResTable& res = am->getResources();
+
+ jobject sparseArray = env->NewObject(gSparseArrayOffsets.classObject,
+ gSparseArrayOffsets.constructor);
+ const size_t N = res.getBasePackageCount();
+ for (size_t i = 0; i < N; i++) {
+ const String16 name = res.getBasePackageName(i);
+ env->CallVoidMethod(
+ sparseArray, gSparseArrayOffsets.put,
+ static_cast<jint>(res.getBasePackageId(i)),
+ env->NewString(reinterpret_cast<const jchar*>(name.string()),
+ name.size()));
+ }
+ return sparseArray;
+}
+
+static jlong android_content_AssetManager_newTheme(JNIEnv* env, jobject clazz)
+{
+ AssetManager* am = assetManagerForJavaObject(env, clazz);
+ if (am == NULL) {
+ return 0;
+ }
+ return reinterpret_cast<jlong>(new ResTable::Theme(am->getResources()));
+}
+
+static void android_content_AssetManager_deleteTheme(JNIEnv* env, jobject clazz,
+ jlong themeHandle)
+{
+ ResTable::Theme* theme = reinterpret_cast<ResTable::Theme*>(themeHandle);
+ delete theme;
+}
+
+static void android_content_AssetManager_applyThemeStyle(JNIEnv* env, jobject clazz,
+ jlong themeHandle,
+ jint styleRes,
+ jboolean force)
+{
+ ResTable::Theme* theme = reinterpret_cast<ResTable::Theme*>(themeHandle);
+ theme->applyStyle(styleRes, force ? true : false);
+}
+
+static void android_content_AssetManager_copyTheme(JNIEnv* env, jobject clazz,
+ jlong destHandle, jlong srcHandle)
+{
+ ResTable::Theme* dest = reinterpret_cast<ResTable::Theme*>(destHandle);
+ ResTable::Theme* src = reinterpret_cast<ResTable::Theme*>(srcHandle);
+ dest->setTo(*src);
+}
+
+static void android_content_AssetManager_clearTheme(JNIEnv* env, jobject clazz, jlong themeHandle)
+{
+ ResTable::Theme* theme = reinterpret_cast<ResTable::Theme*>(themeHandle);
+ theme->clear();
+}
+
+static jint android_content_AssetManager_loadThemeAttributeValue(
+ JNIEnv* env, jobject clazz, jlong themeHandle, jint ident, jobject outValue, jboolean resolve)
+{
+ ResTable::Theme* theme = reinterpret_cast<ResTable::Theme*>(themeHandle);
+ const ResTable& res(theme->getResTable());
+
+ Res_value value;
+ // XXX value could be different in different configs!
+ uint32_t typeSpecFlags = 0;
+ ssize_t block = theme->getAttribute(ident, &value, &typeSpecFlags);
+ uint32_t ref = 0;
+ if (resolve) {
+ block = res.resolveReference(&value, block, &ref, &typeSpecFlags);
+ if (kThrowOnBadId) {
+ if (block == BAD_INDEX) {
+ jniThrowException(env, "java/lang/IllegalStateException", "Bad resource!");
+ return 0;
+ }
+ }
+ }
+ return block >= 0 ? copyValue(env, outValue, &res, value, ref, block, typeSpecFlags) : block;
+}
+
+static jint android_content_AssetManager_getThemeChangingConfigurations(JNIEnv* env, jobject clazz,
+ jlong themeHandle)
+{
+ ResTable::Theme* theme = reinterpret_cast<ResTable::Theme*>(themeHandle);
+ return theme->getChangingConfigurations();
+}
+
+static void android_content_AssetManager_dumpTheme(JNIEnv* env, jobject clazz,
+ jlong themeHandle, jint pri,
+ jstring tag, jstring prefix)
+{
+ ResTable::Theme* theme = reinterpret_cast<ResTable::Theme*>(themeHandle);
+ const ResTable& res(theme->getResTable());
+ (void)res;
+
+ // XXX Need to use params.
+ theme->dumpToLog();
+}
+
+static jboolean android_content_AssetManager_resolveAttrs(JNIEnv* env, jobject clazz,
+ jlong themeToken,
+ jint defStyleAttr,
+ jint defStyleRes,
+ jintArray inValues,
+ jintArray attrs,
+ jintArray outValues,
+ jintArray outIndices)
+{
+ if (themeToken == 0) {
+ jniThrowNullPointerException(env, "theme token");
return JNI_FALSE;
- }
}
- }
-
- ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
- Theme* theme = reinterpret_cast<Theme*>(theme_ptr);
- CHECK(theme->GetAssetManager() == &(*assetmanager));
- (void) assetmanager;
-
- bool result = ResolveAttrs(
- theme, static_cast<uint32_t>(def_style_attr), static_cast<uint32_t>(def_style_resid),
- reinterpret_cast<uint32_t*>(values), values_len, reinterpret_cast<uint32_t*>(attrs),
- attrs_len, reinterpret_cast<uint32_t*>(out_values), reinterpret_cast<uint32_t*>(out_indices));
- if (out_indices != nullptr) {
- env->ReleasePrimitiveArrayCritical(out_java_indices, out_indices, 0);
- }
-
- env->ReleasePrimitiveArrayCritical(out_java_values, out_values, 0);
- if (values != nullptr) {
- env->ReleasePrimitiveArrayCritical(java_values, values, JNI_ABORT);
- }
- env->ReleasePrimitiveArrayCritical(java_attrs, attrs, JNI_ABORT);
- return result ? JNI_TRUE : JNI_FALSE;
-}
-
-static jboolean NativeRetrieveAttributes(JNIEnv* env, jclass /*clazz*/, jlong ptr,
- jlong xml_parser_ptr, jintArray java_attrs,
- jintArray out_java_values, jintArray out_java_indices) {
- const jsize attrs_len = env->GetArrayLength(java_attrs);
- const jsize out_values_len = env->GetArrayLength(out_java_values);
- if (out_values_len < (attrs_len * STYLE_NUM_ENTRIES)) {
- jniThrowException(env, "java/lang/IndexOutOfBoundsException", "outValues too small");
- return JNI_FALSE;
- }
-
- jint* attrs = reinterpret_cast<jint*>(env->GetPrimitiveArrayCritical(java_attrs, nullptr));
- if (attrs == nullptr) {
- return JNI_FALSE;
- }
-
- jint* out_values =
- reinterpret_cast<jint*>(env->GetPrimitiveArrayCritical(out_java_values, nullptr));
- if (out_values == nullptr) {
- env->ReleasePrimitiveArrayCritical(java_attrs, attrs, JNI_ABORT);
- return JNI_FALSE;
- }
-
- jint* out_indices = nullptr;
- if (out_java_indices != nullptr) {
- jsize out_indices_len = env->GetArrayLength(out_java_indices);
- if (out_indices_len > attrs_len) {
- out_indices =
- reinterpret_cast<jint*>(env->GetPrimitiveArrayCritical(out_java_indices, nullptr));
- if (out_indices == nullptr) {
- env->ReleasePrimitiveArrayCritical(java_attrs, attrs, JNI_ABORT);
- env->ReleasePrimitiveArrayCritical(out_java_values, out_values, JNI_ABORT);
+ if (attrs == NULL) {
+ jniThrowNullPointerException(env, "attrs");
return JNI_FALSE;
- }
}
- }
-
- ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
- ResXMLParser* xml_parser = reinterpret_cast<ResXMLParser*>(xml_parser_ptr);
-
- bool result = RetrieveAttributes(assetmanager.get(), xml_parser,
- reinterpret_cast<uint32_t*>(attrs), attrs_len,
- reinterpret_cast<uint32_t*>(out_values),
- reinterpret_cast<uint32_t*>(out_indices));
-
- if (out_indices != nullptr) {
- env->ReleasePrimitiveArrayCritical(out_java_indices, out_indices, 0);
- }
- env->ReleasePrimitiveArrayCritical(out_java_values, out_values, 0);
- env->ReleasePrimitiveArrayCritical(java_attrs, attrs, JNI_ABORT);
- return result ? JNI_TRUE : JNI_FALSE;
-}
-
-static jlong NativeThemeCreate(JNIEnv* /*env*/, jclass /*clazz*/, jlong ptr) {
- ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
- return reinterpret_cast<jlong>(assetmanager->NewTheme().release());
-}
-
-static void NativeThemeDestroy(JNIEnv* /*env*/, jclass /*clazz*/, jlong theme_ptr) {
- delete reinterpret_cast<Theme*>(theme_ptr);
-}
-
-static void NativeThemeApplyStyle(JNIEnv* env, jclass /*clazz*/, jlong ptr, jlong theme_ptr,
- jint resid, jboolean force) {
- // AssetManager is accessed via the theme, so grab an explicit lock here.
- ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
- Theme* theme = reinterpret_cast<Theme*>(theme_ptr);
- CHECK(theme->GetAssetManager() == &(*assetmanager));
- (void) assetmanager;
- theme->ApplyStyle(static_cast<uint32_t>(resid), force);
-
- // TODO(adamlesinski): Consider surfacing exception when result is failure.
- // CTS currently expects no exceptions from this method.
- // std::string error_msg = StringPrintf("Failed to apply style 0x%08x to theme", resid);
- // jniThrowException(env, "java/lang/IllegalArgumentException", error_msg.c_str());
-}
-
-static void NativeThemeCopy(JNIEnv* env, jclass /*clazz*/, jlong dst_theme_ptr,
- jlong src_theme_ptr) {
- Theme* dst_theme = reinterpret_cast<Theme*>(dst_theme_ptr);
- Theme* src_theme = reinterpret_cast<Theme*>(src_theme_ptr);
- if (!dst_theme->SetTo(*src_theme)) {
- jniThrowException(env, "java/lang/IllegalArgumentException",
- "Themes are from different AssetManagers");
- }
-}
-
-static void NativeThemeClear(JNIEnv* /*env*/, jclass /*clazz*/, jlong theme_ptr) {
- reinterpret_cast<Theme*>(theme_ptr)->Clear();
-}
-
-static jint NativeThemeGetAttributeValue(JNIEnv* env, jclass /*clazz*/, jlong ptr, jlong theme_ptr,
- jint resid, jobject typed_value,
- jboolean resolve_references) {
- ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
- Theme* theme = reinterpret_cast<Theme*>(theme_ptr);
- CHECK(theme->GetAssetManager() == &(*assetmanager));
- (void) assetmanager;
-
- Res_value value;
- uint32_t flags;
- ApkAssetsCookie cookie = theme->GetAttribute(static_cast<uint32_t>(resid), &value, &flags);
- if (cookie == kInvalidCookie) {
- return ApkAssetsCookieToJavaCookie(kInvalidCookie);
- }
-
- uint32_t ref = 0u;
- if (resolve_references) {
- ResTable_config selected_config;
- cookie =
- theme->GetAssetManager()->ResolveReference(cookie, &value, &selected_config, &flags, &ref);
- if (cookie == kInvalidCookie) {
- return ApkAssetsCookieToJavaCookie(kInvalidCookie);
+ if (outValues == NULL) {
+ jniThrowNullPointerException(env, "out values");
+ return JNI_FALSE;
}
- }
- return CopyValue(env, cookie, value, ref, flags, nullptr, typed_value);
+
+ const jsize NI = env->GetArrayLength(attrs);
+ const jsize NV = env->GetArrayLength(outValues);
+ if (NV < (NI*STYLE_NUM_ENTRIES)) {
+ jniThrowException(env, "java/lang/IndexOutOfBoundsException", "out values too small");
+ return JNI_FALSE;
+ }
+
+ jint* src = (jint*)env->GetPrimitiveArrayCritical(attrs, 0);
+ if (src == NULL) {
+ return JNI_FALSE;
+ }
+
+ jint* srcValues = (jint*)env->GetPrimitiveArrayCritical(inValues, 0);
+ const jsize NSV = srcValues == NULL ? 0 : env->GetArrayLength(inValues);
+
+ jint* baseDest = (jint*)env->GetPrimitiveArrayCritical(outValues, 0);
+ if (baseDest == NULL) {
+ env->ReleasePrimitiveArrayCritical(attrs, src, 0);
+ return JNI_FALSE;
+ }
+
+ jint* indices = NULL;
+ if (outIndices != NULL) {
+ if (env->GetArrayLength(outIndices) > NI) {
+ indices = (jint*)env->GetPrimitiveArrayCritical(outIndices, 0);
+ }
+ }
+
+ ResTable::Theme* theme = reinterpret_cast<ResTable::Theme*>(themeToken);
+ bool result = ResolveAttrs(theme, defStyleAttr, defStyleRes,
+ (uint32_t*) srcValues, NSV,
+ (uint32_t*) src, NI,
+ (uint32_t*) baseDest,
+ (uint32_t*) indices);
+
+ if (indices != NULL) {
+ env->ReleasePrimitiveArrayCritical(outIndices, indices, 0);
+ }
+ env->ReleasePrimitiveArrayCritical(outValues, baseDest, 0);
+ env->ReleasePrimitiveArrayCritical(inValues, srcValues, 0);
+ env->ReleasePrimitiveArrayCritical(attrs, src, 0);
+ return result ? JNI_TRUE : JNI_FALSE;
}
-static void NativeThemeDump(JNIEnv* /*env*/, jclass /*clazz*/, jlong ptr, jlong theme_ptr,
- jint priority, jstring tag, jstring prefix) {
- ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
- Theme* theme = reinterpret_cast<Theme*>(theme_ptr);
- CHECK(theme->GetAssetManager() == &(*assetmanager));
- (void) assetmanager;
- (void) theme;
- (void) priority;
- (void) tag;
- (void) prefix;
+static void android_content_AssetManager_applyStyle(JNIEnv* env, jobject, jlong themeToken,
+ jint defStyleAttr, jint defStyleRes, jlong xmlParserToken, jintArray attrsObj, jint length,
+ jlong outValuesAddress, jlong outIndicesAddress) {
+ jint* attrs = env->GetIntArrayElements(attrsObj, 0);
+ ResTable::Theme* theme = reinterpret_cast<ResTable::Theme*>(themeToken);
+ ResXMLParser* xmlParser = reinterpret_cast<ResXMLParser*>(xmlParserToken);
+ uint32_t* outValues = reinterpret_cast<uint32_t*>(static_cast<uintptr_t>(outValuesAddress));
+ uint32_t* outIndices = reinterpret_cast<uint32_t*>(static_cast<uintptr_t>(outIndicesAddress));
+ ApplyStyle(theme, xmlParser, defStyleAttr, defStyleRes,
+ reinterpret_cast<const uint32_t*>(attrs), length, outValues, outIndices);
+ env->ReleaseIntArrayElements(attrsObj, attrs, JNI_ABORT);
}
-static jint NativeThemeGetChangingConfigurations(JNIEnv* /*env*/, jclass /*clazz*/,
- jlong theme_ptr) {
- Theme* theme = reinterpret_cast<Theme*>(theme_ptr);
- return static_cast<jint>(theme->GetChangingConfigurations());
+static jboolean android_content_AssetManager_retrieveAttributes(JNIEnv* env, jobject clazz,
+ jlong xmlParserToken,
+ jintArray attrs,
+ jintArray outValues,
+ jintArray outIndices)
+{
+ if (xmlParserToken == 0) {
+ jniThrowNullPointerException(env, "xmlParserToken");
+ return JNI_FALSE;
+ }
+ if (attrs == NULL) {
+ jniThrowNullPointerException(env, "attrs");
+ return JNI_FALSE;
+ }
+ if (outValues == NULL) {
+ jniThrowNullPointerException(env, "out values");
+ return JNI_FALSE;
+ }
+
+ AssetManager* am = assetManagerForJavaObject(env, clazz);
+ if (am == NULL) {
+ return JNI_FALSE;
+ }
+ const ResTable& res(am->getResources());
+ ResXMLParser* xmlParser = (ResXMLParser*)xmlParserToken;
+
+ const jsize NI = env->GetArrayLength(attrs);
+ const jsize NV = env->GetArrayLength(outValues);
+ if (NV < (NI*STYLE_NUM_ENTRIES)) {
+ jniThrowException(env, "java/lang/IndexOutOfBoundsException", "out values too small");
+ return JNI_FALSE;
+ }
+
+ jint* src = (jint*)env->GetPrimitiveArrayCritical(attrs, 0);
+ if (src == NULL) {
+ return JNI_FALSE;
+ }
+
+ jint* baseDest = (jint*)env->GetPrimitiveArrayCritical(outValues, 0);
+ if (baseDest == NULL) {
+ env->ReleasePrimitiveArrayCritical(attrs, src, 0);
+ return JNI_FALSE;
+ }
+
+ jint* indices = NULL;
+ if (outIndices != NULL) {
+ if (env->GetArrayLength(outIndices) > NI) {
+ indices = (jint*)env->GetPrimitiveArrayCritical(outIndices, 0);
+ }
+ }
+
+ bool result = RetrieveAttributes(&res, xmlParser,
+ (uint32_t*) src, NI,
+ (uint32_t*) baseDest,
+ (uint32_t*) indices);
+
+ if (indices != NULL) {
+ env->ReleasePrimitiveArrayCritical(outIndices, indices, 0);
+ }
+ env->ReleasePrimitiveArrayCritical(outValues, baseDest, 0);
+ env->ReleasePrimitiveArrayCritical(attrs, src, 0);
+ return result ? JNI_TRUE : JNI_FALSE;
}
-static void NativeAssetDestroy(JNIEnv* /*env*/, jclass /*clazz*/, jlong asset_ptr) {
- delete reinterpret_cast<Asset*>(asset_ptr);
+static jint android_content_AssetManager_getArraySize(JNIEnv* env, jobject clazz,
+ jint id)
+{
+ AssetManager* am = assetManagerForJavaObject(env, clazz);
+ if (am == NULL) {
+ return 0;
+ }
+ const ResTable& res(am->getResources());
+
+ res.lock();
+ const ResTable::bag_entry* defStyleEnt = NULL;
+ ssize_t bagOff = res.getBagLocked(id, &defStyleEnt);
+ res.unlock();
+
+ return static_cast<jint>(bagOff);
}
-static jint NativeAssetReadChar(JNIEnv* /*env*/, jclass /*clazz*/, jlong asset_ptr) {
- Asset* asset = reinterpret_cast<Asset*>(asset_ptr);
- uint8_t b;
- ssize_t res = asset->read(&b, sizeof(b));
- return res == sizeof(b) ? static_cast<jint>(b) : -1;
+static jint android_content_AssetManager_retrieveArray(JNIEnv* env, jobject clazz,
+ jint id,
+ jintArray outValues)
+{
+ if (outValues == NULL) {
+ jniThrowNullPointerException(env, "out values");
+ return JNI_FALSE;
+ }
+
+ AssetManager* am = assetManagerForJavaObject(env, clazz);
+ if (am == NULL) {
+ return JNI_FALSE;
+ }
+ const ResTable& res(am->getResources());
+ ResTable_config config;
+ Res_value value;
+ ssize_t block;
+
+ const jsize NV = env->GetArrayLength(outValues);
+
+ jint* baseDest = (jint*)env->GetPrimitiveArrayCritical(outValues, 0);
+ jint* dest = baseDest;
+ if (dest == NULL) {
+ jniThrowException(env, "java/lang/OutOfMemoryError", "");
+ return JNI_FALSE;
+ }
+
+ // Now lock down the resource object and start pulling stuff from it.
+ res.lock();
+
+ const ResTable::bag_entry* arrayEnt = NULL;
+ uint32_t arrayTypeSetFlags = 0;
+ ssize_t bagOff = res.getBagLocked(id, &arrayEnt, &arrayTypeSetFlags);
+ const ResTable::bag_entry* endArrayEnt = arrayEnt +
+ (bagOff >= 0 ? bagOff : 0);
+
+ int i = 0;
+ uint32_t typeSetFlags;
+ while (i < NV && arrayEnt < endArrayEnt) {
+ block = arrayEnt->stringBlock;
+ typeSetFlags = arrayTypeSetFlags;
+ config.density = 0;
+ value = arrayEnt->map.value;
+
+ uint32_t resid = 0;
+ if (value.dataType != Res_value::TYPE_NULL) {
+ // Take care of resolving the found resource to its final value.
+ //printf("Resolving attribute reference\n");
+ ssize_t newBlock = res.resolveReference(&value, block, &resid,
+ &typeSetFlags, &config);
+ if (kThrowOnBadId) {
+ if (newBlock == BAD_INDEX) {
+ jniThrowException(env, "java/lang/IllegalStateException", "Bad resource!");
+ return JNI_FALSE;
+ }
+ }
+ if (newBlock >= 0) block = newBlock;
+ }
+
+ // Deal with the special @null value -- it turns back to TYPE_NULL.
+ if (value.dataType == Res_value::TYPE_REFERENCE && value.data == 0) {
+ value.dataType = Res_value::TYPE_NULL;
+ value.data = Res_value::DATA_NULL_UNDEFINED;
+ }
+
+ //printf("Attribute 0x%08x: final type=0x%x, data=0x%08x\n", curIdent, value.dataType, value.data);
+
+ // Write the final value back to Java.
+ dest[STYLE_TYPE] = value.dataType;
+ dest[STYLE_DATA] = value.data;
+ dest[STYLE_ASSET_COOKIE] = reinterpret_cast<jint>(res.getTableCookie(block));
+ dest[STYLE_RESOURCE_ID] = resid;
+ dest[STYLE_CHANGING_CONFIGURATIONS] = typeSetFlags;
+ dest[STYLE_DENSITY] = config.density;
+ dest += STYLE_NUM_ENTRIES;
+ i+= STYLE_NUM_ENTRIES;
+ arrayEnt++;
+ }
+
+ i /= STYLE_NUM_ENTRIES;
+
+ res.unlock();
+
+ env->ReleasePrimitiveArrayCritical(outValues, baseDest, 0);
+
+ return i;
}
-static jint NativeAssetRead(JNIEnv* env, jclass /*clazz*/, jlong asset_ptr, jbyteArray java_buffer,
- jint offset, jint len) {
- if (len == 0) {
- return 0;
- }
+static jlong android_content_AssetManager_openXmlAssetNative(JNIEnv* env, jobject clazz,
+ jint cookie,
+ jstring fileName)
+{
+ AssetManager* am = assetManagerForJavaObject(env, clazz);
+ if (am == NULL) {
+ return 0;
+ }
- jsize buffer_len = env->GetArrayLength(java_buffer);
- if (offset < 0 || offset >= buffer_len || len < 0 || len > buffer_len ||
- offset > buffer_len - len) {
- jniThrowException(env, "java/lang/IndexOutOfBoundsException", "");
- return -1;
- }
+ ALOGV("openXmlAsset in %p (Java object %p)\n", am, clazz);
- ScopedByteArrayRW byte_array(env, java_buffer);
- if (byte_array.get() == nullptr) {
- return -1;
- }
+ ScopedUtfChars fileName8(env, fileName);
+ if (fileName8.c_str() == NULL) {
+ return 0;
+ }
- Asset* asset = reinterpret_cast<Asset*>(asset_ptr);
- ssize_t res = asset->read(byte_array.get() + offset, len);
- if (res < 0) {
- jniThrowException(env, "java/io/IOException", "");
- return -1;
- }
- return res > 0 ? static_cast<jint>(res) : -1;
+ int32_t assetCookie = static_cast<int32_t>(cookie);
+ Asset* a = assetCookie
+ ? am->openNonAsset(assetCookie, fileName8.c_str(), Asset::ACCESS_BUFFER)
+ : am->openNonAsset(fileName8.c_str(), Asset::ACCESS_BUFFER, &assetCookie);
+
+ if (a == NULL) {
+ jniThrowException(env, "java/io/FileNotFoundException", fileName8.c_str());
+ return 0;
+ }
+
+ const DynamicRefTable* dynamicRefTable =
+ am->getResources().getDynamicRefTableForCookie(assetCookie);
+ ResXMLTree* block = new ResXMLTree(dynamicRefTable);
+ status_t err = block->setTo(a->getBuffer(true), a->getLength(), true);
+ a->close();
+ delete a;
+
+ if (err != NO_ERROR) {
+ jniThrowException(env, "java/io/FileNotFoundException", "Corrupt XML binary file");
+ return 0;
+ }
+
+ return reinterpret_cast<jlong>(block);
}
-static jlong NativeAssetSeek(JNIEnv* env, jclass /*clazz*/, jlong asset_ptr, jlong offset,
- jint whence) {
- Asset* asset = reinterpret_cast<Asset*>(asset_ptr);
- return static_cast<jlong>(asset->seek(
- static_cast<off64_t>(offset), (whence > 0 ? SEEK_END : (whence < 0 ? SEEK_SET : SEEK_CUR))));
+static jintArray android_content_AssetManager_getArrayStringInfo(JNIEnv* env, jobject clazz,
+ jint arrayResId)
+{
+ AssetManager* am = assetManagerForJavaObject(env, clazz);
+ if (am == NULL) {
+ return NULL;
+ }
+ const ResTable& res(am->getResources());
+
+ const ResTable::bag_entry* startOfBag;
+ const ssize_t N = res.lockBag(arrayResId, &startOfBag);
+ if (N < 0) {
+ return NULL;
+ }
+
+ jintArray array = env->NewIntArray(N * 2);
+ if (array == NULL) {
+ res.unlockBag(startOfBag);
+ return NULL;
+ }
+
+ Res_value value;
+ const ResTable::bag_entry* bag = startOfBag;
+ for (size_t i = 0, j = 0; ((ssize_t)i)<N; i++, bag++) {
+ jint stringIndex = -1;
+ jint stringBlock = 0;
+ value = bag->map.value;
+
+ // Take care of resolving the found resource to its final value.
+ stringBlock = res.resolveReference(&value, bag->stringBlock, NULL);
+ if (value.dataType == Res_value::TYPE_STRING) {
+ stringIndex = value.data;
+ }
+
+ if (kThrowOnBadId) {
+ if (stringBlock == BAD_INDEX) {
+ jniThrowException(env, "java/lang/IllegalStateException", "Bad resource!");
+ return array;
+ }
+ }
+
+ //todo: It might be faster to allocate a C array to contain
+ // the blocknums and indices, put them in there and then
+ // do just one SetIntArrayRegion()
+ env->SetIntArrayRegion(array, j, 1, &stringBlock);
+ env->SetIntArrayRegion(array, j + 1, 1, &stringIndex);
+ j = j + 2;
+ }
+ res.unlockBag(startOfBag);
+ return array;
}
-static jlong NativeAssetGetLength(JNIEnv* /*env*/, jclass /*clazz*/, jlong asset_ptr) {
- Asset* asset = reinterpret_cast<Asset*>(asset_ptr);
- return static_cast<jlong>(asset->getLength());
+static jobjectArray android_content_AssetManager_getArrayStringResource(JNIEnv* env, jobject clazz,
+ jint arrayResId)
+{
+ AssetManager* am = assetManagerForJavaObject(env, clazz);
+ if (am == NULL) {
+ return NULL;
+ }
+ const ResTable& res(am->getResources());
+
+ const ResTable::bag_entry* startOfBag;
+ const ssize_t N = res.lockBag(arrayResId, &startOfBag);
+ if (N < 0) {
+ return NULL;
+ }
+
+ jobjectArray array = env->NewObjectArray(N, g_stringClass, NULL);
+ if (env->ExceptionCheck()) {
+ res.unlockBag(startOfBag);
+ return NULL;
+ }
+
+ Res_value value;
+ const ResTable::bag_entry* bag = startOfBag;
+ size_t strLen = 0;
+ for (size_t i=0; ((ssize_t)i)<N; i++, bag++) {
+ value = bag->map.value;
+ jstring str = NULL;
+
+ // Take care of resolving the found resource to its final value.
+ ssize_t block = res.resolveReference(&value, bag->stringBlock, NULL);
+ if (kThrowOnBadId) {
+ if (block == BAD_INDEX) {
+ jniThrowException(env, "java/lang/IllegalStateException", "Bad resource!");
+ return array;
+ }
+ }
+ if (value.dataType == Res_value::TYPE_STRING) {
+ const ResStringPool* pool = res.getTableStringBlock(block);
+ const char* str8 = pool->string8At(value.data, &strLen);
+ if (str8 != NULL) {
+ str = env->NewStringUTF(str8);
+ } else {
+ const char16_t* str16 = pool->stringAt(value.data, &strLen);
+ str = env->NewString(reinterpret_cast<const jchar*>(str16),
+ strLen);
+ }
+
+ // If one of our NewString{UTF} calls failed due to memory, an
+ // exception will be pending.
+ if (env->ExceptionCheck()) {
+ res.unlockBag(startOfBag);
+ return NULL;
+ }
+
+ env->SetObjectArrayElement(array, i, str);
+
+ // str is not NULL at that point, otherwise ExceptionCheck would have been true.
+ // If we have a large amount of strings in our array, we might
+ // overflow the local reference table of the VM.
+ env->DeleteLocalRef(str);
+ }
+ }
+ res.unlockBag(startOfBag);
+ return array;
}
-static jlong NativeAssetGetRemainingLength(JNIEnv* /*env*/, jclass /*clazz*/, jlong asset_ptr) {
- Asset* asset = reinterpret_cast<Asset*>(asset_ptr);
- return static_cast<jlong>(asset->getRemainingLength());
+static jintArray android_content_AssetManager_getArrayIntResource(JNIEnv* env, jobject clazz,
+ jint arrayResId)
+{
+ AssetManager* am = assetManagerForJavaObject(env, clazz);
+ if (am == NULL) {
+ return NULL;
+ }
+ const ResTable& res(am->getResources());
+
+ const ResTable::bag_entry* startOfBag;
+ const ssize_t N = res.lockBag(arrayResId, &startOfBag);
+ if (N < 0) {
+ return NULL;
+ }
+
+ jintArray array = env->NewIntArray(N);
+ if (array == NULL) {
+ res.unlockBag(startOfBag);
+ return NULL;
+ }
+
+ Res_value value;
+ const ResTable::bag_entry* bag = startOfBag;
+ for (size_t i=0; ((ssize_t)i)<N; i++, bag++) {
+ value = bag->map.value;
+
+ // Take care of resolving the found resource to its final value.
+ ssize_t block = res.resolveReference(&value, bag->stringBlock, NULL);
+ if (kThrowOnBadId) {
+ if (block == BAD_INDEX) {
+ jniThrowException(env, "java/lang/IllegalStateException", "Bad resource!");
+ return array;
+ }
+ }
+ if (value.dataType >= Res_value::TYPE_FIRST_INT
+ && value.dataType <= Res_value::TYPE_LAST_INT) {
+ int intVal = value.data;
+ env->SetIntArrayRegion(array, i, 1, &intVal);
+ }
+ }
+ res.unlockBag(startOfBag);
+ return array;
+}
+
+static jintArray android_content_AssetManager_getStyleAttributes(JNIEnv* env, jobject clazz,
+ jint styleId)
+{
+ AssetManager* am = assetManagerForJavaObject(env, clazz);
+ if (am == NULL) {
+ return NULL;
+ }
+ const ResTable& res(am->getResources());
+
+ const ResTable::bag_entry* startOfBag;
+ const ssize_t N = res.lockBag(styleId, &startOfBag);
+ if (N < 0) {
+ return NULL;
+ }
+
+ jintArray array = env->NewIntArray(N);
+ if (array == NULL) {
+ res.unlockBag(startOfBag);
+ return NULL;
+ }
+
+ const ResTable::bag_entry* bag = startOfBag;
+ for (size_t i=0; ((ssize_t)i)<N; i++, bag++) {
+ int resourceId = bag->map.name.ident;
+ env->SetIntArrayRegion(array, i, 1, &resourceId);
+ }
+ res.unlockBag(startOfBag);
+ return array;
+}
+
+static void android_content_AssetManager_init(JNIEnv* env, jobject clazz, jboolean isSystem)
+{
+ if (isSystem) {
+ verifySystemIdmaps();
+ }
+ AssetManager* am = new AssetManager();
+ if (am == NULL) {
+ jniThrowException(env, "java/lang/OutOfMemoryError", "");
+ return;
+ }
+
+ am->addDefaultAssets();
+
+ ALOGV("Created AssetManager %p for Java object %p\n", am, clazz);
+ env->SetLongField(clazz, gAssetManagerOffsets.mObject, reinterpret_cast<jlong>(am));
+}
+
+static void android_content_AssetManager_destroy(JNIEnv* env, jobject clazz)
+{
+ AssetManager* am = (AssetManager*)
+ (env->GetLongField(clazz, gAssetManagerOffsets.mObject));
+ ALOGV("Destroying AssetManager %p for Java object %p\n", am, clazz);
+ if (am != NULL) {
+ delete am;
+ env->SetLongField(clazz, gAssetManagerOffsets.mObject, 0);
+ }
+}
+
+static jint android_content_AssetManager_getGlobalAssetCount(JNIEnv* env, jobject clazz)
+{
+ return Asset::getGlobalCount();
+}
+
+static jobject android_content_AssetManager_getAssetAllocations(JNIEnv* env, jobject clazz)
+{
+ String8 alloc = Asset::getAssetAllocations();
+ if (alloc.length() <= 0) {
+ return NULL;
+ }
+
+ jstring str = env->NewStringUTF(alloc.string());
+ return str;
+}
+
+static jint android_content_AssetManager_getGlobalAssetManagerCount(JNIEnv* env, jobject clazz)
+{
+ return AssetManager::getGlobalCount();
}
// ----------------------------------------------------------------------------
-// JNI registration.
+/*
+ * JNI registration.
+ */
static const JNINativeMethod gAssetManagerMethods[] = {
- // AssetManager setup methods.
- {"nativeCreate", "()J", (void*)NativeCreate},
- {"nativeDestroy", "(J)V", (void*)NativeDestroy},
- {"nativeSetApkAssets", "(J[Landroid/content/res/ApkAssets;Z)V", (void*)NativeSetApkAssets},
- {"nativeSetConfiguration", "(JIILjava/lang/String;IIIIIIIIIIIIIII)V",
- (void*)NativeSetConfiguration},
- {"nativeGetAssignedPackageIdentifiers", "(J)Landroid/util/SparseArray;",
- (void*)NativeGetAssignedPackageIdentifiers},
+ /* name, signature, funcPtr */
- // AssetManager file methods.
- {"nativeList", "(JLjava/lang/String;)[Ljava/lang/String;", (void*)NativeList},
- {"nativeOpenAsset", "(JLjava/lang/String;I)J", (void*)NativeOpenAsset},
- {"nativeOpenAssetFd", "(JLjava/lang/String;[J)Landroid/os/ParcelFileDescriptor;",
- (void*)NativeOpenAssetFd},
- {"nativeOpenNonAsset", "(JILjava/lang/String;I)J", (void*)NativeOpenNonAsset},
- {"nativeOpenNonAssetFd", "(JILjava/lang/String;[J)Landroid/os/ParcelFileDescriptor;",
- (void*)NativeOpenNonAssetFd},
- {"nativeOpenXmlAsset", "(JILjava/lang/String;)J", (void*)NativeOpenXmlAsset},
+ // Basic asset stuff.
+ { "openAsset", "(Ljava/lang/String;I)J",
+ (void*) android_content_AssetManager_openAsset },
+ { "openAssetFd", "(Ljava/lang/String;[J)Landroid/os/ParcelFileDescriptor;",
+ (void*) android_content_AssetManager_openAssetFd },
+ { "openNonAssetNative", "(ILjava/lang/String;I)J",
+ (void*) android_content_AssetManager_openNonAssetNative },
+ { "openNonAssetFdNative", "(ILjava/lang/String;[J)Landroid/os/ParcelFileDescriptor;",
+ (void*) android_content_AssetManager_openNonAssetFdNative },
+ { "list", "(Ljava/lang/String;)[Ljava/lang/String;",
+ (void*) android_content_AssetManager_list },
+ { "destroyAsset", "(J)V",
+ (void*) android_content_AssetManager_destroyAsset },
+ { "readAssetChar", "(J)I",
+ (void*) android_content_AssetManager_readAssetChar },
+ { "readAsset", "(J[BII)I",
+ (void*) android_content_AssetManager_readAsset },
+ { "seekAsset", "(JJI)J",
+ (void*) android_content_AssetManager_seekAsset },
+ { "getAssetLength", "(J)J",
+ (void*) android_content_AssetManager_getAssetLength },
+ { "getAssetRemainingLength", "(J)J",
+ (void*) android_content_AssetManager_getAssetRemainingLength },
+ { "addAssetPathNative", "(Ljava/lang/String;Z)I",
+ (void*) android_content_AssetManager_addAssetPath },
+ { "addAssetFdNative", "(Ljava/io/FileDescriptor;Ljava/lang/String;Z)I",
+ (void*) android_content_AssetManager_addAssetFd },
+ { "addOverlayPathNative", "(Ljava/lang/String;)I",
+ (void*) android_content_AssetManager_addOverlayPath },
+ { "isUpToDate", "()Z",
+ (void*) android_content_AssetManager_isUpToDate },
- // AssetManager resource methods.
- {"nativeGetResourceValue", "(JISLandroid/util/TypedValue;Z)I", (void*)NativeGetResourceValue},
- {"nativeGetResourceBagValue", "(JIILandroid/util/TypedValue;)I",
- (void*)NativeGetResourceBagValue},
- {"nativeGetStyleAttributes", "(JI)[I", (void*)NativeGetStyleAttributes},
- {"nativeGetResourceStringArray", "(JI)[Ljava/lang/String;",
- (void*)NativeGetResourceStringArray},
- {"nativeGetResourceStringArrayInfo", "(JI)[I", (void*)NativeGetResourceStringArrayInfo},
- {"nativeGetResourceIntArray", "(JI)[I", (void*)NativeGetResourceIntArray},
- {"nativeGetResourceArraySize", "(JI)I", (void*)NativeGetResourceArraySize},
- {"nativeGetResourceArray", "(JI[I)I", (void*)NativeGetResourceArray},
+ // Resources.
+ { "getLocales", "()[Ljava/lang/String;",
+ (void*) android_content_AssetManager_getLocales },
+ { "getNonSystemLocales", "()[Ljava/lang/String;",
+ (void*) android_content_AssetManager_getNonSystemLocales },
+ { "getSizeConfigurations", "()[Landroid/content/res/Configuration;",
+ (void*) android_content_AssetManager_getSizeConfigurations },
+ { "setConfiguration", "(IILjava/lang/String;IIIIIIIIIIIIIII)V",
+ (void*) android_content_AssetManager_setConfiguration },
+ { "getResourceIdentifier","(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)I",
+ (void*) android_content_AssetManager_getResourceIdentifier },
+ { "getResourceName","(I)Ljava/lang/String;",
+ (void*) android_content_AssetManager_getResourceName },
+ { "getResourcePackageName","(I)Ljava/lang/String;",
+ (void*) android_content_AssetManager_getResourcePackageName },
+ { "getResourceTypeName","(I)Ljava/lang/String;",
+ (void*) android_content_AssetManager_getResourceTypeName },
+ { "getResourceEntryName","(I)Ljava/lang/String;",
+ (void*) android_content_AssetManager_getResourceEntryName },
+ { "loadResourceValue","(ISLandroid/util/TypedValue;Z)I",
+ (void*) android_content_AssetManager_loadResourceValue },
+ { "loadResourceBagValue","(IILandroid/util/TypedValue;Z)I",
+ (void*) android_content_AssetManager_loadResourceBagValue },
+ { "getStringBlockCount","()I",
+ (void*) android_content_AssetManager_getStringBlockCount },
+ { "getNativeStringBlock","(I)J",
+ (void*) android_content_AssetManager_getNativeStringBlock },
+ { "getCookieName","(I)Ljava/lang/String;",
+ (void*) android_content_AssetManager_getCookieName },
+ { "getAssignedPackageIdentifiers","()Landroid/util/SparseArray;",
+ (void*) android_content_AssetManager_getAssignedPackageIdentifiers },
- // AssetManager resource name/ID methods.
- {"nativeGetResourceIdentifier", "(JLjava/lang/String;Ljava/lang/String;Ljava/lang/String;)I",
- (void*)NativeGetResourceIdentifier},
- {"nativeGetResourceName", "(JI)Ljava/lang/String;", (void*)NativeGetResourceName},
- {"nativeGetResourcePackageName", "(JI)Ljava/lang/String;", (void*)NativeGetResourcePackageName},
- {"nativeGetResourceTypeName", "(JI)Ljava/lang/String;", (void*)NativeGetResourceTypeName},
- {"nativeGetResourceEntryName", "(JI)Ljava/lang/String;", (void*)NativeGetResourceEntryName},
- {"nativeGetLocales", "(JZ)[Ljava/lang/String;", (void*)NativeGetLocales},
- {"nativeGetSizeConfigurations", "(J)[Landroid/content/res/Configuration;",
- (void*)NativeGetSizeConfigurations},
+ // Themes.
+ { "newTheme", "()J",
+ (void*) android_content_AssetManager_newTheme },
+ { "deleteTheme", "(J)V",
+ (void*) android_content_AssetManager_deleteTheme },
+ { "applyThemeStyle", "(JIZ)V",
+ (void*) android_content_AssetManager_applyThemeStyle },
+ { "copyTheme", "(JJ)V",
+ (void*) android_content_AssetManager_copyTheme },
+ { "clearTheme", "(J)V",
+ (void*) android_content_AssetManager_clearTheme },
+ { "loadThemeAttributeValue", "(JILandroid/util/TypedValue;Z)I",
+ (void*) android_content_AssetManager_loadThemeAttributeValue },
+ { "getThemeChangingConfigurations", "(J)I",
+ (void*) android_content_AssetManager_getThemeChangingConfigurations },
+ { "dumpTheme", "(JILjava/lang/String;Ljava/lang/String;)V",
+ (void*) android_content_AssetManager_dumpTheme },
+ { "applyStyle","(JIIJ[IIJJ)V",
+ (void*) android_content_AssetManager_applyStyle },
+ { "resolveAttrs","(JII[I[I[I[I)Z",
+ (void*) android_content_AssetManager_resolveAttrs },
+ { "retrieveAttributes","(J[I[I[I)Z",
+ (void*) android_content_AssetManager_retrieveAttributes },
+ { "getArraySize","(I)I",
+ (void*) android_content_AssetManager_getArraySize },
+ { "retrieveArray","(I[I)I",
+ (void*) android_content_AssetManager_retrieveArray },
- // Style attribute related methods.
- {"nativeApplyStyle", "(JJIIJ[IJJ)V", (void*)NativeApplyStyle},
- {"nativeResolveAttrs", "(JJII[I[I[I[I)Z", (void*)NativeResolveAttrs},
- {"nativeRetrieveAttributes", "(JJ[I[I[I)Z", (void*)NativeRetrieveAttributes},
+ // XML files.
+ { "openXmlAssetNative", "(ILjava/lang/String;)J",
+ (void*) android_content_AssetManager_openXmlAssetNative },
- // Theme related methods.
- {"nativeThemeCreate", "(J)J", (void*)NativeThemeCreate},
- {"nativeThemeDestroy", "(J)V", (void*)NativeThemeDestroy},
- {"nativeThemeApplyStyle", "(JJIZ)V", (void*)NativeThemeApplyStyle},
- {"nativeThemeCopy", "(JJ)V", (void*)NativeThemeCopy},
- {"nativeThemeClear", "(J)V", (void*)NativeThemeClear},
- {"nativeThemeGetAttributeValue", "(JJILandroid/util/TypedValue;Z)I",
- (void*)NativeThemeGetAttributeValue},
- {"nativeThemeDump", "(JJILjava/lang/String;Ljava/lang/String;)V", (void*)NativeThemeDump},
- {"nativeThemeGetChangingConfigurations", "(J)I", (void*)NativeThemeGetChangingConfigurations},
+ // Arrays.
+ { "getArrayStringResource","(I)[Ljava/lang/String;",
+ (void*) android_content_AssetManager_getArrayStringResource },
+ { "getArrayStringInfo","(I)[I",
+ (void*) android_content_AssetManager_getArrayStringInfo },
+ { "getArrayIntResource","(I)[I",
+ (void*) android_content_AssetManager_getArrayIntResource },
+ { "getStyleAttributes","(I)[I",
+ (void*) android_content_AssetManager_getStyleAttributes },
- // AssetInputStream methods.
- {"nativeAssetDestroy", "(J)V", (void*)NativeAssetDestroy},
- {"nativeAssetReadChar", "(J)I", (void*)NativeAssetReadChar},
- {"nativeAssetRead", "(J[BII)I", (void*)NativeAssetRead},
- {"nativeAssetSeek", "(JJI)J", (void*)NativeAssetSeek},
- {"nativeAssetGetLength", "(J)J", (void*)NativeAssetGetLength},
- {"nativeAssetGetRemainingLength", "(J)J", (void*)NativeAssetGetRemainingLength},
-
- // System/idmap related methods.
- {"nativeVerifySystemIdmaps", "()V", (void*)NativeVerifySystemIdmaps},
-
- // Global management/debug methods.
- {"getGlobalAssetCount", "()I", (void*)NativeGetGlobalAssetCount},
- {"getAssetAllocations", "()Ljava/lang/String;", (void*)NativeGetAssetAllocations},
- {"getGlobalAssetManagerCount", "()I", (void*)NativeGetGlobalAssetManagerCount},
+ // Bookkeeping.
+ { "init", "(Z)V",
+ (void*) android_content_AssetManager_init },
+ { "destroy", "()V",
+ (void*) android_content_AssetManager_destroy },
+ { "getGlobalAssetCount", "()I",
+ (void*) android_content_AssetManager_getGlobalAssetCount },
+ { "getAssetAllocations", "()Ljava/lang/String;",
+ (void*) android_content_AssetManager_getAssetAllocations },
+ { "getGlobalAssetManagerCount", "()I",
+ (void*) android_content_AssetManager_getGlobalAssetManagerCount },
};
-int register_android_content_AssetManager(JNIEnv* env) {
- jclass apk_assets_class = FindClassOrDie(env, "android/content/res/ApkAssets");
- gApkAssetsFields.native_ptr = GetFieldIDOrDie(env, apk_assets_class, "mNativePtr", "J");
+int register_android_content_AssetManager(JNIEnv* env)
+{
+ jclass typedValue = FindClassOrDie(env, "android/util/TypedValue");
+ gTypedValueOffsets.mType = GetFieldIDOrDie(env, typedValue, "type", "I");
+ gTypedValueOffsets.mData = GetFieldIDOrDie(env, typedValue, "data", "I");
+ gTypedValueOffsets.mString = GetFieldIDOrDie(env, typedValue, "string",
+ "Ljava/lang/CharSequence;");
+ gTypedValueOffsets.mAssetCookie = GetFieldIDOrDie(env, typedValue, "assetCookie", "I");
+ gTypedValueOffsets.mResourceId = GetFieldIDOrDie(env, typedValue, "resourceId", "I");
+ gTypedValueOffsets.mChangingConfigurations = GetFieldIDOrDie(env, typedValue,
+ "changingConfigurations", "I");
+ gTypedValueOffsets.mDensity = GetFieldIDOrDie(env, typedValue, "density", "I");
- jclass typedValue = FindClassOrDie(env, "android/util/TypedValue");
- gTypedValueOffsets.mType = GetFieldIDOrDie(env, typedValue, "type", "I");
- gTypedValueOffsets.mData = GetFieldIDOrDie(env, typedValue, "data", "I");
- gTypedValueOffsets.mString =
- GetFieldIDOrDie(env, typedValue, "string", "Ljava/lang/CharSequence;");
- gTypedValueOffsets.mAssetCookie = GetFieldIDOrDie(env, typedValue, "assetCookie", "I");
- gTypedValueOffsets.mResourceId = GetFieldIDOrDie(env, typedValue, "resourceId", "I");
- gTypedValueOffsets.mChangingConfigurations =
- GetFieldIDOrDie(env, typedValue, "changingConfigurations", "I");
- gTypedValueOffsets.mDensity = GetFieldIDOrDie(env, typedValue, "density", "I");
+ jclass assetFd = FindClassOrDie(env, "android/content/res/AssetFileDescriptor");
+ gAssetFileDescriptorOffsets.mFd = GetFieldIDOrDie(env, assetFd, "mFd",
+ "Landroid/os/ParcelFileDescriptor;");
+ gAssetFileDescriptorOffsets.mStartOffset = GetFieldIDOrDie(env, assetFd, "mStartOffset", "J");
+ gAssetFileDescriptorOffsets.mLength = GetFieldIDOrDie(env, assetFd, "mLength", "J");
- jclass assetFd = FindClassOrDie(env, "android/content/res/AssetFileDescriptor");
- gAssetFileDescriptorOffsets.mFd =
- GetFieldIDOrDie(env, assetFd, "mFd", "Landroid/os/ParcelFileDescriptor;");
- gAssetFileDescriptorOffsets.mStartOffset = GetFieldIDOrDie(env, assetFd, "mStartOffset", "J");
- gAssetFileDescriptorOffsets.mLength = GetFieldIDOrDie(env, assetFd, "mLength", "J");
+ jclass assetManager = FindClassOrDie(env, "android/content/res/AssetManager");
+ gAssetManagerOffsets.mObject = GetFieldIDOrDie(env, assetManager, "mObject", "J");
- jclass assetManager = FindClassOrDie(env, "android/content/res/AssetManager");
- gAssetManagerOffsets.mObject = GetFieldIDOrDie(env, assetManager, "mObject", "J");
+ jclass stringClass = FindClassOrDie(env, "java/lang/String");
+ g_stringClass = MakeGlobalRefOrDie(env, stringClass);
- jclass stringClass = FindClassOrDie(env, "java/lang/String");
- g_stringClass = MakeGlobalRefOrDie(env, stringClass);
+ jclass sparseArrayClass = FindClassOrDie(env, "android/util/SparseArray");
+ gSparseArrayOffsets.classObject = MakeGlobalRefOrDie(env, sparseArrayClass);
+ gSparseArrayOffsets.constructor = GetMethodIDOrDie(env, gSparseArrayOffsets.classObject,
+ "<init>", "()V");
+ gSparseArrayOffsets.put = GetMethodIDOrDie(env, gSparseArrayOffsets.classObject, "put",
+ "(ILjava/lang/Object;)V");
- jclass sparseArrayClass = FindClassOrDie(env, "android/util/SparseArray");
- gSparseArrayOffsets.classObject = MakeGlobalRefOrDie(env, sparseArrayClass);
- gSparseArrayOffsets.constructor =
- GetMethodIDOrDie(env, gSparseArrayOffsets.classObject, "<init>", "()V");
- gSparseArrayOffsets.put =
- GetMethodIDOrDie(env, gSparseArrayOffsets.classObject, "put", "(ILjava/lang/Object;)V");
+ jclass configurationClass = FindClassOrDie(env, "android/content/res/Configuration");
+ gConfigurationOffsets.classObject = MakeGlobalRefOrDie(env, configurationClass);
+ gConfigurationOffsets.constructor = GetMethodIDOrDie(env, configurationClass,
+ "<init>", "()V");
+ gConfigurationOffsets.mSmallestScreenWidthDpOffset = GetFieldIDOrDie(env, configurationClass,
+ "smallestScreenWidthDp", "I");
+ gConfigurationOffsets.mScreenWidthDpOffset = GetFieldIDOrDie(env, configurationClass,
+ "screenWidthDp", "I");
+ gConfigurationOffsets.mScreenHeightDpOffset = GetFieldIDOrDie(env, configurationClass,
+ "screenHeightDp", "I");
- jclass configurationClass = FindClassOrDie(env, "android/content/res/Configuration");
- gConfigurationOffsets.classObject = MakeGlobalRefOrDie(env, configurationClass);
- gConfigurationOffsets.constructor = GetMethodIDOrDie(env, configurationClass, "<init>", "()V");
- gConfigurationOffsets.mSmallestScreenWidthDpOffset =
- GetFieldIDOrDie(env, configurationClass, "smallestScreenWidthDp", "I");
- gConfigurationOffsets.mScreenWidthDpOffset =
- GetFieldIDOrDie(env, configurationClass, "screenWidthDp", "I");
- gConfigurationOffsets.mScreenHeightDpOffset =
- GetFieldIDOrDie(env, configurationClass, "screenHeightDp", "I");
-
- return RegisterMethodsOrDie(env, "android/content/res/AssetManager", gAssetManagerMethods,
- NELEM(gAssetManagerMethods));
+ return RegisterMethodsOrDie(env, "android/content/res/AssetManager", gAssetManagerMethods,
+ NELEM(gAssetManagerMethods));
}
}; // namespace android
diff --git a/core/jni/include/android_runtime/android_util_AssetManager.h b/core/jni/include/android_runtime/android_util_AssetManager.h
index 2c1e357..8dd9337 100644
--- a/core/jni/include/android_runtime/android_util_AssetManager.h
+++ b/core/jni/include/android_runtime/android_util_AssetManager.h
@@ -14,20 +14,17 @@
* limitations under the License.
*/
-#ifndef ANDROID_RUNTIME_ASSETMANAGER_H
-#define ANDROID_RUNTIME_ASSETMANAGER_H
+#ifndef android_util_AssetManager_H
+#define android_util_AssetManager_H
-#include "androidfw/AssetManager2.h"
-#include "androidfw/MutexGuard.h"
+#include <androidfw/AssetManager.h>
#include "jni.h"
namespace android {
-extern AAssetManager* NdkAssetManagerForJavaObject(JNIEnv* env, jobject jassetmanager);
-extern Guarded<AssetManager2>* AssetManagerForJavaObject(JNIEnv* env, jobject jassetmanager);
-extern Guarded<AssetManager2>* AssetManagerForNdkAssetManager(AAssetManager* assetmanager);
+extern AssetManager* assetManagerForJavaObject(JNIEnv* env, jobject assetMgr);
-} // namespace android
+}
-#endif // ANDROID_RUNTIME_ASSETMANAGER_H
+#endif
diff --git a/core/proto/android/server/alarmmanagerservice.proto b/core/proto/android/server/alarmmanagerservice.proto
index 0342c9c..d1c5db6 100644
--- a/core/proto/android/server/alarmmanagerservice.proto
+++ b/core/proto/android/server/alarmmanagerservice.proto
@@ -104,11 +104,6 @@
repeated InFlightProto outstanding_deliveries = 34;
- // Minimum time between ALLOW_WHILE_IDLE alarms when system is idling. It
- // should be either CosntantsProto.allow_while_idle_short_duration_ms or
- // ConstantsProto.allow_while_idle_long_duration_ms.
- optional int64 allow_while_idle_min_duration_ms = 35;
-
message LastAllowWhileIdleDispatch {
option (.android.msg_privacy).dest = DEST_AUTOMATIC;
@@ -121,7 +116,7 @@
}
// Whether the short or long while-idle timeout should be used for each UID.
- repeated int32 use_allow_while_idle_short_time = 42;
+ repeated int32 use_allow_while_idle_short_time = 35;
// For each uid, this is the last time we dispatched an "allow while idle"
// alarm, used to determine the earliest we can dispatch the next such alarm.
diff --git a/core/proto/android/server/powermanagerservice.proto b/core/proto/android/server/powermanagerservice.proto
index b5c3ac0..5cb5319 100644
--- a/core/proto/android/server/powermanagerservice.proto
+++ b/core/proto/android/server/powermanagerservice.proto
@@ -227,7 +227,6 @@
optional int32 setting_minimum = 1;
optional int32 setting_maximum = 2;
optional int32 setting_default = 3;
- optional int32 setting_for_vr_default = 4;
}
// True to decouple auto-suspend mode from the display state.
@@ -293,44 +292,27 @@
// The stay on while plugged in setting.
// A set of battery conditions under which to make the screen stay on.
optional StayOnWhilePluggedInProto stay_on_while_plugged_in = 29;
- // The screen brightness setting, from 0 to 255.
- // Use -1 if no value has been set.
- optional sint32 screen_brightness_setting = 30;
- // The screen auto-brightness adjustment setting, from -1 to 1.
- // Use 0 if there is no adjustment.
- optional float screen_auto_brightness_adjustment_setting = 31;
// The screen brightness mode.
- optional .android.providers.settings.SettingsProto.ScreenBrightnessMode screen_brightness_mode_setting = 32;
+ optional .android.providers.settings.SettingsProto.ScreenBrightnessMode screen_brightness_mode_setting = 30;
// The screen brightness setting override from the window manager
// to allow the current foreground activity to override the brightness.
// Use -1 to disable.
- optional sint32 screen_brightness_override_from_window_manager = 33;
+ optional sint32 screen_brightness_override_from_window_manager = 31;
// The user activity timeout override from the window manager
// to allow the current foreground activity to override the user activity
// timeout. Use -1 to disable.
- optional sint64 user_activity_timeout_override_from_window_manager_ms = 34;
+ optional sint64 user_activity_timeout_override_from_window_manager_ms = 32;
// The window manager has determined the user to be inactive via other means.
// Set this to false to disable.
- optional bool is_user_inactive_override_from_window_manager = 35;
- // The screen brightness setting override from the settings application
- // to temporarily adjust the brightness until next updated,
- // Use -1 to disable.
- optional sint32 temporary_screen_brightness_setting_override = 36;
- // The screen brightness adjustment setting override from the settings
- // application to temporarily adjust the auto-brightness adjustment factor
- // until next updated, in the range -1..1.
- // Use NaN to disable.
- optional float temporary_screen_auto_brightness_adjustment_setting_override = 37;
+ optional bool is_user_inactive_override_from_window_manager = 33;
// The screen state to use while dozing.
- optional .android.view.DisplayStateEnum doze_screen_state_override_from_dream_manager = 38;
+ optional .android.view.DisplayStateEnum doze_screen_state_override_from_dream_manager = 34;
// The screen brightness to use while dozing.
- optional float dozed_screen_brightness_override_from_dream_manager = 39;
+ optional float dozed_screen_brightness_override_from_dream_manager = 35;
// Screen brightness settings limits.
- optional ScreenBrightnessSettingLimitsProto screen_brightness_setting_limits = 40;
- // The screen brightness setting, from 0 to 255, to be used while in VR Mode.
- optional int32 screen_brightness_for_vr_setting = 41;
+ optional ScreenBrightnessSettingLimitsProto screen_brightness_setting_limits = 36;
// True if double tap to wake is enabled
- optional bool is_double_tap_wake_enabled = 42;
+ optional bool is_double_tap_wake_enabled = 37;
// True if we are currently in VR Mode.
- optional bool is_vr_mode_enabled = 43;
+ optional bool is_vr_mode_enabled = 38;
}
diff --git a/core/proto/android/service/package.proto b/core/proto/android/service/package.proto
index ef777de..f8050a1 100644
--- a/core/proto/android/service/package.proto
+++ b/core/proto/android/service/package.proto
@@ -49,7 +49,8 @@
option (android.msg_privacy).dest = DEST_AUTOMATIC;
optional int32 user_id = 1;
- optional string name = 2 [ (android.privacy).dest = DEST_EXPLICIT ];
+ // Name of the shared UID. eg: android.uid.bluetooth
+ optional string name = 2;
}
// Installed packages.
diff --git a/core/proto/android/telecomm/enums.proto b/core/proto/android/telecomm/enums.proto
new file mode 100644
index 0000000..7a2ba62
--- /dev/null
+++ b/core/proto/android/telecomm/enums.proto
@@ -0,0 +1,185 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+syntax = "proto2";
+package android.telecom;
+
+option java_outer_classname = "TelecomProtoEnums";
+option java_multiple_files = true;
+
+/**
+ * Call states, primarily used in CallState.java,
+ * Call.java, and CallsManager.java in packages/services.
+ */
+enum CallStateEnum {
+ /**
+ * Indicates that a call is new and not connected. This is used as the default state internally
+ * within Telecom and should not be used between Telecom and call services. Call services are
+ * not expected to ever interact with NEW calls, but {@link android.telecom.InCallService}s will
+ * see calls in this state.
+ */
+ NEW = 0;
+
+ /**
+ * The initial state of an outgoing {@code Call}.
+ * Common transitions are to {@link #DIALING} state for a successful call or
+ * {@link #DISCONNECTED} if it failed.
+ */
+ CONNECTING = 1;
+
+ /**
+ * The state of an outgoing {@code Call} when waiting on user to select a
+ * {@link android.telecom.PhoneAccount} through which to place the call.
+ */
+ SELECT_PHONE_ACCOUNT = 2;
+
+ /**
+ * Indicates that a call is outgoing and in the dialing state. A call transitions to this state
+ * once an outgoing call has begun (e.g., user presses the dial button in Dialer). Calls in this
+ * state usually transition to {@link #ACTIVE} if the call was answered or {@link #DISCONNECTED}
+ * if the call was disconnected somehow (e.g., failure or cancellation of the call by the user).
+ */
+ DIALING = 3;
+
+ /**
+ * Indicates that a call is incoming and the user still has the option of answering, rejecting,
+ * or doing nothing with the call. This state is usually associated with some type of audible
+ * ringtone. Normal transitions are to {@link #ACTIVE} if answered or {@link #DISCONNECTED}
+ * otherwise.
+ */
+ RINGING = 4;
+
+ /**
+ * Indicates that a call is currently connected to another party and a communication channel is
+ * open between them. The normal transition to this state is by the user answering a
+ * {@link #DIALING} call or a {@link #RINGING} call being answered by the other party.
+ */
+ ACTIVE = 5;
+
+ /**
+ * Indicates that the call is currently on hold. In this state, the call is not terminated
+ * but no communication is allowed until the call is no longer on hold. The typical transition
+ * to this state is by the user putting an {@link #ACTIVE} call on hold by explicitly performing
+ * an action, such as clicking the hold button.
+ */
+ ON_HOLD = 6;
+
+ /**
+ * Indicates that a call is currently disconnected. All states can transition to this state
+ * by the call service giving notice that the connection has been severed. When the user
+ * explicitly ends a call, it will not transition to this state until the call service confirms
+ * the disconnection or communication was lost to the call service currently responsible for
+ * this call (e.g., call service crashes).
+ */
+ DISCONNECTED = 7;
+
+ /**
+ * Indicates that the call was attempted (mostly in the context of outgoing, at least at the
+ * time of writing) but cancelled before it was successfully connected.
+ */
+ ABORTED = 8;
+
+ /**
+ * Indicates that the call is in the process of being disconnected and will transition next
+ * to a {@link #DISCONNECTED} state.
+ * <p>
+ * This state is not expected to be communicated from the Telephony layer, but will be reported
+ * to the InCall UI for calls where disconnection has been initiated by the user but the
+ * ConnectionService has confirmed the call as disconnected.
+ */
+ DISCONNECTING = 9;
+
+ /**
+ * Indicates that the call is in the process of being pulled to the local device.
+ * <p>
+ * This state should only be set on a call with
+ * {@link android.telecom.Connection#PROPERTY_IS_EXTERNAL_CALL} and
+ * {@link android.telecom.Connection#CAPABILITY_CAN_PULL_CALL}.
+ */
+ PULLING = 10;
+}
+
+// Disconnect causes for a call. Primarily used by android/telecom/DisconnectCause.java
+enum DisconnectCauseEnum {
+ /**
+ * Disconnected because of an unknown or unspecified reason.
+ */
+ UNKNOWN = 0;
+
+ /**
+ * Disconnected because there was an error, such as a problem with the network.
+ */
+ ERROR = 1;
+
+ /**
+ * Disconnected because of a local user-initiated action, such as hanging up.
+ */
+ LOCAL = 2;
+
+ /**
+ * Disconnected because of a remote user-initiated action, such as the other party hanging up
+ * up.
+ */
+ REMOTE = 3;
+
+ /**
+ * Disconnected because it has been canceled.
+ */
+ CANCELED = 4;
+
+ /**
+ * Disconnected because there was no response to an incoming call.
+ */
+ MISSED = 5;
+
+ /**
+ * Disconnected because the user rejected an incoming call.
+ */
+ REJECTED = 6;
+
+ /**
+ * Disconnected because the other party was busy.
+ */
+ BUSY = 7;
+
+ /**
+ * Disconnected because of a restriction on placing the call, such as dialing in airplane
+ * mode.
+ */
+ RESTRICTED = 8;
+
+ /**
+ * Disconnected for reason not described by other disconnect codes.
+ */
+ OTHER = 9;
+
+ /**
+ * Disconnected because the connection manager did not support the call. The call will be tried
+ * again without a connection manager. See {@link PhoneAccount#CAPABILITY_CONNECTION_MANAGER}.
+ */
+ CONNECTION_MANAGER_NOT_SUPPORTED = 10;
+
+ /**
+ * Disconnected because the user did not locally answer the incoming call, but it was answered
+ * on another device where the call was ringing.
+ */
+ ANSWERED_ELSEWHERE = 11;
+
+ /**
+ * Disconnected because the call was pulled from the current device to another device.
+ */
+ CALL_PULLED = 12;
+}
diff --git a/core/proto/android/view/displayinfo.proto b/core/proto/android/view/displayinfo.proto
index 3ac8f3b..cbd06fd 100644
--- a/core/proto/android/view/displayinfo.proto
+++ b/core/proto/android/view/displayinfo.proto
@@ -29,4 +29,5 @@
optional int32 logical_height = 2;
optional int32 app_width = 3;
optional int32 app_height = 4;
+ optional string name = 5;
}
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index deefddb..f6f1d81 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -53,6 +53,7 @@
<protected-broadcast android:name="android.intent.action.UID_REMOVED" />
<protected-broadcast android:name="android.intent.action.QUERY_PACKAGE_RESTART" />
<protected-broadcast android:name="android.intent.action.CONFIGURATION_CHANGED" />
+ <protected-broadcast android:name="android.intent.action.SPLIT_CONFIGURATION_CHANGED" />
<protected-broadcast android:name="android.intent.action.LOCALE_CHANGED" />
<protected-broadcast android:name="android.intent.action.BATTERY_CHANGED" />
<protected-broadcast android:name="android.intent.action.BATTERY_LOW" />
@@ -3784,6 +3785,15 @@
<permission android:name="android.permission.INSTANT_APP_FOREGROUND_SERVICE"
android:protectionLevel="signature|development|instant|appop" />
+ <!-- Allows a regular application to use {@link android.app.Service#startForeground
+ Service.startForeground}.
+ <p>Protection level: normal
+ -->
+ <permission android:name="android.permission.FOREGROUND_SERVICE"
+ android:description="@string/permdesc_foregroundService"
+ android:label="@string/permlab_foregroundService"
+ android:protectionLevel="normal|instant" />
+
<!-- @hide Allows system components to access all app shortcuts. -->
<permission android:name="android.permission.ACCESS_SHORTCUTS"
android:protectionLevel="signature" />
diff --git a/core/res/res/values/colors_material.xml b/core/res/res/values/colors_material.xml
index ce4ac61..e80f16c 100644
--- a/core/res/res/values/colors_material.xml
+++ b/core/res/res/values/colors_material.xml
@@ -79,8 +79,8 @@
<item name="secondary_content_alpha_material_light" format="float" type="dimen">0.54</item>
<item name="highlight_alpha_material_light" format="float" type="dimen">0.16</item>
- <item name="highlight_alpha_material_dark" format="float" type="dimen">0.32</item>
- <item name="highlight_alpha_material_colored" format="float" type="dimen">0.48</item>
+ <item name="highlight_alpha_material_dark" format="float" type="dimen">0.16</item>
+ <item name="highlight_alpha_material_colored" format="float" type="dimen">0.16</item>
<!-- Primary & accent colors -->
<eat-comment />
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index d99f28e..d2194ba 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2494,9 +2494,9 @@
<string-array translatable="false" name="config_globalActionsList">
<item>power</item>
<item>restart</item>
- <item>screenshot</item>
- <item>logout</item>
<item>lockdown</item>
+ <item>logout</item>
+ <item>screenshot</item>
<item>bugreport</item>
<item>users</item>
</string-array>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index ec81df7..2b7b056 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -916,6 +916,11 @@
<string name="permdesc_persistentActivity" product="default">Allows the app to make parts of itself persistent in memory. This can limit memory available to other apps slowing down the phone.</string>
<!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+ <string name="permlab_foregroundService">run foreground service</string>
+ <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+ <string name="permdesc_foregroundService">Allows the app to make use of foreground services.</string>
+
+ <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
<string name="permlab_getPackageSize">measure app storage space</string>
<!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
<string name="permdesc_getPackageSize">Allows the app to retrieve its code, data, and cache sizes</string>
diff --git a/core/tests/coretests/AndroidManifest.xml b/core/tests/coretests/AndroidManifest.xml
index 7d5c60a..53c22f6 100644
--- a/core/tests/coretests/AndroidManifest.xml
+++ b/core/tests/coretests/AndroidManifest.xml
@@ -51,6 +51,7 @@
<uses-permission android:name="android.permission.CLEAR_APP_USER_DATA" />
<uses-permission android:name="android.permission.DELETE_CACHE_FILES" />
<uses-permission android:name="android.permission.DOWNLOAD_CACHE_NON_PURGEABLE" />
+ <uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
<uses-permission android:name="android.permission.GET_PACKAGE_SIZE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.INJECT_EVENTS" />
diff --git a/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java b/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java
index aefc47e..fb0f534 100644
--- a/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java
+++ b/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java
@@ -17,6 +17,7 @@
package android.app.servertransaction;
import static android.app.servertransaction.TestUtils.config;
+import static android.app.servertransaction.TestUtils.mergedConfig;
import static android.app.servertransaction.TestUtils.referrerIntentList;
import static android.app.servertransaction.TestUtils.resultInfoList;
@@ -151,6 +152,25 @@
}
@Test
+ public void testRecycleActivityRelaunchItem() {
+ ActivityRelaunchItem emptyItem = ActivityRelaunchItem.obtain(null, null, 0, null, false);
+ Configuration overrideConfig = new Configuration();
+ overrideConfig.assetsSeq = 5;
+ ActivityRelaunchItem item = ActivityRelaunchItem.obtain(resultInfoList(),
+ referrerIntentList(), 42, mergedConfig(), true);
+ assertNotSame(item, emptyItem);
+ assertFalse(item.equals(emptyItem));
+
+ item.recycle();
+ assertEquals(item, emptyItem);
+
+ ActivityRelaunchItem item2 = ActivityRelaunchItem.obtain(resultInfoList(),
+ referrerIntentList(), 42, mergedConfig(), true);
+ assertSame(item, item2);
+ assertFalse(item2.equals(emptyItem));
+ }
+
+ @Test
public void testRecycleMoveToDisplayItem() {
MoveToDisplayItem emptyItem = MoveToDisplayItem.obtain(0, null);
MoveToDisplayItem item = MoveToDisplayItem.obtain(4, config());
diff --git a/core/tests/coretests/src/android/app/servertransaction/TestUtils.java b/core/tests/coretests/src/android/app/servertransaction/TestUtils.java
index e923516..d125fe7 100644
--- a/core/tests/coretests/src/android/app/servertransaction/TestUtils.java
+++ b/core/tests/coretests/src/android/app/servertransaction/TestUtils.java
@@ -21,6 +21,7 @@
import android.app.ResultInfo;
import android.content.Intent;
import android.content.res.Configuration;
+import android.util.MergedConfiguration;
import com.android.internal.content.ReferrerIntent;
@@ -38,6 +39,15 @@
return config;
}
+ static MergedConfiguration mergedConfig() {
+ Configuration config = config();
+ Configuration overrideConfig = new Configuration();
+ overrideConfig.densityDpi = 30;
+ overrideConfig.screenWidthDp = 40;
+ overrideConfig.smallestScreenWidthDp = 15;
+ return new MergedConfiguration(config, overrideConfig);
+ }
+
static List<ResultInfo> resultInfoList() {
String resultWho1 = "resultWho1";
int requestCode1 = 7;
diff --git a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java
index 77aaa2d..0906435 100644
--- a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java
+++ b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java
@@ -17,6 +17,7 @@
package android.app.servertransaction;
import static android.app.servertransaction.TestUtils.config;
+import static android.app.servertransaction.TestUtils.mergedConfig;
import static android.app.servertransaction.TestUtils.referrerIntentList;
import static android.app.servertransaction.TestUtils.resultInfoList;
@@ -27,7 +28,6 @@
import android.app.IInstrumentationWatcher;
import android.app.IUiAutomationConnection;
import android.app.ProfilerInfo;
-import android.app.ResultInfo;
import android.content.ComponentName;
import android.content.IIntentReceiver;
import android.content.Intent;
@@ -53,7 +53,6 @@
import android.support.test.runner.AndroidJUnit4;
import com.android.internal.app.IVoiceInteractor;
-import com.android.internal.content.ReferrerIntent;
import org.junit.Before;
import org.junit.Test;
@@ -243,6 +242,22 @@
}
@Test
+ public void testRelaunch() {
+ // Write to parcel
+ Configuration overrideConfig = new Configuration();
+ overrideConfig.assetsSeq = 5;
+ ActivityRelaunchItem item = ActivityRelaunchItem.obtain(resultInfoList(),
+ referrerIntentList(), 35, mergedConfig(), true);
+ writeAndPrepareForReading(item);
+
+ // Read from parcel and assert
+ ActivityRelaunchItem result = ActivityRelaunchItem.CREATOR.createFromParcel(mParcel);
+
+ assertEquals(item.hashCode(), result.hashCode());
+ assertTrue(item.equals(result));
+ }
+
+ @Test
public void testPause() {
// Write to parcel
PauseActivityItem item = PauseActivityItem.obtain(true /* finished */,
@@ -435,12 +450,6 @@
}
@Override
- public void scheduleRelaunchActivity(IBinder iBinder, List<ResultInfo> list,
- List<ReferrerIntent> list1, int i, boolean b, Configuration configuration,
- Configuration configuration1, boolean b1) throws RemoteException {
- }
-
- @Override
public void scheduleSleeping(IBinder iBinder, boolean b) throws RemoteException {
}
diff --git a/core/tests/coretests/src/android/text/OWNERS b/core/tests/coretests/src/android/text/OWNERS
index 0f85e1f..9f2182e 100644
--- a/core/tests/coretests/src/android/text/OWNERS
+++ b/core/tests/coretests/src/android/text/OWNERS
@@ -1,4 +1,3 @@
siyamed@google.com
nona@google.com
clarabayarri@google.com
-toki@google.com
diff --git a/core/tests/coretests/src/android/view/menu/ContextMenuTest.java b/core/tests/coretests/src/android/view/menu/ContextMenuTest.java
index 59d4e55..657a7fc 100644
--- a/core/tests/coretests/src/android/view/menu/ContextMenuTest.java
+++ b/core/tests/coretests/src/android/view/menu/ContextMenuTest.java
@@ -41,6 +41,13 @@
testMenuPosition(getActivity().getTargetRtl());
}
+ public void testContextMenuPositionRepetitive() throws InterruptedException {
+ // Regression test for b/72507876
+ testMenuPosition(getActivity().getTargetLtr());
+ testMenuPosition(getActivity().getTargetRtl());
+ testMenuPosition(getActivity().getTargetLtr());
+ }
+
private void testMenuPosition(View target) throws InterruptedException {
final int minScreenDimension = getMinScreenDimension();
if (minScreenDimension < 320) {
diff --git a/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java b/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java
index 660c744..7b239f0 100644
--- a/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java
+++ b/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java
@@ -20,6 +20,7 @@
import android.os.Looper;
import android.util.SparseIntArray;
+import com.android.internal.location.gnssmetrics.GnssMetrics;
import java.util.ArrayList;
import java.util.concurrent.Future;
@@ -40,6 +41,11 @@
mBluetoothScanTimer = new StopwatchTimer(mClocks, null, -14, null, mOnBatteryTimeBase);
setExternalStatsSyncLocked(new DummyExternalStatsSync());
+ for (int i=0; i< GnssMetrics.NUM_GPS_SIGNAL_QUALITY_LEVELS; i++) {
+ mGpsSignalQualityTimer[i] = new StopwatchTimer(clocks, null, -1000-i, null,
+ mOnBatteryTimeBase);
+ }
+
// A no-op handler.
mHandler = new Handler(Looper.getMainLooper()) {};
}
diff --git a/data/keyboards/Generic.kcm b/data/keyboards/Generic.kcm
index 1ef74ba..d0565ca 100644
--- a/data/keyboards/Generic.kcm
+++ b/data/keyboards/Generic.kcm
@@ -472,11 +472,15 @@
### Non-printing keys ###
key ESCAPE {
- base: fallback BACK
+ base: none
alt, meta: fallback HOME
ctrl: fallback MENU
}
+key DEL {
+ ctrl+alt: fallback BACK
+}
+
### Gamepad buttons ###
key BUTTON_A {
diff --git a/data/keyboards/Virtual.kcm b/data/keyboards/Virtual.kcm
index c4647e0..c763cc09 100644
--- a/data/keyboards/Virtual.kcm
+++ b/data/keyboards/Virtual.kcm
@@ -469,11 +469,15 @@
### Non-printing keys ###
key ESCAPE {
- base: fallback BACK
+ base: none
alt, meta: fallback HOME
ctrl: fallback MENU
}
+key DEL {
+ ctrl+alt: fallback BACK
+}
+
### Gamepad buttons ###
key BUTTON_A {
diff --git a/graphics/java/android/graphics/BaseCanvas.java b/graphics/java/android/graphics/BaseCanvas.java
index 69a5874..eacb727 100644
--- a/graphics/java/android/graphics/BaseCanvas.java
+++ b/graphics/java/android/graphics/BaseCanvas.java
@@ -22,7 +22,6 @@
import android.annotation.Size;
import android.graphics.Canvas.VertexMode;
import android.text.GraphicsOperations;
-import android.text.MeasuredParagraph;
import android.text.MeasuredText;
import android.text.SpannableString;
import android.text.SpannedString;
diff --git a/graphics/java/android/graphics/BitmapFactory.java b/graphics/java/android/graphics/BitmapFactory.java
index f5bf754..7ea35e7 100644
--- a/graphics/java/android/graphics/BitmapFactory.java
+++ b/graphics/java/android/graphics/BitmapFactory.java
@@ -18,6 +18,8 @@
import static android.graphics.BitmapFactory.Options.validate;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.content.res.AssetManager;
import android.content.res.Resources;
import android.os.Trace;
@@ -518,8 +520,9 @@
* is not {@link ColorSpace.Model#RGB RGB}, or if the specified color space's transfer
* function is not an {@link ColorSpace.Rgb.TransferParameters ICC parametric curve}
*/
- public static Bitmap decodeResourceStream(Resources res, TypedValue value,
- InputStream is, Rect pad, Options opts) {
+ @Nullable
+ public static Bitmap decodeResourceStream(@Nullable Resources res, @Nullable TypedValue value,
+ @Nullable InputStream is, @Nullable Rect pad, @Nullable Options opts) {
validate(opts);
if (opts == null) {
opts = new Options();
@@ -707,7 +710,9 @@
* <code>is.mark(1024)</code> would be called. As of
* {@link android.os.Build.VERSION_CODES#KITKAT}, this is no longer the case.</p>
*/
- public static Bitmap decodeStream(InputStream is, Rect outPadding, Options opts) {
+ @Nullable
+ public static Bitmap decodeStream(@Nullable InputStream is, @Nullable Rect outPadding,
+ @Nullable Options opts) {
// we don't throw in this case, thus allowing the caller to only check
// the cache, and not force the image to be decoded.
if (is == null) {
@@ -742,7 +747,8 @@
* Private helper function for decoding an InputStream natively. Buffers the input enough to
* do a rewind as needed, and supplies temporary storage if necessary. is MUST NOT be null.
*/
- private static Bitmap decodeStreamInternal(InputStream is, Rect outPadding, Options opts) {
+ private static Bitmap decodeStreamInternal(@NonNull InputStream is,
+ @Nullable Rect outPadding, @Nullable Options opts) {
// ASSERT(is != null);
byte [] tempStorage = null;
if (opts != null) tempStorage = opts.inTempStorage;
diff --git a/graphics/java/android/graphics/Canvas.java b/graphics/java/android/graphics/Canvas.java
index f5e8633..d925441 100644
--- a/graphics/java/android/graphics/Canvas.java
+++ b/graphics/java/android/graphics/Canvas.java
@@ -1219,10 +1219,14 @@
nFreeTextLayoutCaches();
}
+ /** @hide */
+ public static void setCompatibilityVersion(int apiLevel) { nSetCompatibilityVersion(apiLevel); }
+
private static native void nFreeCaches();
private static native void nFreeTextLayoutCaches();
private static native long nInitRaster(Bitmap bitmap);
private static native long nGetNativeFinalizer();
+ private static native void nSetCompatibilityVersion(int apiLevel);
// ---------------- @FastNative -------------------
diff --git a/graphics/java/android/graphics/FontFamily.java b/graphics/java/android/graphics/FontFamily.java
index fe2b523..e7cfcfd 100644
--- a/graphics/java/android/graphics/FontFamily.java
+++ b/graphics/java/android/graphics/FontFamily.java
@@ -19,9 +19,9 @@
import android.annotation.Nullable;
import android.content.res.AssetManager;
import android.graphics.fonts.FontVariationAxis;
-import android.text.FontConfig;
import android.text.TextUtils;
import android.util.Log;
+
import dalvik.annotation.optimization.CriticalNative;
import java.io.FileInputStream;
diff --git a/graphics/java/android/graphics/FontListParser.java b/graphics/java/android/graphics/FontListParser.java
index 9f672e3..431d0e0 100644
--- a/graphics/java/android/graphics/FontListParser.java
+++ b/graphics/java/android/graphics/FontListParser.java
@@ -16,16 +16,13 @@
package android.graphics;
-import android.text.FontConfig;
import android.graphics.fonts.FontVariationAxis;
+import android.text.FontConfig;
import android.util.Xml;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
-import android.annotation.Nullable;
-import com.android.internal.annotations.VisibleForTesting;
-
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
diff --git a/graphics/java/android/graphics/ImageDecoder.java b/graphics/java/android/graphics/ImageDecoder.java
index 3cca47b..acefead 100644
--- a/graphics/java/android/graphics/ImageDecoder.java
+++ b/graphics/java/android/graphics/ImageDecoder.java
@@ -25,7 +25,7 @@
import android.annotation.RawRes;
import android.content.ContentResolver;
import android.content.res.AssetFileDescriptor;
-import android.content.res.AssetManager.AssetInputStream;
+import android.content.res.AssetManager;
import android.content.res.Resources;
import android.graphics.drawable.AnimatedImageDrawable;
import android.graphics.drawable.Drawable;
@@ -263,63 +263,6 @@
}
}
- /**
- * Takes ownership of the AssetInputStream.
- *
- * @hide
- */
- public static class AssetInputStreamSource extends Source {
- public AssetInputStreamSource(@NonNull AssetInputStream ais,
- @NonNull Resources res, @NonNull TypedValue value) {
- mAssetInputStream = ais;
- mResources = res;
-
- if (value.density == TypedValue.DENSITY_DEFAULT) {
- mDensity = DisplayMetrics.DENSITY_DEFAULT;
- } else if (value.density != TypedValue.DENSITY_NONE) {
- mDensity = value.density;
- } else {
- mDensity = Bitmap.DENSITY_NONE;
- }
- }
-
- private AssetInputStream mAssetInputStream;
- private final Resources mResources;
- private final int mDensity;
-
- @Override
- public Resources getResources() { return mResources; }
-
- @Override
- public int getDensity() {
- return mDensity;
- }
-
- @Override
- public ImageDecoder createImageDecoder() throws IOException {
- ImageDecoder decoder = null;
- synchronized (this) {
- if (mAssetInputStream == null) {
- throw new IOException("Cannot reuse AssetInputStreamSource");
- }
- AssetInputStream ais = mAssetInputStream;
- mAssetInputStream = null;
- try {
- long asset = ais.getNativeAsset();
- decoder = nCreate(asset);
- } finally {
- if (decoder == null) {
- IoUtils.closeQuietly(ais);
- } else {
- decoder.mInputStream = ais;
- decoder.mOwnsInputStream = true;
- }
- }
- return decoder;
- }
- }
- }
-
private static class ResourceSource extends Source {
ResourceSource(@NonNull Resources res, int resId) {
mResources = res;
@@ -353,7 +296,11 @@
mResDensity = value.density;
}
- long asset = ((AssetInputStream) is).getNativeAsset();
+ if (!(is instanceof AssetManager.AssetInputStream)) {
+ // This should never happen.
+ throw new RuntimeException("Resource is not an asset?");
+ }
+ long asset = ((AssetManager.AssetInputStream) is).getNativeAsset();
decoder = nCreate(asset);
} finally {
if (decoder == null) {
diff --git a/graphics/java/android/graphics/Paint.java b/graphics/java/android/graphics/Paint.java
index 5a80ee2..ed147e9 100644
--- a/graphics/java/android/graphics/Paint.java
+++ b/graphics/java/android/graphics/Paint.java
@@ -19,10 +19,8 @@
import android.annotation.ColorInt;
import android.annotation.NonNull;
import android.annotation.Size;
-import android.graphics.FontListParser;
import android.graphics.fonts.FontVariationAxis;
import android.os.LocaleList;
-import android.text.FontConfig;
import android.text.GraphicsOperations;
import android.text.SpannableString;
import android.text.SpannedString;
@@ -33,14 +31,13 @@
import dalvik.annotation.optimization.CriticalNative;
import dalvik.annotation.optimization.FastNative;
+import libcore.util.NativeAllocationRegistry;
+
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.Locale;
-import libcore.util.NativeAllocationRegistry;
-
/**
* The Paint class holds the style and color information about how to draw
* geometries, text and bitmaps.
diff --git a/graphics/java/android/graphics/Typeface.java b/graphics/java/android/graphics/Typeface.java
index f41267e..8595165 100644
--- a/graphics/java/android/graphics/Typeface.java
+++ b/graphics/java/android/graphics/Typeface.java
@@ -16,25 +16,17 @@
package android.graphics;
-import static android.content.res.FontResourcesParser.ProviderResourceEntry;
-import static android.content.res.FontResourcesParser.FontFileResourceEntry;
-import static android.content.res.FontResourcesParser.FontFamilyFilesResourceEntry;
import static android.content.res.FontResourcesParser.FamilyResourceEntry;
+import static android.content.res.FontResourcesParser.FontFamilyFilesResourceEntry;
+import static android.content.res.FontResourcesParser.FontFileResourceEntry;
+import static android.content.res.FontResourcesParser.ProviderResourceEntry;
-import static java.lang.annotation.RetentionPolicy.SOURCE;
-
-import android.annotation.IntDef;
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.res.AssetManager;
-import android.graphics.FontListParser;
import android.graphics.fonts.FontVariationAxis;
import android.net.Uri;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.ParcelFileDescriptor;
-import android.os.ResultReceiver;
import android.provider.FontRequest;
import android.provider.FontsContract;
import android.text.FontConfig;
@@ -49,8 +41,6 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.Preconditions;
-import libcore.io.IoUtils;
-
import org.xmlpull.v1.XmlPullParserException;
import java.io.File;
@@ -58,19 +48,15 @@
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
-import java.util.Arrays;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
-import java.util.concurrent.atomic.AtomicReference;
/**
* The Typeface class specifies the typeface and intrinsic style of a font.
@@ -395,7 +381,7 @@
* weight and italic information, so {@link #setWeight} and {@link #setItalic} are used
* for style matching during font selection.
*
- * @param results The array of {@link FontsContract.FontInfo}
+ * @param fonts The array of {@link FontsContract.FontInfo}
* @param buffers The mapping from URI to buffers to be used during building.
* @hide
*/
@@ -879,14 +865,12 @@
* also the font families in the fallback list.
* @param fallbackName the family name. If given families don't support characters, the
* characters will be rendered with this family.
- * @param weight the weight for this family. {@link RESOLVE_BY_FONT_TABLE} can be used. In that
- * case, the table information in the first family's font is used. If the first
- * family has multiple fonts, the closest to the regular weight and upright font
- * is used.
- * @param italic the italic information for this family. {@link RESOLVE_BY_FONT_TABLE} can be
- * used. In that case, the table information in the first family's font is used.
- * If the first family has multiple fonts, the closest to the regular weight and
- * upright font is used.
+ * @param weight the weight for this family. In that case, the table information in the first
+ * family's font is used. If the first family has multiple fonts, the closest to
+ * the regular weight and upright font is used.
+ * @param italic the italic information for this family. In that case, the table information in
+ * the first family's font is used. If the first family has multiple fonts, the
+ * closest to the regular weight and upright font is used.
* @param families array of font families
*/
private static Typeface createFromFamiliesWithDefault(FontFamily[] families,
diff --git a/graphics/java/android/graphics/drawable/Drawable.java b/graphics/java/android/graphics/drawable/Drawable.java
index 44ba785..8af2fd8 100644
--- a/graphics/java/android/graphics/drawable/Drawable.java
+++ b/graphics/java/android/graphics/drawable/Drawable.java
@@ -1174,8 +1174,10 @@
*
* @deprecated Prefer the version without an Options object.
*/
- public static Drawable createFromResourceStream(Resources res, TypedValue value,
- InputStream is, String srcName, BitmapFactory.Options opts) {
+ @Nullable
+ public static Drawable createFromResourceStream(@Nullable Resources res,
+ @Nullable TypedValue value, @Nullable InputStream is, @Nullable String srcName,
+ @Nullable BitmapFactory.Options opts) {
if (is == null) {
return null;
}
@@ -1199,7 +1201,6 @@
// an application in compatibility mode, without scaling those down
// to the compatibility density only to have them scaled back up when
// drawn to the screen.
- if (opts == null) opts = new BitmapFactory.Options();
opts.inScreenDensity = Drawable.resolveDensity(res, 0);
Bitmap bm = BitmapFactory.decodeResourceStream(res, value, is, pad, opts);
if (bm != null) {
diff --git a/graphics/java/android/graphics/drawable/GradientDrawable.java b/graphics/java/android/graphics/drawable/GradientDrawable.java
index f5a6f49..8b5114c 100644
--- a/graphics/java/android/graphics/drawable/GradientDrawable.java
+++ b/graphics/java/android/graphics/drawable/GradientDrawable.java
@@ -825,6 +825,14 @@
mFillPaint.setXfermode(mode);
}
+ /**
+ * @param aa to draw this drawable with
+ * @hide
+ */
+ public void setAntiAlias(boolean aa) {
+ mFillPaint.setAntiAlias(aa);
+ }
+
private void buildPathIfDirty() {
final GradientState st = mGradientState;
if (mPathIsDirty) {
diff --git a/graphics/java/android/graphics/drawable/RippleForeground.java b/graphics/java/android/graphics/drawable/RippleForeground.java
index 4129868..a8dc34a 100644
--- a/graphics/java/android/graphics/drawable/RippleForeground.java
+++ b/graphics/java/android/graphics/drawable/RippleForeground.java
@@ -110,6 +110,7 @@
// Take 60% of the maximum of the width and height, then divided half to get the radius.
mStartRadius = Math.max(bounds.width(), bounds.height()) * 0.3f;
+ clampStartingPosition();
}
@Override
@@ -350,7 +351,7 @@
final float cY = mBounds.exactCenterY();
final float dX = mStartingX - cX;
final float dY = mStartingY - cY;
- final float r = mTargetRadius;
+ final float r = mTargetRadius - mStartRadius;
if (dX * dX + dY * dY > r * r) {
// Point is outside the circle, clamp to the perimeter.
final double angle = Math.atan2(dY, dX);
diff --git a/libs/androidfw/Android.bp b/libs/androidfw/Android.bp
index 70d5216..251b2e7 100644
--- a/libs/androidfw/Android.bp
+++ b/libs/androidfw/Android.bp
@@ -145,7 +145,6 @@
"tests/TypeWrappers_test.cpp",
"tests/ZipUtils_test.cpp",
],
- static_libs: ["libgmock"],
target: {
android: {
srcs: [
@@ -172,7 +171,6 @@
// Actual benchmarks.
"tests/AssetManager2_bench.cpp",
- "tests/AttributeResolution_bench.cpp",
"tests/SparseEntry_bench.cpp",
"tests/Theme_bench.cpp",
],
diff --git a/libs/androidfw/AssetManager2.cpp b/libs/androidfw/AssetManager2.cpp
index a8c916b..415d3e3 100644
--- a/libs/androidfw/AssetManager2.cpp
+++ b/libs/androidfw/AssetManager2.cpp
@@ -36,31 +36,6 @@
namespace android {
-struct FindEntryResult {
- // A pointer to the resource table entry for this resource.
- // If the size of the entry is > sizeof(ResTable_entry), it can be cast to
- // a ResTable_map_entry and processed as a bag/map.
- const ResTable_entry* entry;
-
- // The configuration for which the resulting entry was defined. This is already swapped to host
- // endianness.
- ResTable_config config;
-
- // The bitmask of configuration axis with which the resource value varies.
- uint32_t type_flags;
-
- // The dynamic package ID map for the package from which this resource came from.
- const DynamicRefTable* dynamic_ref_table;
-
- // The string pool reference to the type's name. This uses a different string pool than
- // the global string pool, but this is hidden from the caller.
- StringPoolRef type_string_ref;
-
- // The string pool reference to the entry's name. This uses a different string pool than
- // the global string pool, but this is hidden from the caller.
- StringPoolRef entry_string_ref;
-};
-
AssetManager2::AssetManager2() {
memset(&configuration_, 0, sizeof(configuration_));
}
@@ -69,7 +44,6 @@
bool invalidate_caches) {
apk_assets_ = apk_assets;
BuildDynamicRefTable();
- RebuildFilterList();
if (invalidate_caches) {
InvalidateCaches(static_cast<uint32_t>(-1));
}
@@ -100,14 +74,12 @@
if (idx == 0xff) {
package_ids_[package_id] = idx = static_cast<uint8_t>(package_groups_.size());
package_groups_.push_back({});
- DynamicRefTable& ref_table = package_groups_.back().dynamic_ref_table;
- ref_table.mAssignedPackageId = package_id;
- ref_table.mAppAsLib = package->IsDynamic() && package->GetPackageId() == 0x7f;
+ package_groups_.back().dynamic_ref_table.mAssignedPackageId = package_id;
}
PackageGroup* package_group = &package_groups_[idx];
// Add the package and to the set of packages with the same ID.
- package_group->packages_.push_back(ConfiguredPackage{package.get(), {}});
+ package_group->packages_.push_back(package.get());
package_group->cookies_.push_back(static_cast<ApkAssetsCookie>(i));
// Add the package name -> build time ID mappings.
@@ -122,7 +94,7 @@
// Now assign the runtime IDs so that we have a build-time to runtime ID map.
const auto package_groups_end = package_groups_.end();
for (auto iter = package_groups_.begin(); iter != package_groups_end; ++iter) {
- const std::string& package_name = iter->packages_[0].loaded_package_->GetPackageName();
+ const std::string& package_name = iter->packages_[0]->GetPackageName();
for (auto iter2 = package_groups_.begin(); iter2 != package_groups_end; ++iter2) {
iter2->dynamic_ref_table.addMapping(String16(package_name.c_str(), package_name.size()),
iter->dynamic_ref_table.mAssignedPackageId);
@@ -133,33 +105,20 @@
void AssetManager2::DumpToLog() const {
base::ScopedLogSeverity _log(base::INFO);
- LOG(INFO) << base::StringPrintf("AssetManager2(this=%p)", this);
-
std::string list;
- for (const auto& apk_assets : apk_assets_) {
- base::StringAppendF(&list, "%s,", apk_assets->GetPath().c_str());
- }
- LOG(INFO) << "ApkAssets: " << list;
-
- list = "";
for (size_t i = 0; i < package_ids_.size(); i++) {
if (package_ids_[i] != 0xff) {
- base::StringAppendF(&list, "%02x -> %d, ", (int)i, package_ids_[i]);
+ base::StringAppendF(&list, "%02x -> %d, ", (int) i, package_ids_[i]);
}
}
LOG(INFO) << "Package ID map: " << list;
for (const auto& package_group: package_groups_) {
- list = "";
- for (const auto& package : package_group.packages_) {
- const LoadedPackage* loaded_package = package.loaded_package_;
- base::StringAppendF(&list, "%s(%02x%s), ", loaded_package->GetPackageName().c_str(),
- loaded_package->GetPackageId(),
- (loaded_package->IsDynamic() ? " dynamic" : ""));
- }
- LOG(INFO) << base::StringPrintf("PG (%02x): ",
- package_group.dynamic_ref_table.mAssignedPackageId)
- << list;
+ list = "";
+ for (const auto& package : package_group.packages_) {
+ base::StringAppendF(&list, "%s(%02x), ", package->GetPackageName().c_str(), package->GetPackageId());
+ }
+ LOG(INFO) << base::StringPrintf("PG (%02x): ", package_group.dynamic_ref_table.mAssignedPackageId) << list;
}
}
@@ -198,54 +157,52 @@
configuration_ = configuration;
if (diff) {
- RebuildFilterList();
InvalidateCaches(static_cast<uint32_t>(diff));
}
}
std::set<ResTable_config> AssetManager2::GetResourceConfigurations(bool exclude_system,
- bool exclude_mipmap) const {
+ bool exclude_mipmap) {
ATRACE_CALL();
std::set<ResTable_config> configurations;
for (const PackageGroup& package_group : package_groups_) {
- for (const ConfiguredPackage& package : package_group.packages_) {
- if (exclude_system && package.loaded_package_->IsSystem()) {
+ for (const LoadedPackage* package : package_group.packages_) {
+ if (exclude_system && package->IsSystem()) {
continue;
}
- package.loaded_package_->CollectConfigurations(exclude_mipmap, &configurations);
+ package->CollectConfigurations(exclude_mipmap, &configurations);
}
}
return configurations;
}
std::set<std::string> AssetManager2::GetResourceLocales(bool exclude_system,
- bool merge_equivalent_languages) const {
+ bool merge_equivalent_languages) {
ATRACE_CALL();
std::set<std::string> locales;
for (const PackageGroup& package_group : package_groups_) {
- for (const ConfiguredPackage& package : package_group.packages_) {
- if (exclude_system && package.loaded_package_->IsSystem()) {
+ for (const LoadedPackage* package : package_group.packages_) {
+ if (exclude_system && package->IsSystem()) {
continue;
}
- package.loaded_package_->CollectLocales(merge_equivalent_languages, &locales);
+ package->CollectLocales(merge_equivalent_languages, &locales);
}
}
return locales;
}
-std::unique_ptr<Asset> AssetManager2::Open(const std::string& filename,
- Asset::AccessMode mode) const {
+std::unique_ptr<Asset> AssetManager2::Open(const std::string& filename, Asset::AccessMode mode) {
const std::string new_path = "assets/" + filename;
return OpenNonAsset(new_path, mode);
}
std::unique_ptr<Asset> AssetManager2::Open(const std::string& filename, ApkAssetsCookie cookie,
- Asset::AccessMode mode) const {
+ Asset::AccessMode mode) {
const std::string new_path = "assets/" + filename;
return OpenNonAsset(new_path, cookie, mode);
}
-std::unique_ptr<AssetDir> AssetManager2::OpenDir(const std::string& dirname) const {
+std::unique_ptr<AssetDir> AssetManager2::OpenDir(const std::string& dirname) {
ATRACE_CALL();
std::string full_path = "assets/" + dirname;
@@ -279,7 +236,7 @@
// is inconsistent for split APKs.
std::unique_ptr<Asset> AssetManager2::OpenNonAsset(const std::string& filename,
Asset::AccessMode mode,
- ApkAssetsCookie* out_cookie) const {
+ ApkAssetsCookie* out_cookie) {
ATRACE_CALL();
for (int32_t i = apk_assets_.size() - 1; i >= 0; i--) {
std::unique_ptr<Asset> asset = apk_assets_[i]->Open(filename, mode);
@@ -298,8 +255,7 @@
}
std::unique_ptr<Asset> AssetManager2::OpenNonAsset(const std::string& filename,
- ApkAssetsCookie cookie,
- Asset::AccessMode mode) const {
+ ApkAssetsCookie cookie, Asset::AccessMode mode) {
ATRACE_CALL();
if (cookie < 0 || static_cast<size_t>(cookie) >= apk_assets_.size()) {
return {};
@@ -308,13 +264,14 @@
}
ApkAssetsCookie AssetManager2::FindEntry(uint32_t resid, uint16_t density_override,
- bool /*stop_at_first_match*/,
- FindEntryResult* out_entry) const {
+ bool stop_at_first_match, FindEntryResult* out_entry) {
+ ATRACE_CALL();
+
// Might use this if density_override != 0.
ResTable_config density_override_config;
// Select our configuration or generate a density override configuration.
- const ResTable_config* desired_config = &configuration_;
+ ResTable_config* desired_config = &configuration_;
if (density_override != 0 && density_override != configuration_.density) {
density_override_config = configuration_;
density_override_config.density = density_override;
@@ -328,135 +285,53 @@
const uint32_t package_id = get_package_id(resid);
const uint8_t type_idx = get_type_id(resid) - 1;
- const uint16_t entry_idx = get_entry_id(resid);
+ const uint16_t entry_id = get_entry_id(resid);
- const uint8_t package_idx = package_ids_[package_id];
- if (package_idx == 0xff) {
+ const uint8_t idx = package_ids_[package_id];
+ if (idx == 0xff) {
LOG(ERROR) << base::StringPrintf("No package ID %02x found for ID 0x%08x.", package_id, resid);
return kInvalidCookie;
}
- const PackageGroup& package_group = package_groups_[package_idx];
- const size_t package_count = package_group.packages_.size();
-
+ FindEntryResult best_entry;
ApkAssetsCookie best_cookie = kInvalidCookie;
- const LoadedPackage* best_package = nullptr;
- const ResTable_type* best_type = nullptr;
- const ResTable_config* best_config = nullptr;
- ResTable_config best_config_copy;
- uint32_t best_offset = 0u;
- uint32_t type_flags = 0u;
+ uint32_t cumulated_flags = 0u;
- // If desired_config is the same as the set configuration, then we can use our filtered list
- // and we don't need to match the configurations, since they already matched.
- const bool use_fast_path = desired_config == &configuration_;
-
- for (size_t pi = 0; pi < package_count; pi++) {
- const ConfiguredPackage& loaded_package_impl = package_group.packages_[pi];
- const LoadedPackage* loaded_package = loaded_package_impl.loaded_package_;
- ApkAssetsCookie cookie = package_group.cookies_[pi];
-
- // If the type IDs are offset in this package, we need to take that into account when searching
- // for a type.
- const TypeSpec* type_spec = loaded_package->GetTypeSpecByTypeIndex(type_idx);
- if (UNLIKELY(type_spec == nullptr)) {
+ const PackageGroup& package_group = package_groups_[idx];
+ const size_t package_count = package_group.packages_.size();
+ FindEntryResult current_entry;
+ for (size_t i = 0; i < package_count; i++) {
+ const LoadedPackage* loaded_package = package_group.packages_[i];
+ if (!loaded_package->FindEntry(type_idx, entry_id, *desired_config, ¤t_entry)) {
continue;
}
- uint16_t local_entry_idx = entry_idx;
+ cumulated_flags |= current_entry.type_flags;
- // If there is an IDMAP supplied with this package, translate the entry ID.
- if (type_spec->idmap_entries != nullptr) {
- if (!LoadedIdmap::Lookup(type_spec->idmap_entries, local_entry_idx, &local_entry_idx)) {
- // There is no mapping, so the resource is not meant to be in this overlay package.
- continue;
- }
- }
-
- type_flags |= type_spec->GetFlagsForEntryIndex(local_entry_idx);
-
- // If the package is an overlay, then even configurations that are the same MUST be chosen.
- const bool package_is_overlay = loaded_package->IsOverlay();
-
- const FilteredConfigGroup& filtered_group = loaded_package_impl.filtered_configs_[type_idx];
- if (use_fast_path) {
- const std::vector<ResTable_config>& candidate_configs = filtered_group.configurations;
- const size_t type_count = candidate_configs.size();
- for (uint32_t i = 0; i < type_count; i++) {
- const ResTable_config& this_config = candidate_configs[i];
-
- // We can skip calling ResTable_config::match() because we know that all candidate
- // configurations that do NOT match have been filtered-out.
- if ((best_config == nullptr || this_config.isBetterThan(*best_config, desired_config)) ||
- (package_is_overlay && this_config.compare(*best_config) == 0)) {
- // The configuration matches and is better than the previous selection.
- // Find the entry value if it exists for this configuration.
- const ResTable_type* type_chunk = filtered_group.types[i];
- const uint32_t offset = LoadedPackage::GetEntryOffset(type_chunk, local_entry_idx);
- if (offset == ResTable_type::NO_ENTRY) {
- continue;
- }
-
- best_cookie = cookie;
- best_package = loaded_package;
- best_type = type_chunk;
- best_config = &this_config;
- best_offset = offset;
- }
- }
- } else {
- // This is the slower path, which doesn't use the filtered list of configurations.
- // Here we must read the ResTable_config from the mmapped APK, convert it to host endianness
- // and fill in any new fields that did not exist when the APK was compiled.
- // Furthermore when selecting configurations we can't just record the pointer to the
- // ResTable_config, we must copy it.
- const auto iter_end = type_spec->types + type_spec->type_count;
- for (auto iter = type_spec->types; iter != iter_end; ++iter) {
- ResTable_config this_config;
- this_config.copyFromDtoH((*iter)->config);
-
- if (this_config.match(*desired_config)) {
- if ((best_config == nullptr || this_config.isBetterThan(*best_config, desired_config)) ||
- (package_is_overlay && this_config.compare(*best_config) == 0)) {
- // The configuration matches and is better than the previous selection.
- // Find the entry value if it exists for this configuration.
- const uint32_t offset = LoadedPackage::GetEntryOffset(*iter, local_entry_idx);
- if (offset == ResTable_type::NO_ENTRY) {
- continue;
- }
-
- best_cookie = cookie;
- best_package = loaded_package;
- best_type = *iter;
- best_config_copy = this_config;
- best_config = &best_config_copy;
- best_offset = offset;
- }
- }
+ const ResTable_config* current_config = current_entry.config;
+ const ResTable_config* best_config = best_entry.config;
+ if (best_cookie == kInvalidCookie ||
+ current_config->isBetterThan(*best_config, desired_config) ||
+ (loaded_package->IsOverlay() && current_config->compare(*best_config) == 0)) {
+ best_entry = current_entry;
+ best_cookie = package_group.cookies_[i];
+ if (stop_at_first_match) {
+ break;
}
}
}
- if (UNLIKELY(best_cookie == kInvalidCookie)) {
+ if (best_cookie == kInvalidCookie) {
return kInvalidCookie;
}
- const ResTable_entry* best_entry = LoadedPackage::GetEntryFromOffset(best_type, best_offset);
- if (UNLIKELY(best_entry == nullptr)) {
- return kInvalidCookie;
- }
-
- out_entry->entry = best_entry;
- out_entry->config = *best_config;
- out_entry->type_flags = type_flags;
- out_entry->type_string_ref = StringPoolRef(best_package->GetTypeStringPool(), best_type->id - 1);
- out_entry->entry_string_ref =
- StringPoolRef(best_package->GetKeyStringPool(), best_entry->key.index);
+ *out_entry = best_entry;
out_entry->dynamic_ref_table = &package_group.dynamic_ref_table;
+ out_entry->type_flags = cumulated_flags;
return best_cookie;
}
-bool AssetManager2::GetResourceName(uint32_t resid, ResourceName* out_name) const {
+bool AssetManager2::GetResourceName(uint32_t resid, ResourceName* out_name) {
ATRACE_CALL();
FindEntryResult entry;
@@ -466,8 +341,7 @@
return false;
}
- const LoadedPackage* package =
- apk_assets_[cookie]->GetLoadedArsc()->GetPackageById(get_package_id(resid));
+ const LoadedPackage* package = apk_assets_[cookie]->GetLoadedArsc()->GetPackageForId(resid);
if (package == nullptr) {
return false;
}
@@ -495,7 +369,7 @@
return true;
}
-bool AssetManager2::GetResourceFlags(uint32_t resid, uint32_t* out_flags) const {
+bool AssetManager2::GetResourceFlags(uint32_t resid, uint32_t* out_flags) {
FindEntryResult entry;
ApkAssetsCookie cookie =
FindEntry(resid, 0u /* density_override */, false /* stop_at_first_match */, &entry);
@@ -509,7 +383,7 @@
ApkAssetsCookie AssetManager2::GetResource(uint32_t resid, bool may_be_bag,
uint16_t density_override, Res_value* out_value,
ResTable_config* out_selected_config,
- uint32_t* out_flags) const {
+ uint32_t* out_flags) {
ATRACE_CALL();
FindEntryResult entry;
@@ -528,7 +402,7 @@
// Create a reference since we can't represent this complex type as a Res_value.
out_value->dataType = Res_value::TYPE_REFERENCE;
out_value->data = resid;
- *out_selected_config = entry.config;
+ *out_selected_config = *entry.config;
*out_flags = entry.type_flags;
return cookie;
}
@@ -540,7 +414,7 @@
// Convert the package ID to the runtime assigned package ID.
entry.dynamic_ref_table->lookupResourceValue(out_value);
- *out_selected_config = entry.config;
+ *out_selected_config = *entry.config;
*out_flags = entry.type_flags;
return cookie;
}
@@ -548,14 +422,16 @@
ApkAssetsCookie AssetManager2::ResolveReference(ApkAssetsCookie cookie, Res_value* in_out_value,
ResTable_config* in_out_selected_config,
uint32_t* in_out_flags,
- uint32_t* out_last_reference) const {
+ uint32_t* out_last_reference) {
ATRACE_CALL();
constexpr const int kMaxIterations = 20;
for (size_t iteration = 0u; in_out_value->dataType == Res_value::TYPE_REFERENCE &&
in_out_value->data != 0u && iteration < kMaxIterations;
iteration++) {
- *out_last_reference = in_out_value->data;
+ if (out_last_reference != nullptr) {
+ *out_last_reference = in_out_value->data;
+ }
uint32_t new_flags = 0u;
cookie = GetResource(in_out_value->data, true /*may_be_bag*/, 0u /*density_override*/,
in_out_value, in_out_selected_config, &new_flags);
@@ -616,8 +492,7 @@
// Attributes, arrays, etc don't have a resource id as the name. They specify
// other data, which would be wrong to change via a lookup.
if (entry.dynamic_ref_table->lookupResourceId(&new_key) != NO_ERROR) {
- LOG(ERROR) << base::StringPrintf("Failed to resolve key 0x%08x in bag 0x%08x.", new_key,
- resid);
+ LOG(ERROR) << base::StringPrintf("Failed to resolve key 0x%08x in bag 0x%08x.", new_key, resid);
return nullptr;
}
}
@@ -649,8 +524,7 @@
const ResolvedBag* parent_bag = GetBag(parent_resid);
if (parent_bag == nullptr) {
// Failed to get the parent that should exist.
- LOG(ERROR) << base::StringPrintf("Failed to find parent 0x%08x of bag 0x%08x.", parent_resid,
- resid);
+ LOG(ERROR) << base::StringPrintf("Failed to find parent 0x%08x of bag 0x%08x.", parent_resid, resid);
return nullptr;
}
@@ -669,8 +543,7 @@
uint32_t child_key = dtohl(map_entry->name.ident);
if (!is_internal_resid(child_key)) {
if (entry.dynamic_ref_table->lookupResourceId(&child_key) != NO_ERROR) {
- LOG(ERROR) << base::StringPrintf("Failed to resolve key 0x%08x in bag 0x%08x.", child_key,
- resid);
+ LOG(ERROR) << base::StringPrintf("Failed to resolve key 0x%08x in bag 0x%08x.", child_key, resid);
return nullptr;
}
}
@@ -709,8 +582,7 @@
uint32_t new_key = dtohl(map_entry->name.ident);
if (!is_internal_resid(new_key)) {
if (entry.dynamic_ref_table->lookupResourceId(&new_key) != NO_ERROR) {
- LOG(ERROR) << base::StringPrintf("Failed to resolve key 0x%08x in bag 0x%08x.", new_key,
- resid);
+ LOG(ERROR) << base::StringPrintf("Failed to resolve key 0x%08x in bag 0x%08x.", new_key, resid);
return nullptr;
}
}
@@ -766,7 +638,7 @@
uint32_t AssetManager2::GetResourceId(const std::string& resource_name,
const std::string& fallback_type,
- const std::string& fallback_package) const {
+ const std::string& fallback_package) {
StringPiece package_name, type, entry;
if (!ExtractResourceName(resource_name, &package_name, &type, &entry)) {
return 0u;
@@ -798,8 +670,7 @@
const static std::u16string kAttrPrivate16 = u"^attr-private";
for (const PackageGroup& package_group : package_groups_) {
- for (const ConfiguredPackage& package_impl : package_group.packages_) {
- const LoadedPackage* package = package_impl.loaded_package_;
+ for (const LoadedPackage* package : package_group.packages_) {
if (package_name != package->GetPackageName()) {
// All packages in the same group are expected to have the same package name.
break;
@@ -821,32 +692,6 @@
return 0u;
}
-void AssetManager2::RebuildFilterList() {
- for (PackageGroup& group : package_groups_) {
- for (ConfiguredPackage& impl : group.packages_) {
- // Destroy it.
- impl.filtered_configs_.~ByteBucketArray();
-
- // Re-create it.
- new (&impl.filtered_configs_) ByteBucketArray<FilteredConfigGroup>();
-
- // Create the filters here.
- impl.loaded_package_->ForEachTypeSpec([&](const TypeSpec* spec, uint8_t type_index) {
- FilteredConfigGroup& group = impl.filtered_configs_.editItemAt(type_index);
- const auto iter_end = spec->types + spec->type_count;
- for (auto iter = spec->types; iter != iter_end; ++iter) {
- ResTable_config this_config;
- this_config.copyFromDtoH((*iter)->config);
- if (this_config.match(configuration_)) {
- group.configurations.push_back(this_config);
- group.types.push_back(*iter);
- }
- }
- });
- }
- }
-}
-
void AssetManager2::InvalidateCaches(uint32_t diff) {
if (diff == 0xffffffffu) {
// Everything must go.
@@ -1027,7 +872,7 @@
ApkAssetsCookie Theme::ResolveAttributeReference(ApkAssetsCookie cookie, Res_value* in_out_value,
ResTable_config* in_out_selected_config,
uint32_t* in_out_type_spec_flags,
- uint32_t* out_last_ref) const {
+ uint32_t* out_last_ref) {
if (in_out_value->dataType == Res_value::TYPE_ATTRIBUTE) {
uint32_t new_flags;
cookie = GetAttribute(in_out_value->data, in_out_value, &new_flags);
diff --git a/libs/androidfw/AttributeResolution.cpp b/libs/androidfw/AttributeResolution.cpp
index f912af4..60e3845 100644
--- a/libs/androidfw/AttributeResolution.cpp
+++ b/libs/androidfw/AttributeResolution.cpp
@@ -20,18 +20,13 @@
#include <log/log.h>
-#include "androidfw/AssetManager2.h"
#include "androidfw/AttributeFinder.h"
+#include "androidfw/ResourceTypes.h"
constexpr bool kDebugStyles = false;
namespace android {
-// Java asset cookies have 0 as an invalid cookie, but TypedArray expects < 0.
-static uint32_t ApkAssetsCookieToJavaCookie(ApkAssetsCookie cookie) {
- return cookie != kInvalidCookie ? static_cast<uint32_t>(cookie + 1) : static_cast<uint32_t>(-1);
-}
-
class XmlAttributeFinder
: public BackTrackingAttributeFinder<XmlAttributeFinder, size_t> {
public:
@@ -49,53 +44,58 @@
};
class BagAttributeFinder
- : public BackTrackingAttributeFinder<BagAttributeFinder, const ResolvedBag::Entry*> {
+ : public BackTrackingAttributeFinder<BagAttributeFinder, const ResTable::bag_entry*> {
public:
- BagAttributeFinder(const ResolvedBag* bag)
- : BackTrackingAttributeFinder(bag != nullptr ? bag->entries : nullptr,
- bag != nullptr ? bag->entries + bag->entry_count : nullptr) {
- }
+ BagAttributeFinder(const ResTable::bag_entry* start,
+ const ResTable::bag_entry* end)
+ : BackTrackingAttributeFinder(start, end) {}
- inline uint32_t GetAttribute(const ResolvedBag::Entry* entry) const {
- return entry->key;
+ inline uint32_t GetAttribute(const ResTable::bag_entry* entry) const {
+ return entry->map.name.ident;
}
};
-bool ResolveAttrs(Theme* theme, uint32_t def_style_attr, uint32_t def_style_res,
- uint32_t* src_values, size_t src_values_length, uint32_t* attrs,
- size_t attrs_length, uint32_t* out_values, uint32_t* out_indices) {
+bool ResolveAttrs(ResTable::Theme* theme, uint32_t def_style_attr,
+ uint32_t def_style_res, uint32_t* src_values,
+ size_t src_values_length, uint32_t* attrs,
+ size_t attrs_length, uint32_t* out_values,
+ uint32_t* out_indices) {
if (kDebugStyles) {
ALOGI("APPLY STYLE: theme=0x%p defStyleAttr=0x%x defStyleRes=0x%x", theme,
def_style_attr, def_style_res);
}
- AssetManager2* assetmanager = theme->GetAssetManager();
+ const ResTable& res = theme->getResTable();
ResTable_config config;
Res_value value;
int indices_idx = 0;
// Load default style from attribute, if specified...
- uint32_t def_style_flags = 0u;
+ uint32_t def_style_bag_type_set_flags = 0;
if (def_style_attr != 0) {
Res_value value;
- if (theme->GetAttribute(def_style_attr, &value, &def_style_flags) != kInvalidCookie) {
+ if (theme->getAttribute(def_style_attr, &value, &def_style_bag_type_set_flags) >= 0) {
if (value.dataType == Res_value::TYPE_REFERENCE) {
def_style_res = value.data;
}
}
}
- // Retrieve the default style bag, if requested.
- const ResolvedBag* default_style_bag = nullptr;
- if (def_style_res != 0) {
- default_style_bag = assetmanager->GetBag(def_style_res);
- if (default_style_bag != nullptr) {
- def_style_flags |= default_style_bag->type_spec_flags;
- }
- }
+ // Now lock down the resource object and start pulling stuff from it.
+ res.lock();
- BagAttributeFinder def_style_attr_finder(default_style_bag);
+ // Retrieve the default style bag, if requested.
+ const ResTable::bag_entry* def_style_start = nullptr;
+ uint32_t def_style_type_set_flags = 0;
+ ssize_t bag_off = def_style_res != 0
+ ? res.getBagLocked(def_style_res, &def_style_start,
+ &def_style_type_set_flags)
+ : -1;
+ def_style_type_set_flags |= def_style_bag_type_set_flags;
+ const ResTable::bag_entry* const def_style_end =
+ def_style_start + (bag_off >= 0 ? bag_off : 0);
+ BagAttributeFinder def_style_attr_finder(def_style_start, def_style_end);
// Now iterate through all of the attributes that the client has requested,
// filling in each with whatever data we can find.
@@ -106,7 +106,7 @@
ALOGI("RETRIEVING ATTR 0x%08x...", cur_ident);
}
- ApkAssetsCookie cookie = kInvalidCookie;
+ ssize_t block = -1;
uint32_t type_set_flags = 0;
value.dataType = Res_value::TYPE_NULL;
@@ -122,14 +122,15 @@
value.dataType = Res_value::TYPE_ATTRIBUTE;
value.data = src_values[ii];
if (kDebugStyles) {
- ALOGI("-> From values: type=0x%x, data=0x%08x", value.dataType, value.data);
+ ALOGI("-> From values: type=0x%x, data=0x%08x", value.dataType,
+ value.data);
}
} else {
- const ResolvedBag::Entry* const entry = def_style_attr_finder.Find(cur_ident);
- if (entry != def_style_attr_finder.end()) {
- cookie = entry->cookie;
- type_set_flags = def_style_flags;
- value = entry->value;
+ const ResTable::bag_entry* const def_style_entry = def_style_attr_finder.Find(cur_ident);
+ if (def_style_entry != def_style_end) {
+ block = def_style_entry->stringBlock;
+ type_set_flags = def_style_type_set_flags;
+ value = def_style_entry->map.value;
if (kDebugStyles) {
ALOGI("-> From def style: type=0x%x, data=0x%08x", value.dataType, value.data);
}
@@ -139,26 +140,22 @@
uint32_t resid = 0;
if (value.dataType != Res_value::TYPE_NULL) {
// Take care of resolving the found resource to its final value.
- ApkAssetsCookie new_cookie =
- theme->ResolveAttributeReference(cookie, &value, &config, &type_set_flags, &resid);
- if (new_cookie != kInvalidCookie) {
- cookie = new_cookie;
- }
+ ssize_t new_block =
+ theme->resolveAttributeReference(&value, block, &resid, &type_set_flags, &config);
+ if (new_block >= 0) block = new_block;
if (kDebugStyles) {
ALOGI("-> Resolved attr: type=0x%x, data=0x%08x", value.dataType, value.data);
}
} else if (value.data != Res_value::DATA_NULL_EMPTY) {
- // If we still don't have a value for this attribute, try to find it in the theme!
- ApkAssetsCookie new_cookie = theme->GetAttribute(cur_ident, &value, &type_set_flags);
- if (new_cookie != kInvalidCookie) {
+ // If we still don't have a value for this attribute, try to find
+ // it in the theme!
+ ssize_t new_block = theme->getAttribute(cur_ident, &value, &type_set_flags);
+ if (new_block >= 0) {
if (kDebugStyles) {
ALOGI("-> From theme: type=0x%x, data=0x%08x", value.dataType, value.data);
}
- new_cookie =
- assetmanager->ResolveReference(new_cookie, &value, &config, &type_set_flags, &resid);
- if (new_cookie != kInvalidCookie) {
- cookie = new_cookie;
- }
+ new_block = res.resolveReference(&value, new_block, &resid, &type_set_flags, &config);
+ if (new_block >= 0) block = new_block;
if (kDebugStyles) {
ALOGI("-> Resolved theme: type=0x%x, data=0x%08x", value.dataType, value.data);
}
@@ -172,7 +169,7 @@
}
value.dataType = Res_value::TYPE_NULL;
value.data = Res_value::DATA_NULL_UNDEFINED;
- cookie = kInvalidCookie;
+ block = -1;
}
if (kDebugStyles) {
@@ -182,7 +179,9 @@
// Write the final value back to Java.
out_values[STYLE_TYPE] = value.dataType;
out_values[STYLE_DATA] = value.data;
- out_values[STYLE_ASSET_COOKIE] = ApkAssetsCookieToJavaCookie(cookie);
+ out_values[STYLE_ASSET_COOKIE] =
+ block != -1 ? static_cast<uint32_t>(res.getTableCookie(block))
+ : static_cast<uint32_t>(-1);
out_values[STYLE_RESOURCE_ID] = resid;
out_values[STYLE_CHANGING_CONFIGURATIONS] = type_set_flags;
out_values[STYLE_DENSITY] = config.density;
@@ -196,80 +195,90 @@
out_values += STYLE_NUM_ENTRIES;
}
+ res.unlock();
+
if (out_indices != nullptr) {
out_indices[0] = indices_idx;
}
return true;
}
-void ApplyStyle(Theme* theme, ResXMLParser* xml_parser, uint32_t def_style_attr,
- uint32_t def_style_resid, const uint32_t* attrs, size_t attrs_length,
+void ApplyStyle(ResTable::Theme* theme, ResXMLParser* xml_parser, uint32_t def_style_attr,
+ uint32_t def_style_res, const uint32_t* attrs, size_t attrs_length,
uint32_t* out_values, uint32_t* out_indices) {
if (kDebugStyles) {
- ALOGI("APPLY STYLE: theme=0x%p defStyleAttr=0x%x defStyleRes=0x%x xml=0x%p", theme,
- def_style_attr, def_style_resid, xml_parser);
+ ALOGI("APPLY STYLE: theme=0x%p defStyleAttr=0x%x defStyleRes=0x%x xml=0x%p",
+ theme, def_style_attr, def_style_res, xml_parser);
}
- AssetManager2* assetmanager = theme->GetAssetManager();
+ const ResTable& res = theme->getResTable();
ResTable_config config;
Res_value value;
int indices_idx = 0;
// Load default style from attribute, if specified...
- uint32_t def_style_flags = 0u;
+ uint32_t def_style_bag_type_set_flags = 0;
if (def_style_attr != 0) {
Res_value value;
- if (theme->GetAttribute(def_style_attr, &value, &def_style_flags) != kInvalidCookie) {
+ if (theme->getAttribute(def_style_attr, &value,
+ &def_style_bag_type_set_flags) >= 0) {
if (value.dataType == Res_value::TYPE_REFERENCE) {
- def_style_resid = value.data;
+ def_style_res = value.data;
}
}
}
- // Retrieve the style resource ID associated with the current XML tag's style attribute.
- uint32_t style_resid = 0u;
- uint32_t style_flags = 0u;
+ // Retrieve the style class associated with the current XML tag.
+ int style = 0;
+ uint32_t style_bag_type_set_flags = 0;
if (xml_parser != nullptr) {
ssize_t idx = xml_parser->indexOfStyle();
if (idx >= 0 && xml_parser->getAttributeValue(idx, &value) >= 0) {
if (value.dataType == value.TYPE_ATTRIBUTE) {
- // Resolve the attribute with out theme.
- if (theme->GetAttribute(value.data, &value, &style_flags) == kInvalidCookie) {
+ if (theme->getAttribute(value.data, &value, &style_bag_type_set_flags) < 0) {
value.dataType = Res_value::TYPE_NULL;
}
}
-
if (value.dataType == value.TYPE_REFERENCE) {
- style_resid = value.data;
+ style = value.data;
}
}
}
- // Retrieve the default style bag, if requested.
- const ResolvedBag* default_style_bag = nullptr;
- if (def_style_resid != 0) {
- default_style_bag = assetmanager->GetBag(def_style_resid);
- if (default_style_bag != nullptr) {
- def_style_flags |= default_style_bag->type_spec_flags;
- }
- }
+ // Now lock down the resource object and start pulling stuff from it.
+ res.lock();
- BagAttributeFinder def_style_attr_finder(default_style_bag);
+ // Retrieve the default style bag, if requested.
+ const ResTable::bag_entry* def_style_attr_start = nullptr;
+ uint32_t def_style_type_set_flags = 0;
+ ssize_t bag_off = def_style_res != 0
+ ? res.getBagLocked(def_style_res, &def_style_attr_start,
+ &def_style_type_set_flags)
+ : -1;
+ def_style_type_set_flags |= def_style_bag_type_set_flags;
+ const ResTable::bag_entry* const def_style_attr_end =
+ def_style_attr_start + (bag_off >= 0 ? bag_off : 0);
+ BagAttributeFinder def_style_attr_finder(def_style_attr_start,
+ def_style_attr_end);
// Retrieve the style class bag, if requested.
- const ResolvedBag* xml_style_bag = nullptr;
- if (style_resid != 0) {
- xml_style_bag = assetmanager->GetBag(style_resid);
- if (xml_style_bag != nullptr) {
- style_flags |= xml_style_bag->type_spec_flags;
- }
- }
-
- BagAttributeFinder xml_style_attr_finder(xml_style_bag);
+ const ResTable::bag_entry* style_attr_start = nullptr;
+ uint32_t style_type_set_flags = 0;
+ bag_off =
+ style != 0
+ ? res.getBagLocked(style, &style_attr_start, &style_type_set_flags)
+ : -1;
+ style_type_set_flags |= style_bag_type_set_flags;
+ const ResTable::bag_entry* const style_attr_end =
+ style_attr_start + (bag_off >= 0 ? bag_off : 0);
+ BagAttributeFinder style_attr_finder(style_attr_start, style_attr_end);
// Retrieve the XML attributes, if requested.
+ static const ssize_t kXmlBlock = 0x10000000;
XmlAttributeFinder xml_attr_finder(xml_parser);
+ const size_t xml_attr_end =
+ xml_parser != nullptr ? xml_parser->getAttributeCount() : 0;
// Now iterate through all of the attributes that the client has requested,
// filling in each with whatever data we can find.
@@ -280,8 +289,8 @@
ALOGI("RETRIEVING ATTR 0x%08x...", cur_ident);
}
- ApkAssetsCookie cookie = kInvalidCookie;
- uint32_t type_set_flags = 0u;
+ ssize_t block = kXmlBlock;
+ uint32_t type_set_flags = 0;
value.dataType = Res_value::TYPE_NULL;
value.data = Res_value::DATA_NULL_UNDEFINED;
@@ -293,7 +302,7 @@
// Walk through the xml attributes looking for the requested attribute.
const size_t xml_attr_idx = xml_attr_finder.Find(cur_ident);
- if (xml_attr_idx != xml_attr_finder.end()) {
+ if (xml_attr_idx != xml_attr_end) {
// We found the attribute we were looking for.
xml_parser->getAttributeValue(xml_attr_idx, &value);
if (kDebugStyles) {
@@ -303,12 +312,12 @@
if (value.dataType == Res_value::TYPE_NULL && value.data != Res_value::DATA_NULL_EMPTY) {
// Walk through the style class values looking for the requested attribute.
- const ResolvedBag::Entry* entry = xml_style_attr_finder.Find(cur_ident);
- if (entry != xml_style_attr_finder.end()) {
+ const ResTable::bag_entry* const style_attr_entry = style_attr_finder.Find(cur_ident);
+ if (style_attr_entry != style_attr_end) {
// We found the attribute we were looking for.
- cookie = entry->cookie;
- type_set_flags = style_flags;
- value = entry->value;
+ block = style_attr_entry->stringBlock;
+ type_set_flags = style_type_set_flags;
+ value = style_attr_entry->map.value;
if (kDebugStyles) {
ALOGI("-> From style: type=0x%x, data=0x%08x", value.dataType, value.data);
}
@@ -317,25 +326,25 @@
if (value.dataType == Res_value::TYPE_NULL && value.data != Res_value::DATA_NULL_EMPTY) {
// Walk through the default style values looking for the requested attribute.
- const ResolvedBag::Entry* entry = def_style_attr_finder.Find(cur_ident);
- if (entry != def_style_attr_finder.end()) {
+ const ResTable::bag_entry* const def_style_attr_entry = def_style_attr_finder.Find(cur_ident);
+ if (def_style_attr_entry != def_style_attr_end) {
// We found the attribute we were looking for.
- cookie = entry->cookie;
- type_set_flags = def_style_flags;
- value = entry->value;
+ block = def_style_attr_entry->stringBlock;
+ type_set_flags = style_type_set_flags;
+ value = def_style_attr_entry->map.value;
if (kDebugStyles) {
ALOGI("-> From def style: type=0x%x, data=0x%08x", value.dataType, value.data);
}
}
}
- uint32_t resid = 0u;
+ uint32_t resid = 0;
if (value.dataType != Res_value::TYPE_NULL) {
// Take care of resolving the found resource to its final value.
- ApkAssetsCookie new_cookie =
- theme->ResolveAttributeReference(cookie, &value, &config, &type_set_flags, &resid);
- if (new_cookie != kInvalidCookie) {
- cookie = new_cookie;
+ ssize_t new_block =
+ theme->resolveAttributeReference(&value, block, &resid, &type_set_flags, &config);
+ if (new_block >= 0) {
+ block = new_block;
}
if (kDebugStyles) {
@@ -343,15 +352,14 @@
}
} else if (value.data != Res_value::DATA_NULL_EMPTY) {
// If we still don't have a value for this attribute, try to find it in the theme!
- ApkAssetsCookie new_cookie = theme->GetAttribute(cur_ident, &value, &type_set_flags);
- if (new_cookie != kInvalidCookie) {
+ ssize_t new_block = theme->getAttribute(cur_ident, &value, &type_set_flags);
+ if (new_block >= 0) {
if (kDebugStyles) {
ALOGI("-> From theme: type=0x%x, data=0x%08x", value.dataType, value.data);
}
- new_cookie =
- assetmanager->ResolveReference(new_cookie, &value, &config, &type_set_flags, &resid);
- if (new_cookie != kInvalidCookie) {
- cookie = new_cookie;
+ new_block = res.resolveReference(&value, new_block, &resid, &type_set_flags, &config);
+ if (new_block >= 0) {
+ block = new_block;
}
if (kDebugStyles) {
@@ -367,7 +375,7 @@
}
value.dataType = Res_value::TYPE_NULL;
value.data = Res_value::DATA_NULL_UNDEFINED;
- cookie = kInvalidCookie;
+ block = kXmlBlock;
}
if (kDebugStyles) {
@@ -377,7 +385,9 @@
// Write the final value back to Java.
out_values[STYLE_TYPE] = value.dataType;
out_values[STYLE_DATA] = value.data;
- out_values[STYLE_ASSET_COOKIE] = ApkAssetsCookieToJavaCookie(cookie);
+ out_values[STYLE_ASSET_COOKIE] =
+ block != kXmlBlock ? static_cast<uint32_t>(res.getTableCookie(block))
+ : static_cast<uint32_t>(-1);
out_values[STYLE_RESOURCE_ID] = resid;
out_values[STYLE_CHANGING_CONFIGURATIONS] = type_set_flags;
out_values[STYLE_DENSITY] = config.density;
@@ -392,28 +402,36 @@
out_values += STYLE_NUM_ENTRIES;
}
+ res.unlock();
+
// out_indices must NOT be nullptr.
out_indices[0] = indices_idx;
}
-bool RetrieveAttributes(AssetManager2* assetmanager, ResXMLParser* xml_parser, uint32_t* attrs,
- size_t attrs_length, uint32_t* out_values, uint32_t* out_indices) {
+bool RetrieveAttributes(const ResTable* res, ResXMLParser* xml_parser,
+ uint32_t* attrs, size_t attrs_length,
+ uint32_t* out_values, uint32_t* out_indices) {
ResTable_config config;
Res_value value;
int indices_idx = 0;
+ // Now lock down the resource object and start pulling stuff from it.
+ res->lock();
+
// Retrieve the XML attributes, if requested.
const size_t xml_attr_count = xml_parser->getAttributeCount();
size_t ix = 0;
uint32_t cur_xml_attr = xml_parser->getAttributeNameResID(ix);
+ static const ssize_t kXmlBlock = 0x10000000;
+
// Now iterate through all of the attributes that the client has requested,
// filling in each with whatever data we can find.
for (size_t ii = 0; ii < attrs_length; ii++) {
const uint32_t cur_ident = attrs[ii];
- ApkAssetsCookie cookie = kInvalidCookie;
- uint32_t type_set_flags = 0u;
+ ssize_t block = kXmlBlock;
+ uint32_t type_set_flags = 0;
value.dataType = Res_value::TYPE_NULL;
value.data = Res_value::DATA_NULL_UNDEFINED;
@@ -432,27 +450,28 @@
cur_xml_attr = xml_parser->getAttributeNameResID(ix);
}
- uint32_t resid = 0u;
+ uint32_t resid = 0;
if (value.dataType != Res_value::TYPE_NULL) {
// Take care of resolving the found resource to its final value.
- ApkAssetsCookie new_cookie =
- assetmanager->ResolveReference(cookie, &value, &config, &type_set_flags, &resid);
- if (new_cookie != kInvalidCookie) {
- cookie = new_cookie;
- }
+ // printf("Resolving attribute reference\n");
+ ssize_t new_block = res->resolveReference(&value, block, &resid,
+ &type_set_flags, &config);
+ if (new_block >= 0) block = new_block;
}
// Deal with the special @null value -- it turns back to TYPE_NULL.
if (value.dataType == Res_value::TYPE_REFERENCE && value.data == 0) {
value.dataType = Res_value::TYPE_NULL;
value.data = Res_value::DATA_NULL_UNDEFINED;
- cookie = kInvalidCookie;
+ block = kXmlBlock;
}
// Write the final value back to Java.
out_values[STYLE_TYPE] = value.dataType;
out_values[STYLE_DATA] = value.data;
- out_values[STYLE_ASSET_COOKIE] = ApkAssetsCookieToJavaCookie(cookie);
+ out_values[STYLE_ASSET_COOKIE] =
+ block != kXmlBlock ? static_cast<uint32_t>(res->getTableCookie(block))
+ : static_cast<uint32_t>(-1);
out_values[STYLE_RESOURCE_ID] = resid;
out_values[STYLE_CHANGING_CONFIGURATIONS] = type_set_flags;
out_values[STYLE_DENSITY] = config.density;
@@ -466,6 +485,8 @@
out_values += STYLE_NUM_ENTRIES;
}
+ res->unlock();
+
if (out_indices != nullptr) {
out_indices[0] = indices_idx;
}
diff --git a/libs/androidfw/LoadedArsc.cpp b/libs/androidfw/LoadedArsc.cpp
index 1d2c597..28548e2 100644
--- a/libs/androidfw/LoadedArsc.cpp
+++ b/libs/androidfw/LoadedArsc.cpp
@@ -44,6 +44,44 @@
constexpr const static int kAppPackageId = 0x7f;
+// Element of a TypeSpec array. See TypeSpec.
+struct Type {
+ // The configuration for which this type defines entries.
+ // This is already converted to host endianness.
+ ResTable_config configuration;
+
+ // Pointer to the mmapped data where entry definitions are kept.
+ const ResTable_type* type;
+};
+
+// TypeSpec is going to be immediately proceeded by
+// an array of Type structs, all in the same block of memory.
+struct TypeSpec {
+ // Pointer to the mmapped data where flags are kept.
+ // Flags denote whether the resource entry is public
+ // and under which configurations it varies.
+ const ResTable_typeSpec* type_spec;
+
+ // Pointer to the mmapped data where the IDMAP mappings for this type
+ // exist. May be nullptr if no IDMAP exists.
+ const IdmapEntry_header* idmap_entries;
+
+ // The number of types that follow this struct.
+ // There is a type for each configuration
+ // that entries are defined for.
+ size_t type_count;
+
+ // Trick to easily access a variable number of Type structs
+ // proceeding this struct, and to ensure their alignment.
+ const Type types[0];
+};
+
+// TypeSpecPtr points to the block of memory that holds
+// a TypeSpec struct, followed by an array of Type structs.
+// TypeSpecPtr is a managed pointer that knows how to delete
+// itself.
+using TypeSpecPtr = util::unique_cptr<TypeSpec>;
+
namespace {
// Builder that helps accumulate Type structs and then create a single
@@ -57,22 +95,21 @@
}
void AddType(const ResTable_type* type) {
- types_.push_back(type);
+ ResTable_config config;
+ config.copyFromDtoH(type->config);
+ types_.push_back(Type{config, type});
}
TypeSpecPtr Build() {
// Check for overflow.
- using ElementType = const ResTable_type*;
- if ((std::numeric_limits<size_t>::max() - sizeof(TypeSpec)) / sizeof(ElementType) <
- types_.size()) {
+ if ((std::numeric_limits<size_t>::max() - sizeof(TypeSpec)) / sizeof(Type) < types_.size()) {
return {};
}
- TypeSpec* type_spec =
- (TypeSpec*)::malloc(sizeof(TypeSpec) + (types_.size() * sizeof(ElementType)));
+ TypeSpec* type_spec = (TypeSpec*)::malloc(sizeof(TypeSpec) + (types_.size() * sizeof(Type)));
type_spec->type_spec = header_;
type_spec->idmap_entries = idmap_header_;
type_spec->type_count = types_.size();
- memcpy(type_spec + 1, types_.data(), types_.size() * sizeof(ElementType));
+ memcpy(type_spec + 1, types_.data(), types_.size() * sizeof(Type));
return TypeSpecPtr(type_spec);
}
@@ -81,7 +118,7 @@
const ResTable_typeSpec* header_;
const IdmapEntry_header* idmap_header_;
- std::vector<const ResTable_type*> types_;
+ std::vector<Type> types_;
};
} // namespace
@@ -125,17 +162,18 @@
return true;
}
-static bool VerifyResTableEntry(const ResTable_type* type, uint32_t entry_offset) {
+static bool VerifyResTableEntry(const ResTable_type* type, uint32_t entry_offset,
+ size_t entry_idx) {
// Check that the offset is aligned.
if (entry_offset & 0x03) {
- LOG(ERROR) << "Entry at offset " << entry_offset << " is not 4-byte aligned.";
+ LOG(ERROR) << "Entry offset at index " << entry_idx << " is not 4-byte aligned.";
return false;
}
// Check that the offset doesn't overflow.
if (entry_offset > std::numeric_limits<uint32_t>::max() - dtohl(type->entriesStart)) {
// Overflow in offset.
- LOG(ERROR) << "Entry at offset " << entry_offset << " is too large.";
+ LOG(ERROR) << "Entry offset at index " << entry_idx << " is too large.";
return false;
}
@@ -143,7 +181,7 @@
entry_offset += dtohl(type->entriesStart);
if (entry_offset > chunk_size - sizeof(ResTable_entry)) {
- LOG(ERROR) << "Entry at offset " << entry_offset
+ LOG(ERROR) << "Entry offset at index " << entry_idx
<< " is too large. No room for ResTable_entry.";
return false;
}
@@ -153,13 +191,13 @@
const size_t entry_size = dtohs(entry->size);
if (entry_size < sizeof(*entry)) {
- LOG(ERROR) << "ResTable_entry size " << entry_size << " at offset " << entry_offset
+ LOG(ERROR) << "ResTable_entry size " << entry_size << " at index " << entry_idx
<< " is too small.";
return false;
}
if (entry_size > chunk_size || entry_offset > chunk_size - entry_size) {
- LOG(ERROR) << "ResTable_entry size " << entry_size << " at offset " << entry_offset
+ LOG(ERROR) << "ResTable_entry size " << entry_size << " at index " << entry_idx
<< " is too large.";
return false;
}
@@ -167,7 +205,7 @@
if (entry_size < sizeof(ResTable_map_entry)) {
// There needs to be room for one Res_value struct.
if (entry_offset + entry_size > chunk_size - sizeof(Res_value)) {
- LOG(ERROR) << "No room for Res_value after ResTable_entry at offset " << entry_offset
+ LOG(ERROR) << "No room for Res_value after ResTable_entry at index " << entry_idx
<< " for type " << (int)type->id << ".";
return false;
}
@@ -176,12 +214,12 @@
reinterpret_cast<const Res_value*>(reinterpret_cast<const uint8_t*>(entry) + entry_size);
const size_t value_size = dtohs(value->size);
if (value_size < sizeof(Res_value)) {
- LOG(ERROR) << "Res_value at offset " << entry_offset << " is too small.";
+ LOG(ERROR) << "Res_value at index " << entry_idx << " is too small.";
return false;
}
if (value_size > chunk_size || entry_offset + entry_size > chunk_size - value_size) {
- LOG(ERROR) << "Res_value size " << value_size << " at offset " << entry_offset
+ LOG(ERROR) << "Res_value size " << value_size << " at index " << entry_idx
<< " is too large.";
return false;
}
@@ -190,76 +228,119 @@
const size_t map_entry_count = dtohl(map->count);
size_t map_entries_start = entry_offset + entry_size;
if (map_entries_start & 0x03) {
- LOG(ERROR) << "Map entries at offset " << entry_offset << " start at unaligned offset.";
+ LOG(ERROR) << "Map entries at index " << entry_idx << " start at unaligned offset.";
return false;
}
// Each entry is sizeof(ResTable_map) big.
if (map_entry_count > ((chunk_size - map_entries_start) / sizeof(ResTable_map))) {
- LOG(ERROR) << "Too many map entries in ResTable_map_entry at offset " << entry_offset << ".";
+ LOG(ERROR) << "Too many map entries in ResTable_map_entry at index " << entry_idx << ".";
return false;
}
}
return true;
}
-const ResTable_entry* LoadedPackage::GetEntry(const ResTable_type* type_chunk,
- uint16_t entry_index) {
- uint32_t entry_offset = GetEntryOffset(type_chunk, entry_index);
- if (entry_offset == ResTable_type::NO_ENTRY) {
- return nullptr;
- }
- return GetEntryFromOffset(type_chunk, entry_offset);
-}
+bool LoadedPackage::FindEntry(const TypeSpecPtr& type_spec_ptr, uint16_t entry_idx,
+ const ResTable_config& config, FindEntryResult* out_entry) const {
+ const ResTable_config* best_config = nullptr;
+ const ResTable_type* best_type = nullptr;
+ uint32_t best_offset = 0;
-uint32_t LoadedPackage::GetEntryOffset(const ResTable_type* type_chunk, uint16_t entry_index) {
- // The configuration matches and is better than the previous selection.
- // Find the entry value if it exists for this configuration.
- const size_t entry_count = dtohl(type_chunk->entryCount);
- const size_t offsets_offset = dtohs(type_chunk->header.headerSize);
+ for (uint32_t i = 0; i < type_spec_ptr->type_count; i++) {
+ const Type* type = &type_spec_ptr->types[i];
+ const ResTable_type* type_chunk = type->type;
- // Check if there is the desired entry in this type.
+ if (type->configuration.match(config) &&
+ (best_config == nullptr || type->configuration.isBetterThan(*best_config, &config))) {
+ // The configuration matches and is better than the previous selection.
+ // Find the entry value if it exists for this configuration.
+ const size_t entry_count = dtohl(type_chunk->entryCount);
+ const size_t offsets_offset = dtohs(type_chunk->header.headerSize);
- if (type_chunk->flags & ResTable_type::FLAG_SPARSE) {
- // This is encoded as a sparse map, so perform a binary search.
- const ResTable_sparseTypeEntry* sparse_indices =
- reinterpret_cast<const ResTable_sparseTypeEntry*>(
+ // Check if there is the desired entry in this type.
+
+ if (type_chunk->flags & ResTable_type::FLAG_SPARSE) {
+ // This is encoded as a sparse map, so perform a binary search.
+ const ResTable_sparseTypeEntry* sparse_indices =
+ reinterpret_cast<const ResTable_sparseTypeEntry*>(
+ reinterpret_cast<const uint8_t*>(type_chunk) + offsets_offset);
+ const ResTable_sparseTypeEntry* sparse_indices_end = sparse_indices + entry_count;
+ const ResTable_sparseTypeEntry* result =
+ std::lower_bound(sparse_indices, sparse_indices_end, entry_idx,
+ [](const ResTable_sparseTypeEntry& entry, uint16_t entry_idx) {
+ return dtohs(entry.idx) < entry_idx;
+ });
+
+ if (result == sparse_indices_end || dtohs(result->idx) != entry_idx) {
+ // No entry found.
+ continue;
+ }
+
+ // Extract the offset from the entry. Each offset must be a multiple of 4 so we store it as
+ // the real offset divided by 4.
+ best_offset = uint32_t{dtohs(result->offset)} * 4u;
+ } else {
+ if (entry_idx >= entry_count) {
+ // This entry cannot be here.
+ continue;
+ }
+
+ const uint32_t* entry_offsets = reinterpret_cast<const uint32_t*>(
reinterpret_cast<const uint8_t*>(type_chunk) + offsets_offset);
- const ResTable_sparseTypeEntry* sparse_indices_end = sparse_indices + entry_count;
- const ResTable_sparseTypeEntry* result =
- std::lower_bound(sparse_indices, sparse_indices_end, entry_index,
- [](const ResTable_sparseTypeEntry& entry, uint16_t entry_idx) {
- return dtohs(entry.idx) < entry_idx;
- });
+ const uint32_t offset = dtohl(entry_offsets[entry_idx]);
+ if (offset == ResTable_type::NO_ENTRY) {
+ continue;
+ }
- if (result == sparse_indices_end || dtohs(result->idx) != entry_index) {
- // No entry found.
- return ResTable_type::NO_ENTRY;
+ // There is an entry for this resource, record it.
+ best_offset = offset;
+ }
+
+ best_config = &type->configuration;
+ best_type = type_chunk;
}
-
- // Extract the offset from the entry. Each offset must be a multiple of 4 so we store it as
- // the real offset divided by 4.
- return uint32_t{dtohs(result->offset)} * 4u;
}
- // This type is encoded as a dense array.
- if (entry_index >= entry_count) {
- // This entry cannot be here.
- return ResTable_type::NO_ENTRY;
+ if (best_type == nullptr) {
+ return false;
}
- const uint32_t* entry_offsets = reinterpret_cast<const uint32_t*>(
- reinterpret_cast<const uint8_t*>(type_chunk) + offsets_offset);
- return dtohl(entry_offsets[entry_index]);
+ if (UNLIKELY(!VerifyResTableEntry(best_type, best_offset, entry_idx))) {
+ return false;
+ }
+
+ const ResTable_entry* best_entry = reinterpret_cast<const ResTable_entry*>(
+ reinterpret_cast<const uint8_t*>(best_type) + best_offset + dtohl(best_type->entriesStart));
+
+ const uint32_t* flags = reinterpret_cast<const uint32_t*>(type_spec_ptr->type_spec + 1);
+ out_entry->type_flags = dtohl(flags[entry_idx]);
+ out_entry->entry = best_entry;
+ out_entry->config = best_config;
+ out_entry->type_string_ref = StringPoolRef(&type_string_pool_, best_type->id - 1);
+ out_entry->entry_string_ref = StringPoolRef(&key_string_pool_, dtohl(best_entry->key.index));
+ return true;
}
-const ResTable_entry* LoadedPackage::GetEntryFromOffset(const ResTable_type* type_chunk,
- uint32_t offset) {
- if (UNLIKELY(!VerifyResTableEntry(type_chunk, offset))) {
- return nullptr;
+bool LoadedPackage::FindEntry(uint8_t type_idx, uint16_t entry_idx, const ResTable_config& config,
+ FindEntryResult* out_entry) const {
+ ATRACE_CALL();
+
+ // If the type IDs are offset in this package, we need to take that into account when searching
+ // for a type.
+ const TypeSpecPtr& ptr = type_specs_[type_idx - type_id_offset_];
+ if (UNLIKELY(ptr == nullptr)) {
+ return false;
}
- return reinterpret_cast<const ResTable_entry*>(reinterpret_cast<const uint8_t*>(type_chunk) +
- offset + dtohl(type_chunk->entriesStart));
+
+ // If there is an IDMAP supplied with this package, translate the entry ID.
+ if (ptr->idmap_entries != nullptr) {
+ if (!LoadedIdmap::Lookup(ptr->idmap_entries, entry_idx, &entry_idx)) {
+ // There is no mapping, so the resource is not meant to be in this overlay package.
+ return false;
+ }
+ }
+ return FindEntry(ptr, entry_idx, config, out_entry);
}
void LoadedPackage::CollectConfigurations(bool exclude_mipmap,
@@ -267,7 +348,7 @@
const static std::u16string kMipMap = u"mipmap";
const size_t type_count = type_specs_.size();
for (size_t i = 0; i < type_count; i++) {
- const TypeSpecPtr& type_spec = type_specs_[i];
+ const util::unique_cptr<TypeSpec>& type_spec = type_specs_[i];
if (type_spec != nullptr) {
if (exclude_mipmap) {
const int type_idx = type_spec->type_spec->id - 1;
@@ -288,11 +369,8 @@
}
}
- const auto iter_end = type_spec->types + type_spec->type_count;
- for (auto iter = type_spec->types; iter != iter_end; ++iter) {
- ResTable_config config;
- config.copyFromDtoH((*iter)->config);
- out_configs->insert(config);
+ for (size_t j = 0; j < type_spec->type_count; j++) {
+ out_configs->insert(type_spec->types[j].configuration);
}
}
}
@@ -302,12 +380,10 @@
char temp_locale[RESTABLE_MAX_LOCALE_LEN];
const size_t type_count = type_specs_.size();
for (size_t i = 0; i < type_count; i++) {
- const TypeSpecPtr& type_spec = type_specs_[i];
+ const util::unique_cptr<TypeSpec>& type_spec = type_specs_[i];
if (type_spec != nullptr) {
- const auto iter_end = type_spec->types + type_spec->type_count;
- for (auto iter = type_spec->types; iter != iter_end; ++iter) {
- ResTable_config configuration;
- configuration.copyFromDtoH((*iter)->config);
+ for (size_t j = 0; j < type_spec->type_count; j++) {
+ const ResTable_config& configuration = type_spec->types[j].configuration;
if (configuration.locale != 0) {
configuration.getBcp47Locale(temp_locale, canonicalize);
std::string locale(temp_locale);
@@ -335,17 +411,17 @@
return 0u;
}
- const auto iter_end = type_spec->types + type_spec->type_count;
- for (auto iter = type_spec->types; iter != iter_end; ++iter) {
- const ResTable_type* type = *iter;
- size_t entry_count = dtohl(type->entryCount);
+ for (size_t ti = 0; ti < type_spec->type_count; ti++) {
+ const Type* type = &type_spec->types[ti];
+ size_t entry_count = dtohl(type->type->entryCount);
for (size_t entry_idx = 0; entry_idx < entry_count; entry_idx++) {
const uint32_t* entry_offsets = reinterpret_cast<const uint32_t*>(
- reinterpret_cast<const uint8_t*>(type) + dtohs(type->header.headerSize));
+ reinterpret_cast<const uint8_t*>(type->type) + dtohs(type->type->header.headerSize));
const uint32_t offset = dtohl(entry_offsets[entry_idx]);
if (offset != ResTable_type::NO_ENTRY) {
- const ResTable_entry* entry = reinterpret_cast<const ResTable_entry*>(
- reinterpret_cast<const uint8_t*>(type) + dtohl(type->entriesStart) + offset);
+ const ResTable_entry* entry =
+ reinterpret_cast<const ResTable_entry*>(reinterpret_cast<const uint8_t*>(type->type) +
+ dtohl(type->type->entriesStart) + offset);
if (dtohl(entry->key.index) == static_cast<uint32_t>(key_idx)) {
// The package ID will be overridden by the caller (due to runtime assignment of package
// IDs for shared libraries).
@@ -357,7 +433,8 @@
return 0u;
}
-const LoadedPackage* LoadedArsc::GetPackageById(uint8_t package_id) const {
+const LoadedPackage* LoadedArsc::GetPackageForId(uint32_t resid) const {
+ const uint8_t package_id = get_package_id(resid);
for (const auto& loaded_package : packages_) {
if (loaded_package->GetPackageId() == package_id) {
return loaded_package.get();
@@ -605,6 +682,26 @@
return std::move(loaded_package);
}
+bool LoadedArsc::FindEntry(uint32_t resid, const ResTable_config& config,
+ FindEntryResult* out_entry) const {
+ ATRACE_CALL();
+
+ const uint8_t package_id = get_package_id(resid);
+ const uint8_t type_id = get_type_id(resid);
+ const uint16_t entry_id = get_entry_id(resid);
+
+ if (UNLIKELY(type_id == 0)) {
+ LOG(ERROR) << base::StringPrintf("Invalid ID 0x%08x.", resid);
+ return false;
+ }
+
+ for (const auto& loaded_package : packages_) {
+ if (loaded_package->GetPackageId() == package_id) {
+ return loaded_package->FindEntry(type_id - 1, entry_id, config, out_entry);
+ }
+ }
+ return false;
+}
bool LoadedArsc::LoadTable(const Chunk& chunk, const LoadedIdmap* loaded_idmap,
bool load_as_shared_library) {
diff --git a/libs/androidfw/include/androidfw/AssetManager2.h b/libs/androidfw/include/androidfw/AssetManager2.h
index ef08897..b033137 100644
--- a/libs/androidfw/include/androidfw/AssetManager2.h
+++ b/libs/androidfw/include/androidfw/AssetManager2.h
@@ -69,8 +69,6 @@
Entry entries[0];
};
-struct FindEntryResult;
-
// AssetManager2 is the main entry point for accessing assets and resources.
// AssetManager2 provides caching of resources retrieved via the underlying ApkAssets.
class AssetManager2 {
@@ -129,7 +127,7 @@
// If `exclude_mipmap` is set to true, resource configurations defined for resource type 'mipmap'
// will be excluded from the list.
std::set<ResTable_config> GetResourceConfigurations(bool exclude_system = false,
- bool exclude_mipmap = false) const;
+ bool exclude_mipmap = false);
// Returns all the locales for which there are resources defined. This includes resource
// locales in all the ApkAssets set for this AssetManager.
@@ -138,24 +136,24 @@
// If `merge_equivalent_languages` is set to true, resource locales will be canonicalized
// and de-duped in the resulting list.
std::set<std::string> GetResourceLocales(bool exclude_system = false,
- bool merge_equivalent_languages = false) const;
+ bool merge_equivalent_languages = false);
// Searches the set of APKs loaded by this AssetManager and opens the first one found located
// in the assets/ directory.
// `mode` controls how the file is opened.
//
// NOTE: The loaded APKs are searched in reverse order.
- std::unique_ptr<Asset> Open(const std::string& filename, Asset::AccessMode mode) const;
+ std::unique_ptr<Asset> Open(const std::string& filename, Asset::AccessMode mode);
// Opens a file within the assets/ directory of the APK specified by `cookie`.
// `mode` controls how the file is opened.
std::unique_ptr<Asset> Open(const std::string& filename, ApkAssetsCookie cookie,
- Asset::AccessMode mode) const;
+ Asset::AccessMode mode);
// Opens the directory specified by `dirname`. The result is an AssetDir that is the combination
// of all directories matching `dirname` under the assets/ directory of every ApkAssets loaded.
// The entries are sorted by their ASCII name.
- std::unique_ptr<AssetDir> OpenDir(const std::string& dirname) const;
+ std::unique_ptr<AssetDir> OpenDir(const std::string& dirname);
// Searches the set of APKs loaded by this AssetManager and opens the first one found.
// `mode` controls how the file is opened.
@@ -163,24 +161,24 @@
//
// NOTE: The loaded APKs are searched in reverse order.
std::unique_ptr<Asset> OpenNonAsset(const std::string& filename, Asset::AccessMode mode,
- ApkAssetsCookie* out_cookie = nullptr) const;
+ ApkAssetsCookie* out_cookie = nullptr);
// Opens a file in the APK specified by `cookie`. `mode` controls how the file is opened.
// This is typically used to open a specific AndroidManifest.xml, or a binary XML file
// referenced by a resource lookup with GetResource().
std::unique_ptr<Asset> OpenNonAsset(const std::string& filename, ApkAssetsCookie cookie,
- Asset::AccessMode mode) const;
+ Asset::AccessMode mode);
// Populates the `out_name` parameter with resource name information.
// Utf8 strings are preferred, and only if they are unavailable are
// the Utf16 variants populated.
// Returns false if the resource was not found or the name was missing/corrupt.
- bool GetResourceName(uint32_t resid, ResourceName* out_name) const;
+ bool GetResourceName(uint32_t resid, ResourceName* out_name);
// Populates `out_flags` with the bitmask of configuration axis that this resource varies with.
// See ResTable_config for the list of configuration axis.
// Returns false if the resource was not found.
- bool GetResourceFlags(uint32_t resid, uint32_t* out_flags) const;
+ bool GetResourceFlags(uint32_t resid, uint32_t* out_flags);
// Finds the resource ID assigned to `resource_name`.
// `resource_name` must be of the form '[package:][type/]entry'.
@@ -188,7 +186,7 @@
// If no type is specified in `resource_name`, then `fallback_type` is used as the type.
// Returns 0x0 if no resource by that name was found.
uint32_t GetResourceId(const std::string& resource_name, const std::string& fallback_type = {},
- const std::string& fallback_package = {}) const;
+ const std::string& fallback_package = {});
// Retrieves the best matching resource with ID `resid`. The resource value is filled into
// `out_value` and the configuration for the selected value is populated in `out_selected_config`.
@@ -201,7 +199,7 @@
// this function logs if the resource was a map/bag type before returning kInvalidCookie.
ApkAssetsCookie GetResource(uint32_t resid, bool may_be_bag, uint16_t density_override,
Res_value* out_value, ResTable_config* out_selected_config,
- uint32_t* out_flags) const;
+ uint32_t* out_flags);
// Resolves the resource reference in `in_out_value` if the data type is
// Res_value::TYPE_REFERENCE.
@@ -217,7 +215,7 @@
// it was not found.
ApkAssetsCookie ResolveReference(ApkAssetsCookie cookie, Res_value* in_out_value,
ResTable_config* in_out_selected_config, uint32_t* in_out_flags,
- uint32_t* out_last_reference) const;
+ uint32_t* out_last_reference);
// Retrieves the best matching bag/map resource with ID `resid`.
// This method will resolve all parent references for this bag and merge keys with the child.
@@ -235,9 +233,9 @@
std::unique_ptr<Theme> NewTheme();
template <typename Func>
- void ForEachPackage(Func func) const {
+ void ForEachPackage(Func func) {
for (const PackageGroup& package_group : package_groups_) {
- func(package_group.packages_.front().loaded_package_->GetPackageName(),
+ func(package_group.packages_.front()->GetPackageName(),
package_group.dynamic_ref_table.mAssignedPackageId);
}
}
@@ -262,7 +260,7 @@
// NOTE: FindEntry takes care of ensuring that structs within FindEntryResult have been properly
// bounds-checked. Callers of FindEntry are free to trust the data if this method succeeds.
ApkAssetsCookie FindEntry(uint32_t resid, uint16_t density_override, bool stop_at_first_match,
- FindEntryResult* out_entry) const;
+ FindEntryResult* out_entry);
// Assigns package IDs to all shared library ApkAssets.
// Should be called whenever the ApkAssets are changed.
@@ -272,43 +270,13 @@
// bitmask `diff`.
void InvalidateCaches(uint32_t diff);
- // Triggers the re-construction of lists of types that match the set configuration.
- // This should always be called when mutating the AssetManager's configuration or ApkAssets set.
- void RebuildFilterList();
-
// The ordered list of ApkAssets to search. These are not owned by the AssetManager, and must
// have a longer lifetime.
std::vector<const ApkAssets*> apk_assets_;
- // A collection of configurations and their associated ResTable_type that match the current
- // AssetManager configuration.
- struct FilteredConfigGroup {
- std::vector<ResTable_config> configurations;
- std::vector<const ResTable_type*> types;
- };
-
- // Represents an single package.
- struct ConfiguredPackage {
- // A pointer to the immutable, loaded package info.
- const LoadedPackage* loaded_package_;
-
- // A mutable AssetManager-specific list of configurations that match the AssetManager's
- // current configuration. This is used as an optimization to avoid checking every single
- // candidate configuration when looking up resources.
- ByteBucketArray<FilteredConfigGroup> filtered_configs_;
- };
-
- // Represents a logical package, which can be made up of many individual packages. Each package
- // in a PackageGroup shares the same package name and package ID.
struct PackageGroup {
- // The set of packages that make-up this group.
- std::vector<ConfiguredPackage> packages_;
-
- // The cookies associated with each package in the group. They share the same order as
- // packages_.
+ std::vector<const LoadedPackage*> packages_;
std::vector<ApkAssetsCookie> cookies_;
-
- // A library reference table that contains build-package ID to runtime-package ID mappings.
DynamicRefTable dynamic_ref_table;
};
@@ -382,7 +350,7 @@
ApkAssetsCookie ResolveAttributeReference(ApkAssetsCookie cookie, Res_value* in_out_value,
ResTable_config* in_out_selected_config = nullptr,
uint32_t* in_out_type_spec_flags = nullptr,
- uint32_t* out_last_ref = nullptr) const;
+ uint32_t* out_last_ref = nullptr);
private:
DISALLOW_COPY_AND_ASSIGN(Theme);
diff --git a/libs/androidfw/include/androidfw/AttributeFinder.h b/libs/androidfw/include/androidfw/AttributeFinder.h
index 03fad49..f281921 100644
--- a/libs/androidfw/include/androidfw/AttributeFinder.h
+++ b/libs/androidfw/include/androidfw/AttributeFinder.h
@@ -58,7 +58,6 @@
BackTrackingAttributeFinder(const Iterator& begin, const Iterator& end);
Iterator Find(uint32_t attr);
- inline Iterator end();
private:
void JumpToClosestAttribute(uint32_t package_id);
@@ -202,11 +201,6 @@
return end_;
}
-template <typename Derived, typename Iterator>
-Iterator BackTrackingAttributeFinder<Derived, Iterator>::end() {
- return end_;
-}
-
} // namespace android
#endif // ANDROIDFW_ATTRIBUTE_FINDER_H
diff --git a/libs/androidfw/include/androidfw/AttributeResolution.h b/libs/androidfw/include/androidfw/AttributeResolution.h
index 35ef98d..69b76041 100644
--- a/libs/androidfw/include/androidfw/AttributeResolution.h
+++ b/libs/androidfw/include/androidfw/AttributeResolution.h
@@ -17,8 +17,7 @@
#ifndef ANDROIDFW_ATTRIBUTERESOLUTION_H
#define ANDROIDFW_ATTRIBUTERESOLUTION_H
-#include "androidfw/AssetManager2.h"
-#include "androidfw/ResourceTypes.h"
+#include <androidfw/ResourceTypes.h>
namespace android {
@@ -43,19 +42,19 @@
// `out_values` must NOT be nullptr.
// `out_indices` may be nullptr.
-bool ResolveAttrs(Theme* theme, uint32_t def_style_attr, uint32_t def_style_resid,
+bool ResolveAttrs(ResTable::Theme* theme, uint32_t def_style_attr, uint32_t def_style_res,
uint32_t* src_values, size_t src_values_length, uint32_t* attrs,
size_t attrs_length, uint32_t* out_values, uint32_t* out_indices);
// `out_values` must NOT be nullptr.
// `out_indices` is NOT optional and must NOT be nullptr.
-void ApplyStyle(Theme* theme, ResXMLParser* xml_parser, uint32_t def_style_attr,
- uint32_t def_style_resid, const uint32_t* attrs, size_t attrs_length,
+void ApplyStyle(ResTable::Theme* theme, ResXMLParser* xml_parser, uint32_t def_style_attr,
+ uint32_t def_style_res, const uint32_t* attrs, size_t attrs_length,
uint32_t* out_values, uint32_t* out_indices);
// `out_values` must NOT be nullptr.
// `out_indices` may be nullptr.
-bool RetrieveAttributes(AssetManager2* assetmanager, ResXMLParser* xml_parser, uint32_t* attrs,
+bool RetrieveAttributes(const ResTable* res, ResXMLParser* xml_parser, uint32_t* attrs,
size_t attrs_length, uint32_t* out_values, uint32_t* out_indices);
} // namespace android
diff --git a/libs/androidfw/include/androidfw/LoadedArsc.h b/libs/androidfw/include/androidfw/LoadedArsc.h
index 35ae5fc..965e2db 100644
--- a/libs/androidfw/include/androidfw/LoadedArsc.h
+++ b/libs/androidfw/include/androidfw/LoadedArsc.h
@@ -41,40 +41,32 @@
int package_id = 0;
};
-// TypeSpec is going to be immediately proceeded by
-// an array of Type structs, all in the same block of memory.
-struct TypeSpec {
- // Pointer to the mmapped data where flags are kept.
- // Flags denote whether the resource entry is public
- // and under which configurations it varies.
- const ResTable_typeSpec* type_spec;
+struct FindEntryResult {
+ // A pointer to the resource table entry for this resource.
+ // If the size of the entry is > sizeof(ResTable_entry), it can be cast to
+ // a ResTable_map_entry and processed as a bag/map.
+ const ResTable_entry* entry = nullptr;
- // Pointer to the mmapped data where the IDMAP mappings for this type
- // exist. May be nullptr if no IDMAP exists.
- const IdmapEntry_header* idmap_entries;
+ // The configuration for which the resulting entry was defined.
+ const ResTable_config* config = nullptr;
- // The number of types that follow this struct.
- // There is a type for each configuration that entries are defined for.
- size_t type_count;
+ // Stores the resulting bitmask of configuration axis with which the resource value varies.
+ uint32_t type_flags = 0u;
- // Trick to easily access a variable number of Type structs
- // proceeding this struct, and to ensure their alignment.
- const ResTable_type* types[0];
+ // The dynamic package ID map for the package from which this resource came from.
+ const DynamicRefTable* dynamic_ref_table = nullptr;
- inline uint32_t GetFlagsForEntryIndex(uint16_t entry_index) const {
- if (entry_index >= dtohl(type_spec->entryCount)) {
- return 0u;
- }
+ // The string pool reference to the type's name. This uses a different string pool than
+ // the global string pool, but this is hidden from the caller.
+ StringPoolRef type_string_ref;
- const uint32_t* flags = reinterpret_cast<const uint32_t*>(type_spec + 1);
- return flags[entry_index];
- }
+ // The string pool reference to the entry's name. This uses a different string pool than
+ // the global string pool, but this is hidden from the caller.
+ StringPoolRef entry_string_ref;
};
-// TypeSpecPtr points to a block of memory that holds a TypeSpec struct, followed by an array of
-// ResTable_type pointers.
-// TypeSpecPtr is a managed pointer that knows how to delete itself.
-using TypeSpecPtr = util::unique_cptr<TypeSpec>;
+struct TypeSpec;
+class LoadedArsc;
class LoadedPackage {
public:
@@ -84,6 +76,9 @@
~LoadedPackage();
+ bool FindEntry(uint8_t type_idx, uint16_t entry_idx, const ResTable_config& config,
+ FindEntryResult* out_entry) const;
+
// Finds the entry with the specified type name and entry name. The names are in UTF-16 because
// the underlying ResStringPool API expects this. For now this is acceptable, but since
// the default policy in AAPT2 is to build UTF-8 string pools, this needs to change.
@@ -91,12 +86,6 @@
// for patching the correct package ID to the resource ID.
uint32_t FindEntryByName(const std::u16string& type_name, const std::u16string& entry_name) const;
- static const ResTable_entry* GetEntry(const ResTable_type* type_chunk, uint16_t entry_index);
-
- static uint32_t GetEntryOffset(const ResTable_type* type_chunk, uint16_t entry_index);
-
- static const ResTable_entry* GetEntryFromOffset(const ResTable_type* type_chunk, uint32_t offset);
-
// Returns the string pool where type names are stored.
inline const ResStringPool* GetTypeStringPool() const {
return &type_string_pool_;
@@ -146,32 +135,14 @@
// before being inserted into the set. This may cause some equivalent locales to de-dupe.
void CollectLocales(bool canonicalize, std::set<std::string>* out_locales) const;
- // type_idx is TT - 1 from 0xPPTTEEEE.
- inline const TypeSpec* GetTypeSpecByTypeIndex(uint8_t type_index) const {
- // If the type IDs are offset in this package, we need to take that into account when searching
- // for a type.
- return type_specs_[type_index - type_id_offset_].get();
- }
-
- template <typename Func>
- void ForEachTypeSpec(Func f) const {
- for (size_t i = 0; i < type_specs_.size(); i++) {
- const TypeSpecPtr& ptr = type_specs_[i];
- if (ptr != nullptr) {
- uint8_t type_id = ptr->type_spec->id;
- if (ptr->idmap_entries != nullptr) {
- type_id = ptr->idmap_entries->target_type_id;
- }
- f(ptr.get(), type_id - 1);
- }
- }
- }
-
private:
DISALLOW_COPY_AND_ASSIGN(LoadedPackage);
LoadedPackage();
+ bool FindEntry(const util::unique_cptr<TypeSpec>& type_spec_ptr, uint16_t entry_idx,
+ const ResTable_config& config, FindEntryResult* out_entry) const;
+
ResStringPool type_string_pool_;
ResStringPool key_string_pool_;
std::string package_name_;
@@ -181,7 +152,7 @@
bool system_ = false;
bool overlay_ = false;
- ByteBucketArray<TypeSpecPtr> type_specs_;
+ ByteBucketArray<util::unique_cptr<TypeSpec>> type_specs_;
std::vector<DynamicPackageEntry> dynamic_package_map_;
};
@@ -209,20 +180,25 @@
return &global_string_pool_;
}
- // Gets a pointer to the package with the specified package ID, or nullptr if no such package
- // exists.
- const LoadedPackage* GetPackageById(uint8_t package_id) const;
+ // Finds the resource with ID `resid` with the best value for configuration `config`.
+ // The parameter `out_entry` will be filled with the resulting resource entry.
+ // The resource entry can be a simple entry (ResTable_entry) or a complex bag
+ // (ResTable_entry_map).
+ bool FindEntry(uint32_t resid, const ResTable_config& config, FindEntryResult* out_entry) const;
- // Returns a vector of LoadedPackage pointers, representing the packages in this LoadedArsc.
- inline const std::vector<std::unique_ptr<const LoadedPackage>>& GetPackages() const {
- return packages_;
- }
+ // Gets a pointer to the name of the package in `resid`, or nullptr if the package doesn't exist.
+ const LoadedPackage* GetPackageForId(uint32_t resid) const;
// Returns true if this is a system provided resource.
inline bool IsSystem() const {
return system_;
}
+ // Returns a vector of LoadedPackage pointers, representing the packages in this LoadedArsc.
+ inline const std::vector<std::unique_ptr<const LoadedPackage>>& GetPackages() const {
+ return packages_;
+ }
+
private:
DISALLOW_COPY_AND_ASSIGN(LoadedArsc);
diff --git a/libs/androidfw/include/androidfw/MutexGuard.h b/libs/androidfw/include/androidfw/MutexGuard.h
deleted file mode 100644
index 64924f4..0000000
--- a/libs/androidfw/include/androidfw/MutexGuard.h
+++ /dev/null
@@ -1,101 +0,0 @@
-/*
- * 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.
- */
-
-#ifndef ANDROIDFW_MUTEXGUARD_H
-#define ANDROIDFW_MUTEXGUARD_H
-
-#include <mutex>
-#include <type_traits>
-
-#include "android-base/macros.h"
-
-namespace android {
-
-template <typename T>
-class ScopedLock;
-
-// Owns the guarded object and protects access to it via a mutex.
-// The guarded object is inaccessible via this class.
-// The mutex is locked and the object accessed via the ScopedLock<T> class.
-//
-// NOTE: The template parameter T should not be a raw pointer, since ownership
-// is ambiguous and error-prone. Instead use an std::unique_ptr<>.
-//
-// Example use:
-//
-// Guarded<std::string> shared_string("hello");
-// {
-// ScopedLock<std::string> locked_string(shared_string);
-// *locked_string += " world";
-// }
-//
-template <typename T>
-class Guarded {
- static_assert(!std::is_pointer<T>::value, "T must not be a raw pointer");
-
- public:
- explicit Guarded() : guarded_() {
- }
-
- template <typename U = T>
- explicit Guarded(const T& guarded,
- typename std::enable_if<std::is_copy_constructible<U>::value>::type = void())
- : guarded_(guarded) {
- }
-
- template <typename U = T>
- explicit Guarded(T&& guarded,
- typename std::enable_if<std::is_move_constructible<U>::value>::type = void())
- : guarded_(std::move(guarded)) {
- }
-
- private:
- friend class ScopedLock<T>;
-
- DISALLOW_COPY_AND_ASSIGN(Guarded);
-
- std::mutex lock_;
- T guarded_;
-};
-
-template <typename T>
-class ScopedLock {
- public:
- explicit ScopedLock(Guarded<T>& guarded) : lock_(guarded.lock_), guarded_(guarded.guarded_) {
- }
-
- T& operator*() {
- return guarded_;
- }
-
- T* operator->() {
- return &guarded_;
- }
-
- T* get() {
- return &guarded_;
- }
-
- private:
- DISALLOW_COPY_AND_ASSIGN(ScopedLock);
-
- std::lock_guard<std::mutex> lock_;
- T& guarded_;
-};
-
-} // namespace android
-
-#endif // ANDROIDFW_MUTEXGUARD_H
diff --git a/libs/androidfw/include/androidfw/ResourceUtils.h b/libs/androidfw/include/androidfw/ResourceUtils.h
index d94779b..c2eae85 100644
--- a/libs/androidfw/include/androidfw/ResourceUtils.h
+++ b/libs/androidfw/include/androidfw/ResourceUtils.h
@@ -28,7 +28,7 @@
StringPiece* out_entry);
inline uint32_t fix_package_id(uint32_t resid, uint8_t package_id) {
- return (resid & 0x00ffffffu) | (static_cast<uint32_t>(package_id) << 24);
+ return resid | (static_cast<uint32_t>(package_id) << 24);
}
inline uint8_t get_package_id(uint32_t resid) {
diff --git a/libs/androidfw/tests/ApkAssets_test.cpp b/libs/androidfw/tests/ApkAssets_test.cpp
index e2b9f00..6c43a67 100644
--- a/libs/androidfw/tests/ApkAssets_test.cpp
+++ b/libs/androidfw/tests/ApkAssets_test.cpp
@@ -26,56 +26,58 @@
using ::android::base::unique_fd;
using ::com::android::basic::R;
-using ::testing::Eq;
-using ::testing::Ge;
-using ::testing::NotNull;
-using ::testing::SizeIs;
-using ::testing::StrEq;
namespace android {
TEST(ApkAssetsTest, LoadApk) {
std::unique_ptr<const ApkAssets> loaded_apk =
ApkAssets::Load(GetTestDataPath() + "/basic/basic.apk");
- ASSERT_THAT(loaded_apk, NotNull());
+ ASSERT_NE(nullptr, loaded_apk);
const LoadedArsc* loaded_arsc = loaded_apk->GetLoadedArsc();
- ASSERT_THAT(loaded_arsc, NotNull());
- ASSERT_THAT(loaded_arsc->GetPackageById(0x7fu), NotNull());
- ASSERT_THAT(loaded_apk->Open("res/layout/main.xml"), NotNull());
+ ASSERT_NE(nullptr, loaded_arsc);
+
+ const LoadedPackage* loaded_package = loaded_arsc->GetPackageForId(0x7f010000);
+ ASSERT_NE(nullptr, loaded_package);
+
+ std::unique_ptr<Asset> asset = loaded_apk->Open("res/layout/main.xml");
+ ASSERT_NE(nullptr, asset);
}
TEST(ApkAssetsTest, LoadApkFromFd) {
const std::string path = GetTestDataPath() + "/basic/basic.apk";
unique_fd fd(::open(path.c_str(), O_RDONLY | O_BINARY));
- ASSERT_THAT(fd.get(), Ge(0));
+ ASSERT_GE(fd.get(), 0);
std::unique_ptr<const ApkAssets> loaded_apk =
ApkAssets::LoadFromFd(std::move(fd), path, false /*system*/, false /*force_shared_lib*/);
- ASSERT_THAT(loaded_apk, NotNull());
+ ASSERT_NE(nullptr, loaded_apk);
const LoadedArsc* loaded_arsc = loaded_apk->GetLoadedArsc();
- ASSERT_THAT(loaded_arsc, NotNull());
- ASSERT_THAT(loaded_arsc->GetPackageById(0x7fu), NotNull());
- ASSERT_THAT(loaded_apk->Open("res/layout/main.xml"), NotNull());
+ ASSERT_NE(nullptr, loaded_arsc);
+
+ const LoadedPackage* loaded_package = loaded_arsc->GetPackageForId(0x7f010000);
+ ASSERT_NE(nullptr, loaded_package);
+
+ std::unique_ptr<Asset> asset = loaded_apk->Open("res/layout/main.xml");
+ ASSERT_NE(nullptr, asset);
}
TEST(ApkAssetsTest, LoadApkAsSharedLibrary) {
std::unique_ptr<const ApkAssets> loaded_apk =
ApkAssets::Load(GetTestDataPath() + "/appaslib/appaslib.apk");
- ASSERT_THAT(loaded_apk, NotNull());
-
+ ASSERT_NE(nullptr, loaded_apk);
const LoadedArsc* loaded_arsc = loaded_apk->GetLoadedArsc();
- ASSERT_THAT(loaded_arsc, NotNull());
- ASSERT_THAT(loaded_arsc->GetPackages(), SizeIs(1u));
+ ASSERT_NE(nullptr, loaded_arsc);
+ ASSERT_EQ(1u, loaded_arsc->GetPackages().size());
EXPECT_FALSE(loaded_arsc->GetPackages()[0]->IsDynamic());
loaded_apk = ApkAssets::LoadAsSharedLibrary(GetTestDataPath() + "/appaslib/appaslib.apk");
- ASSERT_THAT(loaded_apk, NotNull());
+ ASSERT_NE(nullptr, loaded_apk);
loaded_arsc = loaded_apk->GetLoadedArsc();
- ASSERT_THAT(loaded_arsc, NotNull());
- ASSERT_THAT(loaded_arsc->GetPackages(), SizeIs(1u));
+ ASSERT_NE(nullptr, loaded_arsc);
+ ASSERT_EQ(1u, loaded_arsc->GetPackages().size());
EXPECT_TRUE(loaded_arsc->GetPackages()[0]->IsDynamic());
}
@@ -84,22 +86,19 @@
ResTable target_table;
const std::string target_path = GetTestDataPath() + "/basic/basic.apk";
ASSERT_TRUE(ReadFileFromZipToString(target_path, "resources.arsc", &contents));
- ASSERT_THAT(target_table.add(contents.data(), contents.size(), 0, true /*copyData*/),
- Eq(NO_ERROR));
+ ASSERT_EQ(NO_ERROR, target_table.add(contents.data(), contents.size(), 0, true /*copyData*/));
ResTable overlay_table;
const std::string overlay_path = GetTestDataPath() + "/overlay/overlay.apk";
ASSERT_TRUE(ReadFileFromZipToString(overlay_path, "resources.arsc", &contents));
- ASSERT_THAT(overlay_table.add(contents.data(), contents.size(), 0, true /*copyData*/),
- Eq(NO_ERROR));
+ ASSERT_EQ(NO_ERROR, overlay_table.add(contents.data(), contents.size(), 0, true /*copyData*/));
util::unique_cptr<void> idmap_data;
void* temp_data;
size_t idmap_len;
- ASSERT_THAT(target_table.createIdmap(overlay_table, 0u, 0u, target_path.c_str(),
- overlay_path.c_str(), &temp_data, &idmap_len),
- Eq(NO_ERROR));
+ ASSERT_EQ(NO_ERROR, target_table.createIdmap(overlay_table, 0u, 0u, target_path.c_str(),
+ overlay_path.c_str(), &temp_data, &idmap_len));
idmap_data.reset(temp_data);
TemporaryFile tf;
@@ -109,30 +108,37 @@
// Open something so that the destructor of TemporaryFile closes a valid fd.
tf.fd = open("/dev/null", O_WRONLY);
- ASSERT_THAT(ApkAssets::LoadOverlay(tf.path), NotNull());
+ std::unique_ptr<const ApkAssets> loaded_overlay_apk = ApkAssets::LoadOverlay(tf.path);
+ ASSERT_NE(nullptr, loaded_overlay_apk);
}
TEST(ApkAssetsTest, CreateAndDestroyAssetKeepsApkAssetsOpen) {
std::unique_ptr<const ApkAssets> loaded_apk =
ApkAssets::Load(GetTestDataPath() + "/basic/basic.apk");
- ASSERT_THAT(loaded_apk, NotNull());
+ ASSERT_NE(nullptr, loaded_apk);
- { ASSERT_THAT(loaded_apk->Open("res/layout/main.xml", Asset::ACCESS_BUFFER), NotNull()); }
+ {
+ std::unique_ptr<Asset> assets = loaded_apk->Open("res/layout/main.xml", Asset::ACCESS_BUFFER);
+ ASSERT_NE(nullptr, assets);
+ }
- { ASSERT_THAT(loaded_apk->Open("res/layout/main.xml", Asset::ACCESS_BUFFER), NotNull()); }
+ {
+ std::unique_ptr<Asset> assets = loaded_apk->Open("res/layout/main.xml", Asset::ACCESS_BUFFER);
+ ASSERT_NE(nullptr, assets);
+ }
}
TEST(ApkAssetsTest, OpenUncompressedAssetFd) {
std::unique_ptr<const ApkAssets> loaded_apk =
ApkAssets::Load(GetTestDataPath() + "/basic/basic.apk");
- ASSERT_THAT(loaded_apk, NotNull());
+ ASSERT_NE(nullptr, loaded_apk);
auto asset = loaded_apk->Open("assets/uncompressed.txt", Asset::ACCESS_UNKNOWN);
- ASSERT_THAT(asset, NotNull());
+ ASSERT_NE(nullptr, asset);
off64_t start, length;
unique_fd fd(asset->openFileDescriptor(&start, &length));
- ASSERT_THAT(fd.get(), Ge(0));
+ EXPECT_GE(fd.get(), 0);
lseek64(fd.get(), start, SEEK_SET);
@@ -140,7 +146,7 @@
buffer.resize(length);
ASSERT_TRUE(base::ReadFully(fd.get(), &*buffer.begin(), length));
- EXPECT_THAT(buffer, StrEq("This should be uncompressed.\n\n"));
+ EXPECT_EQ("This should be uncompressed.\n\n", buffer);
}
} // namespace android
diff --git a/libs/androidfw/tests/AssetManager2_bench.cpp b/libs/androidfw/tests/AssetManager2_bench.cpp
index 437e147..85e8f25 100644
--- a/libs/androidfw/tests/AssetManager2_bench.cpp
+++ b/libs/androidfw/tests/AssetManager2_bench.cpp
@@ -81,18 +81,17 @@
}
BENCHMARK(BM_AssetManagerLoadFrameworkAssetsOld);
-static void BM_AssetManagerGetResource(benchmark::State& state, uint32_t resid) {
- GetResourceBenchmark({GetTestDataPath() + "/basic/basic.apk"}, nullptr /*config*/, resid, state);
+static void BM_AssetManagerGetResource(benchmark::State& state) {
+ GetResourceBenchmark({GetTestDataPath() + "/basic/basic.apk"}, nullptr /*config*/,
+ basic::R::integer::number1, state);
}
-BENCHMARK_CAPTURE(BM_AssetManagerGetResource, number1, basic::R::integer::number1);
-BENCHMARK_CAPTURE(BM_AssetManagerGetResource, deep_ref, basic::R::integer::deep_ref);
+BENCHMARK(BM_AssetManagerGetResource);
-static void BM_AssetManagerGetResourceOld(benchmark::State& state, uint32_t resid) {
- GetResourceBenchmarkOld({GetTestDataPath() + "/basic/basic.apk"}, nullptr /*config*/, resid,
- state);
+static void BM_AssetManagerGetResourceOld(benchmark::State& state) {
+ GetResourceBenchmarkOld({GetTestDataPath() + "/basic/basic.apk"}, nullptr /*config*/,
+ basic::R::integer::number1, state);
}
-BENCHMARK_CAPTURE(BM_AssetManagerGetResourceOld, number1, basic::R::integer::number1);
-BENCHMARK_CAPTURE(BM_AssetManagerGetResourceOld, deep_ref, basic::R::integer::deep_ref);
+BENCHMARK(BM_AssetManagerGetResourceOld);
static void BM_AssetManagerGetLibraryResource(benchmark::State& state) {
GetResourceBenchmark(
@@ -197,7 +196,7 @@
static void BM_AssetManagerGetResourceLocalesOld(benchmark::State& state) {
AssetManager assets;
if (!assets.addAssetPath(String8(kFrameworkPath), nullptr /*cookie*/, false /*appAsLib*/,
- true /*isSystemAssets*/)) {
+ false /*isSystemAssets*/)) {
state.SkipWithError("Failed to load assets");
return;
}
@@ -212,44 +211,4 @@
}
BENCHMARK(BM_AssetManagerGetResourceLocalesOld);
-static void BM_AssetManagerSetConfigurationFramework(benchmark::State& state) {
- std::unique_ptr<const ApkAssets> apk = ApkAssets::Load(kFrameworkPath);
- if (apk == nullptr) {
- state.SkipWithError("Failed to load assets");
- return;
- }
-
- AssetManager2 assets;
- assets.SetApkAssets({apk.get()});
-
- ResTable_config config;
- memset(&config, 0, sizeof(config));
-
- while (state.KeepRunning()) {
- config.sdkVersion = ~config.sdkVersion;
- assets.SetConfiguration(config);
- }
-}
-BENCHMARK(BM_AssetManagerSetConfigurationFramework);
-
-static void BM_AssetManagerSetConfigurationFrameworkOld(benchmark::State& state) {
- AssetManager assets;
- if (!assets.addAssetPath(String8(kFrameworkPath), nullptr /*cookie*/, false /*appAsLib*/,
- true /*isSystemAssets*/)) {
- state.SkipWithError("Failed to load assets");
- return;
- }
-
- const ResTable& table = assets.getResources(true);
-
- ResTable_config config;
- memset(&config, 0, sizeof(config));
-
- while (state.KeepRunning()) {
- config.sdkVersion = ~config.sdkVersion;
- assets.setConfiguration(config);
- }
-}
-BENCHMARK(BM_AssetManagerSetConfigurationFrameworkOld);
-
} // namespace android
diff --git a/libs/androidfw/tests/AssetManager2_test.cpp b/libs/androidfw/tests/AssetManager2_test.cpp
index eaf79cb..92462a6 100644
--- a/libs/androidfw/tests/AssetManager2_test.cpp
+++ b/libs/androidfw/tests/AssetManager2_test.cpp
@@ -59,7 +59,7 @@
libclient_assets_ = ApkAssets::Load(GetTestDataPath() + "/libclient/libclient.apk");
ASSERT_NE(nullptr, libclient_assets_);
- appaslib_assets_ = ApkAssets::LoadAsSharedLibrary(GetTestDataPath() + "/appaslib/appaslib.apk");
+ appaslib_assets_ = ApkAssets::Load(GetTestDataPath() + "/appaslib/appaslib.apk");
ASSERT_NE(nullptr, appaslib_assets_);
system_assets_ = ApkAssets::Load(GetTestDataPath() + "/system/system.apk", true /*system*/);
@@ -233,25 +233,6 @@
assetmanager.SetApkAssets(
{lib_two_assets_.get(), lib_one_assets_.get(), libclient_assets_.get()});
- const ResolvedBag* bag = assetmanager.GetBag(fix_package_id(lib_one::R::style::Theme, 0x03));
- ASSERT_NE(nullptr, bag);
- ASSERT_GE(bag->entry_count, 2u);
-
- // First two attributes come from lib_one.
- EXPECT_EQ(1, bag->entries[0].cookie);
- EXPECT_EQ(0x03, get_package_id(bag->entries[0].key));
- EXPECT_EQ(1, bag->entries[1].cookie);
- EXPECT_EQ(0x03, get_package_id(bag->entries[1].key));
-}
-
-TEST_F(AssetManager2Test, FindsStyleResourceWithParentFromSharedLibrary) {
- AssetManager2 assetmanager;
-
- // libclient is built with lib_one and then lib_two in order.
- // Reverse the order to test that proper package ID re-assignment is happening.
- assetmanager.SetApkAssets(
- {lib_two_assets_.get(), lib_one_assets_.get(), libclient_assets_.get()});
-
const ResolvedBag* bag = assetmanager.GetBag(libclient::R::style::Theme);
ASSERT_NE(nullptr, bag);
ASSERT_GE(bag->entry_count, 2u);
diff --git a/libs/androidfw/tests/AttributeResolution_bench.cpp b/libs/androidfw/tests/AttributeResolution_bench.cpp
deleted file mode 100644
index fa300c5..0000000
--- a/libs/androidfw/tests/AttributeResolution_bench.cpp
+++ /dev/null
@@ -1,175 +0,0 @@
-/*
- * 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.
- */
-
-#include "benchmark/benchmark.h"
-
-//#include "android-base/stringprintf.h"
-#include "androidfw/ApkAssets.h"
-#include "androidfw/AssetManager.h"
-#include "androidfw/AssetManager2.h"
-#include "androidfw/AttributeResolution.h"
-#include "androidfw/ResourceTypes.h"
-
-#include "BenchmarkHelpers.h"
-#include "data/basic/R.h"
-#include "data/styles/R.h"
-
-namespace app = com::android::app;
-namespace basic = com::android::basic;
-
-namespace android {
-
-constexpr const static char* kFrameworkPath = "/system/framework/framework-res.apk";
-constexpr const static uint32_t Theme_Material_Light = 0x01030237u;
-
-static void BM_ApplyStyle(benchmark::State& state) {
- std::unique_ptr<const ApkAssets> styles_apk =
- ApkAssets::Load(GetTestDataPath() + "/styles/styles.apk");
- if (styles_apk == nullptr) {
- state.SkipWithError("failed to load assets");
- return;
- }
-
- AssetManager2 assetmanager;
- assetmanager.SetApkAssets({styles_apk.get()});
-
- std::unique_ptr<Asset> asset =
- assetmanager.OpenNonAsset("res/layout/layout.xml", Asset::ACCESS_BUFFER);
- if (asset == nullptr) {
- state.SkipWithError("failed to load layout");
- return;
- }
-
- ResXMLTree xml_tree;
- if (xml_tree.setTo(asset->getBuffer(true), asset->getLength(), false /*copyData*/) != NO_ERROR) {
- state.SkipWithError("corrupt xml layout");
- return;
- }
-
- // Skip to the first tag.
- while (xml_tree.next() != ResXMLParser::START_TAG) {
- }
-
- std::unique_ptr<Theme> theme = assetmanager.NewTheme();
- theme->ApplyStyle(app::R::style::StyleTwo);
-
- std::array<uint32_t, 6> attrs{{app::R::attr::attr_one, app::R::attr::attr_two,
- app::R::attr::attr_three, app::R::attr::attr_four,
- app::R::attr::attr_five, app::R::attr::attr_empty}};
- std::array<uint32_t, attrs.size() * STYLE_NUM_ENTRIES> values;
- std::array<uint32_t, attrs.size() + 1> indices;
-
- while (state.KeepRunning()) {
- ApplyStyle(theme.get(), &xml_tree, 0u /*def_style_attr*/, 0u /*def_style_res*/, attrs.data(),
- attrs.size(), values.data(), indices.data());
- }
-}
-BENCHMARK(BM_ApplyStyle);
-
-static void BM_ApplyStyleFramework(benchmark::State& state) {
- std::unique_ptr<const ApkAssets> framework_apk = ApkAssets::Load(kFrameworkPath);
- if (framework_apk == nullptr) {
- state.SkipWithError("failed to load framework assets");
- return;
- }
-
- std::unique_ptr<const ApkAssets> basic_apk =
- ApkAssets::Load(GetTestDataPath() + "/basic/basic.apk");
- if (basic_apk == nullptr) {
- state.SkipWithError("failed to load assets");
- return;
- }
-
- AssetManager2 assetmanager;
- assetmanager.SetApkAssets({framework_apk.get(), basic_apk.get()});
-
- ResTable_config device_config;
- memset(&device_config, 0, sizeof(device_config));
- device_config.language[0] = 'e';
- device_config.language[1] = 'n';
- device_config.country[0] = 'U';
- device_config.country[1] = 'S';
- device_config.orientation = ResTable_config::ORIENTATION_PORT;
- device_config.smallestScreenWidthDp = 700;
- device_config.screenWidthDp = 700;
- device_config.screenHeightDp = 1024;
- device_config.sdkVersion = 27;
-
- Res_value value;
- ResTable_config config;
- uint32_t flags = 0u;
- ApkAssetsCookie cookie =
- assetmanager.GetResource(basic::R::layout::layoutt, false /*may_be_bag*/,
- 0u /*density_override*/, &value, &config, &flags);
- if (cookie == kInvalidCookie) {
- state.SkipWithError("failed to find R.layout.layout");
- return;
- }
-
- size_t len = 0u;
- const char* layout_path =
- assetmanager.GetStringPoolForCookie(cookie)->string8At(value.data, &len);
- if (layout_path == nullptr || len == 0u) {
- state.SkipWithError("failed to lookup layout path");
- return;
- }
-
- std::unique_ptr<Asset> asset = assetmanager.OpenNonAsset(
- StringPiece(layout_path, len).to_string(), cookie, Asset::ACCESS_BUFFER);
- if (asset == nullptr) {
- state.SkipWithError("failed to load layout");
- return;
- }
-
- ResXMLTree xml_tree;
- if (xml_tree.setTo(asset->getBuffer(true), asset->getLength(), false /*copyData*/) != NO_ERROR) {
- state.SkipWithError("corrupt xml layout");
- return;
- }
-
- // Skip to the first tag.
- while (xml_tree.next() != ResXMLParser::START_TAG) {
- }
-
- std::unique_ptr<Theme> theme = assetmanager.NewTheme();
- theme->ApplyStyle(Theme_Material_Light);
-
- std::array<uint32_t, 92> attrs{
- {0x0101000e, 0x01010034, 0x01010095, 0x01010096, 0x01010097, 0x01010098, 0x01010099,
- 0x0101009a, 0x0101009b, 0x010100ab, 0x010100af, 0x010100b0, 0x010100b1, 0x0101011f,
- 0x01010120, 0x0101013f, 0x01010140, 0x0101014e, 0x0101014f, 0x01010150, 0x01010151,
- 0x01010152, 0x01010153, 0x01010154, 0x01010155, 0x01010156, 0x01010157, 0x01010158,
- 0x01010159, 0x0101015a, 0x0101015b, 0x0101015c, 0x0101015d, 0x0101015e, 0x0101015f,
- 0x01010160, 0x01010161, 0x01010162, 0x01010163, 0x01010164, 0x01010165, 0x01010166,
- 0x01010167, 0x01010168, 0x01010169, 0x0101016a, 0x0101016b, 0x0101016c, 0x0101016d,
- 0x0101016e, 0x0101016f, 0x01010170, 0x01010171, 0x01010217, 0x01010218, 0x0101021d,
- 0x01010220, 0x01010223, 0x01010224, 0x01010264, 0x01010265, 0x01010266, 0x010102c5,
- 0x010102c6, 0x010102c7, 0x01010314, 0x01010315, 0x01010316, 0x0101035e, 0x0101035f,
- 0x01010362, 0x01010374, 0x0101038c, 0x01010392, 0x01010393, 0x010103ac, 0x0101045d,
- 0x010104b6, 0x010104b7, 0x010104d6, 0x010104d7, 0x010104dd, 0x010104de, 0x010104df,
- 0x01010535, 0x01010536, 0x01010537, 0x01010538, 0x01010546, 0x01010567, 0x011100c9,
- 0x011100ca}};
-
- std::array<uint32_t, attrs.size() * STYLE_NUM_ENTRIES> values;
- std::array<uint32_t, attrs.size() + 1> indices;
- while (state.KeepRunning()) {
- ApplyStyle(theme.get(), &xml_tree, 0x01010084u /*def_style_attr*/, 0u /*def_style_res*/,
- attrs.data(), attrs.size(), values.data(), indices.data());
- }
-}
-BENCHMARK(BM_ApplyStyleFramework);
-
-} // namespace android
diff --git a/libs/androidfw/tests/AttributeResolution_test.cpp b/libs/androidfw/tests/AttributeResolution_test.cpp
index c8dbe20..2d73ce8 100644
--- a/libs/androidfw/tests/AttributeResolution_test.cpp
+++ b/libs/androidfw/tests/AttributeResolution_test.cpp
@@ -21,8 +21,6 @@
#include "android-base/file.h"
#include "android-base/logging.h"
#include "android-base/macros.h"
-#include "androidfw/AssetManager2.h"
-#include "androidfw/ResourceUtils.h"
#include "TestHelpers.h"
#include "data/styles/R.h"
@@ -34,14 +32,15 @@
class AttributeResolutionTest : public ::testing::Test {
public:
virtual void SetUp() override {
- styles_assets_ = ApkAssets::Load(GetTestDataPath() + "/styles/styles.apk");
- ASSERT_NE(nullptr, styles_assets_);
- assetmanager_.SetApkAssets({styles_assets_.get()});
+ std::string contents;
+ ASSERT_TRUE(ReadFileFromZipToString(
+ GetTestDataPath() + "/styles/styles.apk", "resources.arsc", &contents));
+ ASSERT_EQ(NO_ERROR, table_.add(contents.data(), contents.size(),
+ 1 /*cookie*/, true /*copyData*/));
}
protected:
- std::unique_ptr<const ApkAssets> styles_assets_;
- AssetManager2 assetmanager_;
+ ResTable table_;
};
class AttributeResolutionXmlTest : public AttributeResolutionTest {
@@ -49,12 +48,13 @@
virtual void SetUp() override {
AttributeResolutionTest::SetUp();
- std::unique_ptr<Asset> asset =
- assetmanager_.OpenNonAsset("res/layout/layout.xml", Asset::ACCESS_BUFFER);
- ASSERT_NE(nullptr, asset);
+ std::string contents;
+ ASSERT_TRUE(
+ ReadFileFromZipToString(GetTestDataPath() + "/styles/styles.apk",
+ "res/layout/layout.xml", &contents));
- ASSERT_EQ(NO_ERROR,
- xml_parser_.setTo(asset->getBuffer(true), asset->getLength(), true /*copyData*/));
+ ASSERT_EQ(NO_ERROR, xml_parser_.setTo(contents.data(), contents.size(),
+ true /*copyData*/));
// Skip to the first tag.
while (xml_parser_.next() != ResXMLParser::START_TAG) {
@@ -65,50 +65,15 @@
ResXMLTree xml_parser_;
};
-TEST(AttributeResolutionLibraryTest, ApplyStyleWithDefaultStyleResId) {
- AssetManager2 assetmanager;
- auto apk_assets = ApkAssets::LoadAsSharedLibrary(GetTestDataPath() + "/styles/styles.apk");
- ASSERT_NE(nullptr, apk_assets);
- assetmanager.SetApkAssets({apk_assets.get()});
-
- std::unique_ptr<Theme> theme = assetmanager.NewTheme();
-
- std::array<uint32_t, 2> attrs{
- {fix_package_id(R::attr::attr_one, 0x02), fix_package_id(R::attr::attr_two, 0x02)}};
- std::array<uint32_t, attrs.size() * STYLE_NUM_ENTRIES> values;
- std::array<uint32_t, attrs.size() + 1> indices;
- ApplyStyle(theme.get(), nullptr /*xml_parser*/, 0u /*def_style_attr*/,
- fix_package_id(R::style::StyleOne, 0x02), attrs.data(), attrs.size(), values.data(),
- indices.data());
-
- const uint32_t public_flag = ResTable_typeSpec::SPEC_PUBLIC;
-
- const uint32_t* values_cursor = values.data();
- EXPECT_EQ(Res_value::TYPE_INT_DEC, values_cursor[STYLE_TYPE]);
- EXPECT_EQ(1u, values_cursor[STYLE_DATA]);
- EXPECT_EQ(0u, values_cursor[STYLE_RESOURCE_ID]);
- EXPECT_EQ(1u, values_cursor[STYLE_ASSET_COOKIE]);
- EXPECT_EQ(0u, values_cursor[STYLE_DENSITY]);
- EXPECT_EQ(public_flag, values_cursor[STYLE_CHANGING_CONFIGURATIONS]);
-
- values_cursor += STYLE_NUM_ENTRIES;
- EXPECT_EQ(Res_value::TYPE_INT_DEC, values_cursor[STYLE_TYPE]);
- EXPECT_EQ(2u, values_cursor[STYLE_DATA]);
- EXPECT_EQ(0u, values_cursor[STYLE_RESOURCE_ID]);
- EXPECT_EQ(1u, values_cursor[STYLE_ASSET_COOKIE]);
- EXPECT_EQ(0u, values_cursor[STYLE_DENSITY]);
- EXPECT_EQ(public_flag, values_cursor[STYLE_CHANGING_CONFIGURATIONS]);
-}
-
TEST_F(AttributeResolutionTest, Theme) {
- std::unique_ptr<Theme> theme = assetmanager_.NewTheme();
- ASSERT_TRUE(theme->ApplyStyle(R::style::StyleTwo));
+ ResTable::Theme theme(table_);
+ ASSERT_EQ(NO_ERROR, theme.applyStyle(R::style::StyleTwo));
std::array<uint32_t, 5> attrs{{R::attr::attr_one, R::attr::attr_two, R::attr::attr_three,
R::attr::attr_four, R::attr::attr_empty}};
std::array<uint32_t, attrs.size() * STYLE_NUM_ENTRIES> values;
- ASSERT_TRUE(ResolveAttrs(theme.get(), 0u /*def_style_attr*/, 0u /*def_style_res*/,
+ ASSERT_TRUE(ResolveAttrs(&theme, 0 /*def_style_attr*/, 0 /*def_style_res*/,
nullptr /*src_values*/, 0 /*src_values_length*/, attrs.data(),
attrs.size(), values.data(), nullptr /*out_indices*/));
@@ -161,8 +126,8 @@
R::attr::attr_four, R::attr::attr_empty}};
std::array<uint32_t, attrs.size() * STYLE_NUM_ENTRIES> values;
- ASSERT_TRUE(RetrieveAttributes(&assetmanager_, &xml_parser_, attrs.data(), attrs.size(),
- values.data(), nullptr /*out_indices*/));
+ ASSERT_TRUE(RetrieveAttributes(&table_, &xml_parser_, attrs.data(), attrs.size(), values.data(),
+ nullptr /*out_indices*/));
uint32_t* values_cursor = values.data();
EXPECT_EQ(Res_value::TYPE_NULL, values_cursor[STYLE_TYPE]);
@@ -206,15 +171,15 @@
}
TEST_F(AttributeResolutionXmlTest, ThemeAndXmlParser) {
- std::unique_ptr<Theme> theme = assetmanager_.NewTheme();
- ASSERT_TRUE(theme->ApplyStyle(R::style::StyleTwo));
+ ResTable::Theme theme(table_);
+ ASSERT_EQ(NO_ERROR, theme.applyStyle(R::style::StyleTwo));
std::array<uint32_t, 6> attrs{{R::attr::attr_one, R::attr::attr_two, R::attr::attr_three,
R::attr::attr_four, R::attr::attr_five, R::attr::attr_empty}};
std::array<uint32_t, attrs.size() * STYLE_NUM_ENTRIES> values;
std::array<uint32_t, attrs.size() + 1> indices;
- ApplyStyle(theme.get(), &xml_parser_, 0u /*def_style_attr*/, 0u /*def_style_res*/, attrs.data(),
+ ApplyStyle(&theme, &xml_parser_, 0 /*def_style_attr*/, 0 /*def_style_res*/, attrs.data(),
attrs.size(), values.data(), indices.data());
const uint32_t public_flag = ResTable_typeSpec::SPEC_PUBLIC;
diff --git a/libs/androidfw/tests/BenchmarkHelpers.cpp b/libs/androidfw/tests/BenchmarkHelpers.cpp
index faddfe5..7149bee 100644
--- a/libs/androidfw/tests/BenchmarkHelpers.cpp
+++ b/libs/androidfw/tests/BenchmarkHelpers.cpp
@@ -33,21 +33,19 @@
}
}
- // Make sure to force creation of the ResTable first, or else the configuration doesn't get set.
- const ResTable& table = assetmanager.getResources(true);
if (config != nullptr) {
assetmanager.setConfiguration(*config);
}
+ const ResTable& table = assetmanager.getResources(true);
+
Res_value value;
ResTable_config selected_config;
uint32_t flags;
- uint32_t last_ref = 0u;
while (state.KeepRunning()) {
- ssize_t block = table.getResource(resid, &value, false /*may_be_bag*/, 0u /*density*/, &flags,
- &selected_config);
- table.resolveReference(&value, block, &last_ref, &flags, &selected_config);
+ table.getResource(resid, &value, false /*may_be_bag*/, 0u /*density*/, &flags,
+ &selected_config);
}
}
@@ -74,12 +72,10 @@
Res_value value;
ResTable_config selected_config;
uint32_t flags;
- uint32_t last_id = 0u;
while (state.KeepRunning()) {
- ApkAssetsCookie cookie = assetmanager.GetResource(
- resid, false /* may_be_bag */, 0u /* density_override */, &value, &selected_config, &flags);
- assetmanager.ResolveReference(cookie, &value, &selected_config, &flags, &last_id);
+ assetmanager.GetResource(resid, false /* may_be_bag */, 0u /* density_override */, &value,
+ &selected_config, &flags);
}
}
diff --git a/libs/androidfw/tests/LoadedArsc_test.cpp b/libs/androidfw/tests/LoadedArsc_test.cpp
index bedebd6..37ddafb 100644
--- a/libs/androidfw/tests/LoadedArsc_test.cpp
+++ b/libs/androidfw/tests/LoadedArsc_test.cpp
@@ -16,8 +16,6 @@
#include "androidfw/LoadedArsc.h"
-#include "androidfw/ResourceUtils.h"
-
#include "TestHelpers.h"
#include "data/basic/R.h"
#include "data/libclient/R.h"
@@ -29,13 +27,6 @@
namespace libclient = com::android::libclient;
namespace sparse = com::android::sparse;
-using ::testing::Eq;
-using ::testing::Ge;
-using ::testing::IsNull;
-using ::testing::NotNull;
-using ::testing::SizeIs;
-using ::testing::StrEq;
-
namespace android {
TEST(LoadedArscTest, LoadSinglePackageArsc) {
@@ -44,24 +35,39 @@
&contents));
std::unique_ptr<const LoadedArsc> loaded_arsc = LoadedArsc::Load(StringPiece(contents));
- ASSERT_THAT(loaded_arsc, NotNull());
+ ASSERT_NE(nullptr, loaded_arsc);
- const LoadedPackage* package =
- loaded_arsc->GetPackageById(get_package_id(app::R::string::string_one));
- ASSERT_THAT(package, NotNull());
- EXPECT_THAT(package->GetPackageName(), StrEq("com.android.app"));
- EXPECT_THAT(package->GetPackageId(), Eq(0x7f));
+ const std::vector<std::unique_ptr<const LoadedPackage>>& packages = loaded_arsc->GetPackages();
+ ASSERT_EQ(1u, packages.size());
+ EXPECT_EQ(std::string("com.android.app"), packages[0]->GetPackageName());
+ EXPECT_EQ(0x7f, packages[0]->GetPackageId());
- const uint8_t type_index = get_type_id(app::R::string::string_one) - 1;
- const uint16_t entry_index = get_entry_id(app::R::string::string_one);
+ ResTable_config config;
+ memset(&config, 0, sizeof(config));
+ config.sdkVersion = 24;
- const TypeSpec* type_spec = package->GetTypeSpecByTypeIndex(type_index);
- ASSERT_THAT(type_spec, NotNull());
- ASSERT_THAT(type_spec->type_count, Ge(1u));
+ FindEntryResult entry;
- const ResTable_type* type = type_spec->types[0];
- ASSERT_THAT(type, NotNull());
- ASSERT_THAT(LoadedPackage::GetEntry(type, entry_index), NotNull());
+ ASSERT_TRUE(loaded_arsc->FindEntry(app::R::string::string_one, config, &entry));
+ ASSERT_NE(nullptr, entry.entry);
+}
+
+TEST(LoadedArscTest, FindDefaultEntry) {
+ std::string contents;
+ ASSERT_TRUE(
+ ReadFileFromZipToString(GetTestDataPath() + "/basic/basic.apk", "resources.arsc", &contents));
+
+ std::unique_ptr<const LoadedArsc> loaded_arsc = LoadedArsc::Load(StringPiece(contents));
+ ASSERT_NE(nullptr, loaded_arsc);
+
+ ResTable_config desired_config;
+ memset(&desired_config, 0, sizeof(desired_config));
+ desired_config.language[0] = 'd';
+ desired_config.language[1] = 'e';
+
+ FindEntryResult entry;
+ ASSERT_TRUE(loaded_arsc->FindEntry(basic::R::string::test1, desired_config, &entry));
+ ASSERT_NE(nullptr, entry.entry);
}
TEST(LoadedArscTest, LoadSparseEntryApp) {
@@ -70,22 +76,15 @@
&contents));
std::unique_ptr<const LoadedArsc> loaded_arsc = LoadedArsc::Load(StringPiece(contents));
- ASSERT_THAT(loaded_arsc, NotNull());
+ ASSERT_NE(nullptr, loaded_arsc);
- const LoadedPackage* package =
- loaded_arsc->GetPackageById(get_package_id(sparse::R::integer::foo_9));
- ASSERT_THAT(package, NotNull());
+ ResTable_config config;
+ memset(&config, 0, sizeof(config));
+ config.sdkVersion = 26;
- const uint8_t type_index = get_type_id(sparse::R::integer::foo_9) - 1;
- const uint16_t entry_index = get_entry_id(sparse::R::integer::foo_9);
-
- const TypeSpec* type_spec = package->GetTypeSpecByTypeIndex(type_index);
- ASSERT_THAT(type_spec, NotNull());
- ASSERT_THAT(type_spec->type_count, Ge(1u));
-
- const ResTable_type* type = type_spec->types[0];
- ASSERT_THAT(type, NotNull());
- ASSERT_THAT(LoadedPackage::GetEntry(type, entry_index), NotNull());
+ FindEntryResult entry;
+ ASSERT_TRUE(loaded_arsc->FindEntry(sparse::R::integer::foo_9, config, &entry));
+ ASSERT_NE(nullptr, entry.entry);
}
TEST(LoadedArscTest, LoadSharedLibrary) {
@@ -94,13 +93,14 @@
&contents));
std::unique_ptr<const LoadedArsc> loaded_arsc = LoadedArsc::Load(StringPiece(contents));
- ASSERT_THAT(loaded_arsc, NotNull());
+ ASSERT_NE(nullptr, loaded_arsc);
const auto& packages = loaded_arsc->GetPackages();
- ASSERT_THAT(packages, SizeIs(1u));
+ ASSERT_EQ(1u, packages.size());
+
EXPECT_TRUE(packages[0]->IsDynamic());
- EXPECT_THAT(packages[0]->GetPackageName(), StrEq("com.android.lib_one"));
- EXPECT_THAT(packages[0]->GetPackageId(), Eq(0));
+ EXPECT_EQ(std::string("com.android.lib_one"), packages[0]->GetPackageName());
+ EXPECT_EQ(0, packages[0]->GetPackageId());
const auto& dynamic_pkg_map = packages[0]->GetDynamicPackageMap();
@@ -114,23 +114,25 @@
"resources.arsc", &contents));
std::unique_ptr<const LoadedArsc> loaded_arsc = LoadedArsc::Load(StringPiece(contents));
- ASSERT_THAT(loaded_arsc, NotNull());
+ ASSERT_NE(nullptr, loaded_arsc);
const auto& packages = loaded_arsc->GetPackages();
- ASSERT_THAT(packages, SizeIs(1u));
+ ASSERT_EQ(1u, packages.size());
+
EXPECT_FALSE(packages[0]->IsDynamic());
- EXPECT_THAT(packages[0]->GetPackageName(), StrEq("com.android.libclient"));
- EXPECT_THAT(packages[0]->GetPackageId(), Eq(0x7f));
+ EXPECT_EQ(std::string("com.android.libclient"), packages[0]->GetPackageName());
+ EXPECT_EQ(0x7f, packages[0]->GetPackageId());
const auto& dynamic_pkg_map = packages[0]->GetDynamicPackageMap();
// The library has two dependencies.
- ASSERT_THAT(dynamic_pkg_map, SizeIs(2u));
- EXPECT_THAT(dynamic_pkg_map[0].package_name, StrEq("com.android.lib_one"));
- EXPECT_THAT(dynamic_pkg_map[0].package_id, Eq(0x02));
+ ASSERT_EQ(2u, dynamic_pkg_map.size());
- EXPECT_THAT(dynamic_pkg_map[1].package_name, StrEq("com.android.lib_two"));
- EXPECT_THAT(dynamic_pkg_map[1].package_id, Eq(0x03));
+ EXPECT_EQ(std::string("com.android.lib_one"), dynamic_pkg_map[0].package_name);
+ EXPECT_EQ(0x02, dynamic_pkg_map[0].package_id);
+
+ EXPECT_EQ(std::string("com.android.lib_two"), dynamic_pkg_map[1].package_name);
+ EXPECT_EQ(0x03, dynamic_pkg_map[1].package_id);
}
TEST(LoadedArscTest, LoadAppAsSharedLibrary) {
@@ -141,12 +143,13 @@
std::unique_ptr<const LoadedArsc> loaded_arsc =
LoadedArsc::Load(StringPiece(contents), nullptr /*loaded_idmap*/, false /*system*/,
true /*load_as_shared_library*/);
- ASSERT_THAT(loaded_arsc, NotNull());
+ ASSERT_NE(nullptr, loaded_arsc);
const auto& packages = loaded_arsc->GetPackages();
- ASSERT_THAT(packages, SizeIs(1u));
+ ASSERT_EQ(1u, packages.size());
+
EXPECT_TRUE(packages[0]->IsDynamic());
- EXPECT_THAT(packages[0]->GetPackageId(), Eq(0x7f));
+ EXPECT_EQ(0x7f, packages[0]->GetPackageId());
}
TEST(LoadedArscTest, LoadFeatureSplit) {
@@ -154,27 +157,21 @@
ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/feature/feature.apk", "resources.arsc",
&contents));
std::unique_ptr<const LoadedArsc> loaded_arsc = LoadedArsc::Load(StringPiece(contents));
- ASSERT_THAT(loaded_arsc, NotNull());
+ ASSERT_NE(nullptr, loaded_arsc);
- const LoadedPackage* package =
- loaded_arsc->GetPackageById(get_package_id(basic::R::string::test3));
- ASSERT_THAT(package, NotNull());
+ ResTable_config desired_config;
+ memset(&desired_config, 0, sizeof(desired_config));
- uint8_t type_index = get_type_id(basic::R::string::test3) - 1;
- uint8_t entry_index = get_entry_id(basic::R::string::test3);
-
- const TypeSpec* type_spec = package->GetTypeSpecByTypeIndex(type_index);
- ASSERT_THAT(type_spec, NotNull());
- ASSERT_THAT(type_spec->type_count, Ge(1u));
- ASSERT_THAT(type_spec->types[0], NotNull());
+ FindEntryResult entry;
+ ASSERT_TRUE(loaded_arsc->FindEntry(basic::R::string::test3, desired_config, &entry));
size_t len;
- const char16_t* type_name16 =
- package->GetTypeStringPool()->stringAt(type_spec->type_spec->id - 1, &len);
- ASSERT_THAT(type_name16, NotNull());
- EXPECT_THAT(util::Utf16ToUtf8(StringPiece16(type_name16, len)), StrEq("string"));
+ const char16_t* type_name16 = entry.type_string_ref.string16(&len);
+ ASSERT_NE(nullptr, type_name16);
+ ASSERT_NE(0u, len);
- ASSERT_THAT(LoadedPackage::GetEntry(type_spec->types[0], entry_index), NotNull());
+ std::string type_name = util::Utf16ToUtf8(StringPiece16(type_name16, len));
+ EXPECT_EQ(std::string("string"), type_name);
}
class MockLoadedIdmap : public LoadedIdmap {
@@ -202,33 +199,23 @@
};
TEST(LoadedArscTest, LoadOverlay) {
- std::string contents;
+ std::string contents, overlay_contents;
+ ASSERT_TRUE(
+ ReadFileFromZipToString(GetTestDataPath() + "/basic/basic.apk", "resources.arsc", &contents));
ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/overlay/overlay.apk", "resources.arsc",
- &contents));
+ &overlay_contents));
MockLoadedIdmap loaded_idmap;
std::unique_ptr<const LoadedArsc> loaded_arsc =
- LoadedArsc::Load(StringPiece(contents), &loaded_idmap);
- ASSERT_THAT(loaded_arsc, NotNull());
+ LoadedArsc::Load(StringPiece(overlay_contents), &loaded_idmap);
+ ASSERT_NE(nullptr, loaded_arsc);
- const LoadedPackage* package = loaded_arsc->GetPackageById(0x08u);
- ASSERT_THAT(package, NotNull());
+ ResTable_config desired_config;
+ memset(&desired_config, 0, sizeof(desired_config));
- const TypeSpec* type_spec = package->GetTypeSpecByTypeIndex(0x03u - 1);
- ASSERT_THAT(type_spec, NotNull());
- ASSERT_THAT(type_spec->type_count, Ge(1u));
- ASSERT_THAT(type_spec->types[0], NotNull());
-
- // The entry being overlaid doesn't exist at the original entry index.
- ASSERT_THAT(LoadedPackage::GetEntry(type_spec->types[0], 0x0001u), IsNull());
-
- // Since this is an overlay, the actual entry ID must be mapped.
- ASSERT_THAT(type_spec->idmap_entries, NotNull());
- uint16_t target_entry_id = 0u;
- ASSERT_TRUE(LoadedIdmap::Lookup(type_spec->idmap_entries, 0x0001u, &target_entry_id));
- ASSERT_THAT(target_entry_id, Eq(0x0u));
- ASSERT_THAT(LoadedPackage::GetEntry(type_spec->types[0], 0x0000), NotNull());
+ FindEntryResult entry;
+ ASSERT_TRUE(loaded_arsc->FindEntry(0x08030001u, desired_config, &entry));
}
// structs with size fields (like Res_value, ResTable_entry) should be
diff --git a/libs/androidfw/tests/TestHelpers.h b/libs/androidfw/tests/TestHelpers.h
index df0c642..43a9955 100644
--- a/libs/androidfw/tests/TestHelpers.h
+++ b/libs/androidfw/tests/TestHelpers.h
@@ -20,7 +20,6 @@
#include <string>
#include "androidfw/ResourceTypes.h"
-#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "CommonHelpers.h"
diff --git a/libs/androidfw/tests/data/basic/R.h b/libs/androidfw/tests/data/basic/R.h
index b7e814f..94a2a14 100644
--- a/libs/androidfw/tests/data/basic/R.h
+++ b/libs/androidfw/tests/data/basic/R.h
@@ -34,7 +34,6 @@
struct layout {
enum : uint32_t {
main = 0x7f020000,
- layoutt = 0x7f020001,
};
};
@@ -56,7 +55,6 @@
number2 = 0x7f040001,
ref1 = 0x7f040002,
ref2 = 0x7f040003,
- deep_ref = 0x7f040004,
// From feature
number3 = 0x80030000,
diff --git a/libs/androidfw/tests/data/basic/basic.apk b/libs/androidfw/tests/data/basic/basic.apk
index 1733b6a..18ef75e 100644
--- a/libs/androidfw/tests/data/basic/basic.apk
+++ b/libs/androidfw/tests/data/basic/basic.apk
Binary files differ
diff --git a/libs/androidfw/tests/data/basic/res/layout/layout.xml b/libs/androidfw/tests/data/basic/res/layout/layout.xml
deleted file mode 100644
index 045ede4..0000000
--- a/libs/androidfw/tests/data/basic/res/layout/layout.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2018 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.
--->
-<Button xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/ok"
- android:layout_width="0sp"
- android:layout_height="fill_parent"
- android:layout_weight="1"
- android:layout_marginStart="2dip"
- android:layout_marginEnd="2dip"
- android:textAppearance="?android:attr/textAppearanceMedium"
- android:textStyle="bold"
- android:text="@android:string/ok" />
\ No newline at end of file
diff --git a/libs/androidfw/tests/data/basic/res/values/values.xml b/libs/androidfw/tests/data/basic/res/values/values.xml
index b343562..6c47459 100644
--- a/libs/androidfw/tests/data/basic/res/values/values.xml
+++ b/libs/androidfw/tests/data/basic/res/values/values.xml
@@ -22,7 +22,6 @@
<attr name="attr2" format="reference|integer" />
<public type="layout" name="main" id="0x7f020000" />
- <public type="layout" name="layout" id="0x7f020001" />
<public type="string" name="test1" id="0x7f030000" />
<string name="test1">test1</string>
@@ -44,18 +43,6 @@
<public type="integer" name="ref2" id="0x7f040003" />
<integer name="ref2">12000</integer>
- <public type="integer" name="deep_ref" id="0x7f040004" />
- <integer name="deep_ref">@integer/deep_ref_1</integer>
- <integer name="deep_ref_1">@integer/deep_ref_2</integer>
- <integer name="deep_ref_2">@integer/deep_ref_3</integer>
- <integer name="deep_ref_3">@integer/deep_ref_4</integer>
- <integer name="deep_ref_4">@integer/deep_ref_5</integer>
- <integer name="deep_ref_5">@integer/deep_ref_6</integer>
- <integer name="deep_ref_6">@integer/deep_ref_7</integer>
- <integer name="deep_ref_7">@integer/deep_ref_8</integer>
- <integer name="deep_ref_8">@integer/deep_ref_9</integer>
- <integer name="deep_ref_9">100</integer>
-
<public type="style" name="Theme1" id="0x7f050000" />
<style name="Theme1">
<item name="com.android.basic:attr1">100</item>
diff --git a/libs/hwui/SkiaCanvas.cpp b/libs/hwui/SkiaCanvas.cpp
index 9e0d10d..2b0b22d 100644
--- a/libs/hwui/SkiaCanvas.cpp
+++ b/libs/hwui/SkiaCanvas.cpp
@@ -742,6 +742,12 @@
SkPaint paintCopy(paint);
paintCopy.setTextAlign(SkPaint::kLeft_Align);
SkASSERT(paintCopy.getTextEncoding() == SkPaint::kGlyphID_TextEncoding);
+ // Stroke with a hairline is drawn on HW with a fill style for compatibility with Android O and
+ // older.
+ if (!mCanvasOwned && sApiLevel <= 27 && paintCopy.getStrokeWidth() <= 0
+ && paintCopy.getStyle() == SkPaint::kStroke_Style) {
+ paintCopy.setStyle(SkPaint::kFill_Style);
+ }
SkRect bounds =
SkRect::MakeLTRB(boundsLeft + x, boundsTop + y, boundsRight + x, boundsBottom + y);
diff --git a/libs/hwui/VectorDrawable.cpp b/libs/hwui/VectorDrawable.cpp
index f118e8d..18358e2 100644
--- a/libs/hwui/VectorDrawable.cpp
+++ b/libs/hwui/VectorDrawable.cpp
@@ -518,10 +518,7 @@
Bitmap& bitmap = getBitmapUpdateIfDirty();
SkBitmap skiaBitmap;
bitmap.getSkBitmap(&skiaBitmap);
- if (!surface->getCanvas()->writePixels(skiaBitmap, dst.fLeft, dst.fTop)) {
- ALOGD("VectorDrawable caching failed to efficiently upload");
- surface->getCanvas()->drawBitmap(skiaBitmap, dst.fLeft, dst.fTop);
- }
+ surface->writePixels(skiaBitmap, dst.fLeft, dst.fTop);
}
mCache.dirty = false;
}
@@ -557,13 +554,12 @@
mAtlasKey = INVALID_ATLAS_KEY;
}
-void Tree::draw(SkCanvas* canvas) {
+void Tree::draw(SkCanvas* canvas, const SkRect& bounds) {
SkRect src;
sk_sp<SkSurface> vdSurface = mCache.getSurface(&src);
if (vdSurface) {
canvas->drawImageRect(vdSurface->makeImageSnapshot().get(), src,
- mutateProperties()->getBounds(), getPaint(),
- SkCanvas::kFast_SrcRectConstraint);
+ bounds, getPaint(), SkCanvas::kFast_SrcRectConstraint);
} else {
// Handle the case when VectorDrawableAtlas has been destroyed, because of memory pressure.
// We render the VD into a temporary standalone buffer and mark the frame as dirty. Next
@@ -575,8 +571,7 @@
int scaledWidth = SkScalarCeilToInt(mProperties.getScaledWidth());
int scaledHeight = SkScalarCeilToInt(mProperties.getScaledHeight());
canvas->drawBitmapRect(skiaBitmap, SkRect::MakeWH(scaledWidth, scaledHeight),
- mutateProperties()->getBounds(), getPaint(),
- SkCanvas::kFast_SrcRectConstraint);
+ bounds, getPaint(), SkCanvas::kFast_SrcRectConstraint);
mCache.clear();
markDirty();
}
diff --git a/libs/hwui/VectorDrawable.h b/libs/hwui/VectorDrawable.h
index d9cf8ab..da52a95 100644
--- a/libs/hwui/VectorDrawable.h
+++ b/libs/hwui/VectorDrawable.h
@@ -644,7 +644,7 @@
* Draws VD cache into a canvas. This should always be called from RT and it works with Skia
* pipelines only.
*/
- void draw(SkCanvas* canvas);
+ void draw(SkCanvas* canvas, const SkRect& bounds);
/**
* Draws VD into a GPU backed surface.
diff --git a/libs/hwui/hwui/Bitmap.h b/libs/hwui/hwui/Bitmap.h
index a75276f..4f06656 100644
--- a/libs/hwui/hwui/Bitmap.h
+++ b/libs/hwui/hwui/Bitmap.h
@@ -66,7 +66,7 @@
Bitmap(GraphicBuffer* buffer, const SkImageInfo& info);
int rowBytesAsPixels() const {
- return rowBytes() >> SkColorTypeShiftPerPixel(mInfo.colorType());
+ return rowBytes() >> mInfo.shiftPerPixel();
}
void reconfigure(const SkImageInfo& info, size_t rowBytes);
diff --git a/libs/hwui/hwui/Canvas.cpp b/libs/hwui/hwui/Canvas.cpp
index 284fd83..ad4c8be 100644
--- a/libs/hwui/hwui/Canvas.cpp
+++ b/libs/hwui/hwui/Canvas.cpp
@@ -225,4 +225,10 @@
MinikinUtils::forFontRun(layout, &paintCopy, f);
}
+int Canvas::sApiLevel = 1;
+
+void Canvas::setCompatibilityVersion(int apiLevel) {
+ sApiLevel = apiLevel;
+}
+
} // namespace android
diff --git a/libs/hwui/hwui/Canvas.h b/libs/hwui/hwui/Canvas.h
index 3ddf1c4..fabb8d2 100644
--- a/libs/hwui/hwui/Canvas.h
+++ b/libs/hwui/hwui/Canvas.h
@@ -116,6 +116,14 @@
static Canvas* create_canvas(SkCanvas* skiaCanvas);
/**
+ * Sets the target SDK version used to build the app.
+ *
+ * @param apiLevel API level
+ *
+ */
+ static void setCompatibilityVersion(int apiLevel);
+
+ /**
* Provides a Skia SkCanvas interface that acts as a proxy to this Canvas.
* It is useful for testing and clients (e.g. Picture/Movie) that expect to
* draw their contents into an SkCanvas.
@@ -282,6 +290,8 @@
virtual void drawLayoutOnPath(const minikin::Layout& layout, float hOffset, float vOffset,
const SkPaint& paint, const SkPath& path, size_t start,
size_t end) = 0;
+ static int sApiLevel;
+
friend class DrawTextFunctor;
friend class DrawTextOnPathFunctor;
friend class uirenderer::SkiaCanvasProxy;
diff --git a/libs/hwui/hwui/Typeface.cpp b/libs/hwui/hwui/Typeface.cpp
index 091b526..dca9ef5 100644
--- a/libs/hwui/hwui/Typeface.cpp
+++ b/libs/hwui/hwui/Typeface.cpp
@@ -132,8 +132,8 @@
bool italicFromFont;
const minikin::FontStyle defaultStyle;
- const minikin::MinikinFont* mf =
- families.empty() ? nullptr : families[0]->getClosestMatch(defaultStyle).font;
+ const minikin::MinikinFont* mf = families.empty() ? nullptr
+ : families[0]->getClosestMatch(defaultStyle).font->typeface().get();
if (mf != nullptr) {
SkTypeface* skTypeface = reinterpret_cast<const MinikinFontSkia*>(mf)->GetSkTypeface();
const SkFontStyle& style = skTypeface->fontStyle();
@@ -183,7 +183,7 @@
std::shared_ptr<minikin::MinikinFont> font = std::make_shared<MinikinFontSkia>(
std::move(typeface), data, st.st_size, 0, std::vector<minikin::FontVariation>());
std::vector<minikin::Font> fonts;
- fonts.push_back(minikin::Font(std::move(font), minikin::FontStyle()));
+ fonts.push_back(minikin::Font::Builder(font).build());
std::shared_ptr<minikin::FontCollection> collection = std::make_shared<minikin::FontCollection>(
std::make_shared<minikin::FontFamily>(std::move(fonts)));
diff --git a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
index eabe2e8..25c76eb 100644
--- a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
+++ b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
@@ -124,14 +124,19 @@
class VectorDrawable : public SkDrawable {
public:
- VectorDrawable(VectorDrawableRoot* tree) : mRoot(tree) {}
+ VectorDrawable(VectorDrawableRoot* tree)
+ : mRoot(tree)
+ , mBounds(tree->stagingProperties()->getBounds()) {}
protected:
- virtual SkRect onGetBounds() override { return SkRect::MakeLargest(); }
- virtual void onDraw(SkCanvas* canvas) override { mRoot->draw(canvas); }
+ virtual SkRect onGetBounds() override { return mBounds; }
+ virtual void onDraw(SkCanvas* canvas) override {
+ mRoot->draw(canvas, mBounds);
+ }
private:
sp<VectorDrawableRoot> mRoot;
+ SkRect mBounds;
};
void SkiaRecordingCanvas::drawVectorDrawable(VectorDrawableRoot* tree) {
diff --git a/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp b/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp
index 2953ea8..15c0ab1 100644
--- a/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp
+++ b/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp
@@ -471,6 +471,7 @@
sk_sp<SkSurface> onNewSurface(const SkImageInfo&) override { return nullptr; }
void onCopyOnWrite(ContentChangeMode) override {}
int* mDrawCounter;
+ void onWritePixels(const SkPixmap&, int x, int y) {}
};
auto receiverBackground = TestUtils::createSkiaNode(
@@ -1143,4 +1144,47 @@
RenderNodeDrawable drawable(parent.get(), &canvas, false);
canvas.drawDrawable(&drawable);
EXPECT_EQ(6, canvas.getIndex());
-}
\ No newline at end of file
+}
+
+// Draw a vector drawable twice but with different bounds and verify correct bounds are used.
+RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaRecordingCanvas, drawVectorDrawable) {
+ static const int CANVAS_WIDTH = 100;
+ static const int CANVAS_HEIGHT = 200;
+ class VectorDrawableTestCanvas : public TestCanvasBase {
+ public:
+ VectorDrawableTestCanvas() : TestCanvasBase(CANVAS_WIDTH, CANVAS_HEIGHT) {}
+ void onDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src, const SkRect& dst,
+ const SkPaint* paint, SrcRectConstraint constraint) override {
+ const int index = mDrawCounter++;
+ switch (index) {
+ case 0:
+ EXPECT_EQ(dst, SkRect::MakeWH(CANVAS_WIDTH, CANVAS_HEIGHT));
+ break;
+ case 1:
+ EXPECT_EQ(dst, SkRect::MakeWH(CANVAS_WIDTH/2, CANVAS_HEIGHT));
+ break;
+ default:
+ ADD_FAILURE();
+ }
+ }
+ };
+
+ VectorDrawable::Group* group = new VectorDrawable::Group();
+ sp<VectorDrawableRoot> vectorDrawable(new VectorDrawableRoot(group));
+ vectorDrawable->mutateStagingProperties()->setScaledSize(CANVAS_WIDTH/10, CANVAS_HEIGHT/10);
+
+ auto node = TestUtils::createSkiaNode(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT,
+ [&](RenderProperties& props, SkiaRecordingCanvas& canvas) {
+ vectorDrawable->mutateStagingProperties()->setBounds(SkRect::MakeWH(CANVAS_WIDTH,
+ CANVAS_HEIGHT));
+ canvas.drawVectorDrawable(vectorDrawable.get());
+ vectorDrawable->mutateStagingProperties()->setBounds(SkRect::MakeWH(CANVAS_WIDTH/2,
+ CANVAS_HEIGHT));
+ canvas.drawVectorDrawable(vectorDrawable.get());
+ });
+
+ VectorDrawableTestCanvas canvas;
+ RenderNodeDrawable drawable(node.get(), &canvas, true);
+ canvas.drawDrawable(&drawable);
+ EXPECT_EQ(2, canvas.mDrawCounter);
+}
diff --git a/libs/hwui/tests/unit/SkiaPipelineTests.cpp b/libs/hwui/tests/unit/SkiaPipelineTests.cpp
index 8fdb0e3..42a92fc 100644
--- a/libs/hwui/tests/unit/SkiaPipelineTests.cpp
+++ b/libs/hwui/tests/unit/SkiaPipelineTests.cpp
@@ -250,6 +250,7 @@
sk_sp<SkImage> onNewImageSnapshot() override { return nullptr; }
T* canvas() { return static_cast<T*>(getCanvas()); }
void onCopyOnWrite(ContentChangeMode) override {}
+ void onWritePixels(const SkPixmap&, int x, int y) override {}
};
}
diff --git a/libs/hwui/tests/unit/TypefaceTests.cpp b/libs/hwui/tests/unit/TypefaceTests.cpp
index 2232c25..e424a26 100644
--- a/libs/hwui/tests/unit/TypefaceTests.cpp
+++ b/libs/hwui/tests/unit/TypefaceTests.cpp
@@ -57,7 +57,7 @@
std::shared_ptr<minikin::MinikinFont> font = std::make_shared<MinikinFontSkia>(
std::move(typeface), data, st.st_size, 0, std::vector<minikin::FontVariation>());
std::vector<minikin::Font> fonts;
- fonts.push_back(minikin::Font(std::move(font), minikin::FontStyle()));
+ fonts.push_back(minikin::Font::Builder(font).build());
return std::make_shared<minikin::FontFamily>(std::move(fonts));
}
diff --git a/libs/protoutil/include/android/util/EncodedBuffer.h b/libs/protoutil/include/android/util/EncodedBuffer.h
index e568e4c..0a8a5aa 100644
--- a/libs/protoutil/include/android/util/EncodedBuffer.h
+++ b/libs/protoutil/include/android/util/EncodedBuffer.h
@@ -64,6 +64,11 @@
size_t mOffset;
};
+ /**
+ * Clears the buffer by rewinding its write pointer to avoid de/allocate buffers in heap.
+ */
+ void clear();
+
/******************************** Write APIs ************************************************/
/**
diff --git a/libs/protoutil/include/android/util/ProtoOutputStream.h b/libs/protoutil/include/android/util/ProtoOutputStream.h
index faea9b2..52830d3 100644
--- a/libs/protoutil/include/android/util/ProtoOutputStream.h
+++ b/libs/protoutil/include/android/util/ProtoOutputStream.h
@@ -123,6 +123,11 @@
EncodedBuffer::iterator data(); // Get the reader apis of the data.
bool flush(int fd); // Flush data directly to a file descriptor.
+ /**
+ * Clears the ProtoOutputStream so the buffer can be reused instead of deallocation/allocation again.
+ */
+ void clear();
+
// Please don't use the following functions to dump protos unless you are familiar with protobuf encoding.
void writeRawVarint(uint64_t varint);
void writeLengthDelimitedHeader(uint32_t id, size_t size);
diff --git a/libs/protoutil/src/EncodedBuffer.cpp b/libs/protoutil/src/EncodedBuffer.cpp
index 435ae88..3a5e2e9 100644
--- a/libs/protoutil/src/EncodedBuffer.cpp
+++ b/libs/protoutil/src/EncodedBuffer.cpp
@@ -106,6 +106,13 @@
return mBuffers[p.index()] + p.offset();
}
+void
+EncodedBuffer::clear()
+{
+ mWp.rewind();
+ mEp.rewind();
+}
+
/******************************** Write APIs ************************************************/
size_t
EncodedBuffer::size() const
diff --git a/libs/protoutil/src/ProtoOutputStream.cpp b/libs/protoutil/src/ProtoOutputStream.cpp
index f24abae..9d9ffec 100644
--- a/libs/protoutil/src/ProtoOutputStream.cpp
+++ b/libs/protoutil/src/ProtoOutputStream.cpp
@@ -36,6 +36,18 @@
{
}
+
+void
+ProtoOutputStream::clear()
+{
+ mBuffer.clear();
+ mCopyBegin = 0;
+ mCompact = false;
+ mDepth = 0;
+ mObjectId = 0;
+ mExpectedObjectToken = 0LL;
+}
+
bool
ProtoOutputStream::write(uint64_t fieldId, double val)
{
diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java
index d3c6edd..d194796 100644
--- a/location/java/android/location/LocationManager.java
+++ b/location/java/android/location/LocationManager.java
@@ -1271,6 +1271,9 @@
final String allowedProviders = Settings.Secure.getStringForUser(
mContext.getContentResolver(), Settings.Secure.LOCATION_PROVIDERS_ALLOWED,
userHandle.getIdentifier());
+ if (allowedProviders == null) {
+ return false;
+ }
final List<String> providerList = Arrays.asList(allowedProviders.split(","));
for(String provider : getAllProviders()) {
if (provider.equals(PASSIVE_PROVIDER)) {
diff --git a/media/java/android/media/AudioRecord.java b/media/java/android/media/AudioRecord.java
index d0963cb..3847530 100644
--- a/media/java/android/media/AudioRecord.java
+++ b/media/java/android/media/AudioRecord.java
@@ -1416,6 +1416,7 @@
/*
* Call BEFORE adding a routing callback handler.
*/
+ @GuardedBy("mRoutingChangeListeners")
private void testEnableNativeRoutingCallbacksLocked() {
if (mRoutingChangeListeners.size() == 0) {
native_enableDeviceCallback();
@@ -1425,6 +1426,7 @@
/*
* Call AFTER removing a routing callback handler.
*/
+ @GuardedBy("mRoutingChangeListeners")
private void testDisableNativeRoutingCallbacksLocked() {
if (mRoutingChangeListeners.size() == 0) {
native_disableDeviceCallback();
diff --git a/media/java/android/media/AudioTrack.java b/media/java/android/media/AudioTrack.java
index 8e822a5..2d5fad5 100644
--- a/media/java/android/media/AudioTrack.java
+++ b/media/java/android/media/AudioTrack.java
@@ -2821,6 +2821,7 @@
/*
* Call BEFORE adding a routing callback handler.
*/
+ @GuardedBy("mRoutingChangeListeners")
private void testEnableNativeRoutingCallbacksLocked() {
if (mRoutingChangeListeners.size() == 0) {
native_enableDeviceCallback();
@@ -2830,6 +2831,7 @@
/*
* Call AFTER removing a routing callback handler.
*/
+ @GuardedBy("mRoutingChangeListeners")
private void testDisableNativeRoutingCallbacksLocked() {
if (mRoutingChangeListeners.size() == 0) {
native_disableDeviceCallback();
diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl
index cd4143c..4c37014 100644
--- a/media/java/android/media/IAudioService.aidl
+++ b/media/java/android/media/IAudioService.aidl
@@ -16,9 +16,7 @@
package android.media;
-import android.app.PendingIntent;
import android.bluetooth.BluetoothDevice;
-import android.content.ComponentName;
import android.media.AudioAttributes;
import android.media.AudioFocusInfo;
import android.media.AudioPlaybackConfiguration;
@@ -31,25 +29,33 @@
import android.media.IRingtonePlayer;
import android.media.IVolumeController;
import android.media.PlayerBase;
-import android.media.Rating;
import android.media.VolumePolicy;
import android.media.audiopolicy.AudioPolicyConfig;
import android.media.audiopolicy.IAudioPolicyCallback;
-import android.net.Uri;
-import android.view.KeyEvent;
/**
* {@hide}
*/
interface IAudioService {
+ // C++ and Java methods below.
- // WARNING: When methods are inserted or deleted, the transaction IDs in
+ // WARNING: When methods are inserted or deleted in this section, the transaction IDs in
// frameworks/native/include/audiomanager/IAudioManager.h must be updated to match the order
// in this file.
//
// When a method's argument list is changed, BpAudioManager's corresponding serialization code
// (if any) in frameworks/native/services/audiomanager/IAudioManager.cpp must be updated.
+ int trackPlayer(in PlayerBase.PlayerIdCard pic);
+
+ oneway void playerAttributes(in int piid, in AudioAttributes attr);
+
+ oneway void playerEvent(in int piid, in int event);
+
+ oneway void releasePlayer(in int piid);
+
+ // Java-only methods below.
+
oneway void adjustSuggestedStreamVolume(int direction, int suggestedStreamType, int flags,
String callingPackage, String caller);
@@ -187,14 +193,6 @@
List<AudioPlaybackConfiguration> getActivePlaybackConfigurations();
- int trackPlayer(in PlayerBase.PlayerIdCard pic);
-
- oneway void playerAttributes(in int piid, in AudioAttributes attr);
-
- oneway void playerEvent(in int piid, in int event);
-
- oneway void releasePlayer(in int piid);
-
void disableRingtoneSync(in int userId);
int getFocusRampTimeMs(in int focusGain, in AudioAttributes attr);
@@ -210,5 +208,6 @@
oneway void setFocusRequestResultFromExtPolicy(in AudioFocusInfo afi, int requestResult,
in IAudioPolicyCallback pcb);
- // WARNING: read warning at top of file, it is recommended to add new methods at the end
+ // WARNING: read warning at top of file, new methods that need to be used by native
+ // code via IAudioManager.h need to be added to the top section.
}
diff --git a/media/java/android/media/MediaController2.java b/media/java/android/media/MediaController2.java
index e9ffe60..bd6c7e6 100644
--- a/media/java/android/media/MediaController2.java
+++ b/media/java/android/media/MediaController2.java
@@ -30,7 +30,7 @@
import android.media.session.MediaSessionManager;
import android.media.update.ApiLoader;
import android.media.update.MediaController2Provider;
-import android.media.update.PlaybackInfoProvider;
+import android.media.update.MediaController2Provider.PlaybackInfoProvider;
import android.net.Uri;
import android.os.Bundle;
import android.os.ResultReceiver;
diff --git a/media/java/android/media/MediaDataSource.java b/media/java/android/media/MediaDataSource.java
index 948da0b..4ba2120 100644
--- a/media/java/android/media/MediaDataSource.java
+++ b/media/java/android/media/MediaDataSource.java
@@ -34,8 +34,8 @@
/**
* Called to request data from the given position.
*
- * Implementations should should write up to {@code size} bytes into
- * {@code buffer}, and return the number of bytes written.
+ * Implementations should fill {@code buffer} with up to {@code size}
+ * bytes of data, and return the number of valid bytes in the buffer.
*
* Return {@code 0} if size is zero (thus no bytes are read).
*
diff --git a/media/java/android/media/MediaDrm.java b/media/java/android/media/MediaDrm.java
index 90fcaab..279e05f 100644
--- a/media/java/android/media/MediaDrm.java
+++ b/media/java/android/media/MediaDrm.java
@@ -634,8 +634,39 @@
* @throws ResourceBusyException if required resources are in use
*/
@NonNull
- public native byte[] openSession() throws NotProvisionedException,
- ResourceBusyException;
+ public byte[] openSession() throws NotProvisionedException,
+ ResourceBusyException {
+ return openSession(getMaxSecurityLevel());
+ }
+
+ /**
+ * Open a new session at a requested security level. The security level
+ * represents the robustness of the device's DRM implementation. By default,
+ * sessions are opened at the native security level of the device.
+ * Overriding the security level is necessary when the decrypted frames need
+ * to be manipulated, such as for image compositing. The security level
+ * parameter must be lower than the native level. Reducing the security
+ * level will typically limit the content to lower resolutions, as
+ * determined by the license policy. If the requested level is not
+ * supported, the next lower supported security level will be set. The level
+ * can be queried using {@link #getSecurityLevel}. A session
+ * ID is returned.
+ *
+ * @param level the new security level, one of
+ * {@link #SW_SECURE_CRYPTO}, {@link #SW_SECURE_DECODE},
+ * {@link #HW_SECURE_CRYPTO}, {@link #HW_SECURE_DECODE} or
+ * {@link #HW_SECURE_ALL}.
+ *
+ * @throws NotProvisionedException if provisioning is needed
+ * @throws ResourceBusyException if required resources are in use
+ * @throws IllegalArgumentException if the requested security level is
+ * higher than the native level or lower than the lowest supported level or
+ * if the device does not support specifying the security level when opening
+ * a session
+ */
+ @NonNull
+ public native byte[] openSession(@SecurityLevel int level) throws
+ NotProvisionedException, ResourceBusyException;
/**
* Close a session on the MediaDrm object that was previously opened
@@ -1109,7 +1140,7 @@
*/
@Retention(RetentionPolicy.SOURCE)
@IntDef({SECURITY_LEVEL_UNKNOWN, SW_SECURE_CRYPTO, SW_SECURE_DECODE,
- HW_SECURE_CRYPTO, HW_SECURE_DECODE, HW_SECURE_ALL})
+ HW_SECURE_CRYPTO, HW_SECURE_DECODE, HW_SECURE_ALL})
public @interface SecurityLevel {}
/**
@@ -1119,39 +1150,55 @@
public static final int SECURITY_LEVEL_UNKNOWN = 0;
/**
- * Software-based whitebox crypto
+ * DRM key management uses software-based whitebox crypto.
*/
public static final int SW_SECURE_CRYPTO = 1;
/**
- * Software-based whitebox crypto and an obfuscated decoder
+ * DRM key management and decoding use software-based whitebox crypto.
*/
- public static final int SW_SECURE_DECODE = 2;
+ public static final int SW_SECURE_DECODE = 2;
/**
- * DRM key management and crypto operations are performed within a
- * hardware backed trusted execution environment
+ * DRM key management and crypto operations are performed within a hardware
+ * backed trusted execution environment.
*/
public static final int HW_SECURE_CRYPTO = 3;
/**
- * DRM key management, crypto operations and decoding of content
- * are performed within a hardware backed trusted execution environment
+ * DRM key management, crypto operations and decoding of content are
+ * performed within a hardware backed trusted execution environment.
*/
- public static final int HW_SECURE_DECODE = 4;
+ public static final int HW_SECURE_DECODE = 4;
/**
* DRM key management, crypto operations, decoding of content and all
- * handling of the media (compressed and uncompressed) is handled within
- * a hardware backed trusted execution environment.
+ * handling of the media (compressed and uncompressed) is handled within a
+ * hardware backed trusted execution environment.
*/
public static final int HW_SECURE_ALL = 5;
/**
- * Return the current security level of a session. A session
- * has an initial security level determined by the robustness of
- * the DRM system's implementation on the device. The security
- * level may be adjusted using {@link #setSecurityLevel}.
+ * The maximum security level supported by the device. This is the default
+ * security level when a session is opened.
+ * @hide
+ */
+ public static final int SECURITY_LEVEL_MAX = 6;
+
+ /**
+ * The maximum security level supported by the device. This is the default
+ * security level when a session is opened.
+ */
+ @SecurityLevel
+ public static final int getMaxSecurityLevel() {
+ return SECURITY_LEVEL_MAX;
+ }
+
+ /**
+ * Return the current security level of a session. A session has an initial
+ * security level determined by the robustness of the DRM system's
+ * implementation on the device. The security level may be changed at the
+ * time a session is opened using {@link #openSession}.
* @param sessionId the session to query.
* <p>
* @return one of {@link #SECURITY_LEVEL_UNKNOWN},
@@ -1163,21 +1210,6 @@
public native int getSecurityLevel(@NonNull byte[] sessionId);
/**
- * Set the security level of a session. This can be useful if specific
- * attributes of a lower security level are needed by an application,
- * such as image manipulation or compositing. Reducing the security
- * level will typically limit decryption to lower content resolutions,
- * depending on the license policy.
- * @param sessionId the session to set the security level on.
- * @param level the new security level, one of
- * {@link #SW_SECURE_CRYPTO}, {@link #SW_SECURE_DECODE},
- * {@link #HW_SECURE_CRYPTO}, {@link #HW_SECURE_DECODE} or
- * {@link #HW_SECURE_ALL}.
- */
- public native void setSecurityLevel(@NonNull byte[] sessionId,
- @SecurityLevel int level);
-
- /**
* String property name: identifies the maker of the DRM plugin
*/
public static final String PROPERTY_VENDOR = "vendor";
diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java
index 1bc3dfa..fe5e822 100644
--- a/media/java/android/media/MediaPlayer.java
+++ b/media/java/android/media/MediaPlayer.java
@@ -1484,6 +1484,7 @@
/*
* Call BEFORE adding a routing callback handler or AFTER removing a routing callback handler.
*/
+ @GuardedBy("mRoutingChangeListeners")
private void enableNativeRoutingCallbacksLocked(boolean enabled) {
if (mRoutingChangeListeners.size() == 0) {
native_enableDeviceCallback(enabled);
diff --git a/media/java/android/media/MediaPlayer2Impl.java b/media/java/android/media/MediaPlayer2Impl.java
index e3d5ac0..49bbc2b 100644
--- a/media/java/android/media/MediaPlayer2Impl.java
+++ b/media/java/android/media/MediaPlayer2Impl.java
@@ -1212,6 +1212,7 @@
} else if (scheme != null) {
// handle non-file sources
nativeSetDataSource(
+ srcId,
Media2HTTPService.createHTTPService(path, cookies),
path,
keys,
@@ -1231,7 +1232,7 @@
}
private native void nativeSetDataSource(
- Media2HTTPService httpService, String path, String[] keys, String[] values)
+ long srcId, Media2HTTPService httpService, String path, String[] keys, String[] values)
throws IOException, IllegalArgumentException, SecurityException, IllegalStateException;
/**
@@ -1245,10 +1246,10 @@
*/
private void setDataSourcePriv(long srcId, FileDescriptor fd, long offset, long length)
throws IOException {
- _setDataSource(fd, offset, length);
+ _setDataSource(srcId, fd, offset, length);
}
- private native void _setDataSource(FileDescriptor fd, long offset, long length)
+ private native void _setDataSource(long srcId, FileDescriptor fd, long offset, long length)
throws IOException;
/**
@@ -1256,10 +1257,10 @@
* @throws IllegalArgumentException if dataSource is not a valid Media2DataSource
*/
private void setDataSourcePriv(long srcId, Media2DataSource dataSource) {
- _setDataSource(dataSource);
+ _setDataSource(srcId, dataSource);
}
- private native void _setDataSource(Media2DataSource dataSource);
+ private native void _setDataSource(long srcId, Media2DataSource dataSource);
/**
* Prepares the player for playback, synchronously.
@@ -1417,6 +1418,7 @@
/*
* Call BEFORE adding a routing callback handler or AFTER removing a routing callback handler.
*/
+ @GuardedBy("mRoutingChangeListeners")
private void enableNativeRoutingCallbacksLocked(boolean enabled) {
if (mRoutingChangeListeners.size() == 0) {
native_enableDeviceCallback(enabled);
@@ -3072,6 +3074,10 @@
@Override
public void handleMessage(Message msg) {
+ handleMessage(msg, 0);
+ }
+
+ public void handleMessage(Message msg, long srcId) {
if (mMediaPlayer.mNativeContext == 0) {
Log.w(TAG, "mediaplayer2 went away with unhandled events");
return;
@@ -3094,7 +3100,7 @@
synchronized (mEventCbLock) {
for (Pair<Executor, EventCallback> cb : mEventCallbackRecords) {
cb.first.execute(() -> cb.second.onInfo(
- mMediaPlayer, 0, MEDIA_INFO_PREPARED, 0));
+ mMediaPlayer, srcId, MEDIA_INFO_PREPARED, 0));
}
}
return;
@@ -3132,7 +3138,7 @@
synchronized (mEventCbLock) {
for (Pair<Executor, EventCallback> cb : mEventCallbackRecords) {
cb.first.execute(() -> cb.second.onInfo(
- mMediaPlayer, 0, MEDIA_INFO_PLAYBACK_COMPLETE, 0));
+ mMediaPlayer, srcId, MEDIA_INFO_PLAYBACK_COMPLETE, 0));
}
}
stayAwake(false);
@@ -3162,7 +3168,7 @@
synchronized (mEventCbLock) {
for (Pair<Executor, EventCallback> cb : mEventCallbackRecords) {
cb.first.execute(() -> cb.second.onBufferingUpdate(
- mMediaPlayer, 0, percent));
+ mMediaPlayer, srcId, percent));
}
}
return;
@@ -3171,7 +3177,7 @@
synchronized (mEventCbLock) {
for (Pair<Executor, EventCallback> cb : mEventCallbackRecords) {
cb.first.execute(() -> cb.second.onInfo(
- mMediaPlayer, 0, MEDIA_INFO_COMPLETE_CALL_SEEK, 0));
+ mMediaPlayer, srcId, MEDIA_INFO_COMPLETE_CALL_SEEK, 0));
}
}
// fall through
@@ -3191,7 +3197,7 @@
synchronized (mEventCbLock) {
for (Pair<Executor, EventCallback> cb : mEventCallbackRecords) {
cb.first.execute(() -> cb.second.onVideoSizeChanged(
- mMediaPlayer, 0, width, height));
+ mMediaPlayer, srcId, width, height));
}
}
return;
@@ -3201,9 +3207,9 @@
synchronized (mEventCbLock) {
for (Pair<Executor, EventCallback> cb : mEventCallbackRecords) {
cb.first.execute(() -> cb.second.onError(
- mMediaPlayer, 0, what, extra));
+ mMediaPlayer, srcId, what, extra));
cb.first.execute(() -> cb.second.onInfo(
- mMediaPlayer, 0, MEDIA_INFO_PLAYBACK_COMPLETE, 0));
+ mMediaPlayer, srcId, MEDIA_INFO_PLAYBACK_COMPLETE, 0));
}
}
stayAwake(false);
@@ -3246,7 +3252,7 @@
synchronized (mEventCbLock) {
for (Pair<Executor, EventCallback> cb : mEventCallbackRecords) {
cb.first.execute(() -> cb.second.onInfo(
- mMediaPlayer, 0, what, extra));
+ mMediaPlayer, srcId, what, extra));
}
}
// No real default action so far.
@@ -3271,7 +3277,7 @@
synchronized (mEventCbLock) {
for (Pair<Executor, EventCallback> cb : mEventCallbackRecords) {
- cb.first.execute(() -> cb.second.onTimedText(mMediaPlayer, 0, text));
+ cb.first.execute(() -> cb.second.onTimedText(mMediaPlayer, srcId, text));
}
}
return;
@@ -3302,7 +3308,7 @@
synchronized (mEventCbLock) {
for (Pair<Executor, EventCallback> cb : mEventCallbackRecords) {
cb.first.execute(() -> cb.second.onTimedMetaDataAvailable(
- mMediaPlayer, 0, data));
+ mMediaPlayer, srcId, data));
}
}
return;
@@ -3334,7 +3340,7 @@
* code is safe from the object disappearing from underneath it. (This is
* the cookie passed to native_setup().)
*/
- private static void postEventFromNative(Object mediaplayer2_ref,
+ private static void postEventFromNative(Object mediaplayer2_ref, long srcId,
int what, int arg1, int arg2, Object obj)
{
final MediaPlayer2Impl mp = (MediaPlayer2Impl)((WeakReference)mediaplayer2_ref).get();
@@ -3387,7 +3393,13 @@
if (mp.mEventHandler != null) {
Message m = mp.mEventHandler.obtainMessage(what, arg1, arg2, obj);
- mp.mEventHandler.sendMessage(m);
+
+ mp.mEventHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ mp.mEventHandler.handleMessage(m, srcId);
+ }
+ });
}
}
@@ -4489,7 +4501,7 @@
// no need for log(N) search performance
private MediaTimeProvider.OnMediaTimeListener mListeners[];
private long mTimes[];
- private Handler mEventHandler;
+ private EventHandler mEventHandler;
private boolean mRefresh = false;
private boolean mPausing = false;
private boolean mSeeking = false;
diff --git a/media/java/android/media/MediaRecorder.java b/media/java/android/media/MediaRecorder.java
index 62240ce..823410f 100644
--- a/media/java/android/media/MediaRecorder.java
+++ b/media/java/android/media/MediaRecorder.java
@@ -1353,6 +1353,7 @@
/*
* Call BEFORE adding a routing callback handler or AFTER removing a routing callback handler.
*/
+ @GuardedBy("mRoutingChangeListeners")
private void enableNativeRoutingCallbacksLocked(boolean enabled) {
if (mRoutingChangeListeners.size() == 0) {
native_enableDeviceCallback(enabled);
diff --git a/media/java/android/media/update/MediaController2Provider.java b/media/java/android/media/update/MediaController2Provider.java
index 71bc64a..c492d307 100644
--- a/media/java/android/media/update/MediaController2Provider.java
+++ b/media/java/android/media/update/MediaController2Provider.java
@@ -18,6 +18,7 @@
import android.annotation.SystemApi;
import android.app.PendingIntent;
+import android.media.AudioAttributes;
import android.media.MediaController2.PlaybackInfo;
import android.media.MediaItem2;
import android.media.MediaSession2.Command;
@@ -65,4 +66,12 @@
PlaylistParams getPlaylistParams_impl();
void setPlaylistParams_impl(PlaylistParams params);
PlaybackState2 getPlaybackState_impl();
+
+ interface PlaybackInfoProvider {
+ int getPlaybackType_impl();
+ AudioAttributes getAudioAttributes_impl();
+ int getControlType_impl();
+ int getMaxVolume_impl();
+ int getCurrentVolume_impl();
+ }
}
diff --git a/media/java/android/media/update/PlaybackInfoProvider.java b/media/java/android/media/update/PlaybackInfoProvider.java
deleted file mode 100644
index 36eb58a..0000000
--- a/media/java/android/media/update/PlaybackInfoProvider.java
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Copyright 2018 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.media.update;
-
-import android.media.AudioAttributes;
-
-/**
- * @hide
- */
-// TODO(jaewan): @SystemApi
-public interface PlaybackInfoProvider {
- int getPlaybackType_impl();
- AudioAttributes getAudioAttributes_impl();
- int getControlType_impl();
- int getMaxVolume_impl();
- int getCurrentVolume_impl();
-}
diff --git a/media/jni/android_media_MediaDrm.cpp b/media/jni/android_media_MediaDrm.cpp
index 4f06caa..d7f51d4 100644
--- a/media/jni/android_media_MediaDrm.cpp
+++ b/media/jni/android_media_MediaDrm.cpp
@@ -145,6 +145,7 @@
struct SecurityLevels {
jint kSecurityLevelUnknown;
+ jint kSecurityLevelMax;
jint kSecurityLevelSwSecureCrypto;
jint kSecurityLevelSwSecureDecode;
jint kSecurityLevelHwSecureCrypto;
@@ -683,6 +684,10 @@
GET_STATIC_FIELD_ID(field, clazz, "HW_SECURE_ALL", "I");
gSecurityLevels.kSecurityLevelHwSecureAll = env->GetStaticIntField(clazz, field);
+ jmethodID getMaxSecurityLevel;
+ GET_STATIC_METHOD_ID(getMaxSecurityLevel, clazz, "getMaxSecurityLevel", "()I");
+ gSecurityLevels.kSecurityLevelMax = env->CallStaticIntMethod(clazz, getMaxSecurityLevel);
+
FIND_CLASS(clazz, "android/media/MediaDrm$KeyRequest");
GET_FIELD_ID(gFields.keyRequest.data, clazz, "mData", "[B");
GET_FIELD_ID(gFields.keyRequest.defaultUrl, clazz, "mDefaultUrl", "Ljava/lang/String;");
@@ -813,7 +818,7 @@
}
static jbyteArray android_media_MediaDrm_openSession(
- JNIEnv *env, jobject thiz) {
+ JNIEnv *env, jobject thiz, jint jlevel) {
sp<IDrm> drm = GetDrm(env, thiz);
if (drm == NULL) {
@@ -823,7 +828,26 @@
}
Vector<uint8_t> sessionId;
- status_t err = drm->openSession(sessionId);
+ DrmPlugin::SecurityLevel level;
+
+ if (jlevel == gSecurityLevels.kSecurityLevelMax) {
+ level = DrmPlugin::kSecurityLevelMax;
+ } else if (jlevel == gSecurityLevels.kSecurityLevelSwSecureCrypto) {
+ level = DrmPlugin::kSecurityLevelSwSecureCrypto;
+ } else if (jlevel == gSecurityLevels.kSecurityLevelSwSecureDecode) {
+ level = DrmPlugin::kSecurityLevelSwSecureDecode;
+ } else if (jlevel == gSecurityLevels.kSecurityLevelHwSecureCrypto) {
+ level = DrmPlugin::kSecurityLevelHwSecureCrypto;
+ } else if (jlevel == gSecurityLevels.kSecurityLevelHwSecureDecode) {
+ level = DrmPlugin::kSecurityLevelHwSecureDecode;
+ } else if (jlevel == gSecurityLevels.kSecurityLevelHwSecureAll) {
+ level = DrmPlugin::kSecurityLevelHwSecureAll;
+ } else {
+ jniThrowException(env, "java/lang/IllegalArgumentException", "Invalid security level");
+ return NULL;
+ }
+
+ status_t err = drm->openSession(level, sessionId);
if (throwExceptionAsNecessary(env, err, "Failed to open session")) {
return NULL;
@@ -1345,40 +1369,6 @@
}
-static void android_media_MediaDrm_setSecurityLevel(JNIEnv *env,
- jobject thiz, jbyteArray jsessionId, jint jlevel) {
- sp<IDrm> drm = GetDrm(env, thiz);
-
- if (!CheckSession(env, drm, jsessionId)) {
- return;
- }
-
- Vector<uint8_t> sessionId(JByteArrayToVector(env, jsessionId));
- DrmPlugin::SecurityLevel level;
-
- if (jlevel == gSecurityLevels.kSecurityLevelSwSecureCrypto) {
- level = DrmPlugin::kSecurityLevelSwSecureCrypto;
- } else if (jlevel == gSecurityLevels.kSecurityLevelSwSecureDecode) {
- level = DrmPlugin::kSecurityLevelSwSecureDecode;
- } else if (jlevel == gSecurityLevels.kSecurityLevelHwSecureCrypto) {
- level = DrmPlugin::kSecurityLevelHwSecureCrypto;
- } else if (jlevel == gSecurityLevels.kSecurityLevelHwSecureDecode) {
- level = DrmPlugin::kSecurityLevelHwSecureDecode;
- } else if (jlevel == gSecurityLevels.kSecurityLevelHwSecureAll) {
- level = DrmPlugin::kSecurityLevelHwSecureAll;
- } else {
- jniThrowException(env, "java/lang/IllegalArgumentException", "Invalid security level");
- return;
- }
-
- status_t err = drm->setSecurityLevel(sessionId, level);
-
- if (throwExceptionAsNecessary(env, err, "Failed to set security level")) {
- return;
- }
-}
-
-
static jstring android_media_MediaDrm_getPropertyString(
JNIEnv *env, jobject thiz, jstring jname) {
sp<IDrm> drm = GetDrm(env, thiz);
@@ -1724,7 +1714,7 @@
{ "isCryptoSchemeSupportedNative", "([BLjava/lang/String;)Z",
(void *)android_media_MediaDrm_isCryptoSchemeSupportedNative },
- { "openSession", "()[B",
+ { "openSession", "(I)[B",
(void *)android_media_MediaDrm_openSession },
{ "closeSession", "([B)V",
@@ -1785,9 +1775,6 @@
{ "getSecurityLevel", "([B)I",
(void *)android_media_MediaDrm_getSecurityLevel },
- { "setSecurityLevel", "([BI)V",
- (void *)android_media_MediaDrm_setSecurityLevel },
-
{ "getPropertyString", "(Ljava/lang/String;)Ljava/lang/String;",
(void *)android_media_MediaDrm_getPropertyString },
diff --git a/media/jni/android_media_MediaPlayer2.cpp b/media/jni/android_media_MediaPlayer2.cpp
index 0eb98f3..e73b2f8 100644
--- a/media/jni/android_media_MediaPlayer2.cpp
+++ b/media/jni/android_media_MediaPlayer2.cpp
@@ -154,7 +154,8 @@
public:
JNIMediaPlayer2Listener(JNIEnv* env, jobject thiz, jobject weak_thiz);
~JNIMediaPlayer2Listener();
- virtual void notify(int msg, int ext1, int ext2, const Parcel *obj = NULL);
+ virtual void notify(int64_t srcId, int msg, int ext1, int ext2,
+ const Parcel *obj = NULL) override;
private:
JNIMediaPlayer2Listener();
jclass mClass; // Reference to MediaPlayer2 class
@@ -187,7 +188,7 @@
env->DeleteGlobalRef(mClass);
}
-void JNIMediaPlayer2Listener::notify(int msg, int ext1, int ext2, const Parcel *obj)
+void JNIMediaPlayer2Listener::notify(int64_t srcId, int msg, int ext1, int ext2, const Parcel *obj)
{
JNIEnv *env = AndroidRuntime::getJNIEnv();
if (obj && obj->dataSize() > 0) {
@@ -196,12 +197,12 @@
Parcel* nativeParcel = parcelForJavaObject(env, jParcel);
nativeParcel->setData(obj->data(), obj->dataSize());
env->CallStaticVoidMethod(mClass, fields.post_event, mObject,
- msg, ext1, ext2, jParcel);
+ srcId, msg, ext1, ext2, jParcel);
env->DeleteLocalRef(jParcel);
}
} else {
env->CallStaticVoidMethod(mClass, fields.post_event, mObject,
- msg, ext1, ext2, NULL);
+ srcId, msg, ext1, ext2, NULL);
}
if (env->ExceptionCheck()) {
ALOGW("An exception occurred while notifying an event.");
@@ -243,7 +244,11 @@
if (exception == NULL) { // Don't throw exception. Instead, send an event.
if (opStatus != (status_t) OK) {
sp<MediaPlayer2> mp = getMediaPlayer(env, thiz);
- if (mp != 0) mp->notify(MEDIA2_ERROR, opStatus, 0);
+ if (mp != 0) {
+ int64_t srcId = 0;
+ mp->getSrcId(&srcId);
+ mp->notify(srcId, MEDIA2_ERROR, opStatus, 0);
+ }
}
} else { // Throw exception!
if ( opStatus == (status_t) INVALID_OPERATION ) {
@@ -268,7 +273,7 @@
static void
android_media_MediaPlayer2_setDataSourceAndHeaders(
- JNIEnv *env, jobject thiz, jobject httpServiceObj, jstring path,
+ JNIEnv *env, jobject thiz, jlong srcId, jobject httpServiceObj, jstring path,
jobjectArray keys, jobjectArray values) {
sp<MediaPlayer2> mp = getMediaPlayer(env, thiz);
@@ -286,7 +291,7 @@
if (tmp == NULL) { // Out of memory
return;
}
- ALOGV("setDataSourceAndHeaders: path %s", tmp);
+ ALOGV("setDataSourceAndHeaders: path %s, srcId %lld", tmp, (long long)srcId);
if (strncmp(tmp, "content://", 10) == 0) {
ALOGE("setDataSourceAndHeaders: content scheme is not supported in native code");
@@ -296,6 +301,7 @@
}
sp<DataSourceDesc> dsd = new DataSourceDesc();
+ dsd->mId = srcId;
dsd->mType = DataSourceDesc::TYPE_URL;
dsd->mUrl = tmp;
@@ -321,7 +327,7 @@
static void
android_media_MediaPlayer2_setDataSourceFD(
- JNIEnv *env, jobject thiz, jobject fileDescriptor, jlong offset, jlong length)
+ JNIEnv *env, jobject thiz, jlong srcId, jobject fileDescriptor, jlong offset, jlong length)
{
sp<MediaPlayer2> mp = getMediaPlayer(env, thiz);
if (mp == NULL ) {
@@ -334,8 +340,8 @@
return;
}
int fd = jniGetFDFromFileDescriptor(env, fileDescriptor);
- ALOGV("setDataSourceFD: fd=%d (%s), offset=%lld, length=%lld",
- fd, nameForFd(fd).c_str(), (long long)offset, (long long)length);
+ ALOGV("setDataSourceFD: srcId=%lld, fd=%d (%s), offset=%lld, length=%lld",
+ (long long)srcId, fd, nameForFd(fd).c_str(), (long long)offset, (long long)length);
struct stat sb;
int ret = fstat(fd, &sb);
@@ -363,6 +369,7 @@
}
sp<DataSourceDesc> dsd = new DataSourceDesc();
+ dsd->mId = srcId;
dsd->mType = DataSourceDesc::TYPE_FD;
dsd->mFD = fd;
dsd->mFDOffset = offset;
@@ -373,7 +380,7 @@
static void
android_media_MediaPlayer2_setDataSourceCallback(
- JNIEnv *env, jobject thiz, jobject dataSource)
+ JNIEnv *env, jobject thiz, jlong srcId, jobject dataSource)
{
sp<MediaPlayer2> mp = getMediaPlayer(env, thiz);
if (mp == NULL ) {
@@ -387,6 +394,7 @@
}
sp<DataSource> callbackDataSource = new JMedia2DataSource(env, dataSource);
sp<DataSourceDesc> dsd = new DataSourceDesc();
+ dsd->mId = srcId;
dsd->mType = DataSourceDesc::TYPE_CALLBACK;
dsd->mCallbackSource = callbackDataSource;
process_media_player_call(env, thiz, mp->setDataSource(dsd),
@@ -1012,7 +1020,7 @@
}
fields.post_event = env->GetStaticMethodID(clazz, "postEventFromNative",
- "(Ljava/lang/Object;IIILjava/lang/Object;)V");
+ "(Ljava/lang/Object;JIIILjava/lang/Object;)V");
if (fields.post_event == NULL) {
return;
}
@@ -1382,13 +1390,13 @@
static const JNINativeMethod gMethods[] = {
{
"nativeSetDataSource",
- "(Landroid/media/Media2HTTPService;Ljava/lang/String;[Ljava/lang/String;"
+ "(JLandroid/media/Media2HTTPService;Ljava/lang/String;[Ljava/lang/String;"
"[Ljava/lang/String;)V",
(void *)android_media_MediaPlayer2_setDataSourceAndHeaders
},
- {"_setDataSource", "(Ljava/io/FileDescriptor;JJ)V", (void *)android_media_MediaPlayer2_setDataSourceFD},
- {"_setDataSource", "(Landroid/media/Media2DataSource;)V",(void *)android_media_MediaPlayer2_setDataSourceCallback },
+ {"_setDataSource", "(JLjava/io/FileDescriptor;JJ)V", (void *)android_media_MediaPlayer2_setDataSourceFD},
+ {"_setDataSource", "(JLandroid/media/Media2DataSource;)V",(void *)android_media_MediaPlayer2_setDataSourceCallback },
{"_setVideoSurface", "(Landroid/view/Surface;)V", (void *)android_media_MediaPlayer2_setVideoSurface},
{"getBufferingParams", "()Landroid/media/BufferingParams;", (void *)android_media_MediaPlayer2_getBufferingParams},
{"setBufferingParams", "(Landroid/media/BufferingParams;)V", (void *)android_media_MediaPlayer2_setBufferingParams},
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/stress/Camera2RecordingTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/stress/Camera2RecordingTest.java
index 5566b4e..ddb05f0 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/stress/Camera2RecordingTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/stress/Camera2RecordingTest.java
@@ -659,6 +659,10 @@
updatePreviewSurfaceWithVideo(videoSz, profile.videoFrameRate);
prepareVideoSnapshot(videoSnapshotRequestBuilder, imageListener);
+ Range<Integer> fpsRange = Range.create(profile.videoFrameRate,
+ profile.videoFrameRate);
+ videoSnapshotRequestBuilder.set(CaptureRequest.CONTROL_AE_TARGET_FPS_RANGE,
+ fpsRange);
CaptureRequest request = videoSnapshotRequestBuilder.build();
// Start recording
diff --git a/native/android/asset_manager.cpp b/native/android/asset_manager.cpp
index e70d5ea..98e9a42 100644
--- a/native/android/asset_manager.cpp
+++ b/native/android/asset_manager.cpp
@@ -18,11 +18,9 @@
#include <utils/Log.h>
#include <android/asset_manager_jni.h>
-#include <android_runtime/android_util_AssetManager.h>
#include <androidfw/Asset.h>
#include <androidfw/AssetDir.h>
#include <androidfw/AssetManager.h>
-#include <androidfw/AssetManager2.h>
#include <utils/threads.h>
#include "jni.h"
@@ -37,20 +35,21 @@
// -----
struct AAssetDir {
- std::unique_ptr<AssetDir> mAssetDir;
+ AssetDir* mAssetDir;
size_t mCurFileIndex;
String8 mCachedFileName;
- explicit AAssetDir(std::unique_ptr<AssetDir> dir) :
- mAssetDir(std::move(dir)), mCurFileIndex(0) { }
+ explicit AAssetDir(AssetDir* dir) : mAssetDir(dir), mCurFileIndex(0) { }
+ ~AAssetDir() { delete mAssetDir; }
};
// -----
struct AAsset {
- std::unique_ptr<Asset> mAsset;
+ Asset* mAsset;
- explicit AAsset(std::unique_ptr<Asset> asset) : mAsset(std::move(asset)) { }
+ explicit AAsset(Asset* asset) : mAsset(asset) { }
+ ~AAsset() { delete mAsset; }
};
// -------------------- Public native C API --------------------
@@ -105,18 +104,19 @@
return NULL;
}
- ScopedLock<AssetManager2> locked_mgr(*AssetManagerForNdkAssetManager(amgr));
- std::unique_ptr<Asset> asset = locked_mgr->Open(filename, amMode);
- if (asset == nullptr) {
- return nullptr;
+ AssetManager* mgr = static_cast<AssetManager*>(amgr);
+ Asset* asset = mgr->open(filename, amMode);
+ if (asset == NULL) {
+ return NULL;
}
- return new AAsset(std::move(asset));
+
+ return new AAsset(asset);
}
AAssetDir* AAssetManager_openDir(AAssetManager* amgr, const char* dirName)
{
- ScopedLock<AssetManager2> locked_mgr(*AssetManagerForNdkAssetManager(amgr));
- return new AAssetDir(locked_mgr->OpenDir(dirName));
+ AssetManager* mgr = static_cast<AssetManager*>(amgr);
+ return new AAssetDir(mgr->openDir(dirName));
}
/**
diff --git a/packages/MtpDocumentsProvider/AndroidManifest.xml b/packages/MtpDocumentsProvider/AndroidManifest.xml
index 8d79f62..c0a59b3 100644
--- a/packages/MtpDocumentsProvider/AndroidManifest.xml
+++ b/packages/MtpDocumentsProvider/AndroidManifest.xml
@@ -3,6 +3,7 @@
package="com.android.mtp"
android:sharedUserId="android.media">
<uses-feature android:name="android.hardware.usb.host" />
+ <uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
<uses-permission android:name="android.permission.MANAGE_USB" />
<application android:label="@string/app_label">
<provider
diff --git a/packages/SettingsLib/res/values/arrays.xml b/packages/SettingsLib/res/values/arrays.xml
index c926e1f..c1aa2dc 100644
--- a/packages/SettingsLib/res/values/arrays.xml
+++ b/packages/SettingsLib/res/values/arrays.xml
@@ -250,6 +250,19 @@
<item>Best Effort (Adaptive Bit Rate)</item>
</string-array>
+ <!-- TODO: Enable for translation per b/73007419 -->
+ <!-- Summaries for Bluetooth Audio Active Device status. [CHAR LIMIT=50]-->
+ <string-array name="bluetooth_audio_active_device_summaries" translatable="false" >
+ <!-- Status message when the device is not Active. -->
+ <item></item>
+ <!-- Status message when the device is Active for Media and Phone. -->
+ <item>, active</item>
+ <!-- Status message when the device is Active for Media only. -->
+ <item>, active(media)</item>
+ <!-- Status message when the device is Active for Phone only. -->
+ <item>, active(phone)</item>
+ </string-array>
+
<!-- Titles for logd limit size selection preference. [CHAR LIMIT=14] -->
<string-array name="select_logd_size_titles">
<item>Off</item>
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index 6ef3fac..e6f4ec6 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -128,27 +128,27 @@
<!-- Bluetooth settings. Message when connecting to a device -->
<string name="bluetooth_connecting">Connecting\u2026</string>
<!-- Bluetooth settings. Message when connected to a device. [CHAR LIMIT=40] -->
- <string name="bluetooth_connected">Connected</string>
+ <string name="bluetooth_connected">Connected<xliff:g id="active_device">%1$s</xliff:g></string>
<!--Bluetooth settings screen, summary text under individual Bluetooth devices when pairing -->
<string name="bluetooth_pairing">Pairing\u2026</string>
<!-- Bluetooth settings. Message when connected to a device, except for phone audio. [CHAR LIMIT=40] -->
- <string name="bluetooth_connected_no_headset">Connected (no phone)</string>
+ <string name="bluetooth_connected_no_headset">Connected (no phone)<xliff:g id="active_device">%1$s</xliff:g></string>
<!-- Bluetooth settings. Message when connected to a device, except for media audio. [CHAR LIMIT=40] -->
- <string name="bluetooth_connected_no_a2dp">Connected (no media)</string>
+ <string name="bluetooth_connected_no_a2dp">Connected (no media)<xliff:g id="active_device">%1$s</xliff:g></string>
<!-- Bluetooth settings. Message when connected to a device, except for map. [CHAR LIMIT=40] -->
- <string name="bluetooth_connected_no_map">Connected (no message access)</string>
+ <string name="bluetooth_connected_no_map">Connected (no message access)<xliff:g id="active_device">%1$s</xliff:g></string>
<!-- 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>
+ <string name="bluetooth_connected_no_headset_no_a2dp">Connected (no phone or media)<xliff:g id="active_device">%1$s</xliff:g></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>
+ <string name="bluetooth_connected_battery_level">Connected, battery <xliff:g id="battery_level_as_percentage">%1$s</xliff:g><xliff:g id="active_device">%2$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>
+ <string name="bluetooth_connected_no_headset_battery_level">Connected (no phone), battery <xliff:g id="battery_level_as_percentage">%1$s</xliff:g><xliff:g id="active_device">%2$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>
+ <string name="bluetooth_connected_no_a2dp_battery_level">Connected (no media), battery <xliff:g id="battery_level_as_percentage">%1$s</xliff:g><xliff:g id="active_device">%2$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>
+ <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><xliff:g id="active_device">%2$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>
@@ -805,13 +805,17 @@
<item>Colors optimized for digital content</item>
</string-array>
- <!-- Settings item title for inactive apps [CHAR LIMIT=35] -->
- <string name="inactive_apps_title">Inactive apps</string>
+ <!-- Settings item title for apps in standby (limiting background activity) [CHAR LIMIT=35] -->
+ <string name="inactive_apps_title">Standby apps</string>
<!-- Settings item summary for inactive app [CHAR LIMIT=100] -->
<string name="inactive_app_inactive_summary">Inactive. Tap to toggle.</string>
<!-- Settings item summary for active app [CHAR LIMIT=100] -->
<string name="inactive_app_active_summary">Active. Tap to toggle.</string>
+ <!-- Settings item summary for standby bucket value of an app. [CHAR LIMIT=100] -->
+ <string name="standby_bucket_summary">App standby
+ state:<xliff:g id="bucket"> %s</xliff:g></string>
+
<!-- Services settings screen, setting option name for the user to go to the screen to view running services -->
<string name="runningservices_settings_title">Running services</string>
<!-- Services settings screen, setting option summary for the user to go to the screen to view running services -->
@@ -865,30 +869,49 @@
<!-- Summary shown for color space correction preference when its value is overridden by another preference [CHAR LIMIT=35] -->
<string name="daltonizer_type_overridden">Overridden by <xliff:g id="title" example="Simulate color space">%1$s</xliff:g></string>
- <!-- [CHAR_LIMIT=40] Label for estimated remaining duration of battery discharging -->
- <string name="power_remaining_duration_only">About <xliff:g id="time">^1</xliff:g> left</string>
- <!-- [CHAR_LIMIT=60] Label for estimated remaining duration of battery discharging -->
- <string name="power_remaining_duration_only_enhanced">About <xliff:g id="time">^1</xliff:g> left based on your usage</string>
- <!-- [CHAR_LIMIT=40] Label for estimated remaining duration of battery charging -->
- <string name="power_remaining_charging_duration_only"><xliff:g id="time">^1</xliff:g> left until fully charged</string>
+ <!-- [CHAR_LIMIT=40] Label for estimated remaining duration of battery discharging -->
+ <string name="power_remaining_duration_only">About <xliff:g id="time">%1$s</xliff:g> left</string>
+ <!-- [CHAR_LIMIT=60] Label for estimated remaining duration of battery discharging -->
+ <string name="power_remaining_duration_only_enhanced">About <xliff:g id="time">%1$s</xliff:g> left based on your usage</string>
+ <!-- [CHAR_LIMIT=40] Label for estimated remaining duration of battery charging -->
+ <string name="power_remaining_charging_duration_only"><xliff:g id="time">%1$s</xliff:g> left until fully charged</string>
- <!-- [CHAR_LIMIT=40] Short label for estimated remaining duration of battery charging/discharging -->
- <string name="power_remaining_duration_only_short"><xliff:g id="time">^1</xliff:g> left</string>
- <!-- [CHAR_LIMIT=60] Short label for estimated remaining duration of battery charging/discharging -->
- <string name="power_remaining_duration_only_short_enhanced"><xliff:g id="time">^1</xliff:g> left based on your usage</string>
+ <!-- [CHAR_LIMIT=40] Short label for estimated remaining duration of battery charging/discharging -->
+ <string name="power_remaining_duration_only_short"><xliff:g id="time">%1$s</xliff:g> left</string>
- <!-- [CHAR_LIMIT=40] Label for battery level chart when discharging with duration -->
- <string name="power_discharging_duration"><xliff:g id="level">^1</xliff:g> - about <xliff:g id="time">^2</xliff:g> left</string>
- <!-- [CHAR_LIMIT=60] Label for battery level chart when discharging with duration and using enhanced estimate -->
- <string name="power_discharging_duration_enhanced"><xliff:g id="level">^1</xliff:g> - about <xliff:g id="time">^2</xliff:g> left based on your usage</string>
+ <!-- [CHAR_LIMIT=60] label for estimated remaining duration of battery when under a certain amount -->
+ <string name="power_remaining_less_than_duration_only">Less than <xliff:g id="threshold">%1$s</xliff:g> remaining</string>
+ <!-- [CHAR_LIMIT=60] label for estimated remaining duration of battery when under a certain amount with the percentage -->
+ <string name="power_remaining_less_than_duration"><xliff:g id="level">%1$s</xliff:g> - Less than <xliff:g id="threshold">%2$s</xliff:g> remaining</string>
- <!-- [CHAR_LIMIT=40] Label for battery level chart when discharging with duration -->
- <string name="power_discharging_duration_short"><xliff:g id="level">^1</xliff:g> - <xliff:g id="time">^2</xliff:g> left</string>
+ <!-- Used to let users know that they have more than some amount of battery life remaining with percentage. ex: 75% - more than 1 day remaining [CHAR LIMIT = 80] -->
+ <string name="power_remaining_more_than_subtext"><xliff:g id="level">%1$s</xliff:g>more than <xliff:g id="time_remaining">%2$s</xliff:g> remaining</string>
+ <!-- Used to let users know that they have more than some amount of battery life remaining. ex: more than 1 day remaining [CHAR LIMIT = 40] -->
+ <string name="power_remaining_only_more_than_subtext">more than <xliff:g id="time_remaining">%1$s</xliff:g> remaining</string>
+
+ <!-- [CHAR_LIMIT=50] Short label for imminent shutdown warning of device -->
+ <string name="power_remaining_duration_only_shutdown_imminent" product="default">phone may shutdown soon</string>
+ <!-- [CHAR_LIMIT=50] Short label for imminent shutdown warning of device -->
+ <string name="power_remaining_duration_only_shutdown_imminent" product="tablet">tablet may shutdown soon</string>
+ <!-- [CHAR_LIMIT=50] Short label for imminent shutdown warning of device -->
+ <string name="power_remaining_duration_only_shutdown_imminent" product="device">device may shutdown soon</string>
+
+ <!-- [CHAR_LIMIT=40] Label for battery level chart when discharging with duration -->
+ <string name="power_discharging_duration"><xliff:g id="level">%1$s</xliff:g> - about <xliff:g id="time">%2$s</xliff:g> left</string>
+ <!-- [CHAR_LIMIT=60] Label for battery level chart when discharging with duration and using enhanced estimate -->
+ <string name="power_discharging_duration_enhanced"><xliff:g id="level">%1$s</xliff:g> - about <xliff:g id="time">%2$s</xliff:g> left based on your usage</string>
+
+ <!-- [CHAR_LIMIT=60] Label for battery level chart when shutdown is imminent-->
+ <string name="power_remaining_duration_shutdown_imminent" product="default"><xliff:g id="level">%1$s</xliff:g> - phone may shutdown soon</string>
+ <!-- [CHAR_LIMIT=60] Label for battery level chart when shutdown is imminent-->
+ <string name="power_remaining_duration_shutdown_imminent" product="tablet"><xliff:g id="level">%1$s</xliff:g> - tablet may shutdown soon</string>
+ <!-- [CHAR_LIMIT=60] Label for battery level chart when shutdown is imminent-->
+ <string name="power_remaining_duration_shutdown_imminent" product="device"><xliff:g id="level">%1$s</xliff:g> - device may shutdown soon</string>
<!-- [CHAR_LIMIT=40] Label for battery level chart when charging -->
<string name="power_charging"><xliff:g id="level">%1$s</xliff:g> - <xliff:g id="state">%2$s</xliff:g></string>
<!-- [CHAR_LIMIT=40] Label for battery level chart when charging with duration -->
- <string name="power_charging_duration"><xliff:g id="level">^1</xliff:g> - <xliff:g id="time">^2</xliff:g> until fully charged</string>
+ <string name="power_charging_duration"><xliff:g id="level">%1$s</xliff:g> - <xliff:g id="time">%2$s</xliff:g> until fully charged</string>
<!-- Battery Info screen. Value for a status item. Used for diagnostic info screens, precise translation isn't needed -->
<string name="battery_info_status_unknown">Unknown</string>
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
index fb0f75b..e1ebbc4 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
@@ -940,60 +940,55 @@
com.android.settingslib.Utils.formatPercentage(batteryLevel);
}
- // TODO: A temporary workaround solution using string description the device is active.
- // Issue tracked by b/72317067 .
- // An alternative solution would be visual indication.
- // Intentionally not adding the strings to strings.xml for now:
- // 1) If this is just a short-term solution, no need to waste translation effort
- // 2) The number of strings with all possible combinations becomes enormously large.
- // If string description becomes part of the final solution, we MUST NOT
- // concatenate the strings here: this does not translate well.
- String activeString = null;
+ // Prepare the string for the Active Device summary
+ String[] activeDeviceStringsArray = mContext.getResources().getStringArray(
+ R.array.bluetooth_audio_active_device_summaries);
+ String activeDeviceString = activeDeviceStringsArray[0]; // Default value: not active
if (mIsActiveDeviceA2dp && mIsActiveDeviceHeadset) {
- activeString = ", active";
+ activeDeviceString = activeDeviceStringsArray[1]; // Active for Media and Phone
} else {
if (mIsActiveDeviceA2dp) {
- activeString = ", active(media)";
+ activeDeviceString = activeDeviceStringsArray[2]; // Active for Media only
}
if (mIsActiveDeviceHeadset) {
- activeString = ", active(phone)";
+ activeDeviceString = activeDeviceStringsArray[3]; // Active for Phone only
}
}
- if (activeString == null) activeString = "";
if (profileConnected) {
if (a2dpNotConnected && hfpNotConnected) {
if (batteryLevelPercentageString != null) {
return mContext.getString(
R.string.bluetooth_connected_no_headset_no_a2dp_battery_level,
- batteryLevelPercentageString) + activeString;
+ batteryLevelPercentageString, activeDeviceString);
} else {
- return mContext.getString(R.string.bluetooth_connected_no_headset_no_a2dp) +
- activeString;
+ return mContext.getString(R.string.bluetooth_connected_no_headset_no_a2dp,
+ activeDeviceString);
}
} else if (a2dpNotConnected) {
if (batteryLevelPercentageString != null) {
return mContext.getString(R.string.bluetooth_connected_no_a2dp_battery_level,
- batteryLevelPercentageString) + activeString;
+ batteryLevelPercentageString, activeDeviceString);
} else {
- return mContext.getString(R.string.bluetooth_connected_no_a2dp) + activeString;
+ return mContext.getString(R.string.bluetooth_connected_no_a2dp,
+ activeDeviceString);
}
} else if (hfpNotConnected) {
if (batteryLevelPercentageString != null) {
return mContext.getString(R.string.bluetooth_connected_no_headset_battery_level,
- batteryLevelPercentageString) + activeString;
+ batteryLevelPercentageString, activeDeviceString);
} else {
- return mContext.getString(R.string.bluetooth_connected_no_headset)
- + activeString;
+ return mContext.getString(R.string.bluetooth_connected_no_headset,
+ activeDeviceString);
}
} else {
if (batteryLevelPercentageString != null) {
return mContext.getString(R.string.bluetooth_connected_battery_level,
- batteryLevelPercentageString) + activeString;
+ batteryLevelPercentageString, activeDeviceString);
} else {
- return mContext.getString(R.string.bluetooth_connected) + activeString;
+ return mContext.getString(R.string.bluetooth_connected, activeDeviceString);
}
}
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/development/DevelopmentSettingsEnabler.java b/packages/SettingsLib/src/com/android/settingslib/development/DevelopmentSettingsEnabler.java
index 4e78d9b..85bf4e8 100644
--- a/packages/SettingsLib/src/com/android/settingslib/development/DevelopmentSettingsEnabler.java
+++ b/packages/SettingsLib/src/com/android/settingslib/development/DevelopmentSettingsEnabler.java
@@ -19,6 +19,7 @@
import android.content.Context;
import android.content.Intent;
import android.os.Build;
+import android.os.UserManager;
import android.provider.Settings;
import android.support.v4.content.LocalBroadcastManager;
@@ -27,7 +28,8 @@
public static final String DEVELOPMENT_SETTINGS_CHANGED_ACTION =
"com.android.settingslib.development.DevelopmentSettingsEnabler.SETTINGS_CHANGED";
- private DevelopmentSettingsEnabler() {}
+ private DevelopmentSettingsEnabler() {
+ }
public static void setDevelopmentSettingsEnabled(Context context, boolean enable) {
Settings.Global.putInt(context.getContentResolver(),
@@ -37,8 +39,14 @@
}
public static boolean isDevelopmentSettingsEnabled(Context context) {
- return Settings.Global.getInt(context.getContentResolver(),
+ final UserManager um = (UserManager) context.getSystemService(Context.USER_SERVICE);
+ final boolean settingEnabled = Settings.Global.getInt(context.getContentResolver(),
Settings.Global.DEVELOPMENT_SETTINGS_ENABLED,
Build.TYPE.equals("eng") ? 1 : 0) != 0;
+ final boolean hasRestriction = um.hasUserRestriction(
+ UserManager.DISALLOW_DEBUGGING_FEATURES);
+ final boolean isAdmin = um.isAdminUser();
+
+ return isAdmin && !hasRestriction && settingEnabled;
}
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerActivity.java b/packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerActivity.java
index 190f5e6..68ead09 100644
--- a/packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerActivity.java
+++ b/packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerActivity.java
@@ -17,7 +17,6 @@
import android.annotation.LayoutRes;
import android.annotation.Nullable;
-import android.app.ActionBar;
import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
@@ -72,9 +71,9 @@
requestWindowFeature(Window.FEATURE_NO_TITLE);
}
super.setContentView(R.layout.settings_with_drawer);
- mContentHeaderContainer = (FrameLayout) findViewById(R.id.content_header_container);
+ mContentHeaderContainer = findViewById(R.id.content_header_container);
- Toolbar toolbar = (Toolbar) findViewById(R.id.action_bar);
+ Toolbar toolbar = findViewById(R.id.action_bar);
if (theme.getBoolean(android.R.styleable.Theme_windowNoTitle, false)) {
toolbar.setVisibility(View.GONE);
return;
@@ -89,7 +88,9 @@
@Override
public boolean onNavigateUp() {
- finish();
+ if (!super.onNavigateUp()) {
+ finish();
+ }
return true;
}
@@ -104,11 +105,6 @@
registerReceiver(mPackageReceiver, filter);
new CategoriesUpdateTask().execute();
- final Intent intent = getIntent();
- if (intent != null && intent.getBooleanExtra(EXTRA_SHOW_MENU, false)) {
- // Intent explicitly set to show menu.
- showMenuIcon();
- }
}
@Override
@@ -125,13 +121,6 @@
mCategoryListeners.remove(listener);
}
- public void setContentHeaderView(View headerView) {
- mContentHeaderContainer.removeAllViews();
- if (headerView != null) {
- mContentHeaderContainer.addView(headerView);
- }
- }
-
@Override
public void setContentView(@LayoutRes int layoutResID) {
final ViewGroup parent = findViewById(R.id.content_frame);
@@ -151,13 +140,6 @@
((ViewGroup) findViewById(R.id.content_frame)).addView(view, params);
}
- private void showMenuIcon() {
- final ActionBar actionBar = getActionBar();
- if (actionBar != null) {
- actionBar.setDisplayHomeAsUpEnabled(true);
- }
- }
-
private void onCategoriesChanged() {
final int N = mCategoryListeners.size();
for (int i = 0; i < N; i++) {
@@ -165,10 +147,6 @@
}
}
- public void onProfileTileOpen() {
- finish();
- }
-
/**
* @return whether or not the enabled state actually changed.
*/
diff --git a/packages/SettingsLib/src/com/android/settingslib/net/DataUsageController.java b/packages/SettingsLib/src/com/android/settingslib/net/DataUsageController.java
index ed3696c..f7aa297 100644
--- a/packages/SettingsLib/src/com/android/settingslib/net/DataUsageController.java
+++ b/packages/SettingsLib/src/com/android/settingslib/net/DataUsageController.java
@@ -158,6 +158,9 @@
usage.startDate = start;
usage.usageLevel = totalBytes;
usage.period = formatDateRange(start, end);
+ usage.cycleStart = start;
+ usage.cycleEnd = end;
+
if (policy != null) {
usage.limitLevel = policy.limitBytes > 0 ? policy.limitBytes : 0;
usage.warningLevel = policy.warningBytes > 0 ? policy.warningBytes : 0;
@@ -245,6 +248,8 @@
public long limitLevel;
public long warningLevel;
public long usageLevel;
+ public long cycleStart;
+ public long cycleEnd;
}
public interface Callback {
diff --git a/packages/SettingsLib/src/com/android/settingslib/utils/PowerUtil.java b/packages/SettingsLib/src/com/android/settingslib/utils/PowerUtil.java
new file mode 100644
index 0000000..346ca66
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/utils/PowerUtil.java
@@ -0,0 +1,143 @@
+/*
+ * Copyright (C) 2018 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.utils;
+
+import android.content.Context;
+import android.icu.text.MeasureFormat;
+import android.icu.text.MeasureFormat.FormatWidth;
+import android.icu.util.Measure;
+import android.icu.util.MeasureUnit;
+import android.support.annotation.Nullable;
+import android.text.TextUtils;
+import com.android.settingslib.R;
+import com.android.settingslib.utils.StringUtil;
+import java.util.Locale;
+import java.util.concurrent.TimeUnit;
+
+/** Utility class for keeping power related strings consistent**/
+public class PowerUtil {
+ private static final long SEVEN_MINUTES_MILLIS = TimeUnit.MINUTES.toMillis(7);
+ private static final long FIFTEEN_MINUTES_MILLIS = TimeUnit.MINUTES.toMillis(15);
+ private static final long ONE_DAY_MILLIS = TimeUnit.DAYS.toMillis(1);
+
+ /**
+ * This method produces the text used in various places throughout the system to describe the
+ * remaining battery life of the phone in a consistent manner.
+ *
+ * @param context
+ * @param drainTimeMs The estimated time remaining before the phone dies in milliseconds.
+ * @param percentageString An optional percentage of battery remaining string.
+ * @param basedOnUsage Whether this estimate is based on usage or simple extrapolation.
+ * @return a properly formatted and localized string describing how much time remains
+ * before the battery runs out.
+ */
+ public static String getBatteryRemainingStringFormatted(Context context, long drainTimeMs,
+ @Nullable String percentageString, boolean basedOnUsage) {
+ if (drainTimeMs > 0) {
+ if (drainTimeMs <= SEVEN_MINUTES_MILLIS) {
+ // show a imminent shutdown warning if less than 7 minutes remain
+ return getShutdownImminentString(context, percentageString);
+ } else if (drainTimeMs <= FIFTEEN_MINUTES_MILLIS) {
+ // show a less than 15 min remaining warning if appropriate
+ CharSequence timeString = StringUtil.formatElapsedTime(context,
+ FIFTEEN_MINUTES_MILLIS,
+ false /* withSeconds */);
+ return getUnderFifteenString(context, timeString, percentageString);
+ } else if (drainTimeMs >= ONE_DAY_MILLIS) {
+ // just say more than one day if over 24 hours
+ return getMoreThanOneDayString(context, percentageString);
+ } else {
+ // show a regular time remaining string
+ return getRegularTimeRemainingString(context, drainTimeMs,
+ percentageString, basedOnUsage);
+ }
+ }
+ return null;
+ }
+
+ private static String getShutdownImminentString(Context context, String percentageString) {
+ return TextUtils.isEmpty(percentageString)
+ ? context.getString(R.string.power_remaining_duration_only_shutdown_imminent)
+ : context.getString(
+ R.string.power_remaining_duration_shutdown_imminent,
+ percentageString);
+ }
+
+ private static String getUnderFifteenString(Context context, CharSequence timeString,
+ String percentageString) {
+ return TextUtils.isEmpty(percentageString)
+ ? context.getString(R.string.power_remaining_less_than_duration_only, timeString)
+ : context.getString(
+ R.string.power_remaining_less_than_duration,
+ percentageString,
+ timeString);
+
+ }
+
+ private static String getMoreThanOneDayString(Context context, String percentageString) {
+ final Locale currentLocale = context.getResources().getConfiguration().getLocales().get(0);
+ final MeasureFormat frmt = MeasureFormat.getInstance(currentLocale, FormatWidth.SHORT);
+
+ final Measure daysMeasure = new Measure(1, MeasureUnit.DAY);
+
+ return TextUtils.isEmpty(percentageString)
+ ? context.getString(R.string.power_remaining_only_more_than_subtext,
+ frmt.formatMeasures(daysMeasure))
+ : context.getString(
+ R.string.power_remaining_more_than_subtext,
+ percentageString,
+ frmt.formatMeasures(daysMeasure));
+ }
+
+ private static String getRegularTimeRemainingString(Context context, long drainTimeMs,
+ String percentageString, boolean basedOnUsage) {
+ // round to the nearest 15 min to not appear oversly precise
+ final long roundedTimeMs = roundToNearestThreshold(drainTimeMs,
+ FIFTEEN_MINUTES_MILLIS);
+ CharSequence timeString = StringUtil.formatElapsedTime(context,
+ roundedTimeMs,
+ false /* withSeconds */);
+ if (TextUtils.isEmpty(percentageString)) {
+ int id = basedOnUsage
+ ? R.string.power_remaining_duration_only_enhanced
+ : R.string.power_remaining_duration_only;
+ return context.getString(id, timeString);
+ } else {
+ int id = basedOnUsage
+ ? R.string.power_discharging_duration_enhanced
+ : R.string.power_discharging_duration;
+ return context.getString(id, percentageString, timeString);
+ }
+ }
+
+ public static long convertUsToMs(long timeUs) {
+ return timeUs / 1000;
+ }
+
+ public static long convertMsToUs(long timeMs) {
+ return timeMs * 1000;
+ }
+
+ private static long roundToNearestThreshold(long drainTime, long threshold) {
+ final long remainder = drainTime % threshold;
+ if (remainder < threshold / 2) {
+ return drainTime - remainder;
+ } else {
+ return drainTime - remainder + threshold;
+ }
+ }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/utils/StringUtil.java b/packages/SettingsLib/src/com/android/settingslib/utils/StringUtil.java
new file mode 100644
index 0000000..45fdd78
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/utils/StringUtil.java
@@ -0,0 +1,149 @@
+/*
+ * Copyright (C) 2018 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.utils;
+
+import android.content.Context;
+import android.icu.text.MeasureFormat;
+import android.icu.text.MeasureFormat.FormatWidth;
+import android.icu.text.RelativeDateTimeFormatter;
+import android.icu.text.RelativeDateTimeFormatter.RelativeUnit;
+import android.icu.util.Measure;
+import android.icu.util.MeasureUnit;
+import android.icu.util.ULocale;
+import android.text.SpannableStringBuilder;
+import android.text.Spanned;
+import android.text.style.TtsSpan;
+import java.util.ArrayList;
+import java.util.Locale;
+
+/** Utility class for generally useful string methods **/
+public class StringUtil {
+
+ public static final int SECONDS_PER_MINUTE = 60;
+ public static final int SECONDS_PER_HOUR = 60 * 60;
+ public static final int SECONDS_PER_DAY = 24 * 60 * 60;
+
+ /**
+ * Returns elapsed time for the given millis, in the following format:
+ * 2d 5h 40m 29s
+ * @param context the application context
+ * @param millis the elapsed time in milli seconds
+ * @param withSeconds include seconds?
+ * @return the formatted elapsed time
+ */
+ public static CharSequence formatElapsedTime(Context context, double millis,
+ boolean withSeconds) {
+ SpannableStringBuilder sb = new SpannableStringBuilder();
+ int seconds = (int) Math.floor(millis / 1000);
+ if (!withSeconds) {
+ // Round up.
+ seconds += 30;
+ }
+
+ int days = 0, hours = 0, minutes = 0;
+ if (seconds >= SECONDS_PER_DAY) {
+ days = seconds / SECONDS_PER_DAY;
+ seconds -= days * SECONDS_PER_DAY;
+ }
+ if (seconds >= SECONDS_PER_HOUR) {
+ hours = seconds / SECONDS_PER_HOUR;
+ seconds -= hours * SECONDS_PER_HOUR;
+ }
+ if (seconds >= SECONDS_PER_MINUTE) {
+ minutes = seconds / SECONDS_PER_MINUTE;
+ seconds -= minutes * SECONDS_PER_MINUTE;
+ }
+
+ final ArrayList<Measure> measureList = new ArrayList(4);
+ if (days > 0) {
+ measureList.add(new Measure(days, MeasureUnit.DAY));
+ }
+ if (hours > 0) {
+ measureList.add(new Measure(hours, MeasureUnit.HOUR));
+ }
+ if (minutes > 0) {
+ measureList.add(new Measure(minutes, MeasureUnit.MINUTE));
+ }
+ if (withSeconds && seconds > 0) {
+ measureList.add(new Measure(seconds, MeasureUnit.SECOND));
+ }
+ if (measureList.size() == 0) {
+ // Everything addable was zero, so nothing was added. We add a zero.
+ measureList.add(new Measure(0, withSeconds ? MeasureUnit.SECOND : MeasureUnit.MINUTE));
+ }
+ final Measure[] measureArray = measureList.toArray(new Measure[measureList.size()]);
+
+ final Locale locale = context.getResources().getConfiguration().locale;
+ final MeasureFormat measureFormat = MeasureFormat.getInstance(
+ locale, FormatWidth.NARROW);
+ sb.append(measureFormat.formatMeasures(measureArray));
+
+ if (measureArray.length == 1 && MeasureUnit.MINUTE.equals(measureArray[0].getUnit())) {
+ // Add ttsSpan if it only have minute value, because it will be read as "meters"
+ final TtsSpan ttsSpan = new TtsSpan.MeasureBuilder().setNumber(minutes)
+ .setUnit("minute").build();
+ sb.setSpan(ttsSpan, 0, sb.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ }
+
+ return sb;
+ }
+
+ /**
+ * Returns relative time for the given millis in the past, in a short format such as "2 days
+ * ago", "5 hr. ago", "40 min. ago", or "29 sec. ago".
+ *
+ * <p>The unit is chosen to have good information value while only using one unit. So 27 hours
+ * and 50 minutes would be formatted as "28 hr. ago", while 50 hours would be formatted as
+ * "2 days ago".
+ *
+ * @param context the application context
+ * @param millis the elapsed time in milli seconds
+ * @param withSeconds include seconds?
+ * @return the formatted elapsed time
+ */
+ public static CharSequence formatRelativeTime(Context context, double millis,
+ boolean withSeconds) {
+ final int seconds = (int) Math.floor(millis / 1000);
+ final RelativeUnit unit;
+ final int value;
+ if (withSeconds && seconds < 2 * SECONDS_PER_MINUTE) {
+ unit = RelativeUnit.SECONDS;
+ value = seconds;
+ } else if (seconds < 2 * SECONDS_PER_HOUR) {
+ unit = RelativeUnit.MINUTES;
+ value = (seconds + SECONDS_PER_MINUTE / 2)
+ / SECONDS_PER_MINUTE;
+ } else if (seconds < 2 * SECONDS_PER_DAY) {
+ unit = RelativeUnit.HOURS;
+ value = (seconds + SECONDS_PER_HOUR / 2)
+ / SECONDS_PER_HOUR;
+ } else {
+ unit = RelativeUnit.DAYS;
+ value = (seconds + SECONDS_PER_DAY / 2)
+ / SECONDS_PER_DAY;
+ }
+
+ final Locale locale = context.getResources().getConfiguration().locale;
+ final RelativeDateTimeFormatter formatter = RelativeDateTimeFormatter.getInstance(
+ ULocale.forLocale(locale),
+ null /* default NumberFormat */,
+ RelativeDateTimeFormatter.Style.SHORT,
+ android.icu.text.DisplayContext.CAPITALIZATION_FOR_MIDDLE_OF_SENTENCE);
+
+ return formatter.format(value, RelativeDateTimeFormatter.Direction.LAST, unit);
+ }
+}
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/drawer/SettingsDrawerActivityTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/drawer/SettingsDrawerActivityTest.java
index 003f905..2f417ad 100644
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/drawer/SettingsDrawerActivityTest.java
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/drawer/SettingsDrawerActivityTest.java
@@ -18,8 +18,6 @@
import static android.support.test.espresso.Espresso.onView;
import static android.support.test.espresso.assertion.ViewAssertions.doesNotExist;
-import static android.support.test.espresso.assertion.ViewAssertions.matches;
-import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
import static android.support.test.espresso.matcher.ViewMatchers.withContentDescription;
import android.app.Instrumentation;
@@ -49,42 +47,22 @@
}
@Test
- public void startActivityWithNoExtra_showNoNavUp() {
- Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
- instrumentation.startActivitySync(new Intent(instrumentation.getTargetContext(),
- TestActivity.class));
-
- onView(withContentDescription(com.android.internal.R.string.action_bar_up_description))
- .check(doesNotExist());
- }
-
- @Test
- public void startActivityWithExtraToHideMenu_showNavUp() {
- Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
- Intent intent = new Intent(instrumentation.getTargetContext(), TestActivity.class)
- .putExtra(TestActivity.EXTRA_SHOW_MENU, false);
+ public void startActivity_doNotShowNavUp() {
+ final Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
+ final Intent intent = new Intent(instrumentation.getTargetContext(), TestActivity.class)
+ .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
instrumentation.startActivitySync(intent);
onView(withContentDescription(com.android.internal.R.string.action_bar_up_description))
.check(doesNotExist());
}
- @Test
- public void startActivityWithExtraToShowMenu_showNavUp() {
- Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
- Intent intent = new Intent(instrumentation.getTargetContext(), TestActivity.class)
- .putExtra(TestActivity.EXTRA_SHOW_MENU, true);
- instrumentation.startActivitySync(intent);
-
- onView(withContentDescription(com.android.internal.R.string.action_bar_up_description))
- .check(matches(isDisplayed()));
- }
-
/**
* Test Activity in this test.
*
* Use this activity because SettingsDrawerActivity hasn't been registered in its
* AndroidManifest.xml
*/
- public static class TestActivity extends SettingsDrawerActivity {}
+ public static class TestActivity extends SettingsDrawerActivity {
+ }
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java
index 4091ce1..1481161 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java
@@ -80,22 +80,12 @@
doAnswer((invocation) -> mBatteryLevel).when(mCachedDevice).getBatteryLevel();
}
- /**
- * Test to verify the current test context object works so that we are not checking null
- * against null
- */
- @Test
- public void testContextMock() {
- assertThat(mContext.getString(R.string.bluetooth_connected)).isEqualTo("Connected");
- }
-
@Test
public void testGetConnectionSummary_testSingleProfileConnectDisconnect() {
// Test without battery level
// Set PAN profile to be connected and test connection state summary
mCachedDevice.onProfileStateChanged(mPanProfile, BluetoothProfile.STATE_CONNECTED);
- assertThat(mCachedDevice.getConnectionSummary()).isEqualTo(mContext.getString(
- R.string.bluetooth_connected));
+ assertThat(mCachedDevice.getConnectionSummary()).isEqualTo("Connected");
// Set PAN profile to be disconnected and test connection state summary
mCachedDevice.onProfileStateChanged(mPanProfile, BluetoothProfile.STATE_DISCONNECTED);
@@ -105,9 +95,7 @@
mBatteryLevel = 10;
// Set PAN profile to be connected and test connection state summary
mCachedDevice.onProfileStateChanged(mPanProfile, BluetoothProfile.STATE_CONNECTED);
- assertThat(mCachedDevice.getConnectionSummary()).isEqualTo(mContext.getString(
- R.string.bluetooth_connected_battery_level,
- com.android.settingslib.Utils.formatPercentage(mBatteryLevel)));
+ assertThat(mCachedDevice.getConnectionSummary()).isEqualTo("Connected, battery 10%");
// Set PAN profile to be disconnected and test connection state summary
mCachedDevice.onProfileStateChanged(mPanProfile, BluetoothProfile.STATE_DISCONNECTED);
@@ -118,8 +106,7 @@
// Set PAN profile to be connected and test connection state summary
mCachedDevice.onProfileStateChanged(mPanProfile, BluetoothProfile.STATE_CONNECTED);
- assertThat(mCachedDevice.getConnectionSummary()).isEqualTo(mContext.getString(
- R.string.bluetooth_connected));
+ assertThat(mCachedDevice.getConnectionSummary()).isEqualTo("Connected");
// Set PAN profile to be disconnected and test connection state summary
mCachedDevice.onProfileStateChanged(mPanProfile, BluetoothProfile.STATE_DISCONNECTED);
@@ -134,28 +121,23 @@
mCachedDevice.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_CONNECTED);
mCachedDevice.onProfileStateChanged(mA2dpProfile, BluetoothProfile.STATE_CONNECTED);
mCachedDevice.onProfileStateChanged(mPanProfile, BluetoothProfile.STATE_CONNECTED);
- assertThat(mCachedDevice.getConnectionSummary()).isEqualTo(mContext.getString(
- R.string.bluetooth_connected_battery_level,
- com.android.settingslib.Utils.formatPercentage(mBatteryLevel)));
+ assertThat(mCachedDevice.getConnectionSummary()).isEqualTo("Connected, battery 10%");
// Disconnect HFP only and test connection state summary
mCachedDevice.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_DISCONNECTED);
- assertThat(mCachedDevice.getConnectionSummary()).isEqualTo(mContext.getString(
- R.string.bluetooth_connected_no_headset_battery_level,
- com.android.settingslib.Utils.formatPercentage(mBatteryLevel)));
+ assertThat(mCachedDevice.getConnectionSummary()).isEqualTo(
+ "Connected (no phone), battery 10%");
// Disconnect A2DP only and test connection state summary
mCachedDevice.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_CONNECTED);
mCachedDevice.onProfileStateChanged(mA2dpProfile, BluetoothProfile.STATE_DISCONNECTED);
- assertThat(mCachedDevice.getConnectionSummary()).isEqualTo(mContext.getString(
- R.string.bluetooth_connected_no_a2dp_battery_level,
- com.android.settingslib.Utils.formatPercentage(mBatteryLevel)));
+ assertThat(mCachedDevice.getConnectionSummary()).isEqualTo(
+ "Connected (no media), battery 10%");
// Disconnect both HFP and A2DP and test connection state summary
mCachedDevice.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_DISCONNECTED);
- assertThat(mCachedDevice.getConnectionSummary()).isEqualTo(mContext.getString(
- R.string.bluetooth_connected_no_headset_no_a2dp_battery_level,
- com.android.settingslib.Utils.formatPercentage(mBatteryLevel)));
+ assertThat(mCachedDevice.getConnectionSummary()).isEqualTo(
+ "Connected (no phone or media), battery 10%");
// Disconnect all profiles and test connection state summary
mCachedDevice.onProfileStateChanged(mPanProfile, BluetoothProfile.STATE_DISCONNECTED);
@@ -163,6 +145,117 @@
}
@Test
+ public void testGetConnectionSummary_testSingleProfileActiveDeviceA2dp() {
+ // Test without battery level
+ // Set A2DP profile to be connected and test connection state summary
+ mCachedDevice.onProfileStateChanged(mA2dpProfile, BluetoothProfile.STATE_CONNECTED);
+ assertThat(mCachedDevice.getConnectionSummary()).isEqualTo("Connected");
+
+ // Set device as Active for A2DP and test connection state summary
+ mCachedDevice.setActiveDevice(true, BluetoothProfile.A2DP);
+ assertThat(mCachedDevice.getConnectionSummary()).isEqualTo("Connected, active(media)");
+
+ // Test with battery level
+ mBatteryLevel = 10;
+ assertThat(mCachedDevice.getConnectionSummary()).isEqualTo(
+ "Connected, battery 10%, active(media)");
+
+ // Set A2DP profile to be disconnected and test connection state summary
+ mCachedDevice.onProfileStateChanged(mA2dpProfile, BluetoothProfile.STATE_DISCONNECTED);
+ assertThat(mCachedDevice.getConnectionSummary()).isNull();
+
+ // Test with BluetoothDevice.BATTERY_LEVEL_UNKNOWN battery level
+ mBatteryLevel = BluetoothDevice.BATTERY_LEVEL_UNKNOWN;
+ // Set A2DP profile to be connected, Active and test connection state summary
+ mCachedDevice.onProfileStateChanged(mA2dpProfile, BluetoothProfile.STATE_CONNECTED);
+ mCachedDevice.setActiveDevice(true, BluetoothProfile.A2DP);
+ assertThat(mCachedDevice.getConnectionSummary()).isEqualTo("Connected, active(media)");
+
+ // Set A2DP profile to be disconnected and test connection state summary
+ mCachedDevice.onProfileStateChanged(mA2dpProfile, BluetoothProfile.STATE_DISCONNECTED);
+ assertThat(mCachedDevice.getConnectionSummary()).isNull();
+ }
+
+ @Test
+ public void testGetConnectionSummary_testSingleProfileActiveDeviceHfp() {
+ // Test without battery level
+ // Set HFP profile to be connected and test connection state summary
+ mCachedDevice.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_CONNECTED);
+ assertThat(mCachedDevice.getConnectionSummary()).isEqualTo("Connected");
+
+ // Set device as Active for HFP and test connection state summary
+ mCachedDevice.setActiveDevice(true, BluetoothProfile.HEADSET);
+ assertThat(mCachedDevice.getConnectionSummary()).isEqualTo("Connected, active(phone)");
+
+ // Test with battery level
+ mBatteryLevel = 10;
+ assertThat(mCachedDevice.getConnectionSummary()).isEqualTo(
+ "Connected, battery 10%, active(phone)");
+
+ // Set HFP profile to be disconnected and test connection state summary
+ mCachedDevice.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_DISCONNECTED);
+ assertThat(mCachedDevice.getConnectionSummary()).isNull();
+
+ // Test with BluetoothDevice.BATTERY_LEVEL_UNKNOWN battery level
+ mBatteryLevel = BluetoothDevice.BATTERY_LEVEL_UNKNOWN;
+ // Set HFP profile to be connected, Active and test connection state summary
+ mCachedDevice.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_CONNECTED);
+ mCachedDevice.setActiveDevice(true, BluetoothProfile.HEADSET);
+ assertThat(mCachedDevice.getConnectionSummary()).isEqualTo("Connected, active(phone)");
+
+ // Set HFP profile to be disconnected and test connection state summary
+ mCachedDevice.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_DISCONNECTED);
+ assertThat(mCachedDevice.getConnectionSummary()).isNull();
+ }
+
+ @Test
+ public void testGetConnectionSummary_testMultipleProfilesActiveDevice() {
+ // Test without battery level
+ // Set A2DP and HFP profiles to be connected and test connection state summary
+ mCachedDevice.onProfileStateChanged(mA2dpProfile, BluetoothProfile.STATE_CONNECTED);
+ mCachedDevice.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_CONNECTED);
+ assertThat(mCachedDevice.getConnectionSummary()).isEqualTo("Connected");
+
+ // Set device as Active for A2DP and HFP and test connection state summary
+ mCachedDevice.setActiveDevice(true, BluetoothProfile.A2DP);
+ mCachedDevice.setActiveDevice(true, BluetoothProfile.HEADSET);
+ assertThat(mCachedDevice.getConnectionSummary()).isEqualTo("Connected, active");
+
+ // Test with battery level
+ mBatteryLevel = 10;
+ assertThat(mCachedDevice.getConnectionSummary()).isEqualTo(
+ "Connected, battery 10%, active");
+
+ // Disconnect A2DP only and test connection state summary
+ mCachedDevice.setActiveDevice(false, BluetoothProfile.A2DP);
+ mCachedDevice.onProfileStateChanged(mA2dpProfile, BluetoothProfile.STATE_DISCONNECTED);
+ assertThat(mCachedDevice.getConnectionSummary()).isEqualTo(
+ "Connected (no media), battery 10%, active(phone)");
+
+ // Disconnect HFP only and test connection state summary
+ mCachedDevice.setActiveDevice(false, BluetoothProfile.HEADSET);
+ mCachedDevice.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_DISCONNECTED);
+ mCachedDevice.onProfileStateChanged(mA2dpProfile, BluetoothProfile.STATE_CONNECTED);
+ mCachedDevice.setActiveDevice(true, BluetoothProfile.A2DP);
+ assertThat(mCachedDevice.getConnectionSummary()).isEqualTo(
+ "Connected (no phone), battery 10%, active(media)");
+
+ // Test with BluetoothDevice.BATTERY_LEVEL_UNKNOWN battery level
+ mBatteryLevel = BluetoothDevice.BATTERY_LEVEL_UNKNOWN;
+ // Set A2DP and HFP profiles to be connected, Active and test connection state summary
+ mCachedDevice.onProfileStateChanged(mA2dpProfile, BluetoothProfile.STATE_CONNECTED);
+ mCachedDevice.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_CONNECTED);
+ mCachedDevice.setActiveDevice(true, BluetoothProfile.A2DP);
+ mCachedDevice.setActiveDevice(true, BluetoothProfile.HEADSET);
+ assertThat(mCachedDevice.getConnectionSummary()).isEqualTo("Connected, active");
+
+ // Set A2DP and HFP profiles to be disconnected and test connection state summary
+ mCachedDevice.onProfileStateChanged(mA2dpProfile, BluetoothProfile.STATE_DISCONNECTED);
+ mCachedDevice.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_DISCONNECTED);
+ assertThat(mCachedDevice.getConnectionSummary()).isNull();
+ }
+
+ @Test
public void testDeviceName_testAliasNameAvailable() {
when(mDevice.getAliasName()).thenReturn(DEVICE_ALIAS);
when(mDevice.getName()).thenReturn(DEVICE_NAME);
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/DevelopmentSettingsEnablerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/DevelopmentSettingsEnablerTest.java
index d19d19a..708353e 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/DevelopmentSettingsEnablerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/DevelopmentSettingsEnablerTest.java
@@ -23,7 +23,9 @@
import com.android.settingslib.SettingsLibRobolectricTestRunner;
import com.android.settingslib.TestConfig;
+import com.android.settingslib.testutils.shadow.ShadowUserManager;
+import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -31,7 +33,9 @@
import org.robolectric.annotation.Config;
@RunWith(SettingsLibRobolectricTestRunner.class)
-@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION, shadows = {
+ ShadowUserManager.class
+})
public class DevelopmentSettingsEnablerTest {
private Context mContext;
@@ -39,10 +43,16 @@
@Before
public void setUp() {
mContext = RuntimeEnvironment.application;
+ ShadowUserManager.getShadow().setIsAdminUser(true);
+ }
+
+ @After
+ public void tearDown() {
+ ShadowUserManager.getShadow().reset();
}
@Test
- public void testEnabling() {
+ public void isEnabled_settingsOn_noRestriction_isAdmin_shouldReturnTrue() {
Settings.Global.putInt(mContext.getContentResolver(),
Settings.Global.DEVELOPMENT_SETTINGS_ENABLED, 0);
@@ -54,7 +64,7 @@
}
@Test
- public void testDisabling() {
+ public void isEnabled_settingsOff_noRestriction_isAdmin_shouldReturnFalse() {
Settings.Global.putInt(mContext.getContentResolver(),
Settings.Global.DEVELOPMENT_SETTINGS_ENABLED, 1);
@@ -64,4 +74,13 @@
assertThat(DevelopmentSettingsEnabler.isDevelopmentSettingsEnabled(mContext)).isFalse();
}
+
+ @Test
+ public void isEnabled_settingsOn_noRestriction_notAdmin_shouldReturnFalse() {
+ Settings.Global.putInt(mContext.getContentResolver(),
+ Settings.Global.DEVELOPMENT_SETTINGS_ENABLED, 1);
+ ShadowUserManager.getShadow().setIsAdminUser(false);
+
+ assertThat(DevelopmentSettingsEnabler.isDevelopmentSettingsEnabled(mContext)).isFalse();
+ }
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/testutils/shadow/ShadowUserManager.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/testutils/shadow/ShadowUserManager.java
new file mode 100644
index 0000000..c8b3269
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/testutils/shadow/ShadowUserManager.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2018 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.testutils.shadow;
+
+import android.content.Context;
+import android.os.UserManager;
+
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Implementation;
+import org.robolectric.annotation.Implements;
+import org.robolectric.annotation.Resetter;
+import org.robolectric.shadow.api.Shadow;
+
+@Implements(UserManager.class)
+public class ShadowUserManager extends org.robolectric.shadows.ShadowUserManager {
+
+ private boolean mAdminUser;
+
+ public void setIsAdminUser(boolean isAdminUser) {
+ mAdminUser = isAdminUser;
+ }
+
+ @Resetter
+ public void reset() {
+ mAdminUser = false;
+ }
+
+ @Implementation
+ public boolean isAdminUser() {
+ return mAdminUser;
+ }
+
+ @Implementation
+ public static UserManager get(Context context) {
+ return (UserManager) context.getSystemService(Context.USER_SERVICE);
+ }
+
+ public static ShadowUserManager getShadow() {
+ return (ShadowUserManager) Shadow.extract(
+ RuntimeEnvironment.application.getSystemService(UserManager.class));
+ }
+}
\ No newline at end of file
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/PowerUtilTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/PowerUtilTest.java
new file mode 100644
index 0000000..f93210f
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/PowerUtilTest.java
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 2018 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.utils;
+
+import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Mockito.spy;
+
+import android.content.Context;
+import com.android.settingslib.R;
+import com.android.settingslib.SettingsLibRobolectricTestRunner;
+import com.android.settingslib.TestConfig;
+import com.android.settingslib.utils.PowerUtil;
+import java.time.Duration;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+
+@RunWith(SettingsLibRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
+public class PowerUtilTest {
+ public static final String TEST_BATTERY_LEVEL_10 = "10%";
+ public static final String FIFTEEN_MIN_FORMATTED = "15m";
+ public static final long SEVENTEEN_MIN_MILLIS = Duration.ofMinutes(17).toMillis();
+ public static final long FIVE_MINUTES_MILLIS = Duration.ofMinutes(5).toMillis();
+ public static final long TEN_MINUTES_MILLIS = Duration.ofMinutes(10).toMillis();
+ public static final long TWO_DAYS_MILLIS = Duration.ofDays(2).toMillis();
+ public static final String ONE_DAY_FORMATTED = "1 day";
+
+ private Context mContext;
+
+ @Before
+ public void setup() {
+ MockitoAnnotations.initMocks(this);
+ mContext = spy(RuntimeEnvironment.application);
+ }
+
+ @Test
+ public void testGetBatteryRemainingStringFormatted_moreThanFifteenMinutes_withPercentage() {
+ String info = PowerUtil.getBatteryRemainingStringFormatted(mContext,
+ SEVENTEEN_MIN_MILLIS,
+ TEST_BATTERY_LEVEL_10,
+ true /* basedOnUsage */);
+ String info2 = PowerUtil.getBatteryRemainingStringFormatted(mContext,
+ SEVENTEEN_MIN_MILLIS,
+ TEST_BATTERY_LEVEL_10,
+ false /* basedOnUsage */);
+
+ // We only add special mention for the long string
+ assertThat(info).isEqualTo(mContext.getString(
+ R.string.power_discharging_duration_enhanced,
+ TEST_BATTERY_LEVEL_10,
+ FIFTEEN_MIN_FORMATTED));
+ // shortened string should not have extra text
+ assertThat(info2).isEqualTo(mContext.getString(
+ R.string.power_discharging_duration,
+ TEST_BATTERY_LEVEL_10,
+ FIFTEEN_MIN_FORMATTED));
+ }
+
+ @Test
+ public void testGetBatteryRemainingStringFormatted_moreThanFifteenMinutes_noPercentage() {
+ String info = PowerUtil.getBatteryRemainingStringFormatted(mContext,
+ SEVENTEEN_MIN_MILLIS,
+ null /* percentageString */,
+ true /* basedOnUsage */);
+ String info2 = PowerUtil.getBatteryRemainingStringFormatted(mContext,
+ SEVENTEEN_MIN_MILLIS,
+ null /* percentageString */,
+ false /* basedOnUsage */);
+
+ // We only add special mention for the long string
+ assertThat(info).isEqualTo(mContext.getString(
+ R.string.power_remaining_duration_only_enhanced,
+ FIFTEEN_MIN_FORMATTED));
+ // shortened string should not have extra text
+ assertThat(info2).isEqualTo(mContext.getString(
+ R.string.power_remaining_duration_only,
+ FIFTEEN_MIN_FORMATTED));
+ }
+
+
+ @Test
+ public void testGetBatteryRemainingStringFormatted_lessThanSevenMinutes_usesCorrectString() {
+ String info = PowerUtil.getBatteryRemainingStringFormatted(mContext,
+ FIVE_MINUTES_MILLIS,
+ TEST_BATTERY_LEVEL_10 /* percentageString */,
+ true /* basedOnUsage */);
+ String info2 = PowerUtil.getBatteryRemainingStringFormatted(mContext,
+ FIVE_MINUTES_MILLIS,
+ null /* percentageString */,
+ true /* basedOnUsage */);
+
+ // additional battery percentage in this string
+ assertThat(info).isEqualTo(mContext.getString(
+ R.string.power_remaining_duration_shutdown_imminent,
+ TEST_BATTERY_LEVEL_10));
+ // shortened string should not have percentage
+ assertThat(info2).isEqualTo(mContext.getString(
+ R.string.power_remaining_duration_only_shutdown_imminent));
+ }
+
+ @Test
+ public void testGetBatteryRemainingStringFormatted_betweenSevenAndFifteenMinutes_usesCorrectString() {
+ String info = PowerUtil.getBatteryRemainingStringFormatted(mContext,
+ TEN_MINUTES_MILLIS,
+ null /* percentageString */,
+ true /* basedOnUsage */);
+ String info2 = PowerUtil.getBatteryRemainingStringFormatted(mContext,
+ TEN_MINUTES_MILLIS,
+ TEST_BATTERY_LEVEL_10 /* percentageString */,
+ true /* basedOnUsage */);
+
+ // shortened string should not have percentage
+ assertThat(info).isEqualTo(mContext.getString(
+ R.string.power_remaining_less_than_duration_only,
+ FIFTEEN_MIN_FORMATTED));
+ // Add percentage to string when provided
+ assertThat(info2).isEqualTo(mContext.getString(
+ R.string.power_remaining_less_than_duration,
+ TEST_BATTERY_LEVEL_10,
+ FIFTEEN_MIN_FORMATTED));
+ }
+
+ @Test
+ public void testGetBatteryRemainingStringFormatted_moreThanOneDay_usesCorrectString() {
+ String info = PowerUtil.getBatteryRemainingStringFormatted(mContext,
+ TWO_DAYS_MILLIS,
+ null /* percentageString */,
+ true /* basedOnUsage */);
+ String info2 = PowerUtil.getBatteryRemainingStringFormatted(mContext,
+ TWO_DAYS_MILLIS,
+ TEST_BATTERY_LEVEL_10 /* percentageString */,
+ true /* basedOnUsage */);
+
+ // shortened string should not have percentage
+ assertThat(info).isEqualTo(mContext.getString(
+ R.string.power_remaining_only_more_than_subtext,
+ ONE_DAY_FORMATTED));
+ // Add percentage to string when provided
+ assertThat(info2).isEqualTo(mContext.getString(
+ R.string.power_remaining_more_than_subtext,
+ TEST_BATTERY_LEVEL_10,
+ ONE_DAY_FORMATTED));
+ }
+}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/StringUtilTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/StringUtilTest.java
new file mode 100644
index 0000000..d5e3cdb
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/StringUtilTest.java
@@ -0,0 +1,213 @@
+/*
+ * Copyright (C) 2018 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.utils;
+
+import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Mockito.spy;
+
+import android.content.Context;
+import android.text.SpannableStringBuilder;
+import android.text.format.DateUtils;
+import android.text.style.TtsSpan;
+import com.android.settingslib.SettingsLibRobolectricTestRunner;
+import com.android.settingslib.TestConfig;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+
+@RunWith(SettingsLibRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
+public class StringUtilTest {
+ private Context mContext;
+
+ @Before
+ public void setUp() {
+ mContext = spy(RuntimeEnvironment.application);
+ }
+
+ @Test
+ public void testFormatElapsedTime_WithSeconds_ShowSeconds() {
+ final double testMillis = 5 * DateUtils.MINUTE_IN_MILLIS + 30 * DateUtils.SECOND_IN_MILLIS;
+ final String expectedTime = "5m 30s";
+
+ assertThat(StringUtil.formatElapsedTime(mContext, testMillis, true).toString())
+ .isEqualTo(expectedTime);
+ }
+
+ @Test
+ public void testFormatElapsedTime_NoSeconds_DoNotShowSeconds() {
+ final double testMillis = 5 * DateUtils.MINUTE_IN_MILLIS + 30 * DateUtils.SECOND_IN_MILLIS;
+ final String expectedTime = "6m";
+
+ assertThat(StringUtil.formatElapsedTime(mContext, testMillis, false).toString())
+ .isEqualTo(expectedTime);
+ }
+
+ @Test
+ public void testFormatElapsedTime_TimeMoreThanOneDay_ShowCorrectly() {
+ final double testMillis = 2 * DateUtils.DAY_IN_MILLIS
+ + 4 * DateUtils.HOUR_IN_MILLIS + 15 * DateUtils.MINUTE_IN_MILLIS;
+ final String expectedTime = "2d 4h 15m";
+
+ assertThat(StringUtil.formatElapsedTime(mContext, testMillis, false).toString())
+ .isEqualTo(expectedTime);
+ }
+
+ @Test
+ public void testFormatElapsedTime_ZeroFieldsInTheMiddleDontShow() {
+ final double testMillis = 2 * DateUtils.DAY_IN_MILLIS + 15 * DateUtils.MINUTE_IN_MILLIS;
+ final String expectedTime = "2d 15m";
+
+ assertThat(StringUtil.formatElapsedTime(mContext, testMillis, false).toString())
+ .isEqualTo(expectedTime);
+ }
+
+ @Test
+ public void testFormatElapsedTime_FormatZero_WithSeconds() {
+ final double testMillis = 0;
+ final String expectedTime = "0s";
+
+ assertThat(StringUtil.formatElapsedTime(mContext, testMillis, true).toString())
+ .isEqualTo(expectedTime);
+ }
+
+ @Test
+ public void testFormatElapsedTime_FormatZero_NoSeconds() {
+ final double testMillis = 0;
+ final String expectedTime = "0m";
+
+ assertThat(StringUtil.formatElapsedTime(mContext, testMillis, false).toString())
+ .isEqualTo(expectedTime);
+ }
+
+ @Test
+ public void testFormatElapsedTime_onlyContainsMinute_hasTtsSpan() {
+ final double testMillis = 15 * DateUtils.MINUTE_IN_MILLIS;
+
+ final CharSequence charSequence =
+ StringUtil.formatElapsedTime(mContext, testMillis, false);
+ assertThat(charSequence).isInstanceOf(SpannableStringBuilder.class);
+
+ final SpannableStringBuilder expectedString = (SpannableStringBuilder) charSequence;
+ final TtsSpan[] ttsSpans = expectedString.getSpans(0, expectedString.length(),
+ TtsSpan.class);
+
+ assertThat(ttsSpans).asList().hasSize(1);
+ assertThat(ttsSpans[0].getType()).isEqualTo(TtsSpan.TYPE_MEASURE);
+ }
+
+ @Test
+ public void testFormatRelativeTime_WithSeconds_ShowSeconds() {
+ final double testMillis = 40 * DateUtils.SECOND_IN_MILLIS;
+ final String expectedTime = "40 sec. ago";
+
+ assertThat(StringUtil.formatRelativeTime(mContext, testMillis, true).toString()).isEqualTo(
+ expectedTime);
+ }
+
+ @Test
+ public void testFormatRelativeTime_NoSeconds_DoNotShowSeconds() {
+ final double testMillis = 40 * DateUtils.SECOND_IN_MILLIS;
+ final String expectedTime = "1 min. ago";
+
+ assertThat(StringUtil.formatRelativeTime(mContext, testMillis, false).toString()).isEqualTo(
+ expectedTime);
+ }
+
+ @Test
+ public void testFormatRelativeTime_LessThanTwoMinutes_withSeconds() {
+ final double testMillis = 119 * DateUtils.SECOND_IN_MILLIS;
+ final String expectedTime = "119 sec. ago";
+
+ assertThat(StringUtil.formatRelativeTime(mContext, testMillis, true).toString()).isEqualTo(
+ expectedTime);
+ }
+
+ @Test
+ public void testFormatRelativeTime_LessThanTwoMinutes_NoSeconds() {
+ final double testMillis = 119 * DateUtils.SECOND_IN_MILLIS;
+ final String expectedTime = "2 min. ago";
+
+ assertThat(StringUtil.formatRelativeTime(mContext, testMillis, false).toString()).isEqualTo(
+ expectedTime);
+ }
+
+ @Test
+ public void testFormatRelativeTime_TwoMinutes_withSeconds() {
+ final double testMillis = 2 * DateUtils.MINUTE_IN_MILLIS;
+ final String expectedTime = "2 min. ago";
+
+ assertThat(StringUtil.formatRelativeTime(mContext, testMillis, true).toString()).isEqualTo(
+ expectedTime);
+ }
+
+ @Test
+ public void testFormatRelativeTime_LessThanTwoHours_withSeconds() {
+ final double testMillis = 119 * DateUtils.MINUTE_IN_MILLIS;
+ final String expectedTime = "119 min. ago";
+
+ assertThat(StringUtil.formatRelativeTime(mContext, testMillis, true).toString()).isEqualTo(
+ expectedTime);
+ }
+
+ @Test
+ public void testFormatRelativeTime_TwoHours_withSeconds() {
+ final double testMillis = 2 * DateUtils.HOUR_IN_MILLIS;
+ final String expectedTime = "2 hr. ago";
+
+ assertThat(StringUtil.formatRelativeTime(mContext, testMillis, true).toString()).isEqualTo(
+ expectedTime);
+ }
+
+ @Test
+ public void testFormatRelativeTime_LessThanTwoDays_withSeconds() {
+ final double testMillis = 47 * DateUtils.HOUR_IN_MILLIS;
+ final String expectedTime = "47 hr. ago";
+
+ assertThat(StringUtil.formatRelativeTime(mContext, testMillis, true).toString()).isEqualTo(
+ expectedTime);
+ }
+
+ @Test
+ public void testFormatRelativeTime_TwoDays_withSeconds() {
+ final double testMillis = 2 * DateUtils.DAY_IN_MILLIS;
+ final String expectedTime = "2 days ago";
+
+ assertThat(StringUtil.formatRelativeTime(mContext, testMillis, true).toString()).isEqualTo(
+ expectedTime);
+ }
+
+ @Test
+ public void testFormatRelativeTime_FormatZero_WithSeconds() {
+ final double testMillis = 0;
+ final String expectedTime = "0 sec. ago";
+
+ assertThat(StringUtil.formatRelativeTime(mContext, testMillis, true).toString()).isEqualTo(
+ expectedTime);
+ }
+
+ @Test
+ public void testFormatRelativeTime_FormatZero_NoSeconds() {
+ final double testMillis = 0;
+ final String expectedTime = "0 min. ago";
+
+ assertThat(StringUtil.formatRelativeTime(mContext, testMillis, false).toString()).isEqualTo(
+ expectedTime);
+ }
+}
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 64b2ae6..79299aa 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -44,6 +44,7 @@
<uses-permission android:name="android.permission.MANAGE_NETWORK_POLICY" />
<uses-permission android:name="android.permission.MANAGE_USB" />
<uses-permission android:name="android.permission.USE_RESERVED_DISK" />
+ <uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
<!-- System tool permissions granted to the shell. -->
<uses-permission android:name="android.permission.REAL_GET_TASKS" />
<uses-permission android:name="android.permission.CHANGE_CONFIGURATION" />
diff --git a/packages/SystemUI/Android.mk b/packages/SystemUI/Android.mk
index 2bcf4ef..1e48213 100644
--- a/packages/SystemUI/Android.mk
+++ b/packages/SystemUI/Android.mk
@@ -56,7 +56,6 @@
SystemUI-proto
LOCAL_JAVA_LIBRARIES := telephony-common
-LOCAL_JAVA_LIBRARIES += android.car
LOCAL_PACKAGE_NAME := SystemUI
LOCAL_CERTIFICATE := platform
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 9613a6a..cbb3e8f 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -176,9 +176,6 @@
<!-- It's like, reality, but, you know, virtual -->
<uses-permission android:name="android.permission.ACCESS_VR_MANAGER" />
- <!-- To control car audio module volume -->
- <uses-permission android:name="android.car.permission.CAR_CONTROL_AUDIO_VOLUME" />
-
<!-- the ability to rename notifications posted by other apps -->
<uses-permission android:name="android.permission.SUBSTITUTE_NOTIFICATION_APP_NAME" />
diff --git a/packages/SystemUI/res-keyguard/values/dimens.xml b/packages/SystemUI/res-keyguard/values/dimens.xml
index 28a0935..4934e14 100644
--- a/packages/SystemUI/res-keyguard/values/dimens.xml
+++ b/packages/SystemUI/res-keyguard/values/dimens.xml
@@ -44,13 +44,13 @@
<!-- Default clock parameters -->
<dimen name="bottom_text_spacing_digital">-24dp</dimen>
<!-- Slice header -->
- <dimen name="widget_title_font_size">28sp</dimen>
+ <dimen name="widget_title_font_size">24sp</dimen>
<!-- Slice subtitle -->
<dimen name="widget_label_font_size">16sp</dimen>
<!-- Clock without header -->
<dimen name="widget_big_font_size">64dp</dimen>
<!-- Clock with header -->
- <dimen name="widget_small_font_size">22dp</dimen>
+ <dimen name="widget_small_font_size">24dp</dimen>
<!-- Dash between clock and header -->
<dimen name="widget_separator_width">16dp</dimen>
<dimen name="widget_separator_thickness">1dp</dimen>
diff --git a/packages/SystemUI/res/layout/volume_dialog.xml b/packages/SystemUI/res/layout/volume_dialog.xml
index 36298ca..bab4eba 100644
--- a/packages/SystemUI/res/layout/volume_dialog.xml
+++ b/packages/SystemUI/res/layout/volume_dialog.xml
@@ -15,8 +15,8 @@
-->
<com.android.systemui.volume.VolumeUiLayout
xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
android:background="@android:color/transparent"
android:theme="@style/qs_theme"
android:clipChildren="false" >
@@ -29,7 +29,6 @@
android:minWidth="@dimen/volume_dialog_panel_width"
android:background="@android:color/transparent"
android:layout_margin="@dimen/volume_dialog_base_margin"
- android:translationZ="8dp"
android:orientation="vertical"
android:clipChildren="false" >
@@ -42,7 +41,7 @@
android:paddingTop="10dp"
android:paddingBottom="10dp"
android:background="@drawable/rounded_bg_full"
- android:translationZ="8dp"
+ android:translationZ="@dimen/volume_panel_elevation"
android:orientation="horizontal" >
<!-- volume rows added and removed here! :-) -->
</LinearLayout>
@@ -59,7 +58,7 @@
android:background="@drawable/rounded_bg_full"
android:gravity="center"
android:layout_gravity="end"
- android:translationZ="8dp"
+ android:translationZ="@dimen/volume_panel_elevation"
android:clickable="true"
android:orientation="vertical" >
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index e55c65a..c351b94 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -704,6 +704,8 @@
<dimen name="volume_expander_margin_end">2dp</dimen>
<dimen name="volume_expander_margin_top">6dp</dimen>
+ <dimen name="volume_panel_elevation">8dp</dimen>
+
<!-- Padding between icon and text for managed profile toast -->
<dimen name="managed_profile_toast_padding">4dp</dimen>
@@ -901,13 +903,28 @@
<dimen name="fingerprint_dialog_fp_icon_size">60dp</dimen>
<dimen name="fingerprint_dialog_animation_translation_offset">350dp</dimen>
- <!-- WirelessCharging Animation values -->
- <!-- Starting text size of batteryLevel for wireless charging animation -->
- <dimen name="config_batteryLevelTextSizeStart" format="float">5.0</dimen>
- <!-- Ending text size of batteryLevel for wireless charging animation -->
- <dimen name="config_batteryLevelTextSizeEnd" format="float">32.0</dimen>
- <!-- Wireless Charging battery level text animation duration -->
- <integer name="config_batteryLevelTextAnimationDuration">400</integer>
+ <!-- Wireless Charging Animation values -->
+ <dimen name="wireless_charging_dots_radius_start">0dp</dimen>
+ <dimen name="wireless_charging_dots_radius_end">4dp</dimen>
+ <dimen name="wireless_charging_circle_radius_start">28dp</dimen>
+ <dimen name="wireless_charging_circle_radius_end">92dp</dimen>
+ <integer name="wireless_charging_angle_offset">20</integer>
+ <integer name="wireless_charging_scale_dots_duration">83</integer>
+ <integer name="wireless_charging_num_dots">20</integer>
+ <!-- Starting text size in dp of batteryLevel for wireless charging animation -->
+ <dimen name="wireless_charging_anim_battery_level_text_size_start">0dp</dimen>
+ <!-- Ending text size in dp of batteryLevel for wireless charging animation -->
+ <dimen name="wireless_charging_anim_battery_level_text_size_end">14dp</dimen>
+ <!-- time until battery info is at full opacity-->
+ <integer name="wireless_charging_anim_opacity_offset">80</integer>
+ <!-- duration batteryLevel opacity goes from 0 to 1 duration -->
+ <integer name="wireless_charging_battery_level_text_opacity_duration">117</integer>
+ <!-- battery text scale animation duration -->
+ <integer name="wireless_charging_battery_level_text_scale_animation_duration">367</integer>
+ <!--time until wireless charging animation starts to fade-->
+ <integer name="wireless_charging_fade_offset">920</integer>
+ <!-- duration wireless charging animation takes to full fade to 0 opacity -->
+ <integer name="wireless_charging_fade_duration">200</integer>
<!-- Wired charging on AOD, text animation duration -->
<integer name="wired_charging_aod_text_animation_duration_down">500</integer>
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl
index 64fa9c6..7f382ac 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl
@@ -21,9 +21,40 @@
oneway interface IOverviewProxy {
void onBind(in ISystemUiProxy sysUiProxy);
+
+ /**
+ * Proxies motion events from the nav bar in SystemUI to the OverviewProxyService. The sender
+ * guarantees the following order of events:
+ *
+ * Normal gesture: DOWN, (MOVE/POINTER_DOWN/POINTER_UP)*, UP
+ * Quick switch: DOWN, (MOVE/POINTER_DOWN/POINTER_UP)*, SWITCH
+ * Quick scrub: DOWN, (MOVE/POINTER_DOWN/POINTER_UP)*, SCRUB_START, SCRUB_PROGRESS*, SCRUB_END
+ *
+ * Once quick switch/scrub is sent, then no further motion events will be provided.
+ */
void onMotionEvent(in MotionEvent event);
+
+ /**
+ * Sent when a user has quickly flinged on the nav bar to switch tasks. Once this event is sent
+ * the caller will stop sending any motion events and will no longer preemptively cancel any
+ * recents animations started as a part of the motion event handling.
+ */
void onQuickSwitch();
+
+ /**
+ * Sent when the user starts to actively scrub the nav bar to switch tasks. Once this event is
+ * sent the caller will stop sending any motion events and will no longer preemptively cancel
+ * any recents animations started as a part of the motion event handling.
+ */
void onQuickScrubStart();
+
+ /**
+ * Sent when the user stops actively scrubbing the nav bar to switch tasks.
+ */
void onQuickScrubEnd();
+
+ /**
+ * Sent for each movement over the nav bar while the user is scrubbing it to switch tasks.
+ */
void onQuickScrubProgress(float progress);
}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
index 90e3b1e..62bd72f 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
@@ -43,6 +43,7 @@
import android.content.res.Resources;
import android.content.res.Resources.NotFoundException;
import android.graphics.Bitmap;
+import android.graphics.Rect;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
@@ -57,6 +58,7 @@
import android.view.IRecentsAnimationRunner;
import android.view.RemoteAnimationTarget;
+import android.view.WindowManagerGlobal;
import com.android.systemui.shared.recents.model.Task;
import com.android.systemui.shared.recents.model.Task.TaskKey;
import com.android.systemui.shared.recents.model.ThumbnailData;
@@ -271,11 +273,20 @@
runner = new IRecentsAnimationRunner.Stub() {
public void onAnimationStart(IRecentsAnimationController controller,
RemoteAnimationTarget[] apps) {
+ final Rect stableInsets = new Rect();
+ WindowManagerWrapper.getInstance().getStableInsets(stableInsets);
+ onAnimationStart_New(controller, apps, stableInsets, null);
+ }
+
+ public void onAnimationStart_New(IRecentsAnimationController controller,
+ RemoteAnimationTarget[] apps, Rect homeContentInsets,
+ Rect minimizedHomeBounds) {
final RecentsAnimationControllerCompat controllerCompat =
new RecentsAnimationControllerCompat(controller);
final RemoteAnimationTargetCompat[] appsCompat =
RemoteAnimationTargetCompat.wrap(apps);
- animationHandler.onAnimationStart(controllerCompat, appsCompat);
+ animationHandler.onAnimationStart(controllerCompat, appsCompat,
+ homeContentInsets, minimizedHomeBounds);
}
public void onAnimationCanceled() {
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationListener.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationListener.java
index bf6179d..a473db1 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationListener.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationListener.java
@@ -16,13 +16,15 @@
package com.android.systemui.shared.system;
+import android.graphics.Rect;
+
public interface RecentsAnimationListener {
/**
* Called when the animation into Recents can start. This call is made on the binder thread.
*/
void onAnimationStart(RecentsAnimationControllerCompat controller,
- RemoteAnimationTargetCompat[] apps);
+ RemoteAnimationTargetCompat[] apps, Rect homeContentInsets, Rect minimizedHomeBounds);
/**
* Called when the animation into Recents was canceled. This call is made on the binder thread.
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java
index 3871980..b8c5049 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java
@@ -16,6 +16,9 @@
package com.android.systemui.shared.system;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
+
+import android.app.WindowConfiguration;
import android.graphics.Point;
import android.graphics.Rect;
import android.view.RemoteAnimationTarget;
@@ -37,7 +40,10 @@
public final Point position;
public final Rect sourceContainerBounds;
+ private final RemoteAnimationTarget mTarget;
+
public RemoteAnimationTargetCompat(RemoteAnimationTarget app) {
+ mTarget = app;
taskId = app.taskId;
mode = app.mode;
leash = new SurfaceControlCompat(app.leash);
@@ -56,4 +62,18 @@
}
return appsCompat;
}
+
+ /**
+ * TODO: Get as a method for compatibility (will move into ctor once Launcher updates)
+ */
+ public Rect getContentInsets() {
+ return mTarget.contentInsets;
+ }
+
+ /**
+ * TODO: Get as a method for compatibility (will move into ctor once Launcher updates)
+ */
+ public boolean isAssistantActivityType() {
+ return mTarget.windowConfiguration.getActivityType() == ACTIVITY_TYPE_ASSISTANT;
+ }
}
\ No newline at end of file
diff --git a/packages/SystemUI/shared/tests/Android.mk b/packages/SystemUI/shared/tests/Android.mk
index 239a4e3..1715983 100644
--- a/packages/SystemUI/shared/tests/Android.mk
+++ b/packages/SystemUI/shared/tests/Android.mk
@@ -41,7 +41,7 @@
testables \
truth-prebuilt \
-LOCAL_JAVA_LIBRARIES := android.test.runner telephony-common android.car
+LOCAL_JAVA_LIBRARIES := android.test.runner telephony-common
# sign this with platform cert, so this test is allowed to inject key events into
# UI it doesn't own. This is necessary to allow screenshots to be taken
diff --git a/packages/SystemUI/src/com/android/systemui/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/OverviewProxyService.java
index b30b0c5..d0128ef 100644
--- a/packages/SystemUI/src/com/android/systemui/OverviewProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/OverviewProxyService.java
@@ -99,9 +99,7 @@
public void onRecentsAnimationStarted() {
long token = Binder.clearCallingIdentity();
try {
- mHandler.post(() -> {
- notifyRecentsAnimationStarted();
- });
+ mHandler.post(OverviewProxyService.this::notifyRecentsAnimationStarted);
} finally {
Binder.restoreCallingIdentity(token);
}
diff --git a/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingAnimation.java b/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingAnimation.java
index 348855b..afc9629 100644
--- a/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingAnimation.java
+++ b/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingAnimation.java
@@ -19,7 +19,6 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
-import android.content.res.Configuration;
import android.graphics.PixelFormat;
import android.os.Handler;
import android.os.Looper;
@@ -36,13 +35,18 @@
*/
public class WirelessChargingAnimation {
- public static final long DURATION = 1400;
+ public static final long DURATION = 1133;
private static final String TAG = "WirelessChargingView";
private static final boolean LOCAL_LOGV = false;
private final WirelessChargingView mCurrentWirelessChargingView;
private static WirelessChargingView mPreviousWirelessChargingView;
+ public interface Callback {
+ void onAnimationStarting();
+ void onAnimationEnded();
+ }
+
/**
* Constructs an empty WirelessChargingAnimation object. If looper is null,
* Looper.myLooper() is used. Must set
@@ -51,9 +55,9 @@
* @hide
*/
public WirelessChargingAnimation(@NonNull Context context, @Nullable Looper looper, int
- batteryLevel) {
+ batteryLevel, Callback callback) {
mCurrentWirelessChargingView = new WirelessChargingView(context, looper,
- batteryLevel);
+ batteryLevel, callback);
}
/**
@@ -61,8 +65,8 @@
* @hide
*/
public static WirelessChargingAnimation makeWirelessChargingAnimation(@NonNull Context context,
- @Nullable Looper looper, int batteryLevel) {
- return new WirelessChargingAnimation(context, looper, batteryLevel);
+ @Nullable Looper looper, int batteryLevel, Callback callback) {
+ return new WirelessChargingAnimation(context, looper, batteryLevel, callback);
}
/**
@@ -95,8 +99,11 @@
private View mView;
private View mNextView;
private WindowManager mWM;
+ private Callback mCallback;
- public WirelessChargingView(Context context, @Nullable Looper looper, int batteryLevel) {
+ public WirelessChargingView(Context context, @Nullable Looper looper, int batteryLevel,
+ Callback callback) {
+ mCallback = callback;
mNextView = new WirelessChargingLayout(context, batteryLevel);
mGravity = Gravity.CENTER_HORIZONTAL | Gravity.CENTER;
@@ -149,6 +156,8 @@
}
public void hide(long duration) {
+ mHandler.removeMessages(HIDE);
+
if (LOCAL_LOGV) Log.v(TAG, "HIDE: " + this);
mHandler.sendMessageDelayed(Message.obtain(mHandler, HIDE), duration);
}
@@ -169,18 +178,6 @@
context = mView.getContext();
}
mWM = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
- // We can resolve the Gravity here by using the Locale for getting
- // the layout direction
- final Configuration config = mView.getContext().getResources().getConfiguration();
- final int gravity = Gravity.getAbsoluteGravity(mGravity,
- config.getLayoutDirection());
- mParams.gravity = gravity;
- if ((gravity & Gravity.HORIZONTAL_GRAVITY_MASK) == Gravity.FILL_HORIZONTAL) {
- mParams.horizontalWeight = 1.0f;
- }
- if ((gravity & Gravity.VERTICAL_GRAVITY_MASK) == Gravity.FILL_VERTICAL) {
- mParams.verticalWeight = 1.0f;
- }
mParams.packageName = packageName;
mParams.hideTimeoutMilliseconds = DURATION;
@@ -191,6 +188,9 @@
if (LOCAL_LOGV) Log.v(TAG, "ADD! " + mView + " in " + this);
try {
+ if (mCallback != null) {
+ mCallback.onAnimationStarting();
+ }
mWM.addView(mView, mParams);
} catch (WindowManager.BadTokenException e) {
Slog.d(TAG, "Unable to add wireless charging view. " + e);
@@ -203,6 +203,9 @@
if (mView != null) {
if (mView.getParent() != null) {
if (LOCAL_LOGV) Log.v(TAG, "REMOVE! " + mView + " in " + this);
+ if (mCallback != null) {
+ mCallback.onAnimationEnded();
+ }
mWM.removeViewImmediate(mView);
}
diff --git a/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingLayout.java b/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingLayout.java
index c78ea56..8f87d64 100644
--- a/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingLayout.java
@@ -16,13 +16,16 @@
package com.android.systemui.charging;
+import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.content.Context;
import android.util.AttributeSet;
+import android.view.animation.PathInterpolator;
import android.widget.FrameLayout;
import android.widget.TextView;
+import com.android.systemui.Interpolators;
import com.android.systemui.R;
import java.text.NumberFormat;
@@ -52,10 +55,9 @@
init(c, attrs, -1);
}
- private void init(Context c, AttributeSet attrs, int batteryLevel) {
+ private void init(Context context, AttributeSet attrs, int batteryLevel) {
final int mBatteryLevel = batteryLevel;
-
- inflate(c, R.layout.wireless_charging_layout, this);
+ inflate(context, R.layout.wireless_charging_layout, this);
// where the circle animation occurs:
final WirelessChargingView mChargingView = findViewById(R.id.wireless_charging_view);
@@ -68,14 +70,57 @@
if (batteryLevel != UNKNOWN_BATTERY_LEVEL) {
mPercentage.setText(NumberFormat.getPercentInstance().format(mBatteryLevel / 100f));
-
- ValueAnimator animator = ObjectAnimator.ofFloat(mPercentage, "textSize",
- getContext().getResources().getFloat(R.dimen.config_batteryLevelTextSizeStart),
- getContext().getResources().getFloat(R.dimen.config_batteryLevelTextSizeEnd));
-
- animator.setDuration((long) getContext().getResources().getInteger(
- R.integer.config_batteryLevelTextAnimationDuration));
- animator.start();
+ mPercentage.setAlpha(0);
}
+
+ final long chargingAnimationFadeStartOffset = (long) context.getResources().getInteger(
+ R.integer.wireless_charging_fade_offset);
+ final long chargingAnimationFadeDuration = (long) context.getResources().getInteger(
+ R.integer.wireless_charging_fade_duration);
+ final int batteryLevelTextSizeStart = context.getResources().getDimensionPixelSize(
+ R.dimen.wireless_charging_anim_battery_level_text_size_start);
+ final int batteryLevelTextSizeEnd = context.getResources().getDimensionPixelSize(
+ R.dimen.wireless_charging_anim_battery_level_text_size_end);
+
+ // Animation Scale: battery percentage text scales from 0% to 100%
+ ValueAnimator textSizeAnimator = ObjectAnimator.ofFloat(mPercentage, "textSize",
+ batteryLevelTextSizeStart, batteryLevelTextSizeEnd);
+ textSizeAnimator.setInterpolator(new PathInterpolator(0, 0, 0, 1));
+ textSizeAnimator.setDuration((long) context.getResources().getInteger(
+ R.integer.wireless_charging_battery_level_text_scale_animation_duration));
+
+ // Animation Opacity: battery percentage text transitions from 0 to 1 opacity
+ ValueAnimator textOpacityAnimator = ObjectAnimator.ofFloat(mPercentage, "alpha", 0, 1);
+ textOpacityAnimator.setInterpolator(Interpolators.LINEAR);
+ textOpacityAnimator.setDuration((long) context.getResources().getInteger(
+ R.integer.wireless_charging_battery_level_text_opacity_duration));
+ textOpacityAnimator.setStartDelay((long) context.getResources().getInteger(
+ R.integer.wireless_charging_anim_opacity_offset));
+
+ // Animation Opacity: battery percentage text fades from 1 to 0 opacity
+ ValueAnimator textFadeAnimator = ObjectAnimator.ofFloat(mPercentage, "alpha", 1, 0);
+ textFadeAnimator.setDuration(chargingAnimationFadeDuration);
+ textFadeAnimator.setInterpolator(Interpolators.LINEAR);
+ textFadeAnimator.setStartDelay(chargingAnimationFadeStartOffset);
+
+ // Animation Opacity: wireless charging circle animation fades from 1 to 0 opacity
+ ValueAnimator circleFadeAnimator = ObjectAnimator.ofFloat(mChargingView, "alpha",
+ 1, 0);
+ circleFadeAnimator.setDuration(chargingAnimationFadeDuration);
+ circleFadeAnimator.setInterpolator(Interpolators.LINEAR);
+ circleFadeAnimator.setStartDelay(chargingAnimationFadeStartOffset);
+
+ // Animation Opacity: secondary text animation fades from 1 to 0 opacity
+ ValueAnimator secondaryTextFadeAnimator = ObjectAnimator.ofFloat(mSecondaryText, "alpha",
+ 1, 0);
+ circleFadeAnimator.setDuration(chargingAnimationFadeDuration);
+ secondaryTextFadeAnimator.setInterpolator(Interpolators.LINEAR);
+ secondaryTextFadeAnimator.setStartDelay(chargingAnimationFadeStartOffset);
+
+ // play all animations together
+ AnimatorSet animatorSet = new AnimatorSet();
+ animatorSet.playTogether(textSizeAnimator, textOpacityAnimator, textFadeAnimator,
+ circleFadeAnimator, secondaryTextFadeAnimator);
+ animatorSet.start();
}
-}
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingView.java b/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingView.java
index f5edf52..19c6dc1 100644
--- a/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingView.java
+++ b/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingView.java
@@ -21,10 +21,10 @@
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.View;
-import android.view.animation.DecelerateInterpolator;
import android.view.animation.Interpolator;
import com.android.settingslib.Utils;
+import com.android.systemui.Interpolators;
import com.android.systemui.R;
final class WirelessChargingView extends View {
@@ -33,21 +33,21 @@
private float mPathGone;
private float mInterpolatedPathGone;
private long mAnimationStartTime;
- private long mStartSpinCircleAnimationTime;
- private long mAnimationOffset = 500;
- private long mTotalAnimationDuration = WirelessChargingAnimation.DURATION - mAnimationOffset;
- private long mExpandingCircle = (long) (mTotalAnimationDuration * .9);
- private long mSpinCircleAnimationTime = mTotalAnimationDuration - mExpandingCircle;
+ private long mScaleDotsDuration;
- private boolean mFinishedAnimatingSpinningCircles = false;
+ private boolean mFinishedAnimatingDots = false;
+ private int mNumDots;
- private int mStartAngle = -90;
- private int mNumSmallCircles = 20;
- private int mSmallCircleRadius = 10;
+ private double mAngleOffset;
+ private double mCurrAngleOffset;
- private int mMainCircleStartRadius = 100;
- private int mMainCircleEndRadius = 230;
- private int mMainCircleCurrentRadius = mMainCircleStartRadius;
+ private int mDotsRadiusStart;
+ private int mDotsRadiusEnd;
+ private int mCurrDotRadius;
+
+ private int mMainCircleStartRadius;
+ private int mMainCircleEndRadius;
+ private int mMainCircleCurrentRadius;
private int mCenterX;
private int mCenterY;
@@ -72,8 +72,25 @@
public void init(Context context, AttributeSet attr) {
mContext = context;
+
+ mDotsRadiusStart = context.getResources().getDimensionPixelSize(
+ R.dimen.wireless_charging_dots_radius_start);
+ mDotsRadiusEnd = context.getResources().getDimensionPixelSize(
+ R.dimen.wireless_charging_dots_radius_end);
+
+ mMainCircleStartRadius = context.getResources().getDimensionPixelSize(
+ R.dimen.wireless_charging_circle_radius_start);
+ mMainCircleEndRadius = context.getResources().getDimensionPixelSize(
+ R.dimen.wireless_charging_circle_radius_end);
+ mMainCircleCurrentRadius = mMainCircleStartRadius;
+
+ mAngleOffset = context.getResources().getInteger(R.integer.wireless_charging_angle_offset);
+ mScaleDotsDuration = (long) context.getResources().getInteger(
+ R.integer.wireless_charging_scale_dots_duration);
+ mNumDots = context.getResources().getInteger(R.integer.wireless_charging_num_dots);
+
setupPaint();
- mInterpolator = new DecelerateInterpolator();
+ mInterpolator = Interpolators.LINEAR_OUT_SLOW_IN;
}
private void setupPaint() {
@@ -92,64 +109,62 @@
updateDrawingParameters();
drawCircles(canvas);
- if (!mFinishedAnimatingSpinningCircles) {
+ if (!mFinishedAnimatingDots) {
invalidate();
}
}
/**
* Draws a larger circle of radius {@link WirelessChargingView#mMainCircleEndRadius} composed of
- * {@link WirelessChargingView#mNumSmallCircles} smaller circles
+ * {@link WirelessChargingView#mNumDots} smaller circles
* @param canvas
*/
private void drawCircles(Canvas canvas) {
mCenterX = canvas.getWidth() / 2;
mCenterY = canvas.getHeight() / 2;
- // angleOffset makes small circles look like they're moving around the main circle
- float angleOffset = mPathGone * 10;
-
- // draws mNumSmallCircles to compose a larger, main circle
- for (int circle = 0; circle < mNumSmallCircles; circle++) {
- double angle = ((mStartAngle + angleOffset) * Math.PI / 180) + (circle * ((2 * Math.PI)
- / mNumSmallCircles));
+ // draws mNumDots to compose a larger, main circle
+ for (int circle = 0; circle < mNumDots; circle++) {
+ double angle = ((mCurrAngleOffset) * Math.PI / 180) + (circle * ((2 * Math.PI)
+ / mNumDots));
int x = (int) (mCenterX + Math.cos(angle) * (mMainCircleCurrentRadius +
- mSmallCircleRadius));
+ mCurrDotRadius));
int y = (int) (mCenterY + Math.sin(angle) * (mMainCircleCurrentRadius +
- mSmallCircleRadius));
+ mCurrDotRadius));
- canvas.drawCircle(x, y, mSmallCircleRadius, mPaint);
+ canvas.drawCircle(x, y, mCurrDotRadius, mPaint);
}
- if (mMainCircleCurrentRadius >= mMainCircleEndRadius && !isSpinCircleAnimationStarted()) {
- mStartSpinCircleAnimationTime = System.currentTimeMillis();
+ if (mMainCircleCurrentRadius >= mMainCircleEndRadius) {
+ mFinishedAnimatingDots = true;
}
-
- if (isSpinAnimationFinished()) {
- mFinishedAnimatingSpinningCircles = true;
- }
- }
-
- private boolean isSpinCircleAnimationStarted() {
- return mStartSpinCircleAnimationTime != 0;
- }
-
- private boolean isSpinAnimationFinished() {
- return isSpinCircleAnimationStarted() && System.currentTimeMillis() -
- mStartSpinCircleAnimationTime > mSpinCircleAnimationTime;
}
private void updateDrawingParameters() {
- mPathGone = getPathGone(System.currentTimeMillis());
+ long now = System.currentTimeMillis();
+ long timeSinceStart = now - mAnimationStartTime;
+ mPathGone = getPathGone(now);
mInterpolatedPathGone = mInterpolator.getInterpolation(mPathGone);
+ // Position Dots: update main circle radius (that the dots compose)
if (mPathGone < 1.0f) {
mMainCircleCurrentRadius = mMainCircleStartRadius + (int) (mInterpolatedPathGone *
(mMainCircleEndRadius - mMainCircleStartRadius));
} else {
mMainCircleCurrentRadius = mMainCircleEndRadius;
}
+
+ // Scale Dots: update dot radius
+ if (timeSinceStart < mScaleDotsDuration) {
+ mCurrDotRadius = mDotsRadiusStart + (int) (mInterpolator.getInterpolation((float)
+ timeSinceStart / mScaleDotsDuration) * (mDotsRadiusEnd - mDotsRadiusStart));
+ } else {
+ mCurrDotRadius = mDotsRadiusEnd;
+ }
+
+ // Rotation Dot Group: dots rotate from 0 to 20 degrees
+ mCurrAngleOffset = mAngleOffset * mPathGone;
}
/**
@@ -158,6 +173,6 @@
* For values > 1.0 the larger circle has been drawn and further animation can occur
*/
private float getPathGone(long now) {
- return (float) (now - mAnimationStartTime) / (mExpandingCircle);
+ return (float) (now - mAnimationStartTime) / (WirelessChargingAnimation.DURATION);
}
}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java b/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java
index bfb3a6e..092f3d2 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java
@@ -64,7 +64,7 @@
createDozeTriggers(context, sensorManager, host, alarmManager, config, params,
handler, wakeLock, machine),
createDozeUi(context, host, wakeLock, machine, handler, alarmManager, params),
- new DozeScreenState(wrappedService, handler),
+ new DozeScreenState(wrappedService, handler, params),
createDozeScreenBrightness(context, wrappedService, sensorManager, host, handler),
new DozeWallpaperState(context)
});
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java b/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java
index 8ec6afc..6ff8e3d 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java
@@ -24,6 +24,7 @@
import com.android.internal.hardware.AmbientDisplayConfiguration;
import com.android.internal.util.Preconditions;
+import com.android.systemui.statusbar.phone.DozeParameters;
import com.android.systemui.util.Assert;
import com.android.systemui.util.wakelock.WakeLock;
@@ -87,12 +88,11 @@
}
}
- int screenState() {
+ int screenState(DozeParameters parameters) {
switch (this) {
case UNINITIALIZED:
case INITIALIZED:
case DOZE:
- case DOZE_REQUEST_PULSE:
case DOZE_AOD_PAUSED:
return Display.STATE_OFF;
case DOZE_PULSING:
@@ -100,6 +100,9 @@
case DOZE_AOD:
case DOZE_AOD_PAUSING:
return Display.STATE_DOZE_SUSPEND;
+ case DOZE_REQUEST_PULSE:
+ return parameters.getDisplayNeedsBlanking() ? Display.STATE_OFF
+ : Display.STATE_ON;
default:
return Display.STATE_UNKNOWN;
}
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenState.java b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenState.java
index bef9cb3..3053de3 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenState.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenState.java
@@ -19,6 +19,8 @@
import android.os.Handler;
import android.view.Display;
+import com.android.systemui.statusbar.phone.DozeParameters;
+
/**
* Controls the screen when dozing.
*/
@@ -26,17 +28,20 @@
private final DozeMachine.Service mDozeService;
private final Handler mHandler;
private final Runnable mApplyPendingScreenState = this::applyPendingScreenState;
+ private final DozeParameters mParameters;
private int mPendingScreenState = Display.STATE_UNKNOWN;
- public DozeScreenState(DozeMachine.Service service, Handler handler) {
+ public DozeScreenState(DozeMachine.Service service, Handler handler,
+ DozeParameters parameters) {
mDozeService = service;
mHandler = handler;
+ mParameters = parameters;
}
@Override
public void transitionTo(DozeMachine.State oldState, DozeMachine.State newState) {
- int screenState = newState.screenState();
+ int screenState = newState.screenState(mParameters);
if (newState == DozeMachine.State.FINISH) {
// Make sure not to apply the screen state after DozeService was destroyed.
diff --git a/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java b/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java
index aa56694..3a2b12f 100644
--- a/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java
+++ b/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java
@@ -26,10 +26,6 @@
import android.content.DialogInterface.OnDismissListener;
import android.content.Intent;
import android.content.IntentFilter;
-import android.icu.text.MeasureFormat;
-import android.icu.text.MeasureFormat.FormatWidth;
-import android.icu.util.Measure;
-import android.icu.util.MeasureUnit;
import android.media.AudioAttributes;
import android.os.AsyncTask;
import android.os.Handler;
@@ -37,11 +33,11 @@
import android.os.PowerManager;
import android.os.UserHandle;
import android.support.annotation.VisibleForTesting;
-import android.text.format.DateUtils;
import android.util.Slog;
import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
import com.android.settingslib.Utils;
+import com.android.settingslib.utils.PowerUtil;
import com.android.systemui.R;
import com.android.systemui.SystemUI;
import com.android.systemui.statusbar.phone.SystemUIDialog;
@@ -49,8 +45,6 @@
import java.io.PrintWriter;
import java.text.NumberFormat;
-import java.util.Locale;
-import java.util.concurrent.TimeUnit;
public class PowerNotificationWarnings implements PowerUI.WarningsUI {
private static final String TAG = PowerUI.TAG + ".Notification";
@@ -200,12 +194,7 @@
// override notification copy if hybrid notification enabled
if (mEstimate != null) {
title = mContext.getString(R.string.battery_low_title_hybrid);
- contentText = mContext.getString(
- mEstimate.isBasedOnUsage
- ? R.string.battery_low_percent_format_hybrid
- : R.string.battery_low_percent_format_hybrid_short,
- percentage,
- getTimeRemainingFormatted());
+ contentText = getHybridContentString(percentage);
}
final Notification.Builder nb =
@@ -239,21 +228,12 @@
mNoMan.notifyAsUser(TAG_BATTERY, SystemMessage.NOTE_POWER_LOW, n, UserHandle.ALL);
}
- @VisibleForTesting
- String getTimeRemainingFormatted() {
- final Locale currentLocale = mContext.getResources().getConfiguration().getLocales().get(0);
- MeasureFormat frmt = MeasureFormat.getInstance(currentLocale, FormatWidth.NARROW);
-
- final long remainder = mEstimate.estimateMillis % DateUtils.HOUR_IN_MILLIS;
- final long hours = TimeUnit.MILLISECONDS.toHours(
- mEstimate.estimateMillis - remainder);
- // round down to the nearest 15 min for now to not appear overly precise
- final long minutes = TimeUnit.MILLISECONDS.toMinutes(
- remainder - (remainder % TimeUnit.MINUTES.toMillis(15)));
- final Measure hoursMeasure = new Measure(hours, MeasureUnit.HOUR);
- final Measure minutesMeasure = new Measure(minutes, MeasureUnit.MINUTE);
-
- return frmt.formatMeasures(hoursMeasure, minutesMeasure);
+ private String getHybridContentString(String percentage) {
+ return PowerUtil.getBatteryRemainingStringFormatted(
+ mContext,
+ mEstimate.estimateMillis,
+ percentage,
+ mEstimate.isBasedOnUsage);
}
private PendingIntent pendingBroadcast(String action) {
diff --git a/packages/SystemUI/src/com/android/systemui/power/PowerUI.java b/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
index ea2a432..ac86c8a 100644
--- a/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
+++ b/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
@@ -53,7 +53,6 @@
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.Arrays;
-import java.util.concurrent.TimeUnit;
public class PowerUI extends SystemUI {
static final String TAG = "PowerUI";
@@ -72,10 +71,11 @@
private final Configuration mLastConfiguration = new Configuration();
private int mBatteryLevel = 100;
private long mTimeRemaining = Long.MAX_VALUE;
- private int mBatteryStatus = BatteryManager.BATTERY_STATUS_UNKNOWN;
private int mPlugType = 0;
private int mInvalidCharger = 0;
private EnhancedEstimates mEnhancedEstimates;
+ private boolean mLowWarningShownThisChargeCycle;
+ private boolean mSevereWarningShownThisChargeCycle;
private int mLowBatteryAlertCloseLevel;
private final int[] mLowBatteryReminderLevels = new int[2];
@@ -88,6 +88,8 @@
private long mNextLogTime;
private IThermalService mThermalService;
+ @VisibleForTesting int mBatteryStatus = BatteryManager.BATTERY_STATUS_UNKNOWN;
+
// by using the same instance (method references are not guaranteed to be the same object
// We create a method reference here so that we are guaranteed that we can remove a callback
// each time they are created).
@@ -218,6 +220,12 @@
final boolean plugged = mPlugType != 0;
final boolean oldPlugged = oldPlugType != 0;
+ // if we are now unplugged but we were previously plugged in we should allow the
+ // time based trigger again.
+ if (!plugged && plugged != oldPlugged) {
+ mLowWarningShownThisChargeCycle = false;
+ mSevereWarningShownThisChargeCycle = false;
+ }
int oldBucket = findBatteryLevelBucket(oldBatteryLevel);
int bucket = findBatteryLevelBucket(mBatteryLevel);
@@ -268,7 +276,6 @@
boolean isPowerSaver = mPowerManager.isPowerSaveMode();
// only play SFX when the dialog comes up or the bucket changes
final boolean playSound = bucket != oldBucket || oldPlugged;
- long oldTimeRemaining = mTimeRemaining;
if (mEnhancedEstimates.isHybridNotificationEnabled()) {
final Estimate estimate = mEnhancedEstimates.getEstimate();
// Turbo is not always booted once SysUI is running so we have ot make sure we actually
@@ -281,10 +288,18 @@
}
}
- if (shouldShowLowBatteryWarning(plugged, oldPlugged, oldBucket, bucket, oldTimeRemaining,
- mTimeRemaining,
- isPowerSaver, mBatteryStatus)) {
+ if (shouldShowLowBatteryWarning(plugged, oldPlugged, oldBucket, bucket,
+ mTimeRemaining, isPowerSaver, mBatteryStatus)) {
mWarnings.showLowBatteryWarning(playSound);
+
+ // mark if we've already shown a warning this cycle. This will prevent the time based
+ // trigger from spamming users since the time remaining can vary based on current
+ // device usage.
+ if (mTimeRemaining < mEnhancedEstimates.getSevereWarningThreshold()) {
+ mSevereWarningShownThisChargeCycle = true;
+ } else {
+ mLowWarningShownThisChargeCycle = true;
+ }
} else if (shouldDismissLowBatteryWarning(plugged, oldBucket, bucket, mTimeRemaining,
isPowerSaver)) {
mWarnings.dismissLowBatteryWarning();
@@ -295,22 +310,14 @@
@VisibleForTesting
boolean shouldShowLowBatteryWarning(boolean plugged, boolean oldPlugged, int oldBucket,
- int bucket, long oldTimeRemaining, long timeRemaining,
- boolean isPowerSaver, int mBatteryStatus) {
+ int bucket, long timeRemaining, boolean isPowerSaver, int mBatteryStatus) {
return !plugged
&& !isPowerSaver
&& (((bucket < oldBucket || oldPlugged) && bucket < 0)
- || (mEnhancedEstimates.isHybridNotificationEnabled()
- && timeRemaining < mEnhancedEstimates.getLowWarningThreshold()
- && isHourLess(oldTimeRemaining, timeRemaining)))
+ || isTimeBasedTrigger(timeRemaining))
&& mBatteryStatus != BatteryManager.BATTERY_STATUS_UNKNOWN;
}
- private boolean isHourLess(long oldTimeRemaining, long timeRemaining) {
- final long dif = oldTimeRemaining - timeRemaining;
- return dif >= TimeUnit.HOURS.toMillis(1);
- }
-
@VisibleForTesting
boolean shouldDismissLowBatteryWarning(boolean plugged, int oldBucket, int bucket,
long timeRemaining, boolean isPowerSaver) {
@@ -323,6 +330,23 @@
|| hybridWouldDismiss));
}
+ private boolean isTimeBasedTrigger(long timeRemaining) {
+ if (!mEnhancedEstimates.isHybridNotificationEnabled()) {
+ return false;
+ }
+
+ // Only show the time based warning once per charge cycle
+ final boolean canShowWarning = timeRemaining < mEnhancedEstimates.getLowWarningThreshold()
+ && !mLowWarningShownThisChargeCycle;
+
+ // Only show the severe time based warning once per charge cycle
+ final boolean canShowSevereWarning =
+ timeRemaining < mEnhancedEstimates.getSevereWarningThreshold()
+ && !mSevereWarningShownThisChargeCycle;
+
+ return canShowWarning || canShowSevereWarning;
+ }
+
private void initTemperatureWarning() {
ContentResolver resolver = mContext.getContentResolver();
Resources resources = mContext.getResources();
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSScrollLayout.java b/packages/SystemUI/src/com/android/systemui/qs/QSScrollLayout.java
index a44fd9a..7b1509d 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSScrollLayout.java
@@ -14,8 +14,11 @@
package com.android.systemui.qs;
+import android.animation.ObjectAnimator;
import android.content.Context;
+import android.graphics.Canvas;
import android.support.v4.widget.NestedScrollView;
+import android.util.Property;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
@@ -23,6 +26,8 @@
import android.widget.LinearLayout;
import com.android.systemui.R;
+import com.android.systemui.qs.touch.OverScroll;
+import com.android.systemui.qs.touch.SwipeDetector;
/**
* Quick setting scroll view containing the brightness slider and the QS tiles.
@@ -35,6 +40,9 @@
private final int mTouchSlop;
private final int mFooterHeight;
private int mLastMotionY;
+ private final SwipeDetector mSwipeDetector;
+ private final OverScrollHelper mOverScrollHelper;
+ private float mContentTranslationY;
public QSScrollLayout(Context context, View... children) {
super(context);
@@ -49,23 +57,35 @@
linearLayout.addView(view);
}
addView(linearLayout);
+ setOverScrollMode(OVER_SCROLL_NEVER);
+ mOverScrollHelper = new OverScrollHelper();
+ mSwipeDetector = new SwipeDetector(context, mOverScrollHelper, SwipeDetector.VERTICAL);
+ mSwipeDetector.setDetectableScrollConditions(SwipeDetector.DIRECTION_BOTH, true);
}
-
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
- if (canScrollVertically(1) || canScrollVertically(-1)) {
- return super.onInterceptTouchEvent(ev);
+ if (!canScrollVertically(1) && !canScrollVertically(-1)) {
+ return false;
}
- return false;
+ mSwipeDetector.onTouchEvent(ev);
+ return super.onInterceptTouchEvent(ev) || mOverScrollHelper.isInOverScroll();
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
- if (canScrollVertically(1) || canScrollVertically(-1)) {
- return super.onTouchEvent(ev);
+ if (!canScrollVertically(1) && !canScrollVertically(-1)) {
+ return false;
}
- return false;
+ mSwipeDetector.onTouchEvent(ev);
+ return super.onTouchEvent(ev);
+ }
+
+ @Override
+ protected void dispatchDraw(Canvas canvas) {
+ canvas.translate(0, mContentTranslationY);
+ super.dispatchDraw(canvas);
+ canvas.translate(0, -mContentTranslationY);
}
public boolean shouldIntercept(MotionEvent ev) {
@@ -98,4 +118,81 @@
parent.requestDisallowInterceptTouchEvent(disallowIntercept);
}
}
+
+ private void setContentTranslationY(float contentTranslationY) {
+ mContentTranslationY = contentTranslationY;
+ invalidate();
+ }
+
+ private static final Property<QSScrollLayout, Float> CONTENT_TRANS_Y =
+ new Property<QSScrollLayout, Float>(Float.class, "qsScrollLayoutContentTransY") {
+ @Override
+ public Float get(QSScrollLayout qsScrollLayout) {
+ return qsScrollLayout.mContentTranslationY;
+ }
+
+ @Override
+ public void set(QSScrollLayout qsScrollLayout, Float y) {
+ qsScrollLayout.setContentTranslationY(y);
+ }
+ };
+
+ private class OverScrollHelper implements SwipeDetector.Listener {
+ private boolean mIsInOverScroll;
+
+ // We use this value to calculate the actual amount the user has overscrolled.
+ private float mFirstDisplacement = 0;
+
+ @Override
+ public void onDragStart(boolean start) {}
+
+ @Override
+ public boolean onDrag(float displacement, float velocity) {
+ // Only overscroll if the user is scrolling down when they're already at the bottom
+ // or scrolling up when they're already at the top.
+ boolean wasInOverScroll = mIsInOverScroll;
+ mIsInOverScroll = (!canScrollVertically(1) && displacement < 0) ||
+ (!canScrollVertically(-1) && displacement > 0);
+
+ if (wasInOverScroll && !mIsInOverScroll) {
+ // Exit overscroll. This can happen when the user is in overscroll and then
+ // scrolls the opposite way. Note that this causes the reset translation animation
+ // to run while the user is dragging, which feels a bit unnatural.
+ reset();
+ } else if (mIsInOverScroll) {
+ if (Float.compare(mFirstDisplacement, 0) == 0) {
+ // Because users can scroll before entering overscroll, we need to
+ // subtract the amount where the user was not in overscroll.
+ mFirstDisplacement = displacement;
+ }
+ float overscrollY = displacement - mFirstDisplacement;
+ setContentTranslationY(getDampedOverScroll(overscrollY));
+ }
+
+ return mIsInOverScroll;
+ }
+
+ @Override
+ public void onDragEnd(float velocity, boolean fling) {
+ reset();
+ }
+
+ private void reset() {
+ if (Float.compare(mContentTranslationY, 0) != 0) {
+ ObjectAnimator.ofFloat(QSScrollLayout.this, CONTENT_TRANS_Y, 0)
+ .setDuration(100)
+ .start();
+ }
+ mIsInOverScroll = false;
+ mFirstDisplacement = 0;
+ }
+
+ public boolean isInOverScroll() {
+ return mIsInOverScroll;
+ }
+
+ private float getDampedOverScroll(float y) {
+ return OverScroll.dampedScroll(y, getHeight());
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java
index 3b9e7bc..65135ab 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java
@@ -104,6 +104,11 @@
setMeasuredDimension(width, height);
}
+ @Override
+ public boolean hasOverlappingRendering() {
+ return false;
+ }
+
private static int exactly(int size) {
return MeasureSpec.makeMeasureSpec(size, MeasureSpec.EXACTLY);
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/touch/OverScroll.java b/packages/SystemUI/src/com/android/systemui/qs/touch/OverScroll.java
new file mode 100644
index 0000000..0464886
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/touch/OverScroll.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2018 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.systemui.qs.touch;
+
+/**
+ * Utility methods for overscroll damping and related effect.
+ *
+ * Copied from packages/apps/Launcher3/src/com/android/launcher3/touch/OverScroll.java
+ */
+public class OverScroll {
+
+ private static final float OVERSCROLL_DAMP_FACTOR = 0.07f;
+
+ /**
+ * This curve determines how the effect of scrolling over the limits of the page diminishes
+ * as the user pulls further and further from the bounds
+ *
+ * @param f The percentage of how much the user has overscrolled.
+ * @return A transformed percentage based on the influence curve.
+ */
+ private static float overScrollInfluenceCurve(float f) {
+ f -= 1.0f;
+ return f * f * f + 1.0f;
+ }
+
+ /**
+ * @param amount The original amount overscrolled.
+ * @param max The maximum amount that the View can overscroll.
+ * @return The dampened overscroll amount.
+ */
+ public static int dampedScroll(float amount, int max) {
+ if (Float.compare(amount, 0) == 0) return 0;
+
+ float f = amount / max;
+ f = f / (Math.abs(f)) * (overScrollInfluenceCurve(Math.abs(f)));
+
+ // Clamp this factor, f, to -1 < f < 1
+ if (Math.abs(f) >= 1) {
+ f /= Math.abs(f);
+ }
+
+ return Math.round(OVERSCROLL_DAMP_FACTOR * f * max);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/touch/SwipeDetector.java b/packages/SystemUI/src/com/android/systemui/qs/touch/SwipeDetector.java
new file mode 100644
index 0000000..2522052
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/touch/SwipeDetector.java
@@ -0,0 +1,356 @@
+/*
+ * Copyright (C) 2018 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.systemui.qs.touch;
+
+import static android.view.MotionEvent.INVALID_POINTER_ID;
+
+import android.content.Context;
+import android.graphics.PointF;
+import android.support.annotation.NonNull;
+import android.support.annotation.VisibleForTesting;
+import android.util.Log;
+import android.view.MotionEvent;
+import android.view.ViewConfiguration;
+
+/**
+ * One dimensional scroll/drag/swipe gesture detector.
+ *
+ * Definition of swipe is different from android system in that this detector handles
+ * 'swipe to dismiss', 'swiping up/down a container' but also keeps scrolling state before
+ * swipe action happens
+ *
+ * Copied from packages/apps/Launcher3/src/com/android/launcher3/touch/SwipeDetector.java
+ */
+public class SwipeDetector {
+
+ private static final boolean DBG = false;
+ private static final String TAG = "SwipeDetector";
+
+ private int mScrollConditions;
+ public static final int DIRECTION_POSITIVE = 1 << 0;
+ public static final int DIRECTION_NEGATIVE = 1 << 1;
+ public static final int DIRECTION_BOTH = DIRECTION_NEGATIVE | DIRECTION_POSITIVE;
+
+ private static final float ANIMATION_DURATION = 1200;
+
+ protected int mActivePointerId = INVALID_POINTER_ID;
+
+ /**
+ * The minimum release velocity in pixels per millisecond that triggers fling..
+ */
+ public static final float RELEASE_VELOCITY_PX_MS = 1.0f;
+
+ /**
+ * The time constant used to calculate dampening in the low-pass filter of scroll velocity.
+ * Cutoff frequency is set at 10 Hz.
+ */
+ public static final float SCROLL_VELOCITY_DAMPENING_RC = 1000f / (2f * (float) Math.PI * 10);
+
+ /* Scroll state, this is set to true during dragging and animation. */
+ private ScrollState mState = ScrollState.IDLE;
+
+ enum ScrollState {
+ IDLE,
+ DRAGGING, // onDragStart, onDrag
+ SETTLING // onDragEnd
+ }
+
+ public static abstract class Direction {
+
+ abstract float getDisplacement(MotionEvent ev, int pointerIndex, PointF refPoint);
+
+ /**
+ * Distance in pixels a touch can wander before we think the user is scrolling.
+ */
+ abstract float getActiveTouchSlop(MotionEvent ev, int pointerIndex, PointF downPos);
+ }
+
+ public static final Direction VERTICAL = new Direction() {
+
+ @Override
+ float getDisplacement(MotionEvent ev, int pointerIndex, PointF refPoint) {
+ return ev.getY(pointerIndex) - refPoint.y;
+ }
+
+ @Override
+ float getActiveTouchSlop(MotionEvent ev, int pointerIndex, PointF downPos) {
+ return Math.abs(ev.getX(pointerIndex) - downPos.x);
+ }
+ };
+
+ public static final Direction HORIZONTAL = new Direction() {
+
+ @Override
+ float getDisplacement(MotionEvent ev, int pointerIndex, PointF refPoint) {
+ return ev.getX(pointerIndex) - refPoint.x;
+ }
+
+ @Override
+ float getActiveTouchSlop(MotionEvent ev, int pointerIndex, PointF downPos) {
+ return Math.abs(ev.getY(pointerIndex) - downPos.y);
+ }
+ };
+
+ //------------------- ScrollState transition diagram -----------------------------------
+ //
+ // IDLE -> (mDisplacement > mTouchSlop) -> DRAGGING
+ // DRAGGING -> (MotionEvent#ACTION_UP, MotionEvent#ACTION_CANCEL) -> SETTLING
+ // SETTLING -> (MotionEvent#ACTION_DOWN) -> DRAGGING
+ // SETTLING -> (View settled) -> IDLE
+
+ private void setState(ScrollState newState) {
+ if (DBG) {
+ Log.d(TAG, "setState:" + mState + "->" + newState);
+ }
+ // onDragStart and onDragEnd is reported ONLY on state transition
+ if (newState == ScrollState.DRAGGING) {
+ initializeDragging();
+ if (mState == ScrollState.IDLE) {
+ reportDragStart(false /* recatch */);
+ } else if (mState == ScrollState.SETTLING) {
+ reportDragStart(true /* recatch */);
+ }
+ }
+ if (newState == ScrollState.SETTLING) {
+ reportDragEnd();
+ }
+
+ mState = newState;
+ }
+
+ public boolean isDraggingOrSettling() {
+ return mState == ScrollState.DRAGGING || mState == ScrollState.SETTLING;
+ }
+
+ /**
+ * There's no touch and there's no animation.
+ */
+ public boolean isIdleState() {
+ return mState == ScrollState.IDLE;
+ }
+
+ public boolean isSettlingState() {
+ return mState == ScrollState.SETTLING;
+ }
+
+ public boolean isDraggingState() {
+ return mState == ScrollState.DRAGGING;
+ }
+
+ private final PointF mDownPos = new PointF();
+ private final PointF mLastPos = new PointF();
+ private final Direction mDir;
+
+ private final float mTouchSlop;
+
+ /* Client of this gesture detector can register a callback. */
+ private final Listener mListener;
+
+ private long mCurrentMillis;
+
+ private float mVelocity;
+ private float mLastDisplacement;
+ private float mDisplacement;
+
+ private float mSubtractDisplacement;
+ private boolean mIgnoreSlopWhenSettling;
+
+ public interface Listener {
+ void onDragStart(boolean start);
+
+ boolean onDrag(float displacement, float velocity);
+
+ void onDragEnd(float velocity, boolean fling);
+ }
+
+ public SwipeDetector(@NonNull Context context, @NonNull Listener l, @NonNull Direction dir) {
+ this(ViewConfiguration.get(context).getScaledTouchSlop(), l, dir);
+ }
+
+ @VisibleForTesting
+ protected SwipeDetector(float touchSlope, @NonNull Listener l, @NonNull Direction dir) {
+ mTouchSlop = touchSlope;
+ mListener = l;
+ mDir = dir;
+ }
+
+ public void setDetectableScrollConditions(int scrollDirectionFlags, boolean ignoreSlop) {
+ mScrollConditions = scrollDirectionFlags;
+ mIgnoreSlopWhenSettling = ignoreSlop;
+ }
+
+ private boolean shouldScrollStart(MotionEvent ev, int pointerIndex) {
+ // reject cases where the angle or slop condition is not met.
+ if (Math.max(mDir.getActiveTouchSlop(ev, pointerIndex, mDownPos), mTouchSlop)
+ > Math.abs(mDisplacement)) {
+ return false;
+ }
+
+ // Check if the client is interested in scroll in current direction.
+ if (((mScrollConditions & DIRECTION_NEGATIVE) > 0 && mDisplacement > 0) ||
+ ((mScrollConditions & DIRECTION_POSITIVE) > 0 && mDisplacement < 0)) {
+ return true;
+ }
+ return false;
+ }
+
+ public boolean onTouchEvent(MotionEvent ev) {
+ switch (ev.getActionMasked()) {
+ case MotionEvent.ACTION_DOWN:
+ mActivePointerId = ev.getPointerId(0);
+ mDownPos.set(ev.getX(), ev.getY());
+ mLastPos.set(mDownPos);
+ mLastDisplacement = 0;
+ mDisplacement = 0;
+ mVelocity = 0;
+
+ if (mState == ScrollState.SETTLING && mIgnoreSlopWhenSettling) {
+ setState(ScrollState.DRAGGING);
+ }
+ break;
+ //case MotionEvent.ACTION_POINTER_DOWN:
+ case MotionEvent.ACTION_POINTER_UP:
+ int ptrIdx = ev.getActionIndex();
+ int ptrId = ev.getPointerId(ptrIdx);
+ if (ptrId == mActivePointerId) {
+ final int newPointerIdx = ptrIdx == 0 ? 1 : 0;
+ mDownPos.set(
+ ev.getX(newPointerIdx) - (mLastPos.x - mDownPos.x),
+ ev.getY(newPointerIdx) - (mLastPos.y - mDownPos.y));
+ mLastPos.set(ev.getX(newPointerIdx), ev.getY(newPointerIdx));
+ mActivePointerId = ev.getPointerId(newPointerIdx);
+ }
+ break;
+ case MotionEvent.ACTION_MOVE:
+ int pointerIndex = ev.findPointerIndex(mActivePointerId);
+ if (pointerIndex == INVALID_POINTER_ID) {
+ break;
+ }
+ mDisplacement = mDir.getDisplacement(ev, pointerIndex, mDownPos);
+ computeVelocity(mDir.getDisplacement(ev, pointerIndex, mLastPos),
+ ev.getEventTime());
+
+ // handle state and listener calls.
+ if (mState != ScrollState.DRAGGING && shouldScrollStart(ev, pointerIndex)) {
+ setState(ScrollState.DRAGGING);
+ }
+ if (mState == ScrollState.DRAGGING) {
+ reportDragging();
+ }
+ mLastPos.set(ev.getX(pointerIndex), ev.getY(pointerIndex));
+ break;
+ case MotionEvent.ACTION_CANCEL:
+ case MotionEvent.ACTION_UP:
+ // These are synthetic events and there is no need to update internal values.
+ if (mState == ScrollState.DRAGGING) {
+ setState(ScrollState.SETTLING);
+ }
+ break;
+ default:
+ break;
+ }
+ return true;
+ }
+
+ public void finishedScrolling() {
+ setState(ScrollState.IDLE);
+ }
+
+ private boolean reportDragStart(boolean recatch) {
+ mListener.onDragStart(!recatch);
+ if (DBG) {
+ Log.d(TAG, "onDragStart recatch:" + recatch);
+ }
+ return true;
+ }
+
+ private void initializeDragging() {
+ if (mState == ScrollState.SETTLING && mIgnoreSlopWhenSettling) {
+ mSubtractDisplacement = 0;
+ }
+ if (mDisplacement > 0) {
+ mSubtractDisplacement = mTouchSlop;
+ } else {
+ mSubtractDisplacement = -mTouchSlop;
+ }
+ }
+
+ private boolean reportDragging() {
+ if (mDisplacement != mLastDisplacement) {
+ if (DBG) {
+ Log.d(TAG, String.format("onDrag disp=%.1f, velocity=%.1f",
+ mDisplacement, mVelocity));
+ }
+
+ mLastDisplacement = mDisplacement;
+ return mListener.onDrag(mDisplacement - mSubtractDisplacement, mVelocity);
+ }
+ return true;
+ }
+
+ private void reportDragEnd() {
+ if (DBG) {
+ Log.d(TAG, String.format("onScrollEnd disp=%.1f, velocity=%.1f",
+ mDisplacement, mVelocity));
+ }
+ mListener.onDragEnd(mVelocity, Math.abs(mVelocity) > RELEASE_VELOCITY_PX_MS);
+
+ }
+
+ /**
+ * Computes the damped velocity.
+ */
+ public float computeVelocity(float delta, long currentMillis) {
+ long previousMillis = mCurrentMillis;
+ mCurrentMillis = currentMillis;
+
+ float deltaTimeMillis = mCurrentMillis - previousMillis;
+ float velocity = (deltaTimeMillis > 0) ? (delta / deltaTimeMillis) : 0;
+ if (Math.abs(mVelocity) < 0.001f) {
+ mVelocity = velocity;
+ } else {
+ float alpha = computeDampeningFactor(deltaTimeMillis);
+ mVelocity = interpolate(mVelocity, velocity, alpha);
+ }
+ return mVelocity;
+ }
+
+ /**
+ * Returns a time-dependent dampening factor using delta time.
+ */
+ private static float computeDampeningFactor(float deltaTime) {
+ return deltaTime / (SCROLL_VELOCITY_DAMPENING_RC + deltaTime);
+ }
+
+ /**
+ * Returns the linear interpolation between two values
+ */
+ private static float interpolate(float from, float to, float alpha) {
+ return (1.0f - alpha) * from + alpha * to;
+ }
+
+ public static long calculateDuration(float velocity, float progressNeeded) {
+ // TODO: make these values constants after tuning.
+ float velocityDivisor = Math.max(2f, Math.abs(0.5f * velocity));
+ float travelDistance = Math.max(0.2f, progressNeeded);
+ long duration = (long) Math.max(100, ANIMATION_DURATION / velocityDivisor * travelDistance);
+ if (DBG) {
+ Log.d(TAG, String.format("calculateDuration=%d, v=%f, d=%f", duration, velocity, progressNeeded));
+ }
+ return duration;
+ }
+}
+
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
index 11bdf6b3..fa177f2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
@@ -156,7 +156,7 @@
default void handleShowGlobalActionsMenu() { }
default void handleShowShutdownUi(boolean isReboot, String reason) { }
- default void showChargingAnimation(int batteryLevel) { }
+ default void showWirelessChargingAnimation(int batteryLevel) { }
default void onRotationProposal(int rotation, boolean isValid) { }
@@ -497,7 +497,7 @@
}
@Override
- public void showChargingAnimation(int batteryLevel) {
+ public void showWirelessChargingAnimation(int batteryLevel) {
mHandler.removeMessages(MSG_SHOW_CHARGING_ANIMATION);
mHandler.obtainMessage(MSG_SHOW_CHARGING_ANIMATION, batteryLevel, 0)
.sendToTarget();
@@ -784,7 +784,7 @@
break;
case MSG_SHOW_CHARGING_ANIMATION:
for (int i = 0; i < mCallbacks.size(); i++) {
- mCallbacks.get(i).showChargingAnimation(msg.arg1);
+ mCallbacks.get(i).showWirelessChargingAnimation(msg.arg1);
}
break;
case MSG_SHOW_PINNING_TOAST_ENTER_EXIT:
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationBackgroundView.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationBackgroundView.java
index d6beb7f..ab89a52 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationBackgroundView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationBackgroundView.java
@@ -249,6 +249,9 @@
(GradientDrawable) ((LayerDrawable) mBackground).getDrawable(0);
gradientDrawable.setXfermode(
running ? new PorterDuffXfermode(PorterDuff.Mode.SRC) : null);
+ // Speed optimization: disable AA if transfer mode is not SRC_OVER. AA is not easy to
+ // spot during animation anyways.
+ gradientDrawable.setAntiAlias(!running);
}
if (!mExpandAnimationRunning) {
setDrawableAlpha(mDrawableAlpha);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
index f25379a..3c480d8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
@@ -299,6 +299,11 @@
}
}, REMOTE_INPUT_KEPT_ENTRY_AUTO_CANCEL_DELAY);
}
+ try {
+ mBarService.onNotificationDirectReplied(entry.notification.getKey());
+ } catch (RemoteException e) {
+ // Nothing to do, system going down
+ }
}
});
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
index 3ebeb4d..3dfb913 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
@@ -379,7 +379,7 @@
// Because space is usually constrained in the auto use-case, there should not be a
// pinned notification when the shade has been expanded. Ensure this by removing all heads-
// up notifications.
- mHeadsUpManager.removeAllHeadsUpEntries();
+ mHeadsUpManager.releaseAllImmediately();
super.animateExpandNotificationsPanel();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
new file mode 100644
index 0000000..aba5cdf
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
@@ -0,0 +1,446 @@
+/*
+ * Copyright (C) 2018 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.systemui.statusbar.phone;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.content.res.Resources;
+import android.support.v4.util.ArraySet;
+import android.util.Log;
+import android.util.Pools;
+import android.view.View;
+import android.view.ViewTreeObserver;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.systemui.Dumpable;
+import com.android.systemui.statusbar.ExpandableNotificationRow;
+import com.android.systemui.statusbar.NotificationData;
+import com.android.systemui.statusbar.StatusBarState;
+import com.android.systemui.statusbar.notification.VisualStabilityManager;
+import com.android.systemui.statusbar.policy.HeadsUpManager;
+import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.HashSet;
+import java.util.Stack;
+
+/**
+ * A implementation of HeadsUpManager for phone and car.
+ */
+public class HeadsUpManagerPhone extends HeadsUpManager implements Dumpable,
+ ViewTreeObserver.OnComputeInternalInsetsListener, VisualStabilityManager.Callback,
+ OnHeadsUpChangedListener {
+ private static final String TAG = "HeadsUpManagerPhone";
+ private static final boolean DEBUG = false;
+
+ private final View mStatusBarWindowView;
+ private final int mStatusBarHeight;
+ private final NotificationGroupManager mGroupManager;
+ private final StatusBar mBar;
+ private final VisualStabilityManager mVisualStabilityManager;
+
+ private boolean mReleaseOnExpandFinish;
+ private boolean mTrackingHeadsUp;
+ private HashSet<String> mSwipedOutKeys = new HashSet<>();
+ private HashSet<NotificationData.Entry> mEntriesToRemoveAfterExpand = new HashSet<>();
+ private ArraySet<NotificationData.Entry> mEntriesToRemoveWhenReorderingAllowed
+ = new ArraySet<>();
+ private boolean mIsExpanded;
+ private int[] mTmpTwoArray = new int[2];
+ private boolean mHeadsUpGoingAway;
+ private boolean mWaitingOnCollapseWhenGoingAway;
+ private boolean mIsObserving;
+ private int mStatusBarState;
+
+ private final Pools.Pool<HeadsUpEntryPhone> mEntryPool = new Pools.Pool<HeadsUpEntryPhone>() {
+ private Stack<HeadsUpEntryPhone> mPoolObjects = new Stack<>();
+
+ @Override
+ public HeadsUpEntryPhone acquire() {
+ if (!mPoolObjects.isEmpty()) {
+ return mPoolObjects.pop();
+ }
+ return new HeadsUpEntryPhone();
+ }
+
+ @Override
+ public boolean release(@NonNull HeadsUpEntryPhone instance) {
+ mPoolObjects.push(instance);
+ return true;
+ }
+ };
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+ // Constructor:
+
+ public HeadsUpManagerPhone(@NonNull final Context context, @NonNull View statusBarWindowView,
+ @NonNull NotificationGroupManager groupManager, @NonNull StatusBar bar,
+ @NonNull VisualStabilityManager visualStabilityManager) {
+ super(context);
+
+ mStatusBarWindowView = statusBarWindowView;
+ mGroupManager = groupManager;
+ mBar = bar;
+ mVisualStabilityManager = visualStabilityManager;
+
+ Resources resources = mContext.getResources();
+ mStatusBarHeight = resources.getDimensionPixelSize(
+ com.android.internal.R.dimen.status_bar_height);
+
+ addListener(new OnHeadsUpChangedListener() {
+ @Override
+ public void onHeadsUpPinnedModeChanged(boolean hasPinnedNotification) {
+ if (DEBUG) Log.w(TAG, "onHeadsUpPinnedModeChanged");
+ updateTouchableRegionListener();
+ }
+ });
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+ // Public methods:
+
+ /**
+ * Decides whether a click is invalid for a notification, i.e it has not been shown long enough
+ * that a user might have consciously clicked on it.
+ *
+ * @param key the key of the touched notification
+ * @return whether the touch is invalid and should be discarded
+ */
+ public boolean shouldSwallowClick(@NonNull String key) {
+ HeadsUpManager.HeadsUpEntry entry = getHeadsUpEntry(key);
+ return entry != null && mClock.currentTimeMillis() < entry.postTime;
+ }
+
+ public void onExpandingFinished() {
+ if (mReleaseOnExpandFinish) {
+ releaseAllImmediately();
+ mReleaseOnExpandFinish = false;
+ } else {
+ for (NotificationData.Entry entry : mEntriesToRemoveAfterExpand) {
+ if (isHeadsUp(entry.key)) {
+ // Maybe the heads-up was removed already
+ removeHeadsUpEntry(entry);
+ }
+ }
+ }
+ mEntriesToRemoveAfterExpand.clear();
+ }
+
+ /**
+ * Sets the tracking-heads-up flag. If the flag is true, HeadsUpManager doesn't remove the entry
+ * from the list even after a Heads Up Notification is gone.
+ */
+ public void setTrackingHeadsUp(boolean trackingHeadsUp) {
+ mTrackingHeadsUp = trackingHeadsUp;
+ }
+
+ /**
+ * Notify that the status bar panel gets expanded or collapsed.
+ *
+ * @param isExpanded True to notify expanded, false to notify collapsed.
+ */
+ public void setIsPanelExpanded(boolean isExpanded) {
+ if (isExpanded != mIsExpanded) {
+ mIsExpanded = isExpanded;
+ if (isExpanded) {
+ // make sure our state is sane
+ mWaitingOnCollapseWhenGoingAway = false;
+ mHeadsUpGoingAway = false;
+ updateTouchableRegionListener();
+ }
+ }
+ }
+
+ /**
+ * Set the current state of the statusbar.
+ */
+ public void setStatusBarState(int statusBarState) {
+ mStatusBarState = statusBarState;
+ }
+
+ /**
+ * Set that we are exiting the headsUp pinned mode, but some notifications might still be
+ * animating out. This is used to keep the touchable regions in a sane state.
+ */
+ public void setHeadsUpGoingAway(boolean headsUpGoingAway) {
+ if (headsUpGoingAway != mHeadsUpGoingAway) {
+ mHeadsUpGoingAway = headsUpGoingAway;
+ if (!headsUpGoingAway) {
+ waitForStatusBarLayout();
+ }
+ updateTouchableRegionListener();
+ }
+ }
+
+ /**
+ * Notifies that a remote input textbox in notification gets active or inactive.
+ * @param entry The entry of the target notification.
+ * @param remoteInputActive True to notify active, False to notify inactive.
+ */
+ public void setRemoteInputActive(
+ @NonNull NotificationData.Entry entry, boolean remoteInputActive) {
+ HeadsUpEntryPhone headsUpEntry = getHeadsUpEntryPhone(entry.key);
+ if (headsUpEntry != null && headsUpEntry.remoteInputActive != remoteInputActive) {
+ headsUpEntry.remoteInputActive = remoteInputActive;
+ if (remoteInputActive) {
+ headsUpEntry.removeAutoRemovalCallbacks();
+ } else {
+ headsUpEntry.updateEntry(false /* updatePostTime */);
+ }
+ }
+ }
+
+ @VisibleForTesting
+ public void removeMinimumDisplayTimeForTesting() {
+ mMinimumDisplayTime = 0;
+ mHeadsUpNotificationDecay = 0;
+ mTouchAcceptanceDelay = 0;
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+ // HeadsUpManager public methods overrides:
+
+ @Override
+ public boolean isTrackingHeadsUp() {
+ return mTrackingHeadsUp;
+ }
+
+ @Override
+ public void snooze() {
+ super.snooze();
+ mReleaseOnExpandFinish = true;
+ }
+
+ /**
+ * React to the removal of the notification in the heads up.
+ *
+ * @return true if the notification was removed and false if it still needs to be kept around
+ * for a bit since it wasn't shown long enough
+ */
+ @Override
+ public boolean removeNotification(@NonNull String key, boolean ignoreEarliestRemovalTime) {
+ if (wasShownLongEnough(key) || ignoreEarliestRemovalTime) {
+ return super.removeNotification(key, ignoreEarliestRemovalTime);
+ } else {
+ HeadsUpEntryPhone entry = getHeadsUpEntryPhone(key);
+ entry.removeAsSoonAsPossible();
+ return false;
+ }
+ }
+
+ public void addSwipedOutNotification(@NonNull String key) {
+ mSwipedOutKeys.add(key);
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+ // Dumpable overrides:
+
+ @Override
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ pw.println("HeadsUpManagerPhone state:");
+ dumpInternal(fd, pw, args);
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+ // ViewTreeObserver.OnComputeInternalInsetsListener overrides:
+
+ /**
+ * Overridden from TreeObserver.
+ */
+ @Override
+ public void onComputeInternalInsets(ViewTreeObserver.InternalInsetsInfo info) {
+ if (mIsExpanded || mBar.isBouncerShowing()) {
+ // The touchable region is always the full area when expanded
+ return;
+ }
+ if (hasPinnedHeadsUp()) {
+ ExpandableNotificationRow topEntry = getTopEntry().row;
+ if (topEntry.isChildInGroup()) {
+ final ExpandableNotificationRow groupSummary
+ = mGroupManager.getGroupSummary(topEntry.getStatusBarNotification());
+ if (groupSummary != null) {
+ topEntry = groupSummary;
+ }
+ }
+ topEntry.getLocationOnScreen(mTmpTwoArray);
+ int minX = mTmpTwoArray[0];
+ int maxX = mTmpTwoArray[0] + topEntry.getWidth();
+ int maxY = topEntry.getIntrinsicHeight();
+
+ info.setTouchableInsets(ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION);
+ info.touchableRegion.set(minX, 0, maxX, maxY);
+ } else if (mHeadsUpGoingAway || mWaitingOnCollapseWhenGoingAway) {
+ info.setTouchableInsets(ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION);
+ info.touchableRegion.set(0, 0, mStatusBarWindowView.getWidth(), mStatusBarHeight);
+ }
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+ // VisualStabilityManager.Callback overrides:
+
+ @Override
+ public void onReorderingAllowed() {
+ mBar.getNotificationScrollLayout().setHeadsUpGoingAwayAnimationsAllowed(false);
+ for (NotificationData.Entry entry : mEntriesToRemoveWhenReorderingAllowed) {
+ if (isHeadsUp(entry.key)) {
+ // Maybe the heads-up was removed already
+ removeHeadsUpEntry(entry);
+ }
+ }
+ mEntriesToRemoveWhenReorderingAllowed.clear();
+ mBar.getNotificationScrollLayout().setHeadsUpGoingAwayAnimationsAllowed(true);
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+ // HeadsUpManager utility (protected) methods overrides:
+
+ @Override
+ protected HeadsUpEntry createHeadsUpEntry() {
+ return mEntryPool.acquire();
+ }
+
+ @Override
+ protected void releaseHeadsUpEntry(HeadsUpEntry entry) {
+ entry.reset();
+ mEntryPool.release((HeadsUpEntryPhone) entry);
+ }
+
+ @Override
+ protected boolean shouldHeadsUpBecomePinned(NotificationData.Entry entry) {
+ return mStatusBarState != StatusBarState.KEYGUARD && !mIsExpanded
+ || super.shouldHeadsUpBecomePinned(entry);
+ }
+
+ @Override
+ protected void dumpInternal(FileDescriptor fd, PrintWriter pw, String[] args) {
+ super.dumpInternal(fd, pw, args);
+ pw.print(" mStatusBarState="); pw.println(mStatusBarState);
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+ // Private utility methods:
+
+ @Nullable
+ private HeadsUpEntryPhone getHeadsUpEntryPhone(@NonNull String key) {
+ return (HeadsUpEntryPhone) getHeadsUpEntry(key);
+ }
+
+ @Nullable
+ private HeadsUpEntryPhone getTopHeadsUpEntryPhone() {
+ return (HeadsUpEntryPhone) getTopHeadsUpEntry();
+ }
+
+ private boolean wasShownLongEnough(@NonNull String key) {
+ if (mSwipedOutKeys.contains(key)) {
+ // We always instantly dismiss views being manually swiped out.
+ mSwipedOutKeys.remove(key);
+ return true;
+ }
+
+ HeadsUpEntryPhone headsUpEntry = getHeadsUpEntryPhone(key);
+ HeadsUpEntryPhone topEntry = getTopHeadsUpEntryPhone();
+ return headsUpEntry != topEntry || headsUpEntry.wasShownLongEnough();
+ }
+
+ /**
+ * We need to wait on the whole panel to collapse, before we can remove the touchable region
+ * listener.
+ */
+ private void waitForStatusBarLayout() {
+ mWaitingOnCollapseWhenGoingAway = true;
+ mStatusBarWindowView.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
+ @Override
+ public void onLayoutChange(View v, int left, int top, int right, int bottom,
+ int oldLeft,
+ int oldTop, int oldRight, int oldBottom) {
+ if (mStatusBarWindowView.getHeight() <= mStatusBarHeight) {
+ mStatusBarWindowView.removeOnLayoutChangeListener(this);
+ mWaitingOnCollapseWhenGoingAway = false;
+ updateTouchableRegionListener();
+ }
+ }
+ });
+ }
+
+ private void updateTouchableRegionListener() {
+ boolean shouldObserve = hasPinnedHeadsUp() || mHeadsUpGoingAway
+ || mWaitingOnCollapseWhenGoingAway;
+ if (shouldObserve == mIsObserving) {
+ return;
+ }
+ if (shouldObserve) {
+ mStatusBarWindowView.getViewTreeObserver().addOnComputeInternalInsetsListener(this);
+ mStatusBarWindowView.requestLayout();
+ } else {
+ mStatusBarWindowView.getViewTreeObserver().removeOnComputeInternalInsetsListener(this);
+ }
+ mIsObserving = shouldObserve;
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+ // HeadsUpEntryPhone:
+
+ protected class HeadsUpEntryPhone extends HeadsUpManager.HeadsUpEntry {
+ public void setEntry(@NonNull final NotificationData.Entry entry) {
+ Runnable removeHeadsUpRunnable = () -> {
+ if (!mVisualStabilityManager.isReorderingAllowed()) {
+ mEntriesToRemoveWhenReorderingAllowed.add(entry);
+ mVisualStabilityManager.addReorderingAllowedCallback(
+ HeadsUpManagerPhone.this);
+ } else if (!mTrackingHeadsUp) {
+ removeHeadsUpEntry(entry);
+ } else {
+ mEntriesToRemoveAfterExpand.add(entry);
+ }
+ };
+
+ super.setEntry(entry, removeHeadsUpRunnable);
+ }
+
+ public boolean wasShownLongEnough() {
+ return earliestRemovaltime < mClock.currentTimeMillis();
+ }
+
+ @Override
+ public void updateEntry(boolean updatePostTime) {
+ super.updateEntry(updatePostTime);
+
+ if (mEntriesToRemoveAfterExpand.contains(entry)) {
+ mEntriesToRemoveAfterExpand.remove(entry);
+ }
+ if (mEntriesToRemoveWhenReorderingAllowed.contains(entry)) {
+ mEntriesToRemoveWhenReorderingAllowed.remove(entry);
+ }
+ }
+
+ @Override
+ public void expanded(boolean expanded) {
+ if (this.expanded == expanded) {
+ return;
+ }
+
+ this.expanded = expanded;
+ if (expanded) {
+ removeAutoRemovalCallbacks();
+ } else {
+ updateEntry(false /* updatePostTime */);
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java
index c85571c..2bfdefe 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java
@@ -23,7 +23,7 @@
import com.android.systemui.Gefingerpoken;
import com.android.systemui.statusbar.ExpandableNotificationRow;
import com.android.systemui.statusbar.ExpandableView;
-import com.android.systemui.statusbar.policy.HeadsUpManager;
+import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
/**
@@ -31,7 +31,7 @@
*/
public class HeadsUpTouchHelper implements Gefingerpoken {
- private HeadsUpManager mHeadsUpManager;
+ private HeadsUpManagerPhone mHeadsUpManager;
private NotificationStackScrollLayout mStackScroller;
private int mTrackingPointer;
private float mTouchSlop;
@@ -43,7 +43,7 @@
private NotificationPanelView mPanel;
private ExpandableNotificationRow mPickedChild;
- public HeadsUpTouchHelper(HeadsUpManager headsUpManager,
+ public HeadsUpTouchHelper(HeadsUpManagerPhone headsUpManager,
NotificationStackScrollLayout stackScroller,
NotificationPanelView notificationPanelView) {
mHeadsUpManager = headsUpManager;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarGestureHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarGestureHelper.java
index 7db6e28..d15c771 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarGestureHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarGestureHelper.java
@@ -16,6 +16,12 @@
package com.android.systemui.statusbar.phone;
+import static android.view.WindowManager.DOCKED_INVALID;
+import static android.view.WindowManager.DOCKED_LEFT;
+import static android.view.WindowManager.DOCKED_TOP;
+import static com.android.systemui.OverviewProxyService.DEBUG_OVERVIEW_PROXY;
+import static com.android.systemui.OverviewProxyService.TAG_OPS;
+
import android.app.ActivityManager;
import android.content.Context;
import android.content.res.Resources;
@@ -27,26 +33,19 @@
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.View;
-
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.policy.DividerSnapAlgorithm.SnapTarget;
import com.android.systemui.Dependency;
import com.android.systemui.OverviewProxyService;
+import com.android.systemui.OverviewProxyService.OverviewProxyListener;
import com.android.systemui.R;
import com.android.systemui.RecentsComponent;
import com.android.systemui.SysUiServiceProvider;
import com.android.systemui.plugins.statusbar.phone.NavGesture.GestureHelper;
import com.android.systemui.shared.recents.IOverviewProxy;
-import com.android.systemui.shared.recents.utilities.Utilities;
import com.android.systemui.stackdivider.Divider;
import com.android.systemui.tuner.TunerService;
-import static android.view.WindowManager.DOCKED_INVALID;
-import static android.view.WindowManager.DOCKED_LEFT;
-import static android.view.WindowManager.DOCKED_TOP;
-import static com.android.systemui.OverviewProxyService.DEBUG_OVERVIEW_PROXY;
-import static com.android.systemui.OverviewProxyService.TAG_OPS;
-
/**
* Class to detect gestures on the navigation bar.
*/
@@ -84,8 +83,16 @@
private int mTouchDownY;
private boolean mDownOnRecents;
private VelocityTracker mVelocityTracker;
- private OverviewProxyService mOverviewEventSender = Dependency.get(OverviewProxyService.class);
+ private OverviewProxyService mOverviewProxyService = Dependency.get(OverviewProxyService.class);
+ private final OverviewProxyListener mOverviewProxyListener = new OverviewProxyListener() {
+ @Override
+ public void onRecentsAnimationStarted() {
+ mRecentsAnimationStarted = true;
+ mQuickScrubController.cancelQuickSwitch();
+ }
+ };
+ private boolean mRecentsAnimationStarted;
private boolean mDockWindowEnabled;
private boolean mDockWindowTouchSlopExceeded;
private int mDragMode;
@@ -97,10 +104,12 @@
mScrollTouchSlop = r.getDimensionPixelSize(R.dimen.navigation_bar_min_swipe_distance);
mQuickScrubController = new QuickScrubController(context);
Dependency.get(TunerService.class).addTunable(this, KEY_DOCK_WINDOW_GESTURE);
+ mOverviewProxyService.addCallback(mOverviewProxyListener);
}
public void destroy() {
Dependency.get(TunerService.class).removeTunable(this);
+ mOverviewProxyService.removeCallback(mOverviewProxyListener);
}
public void setComponents(RecentsComponent recentsComponent, Divider divider,
@@ -117,7 +126,7 @@
}
private boolean proxyMotionEvents(MotionEvent event) {
- final IOverviewProxy overviewProxy = mOverviewEventSender.getProxy();
+ final IOverviewProxy overviewProxy = mOverviewProxyService.getProxy();
if (overviewProxy != null) {
mNavigationBarView.requestUnbufferedDispatch(event);
event.transform(mTransformGlobalMatrix);
@@ -146,25 +155,33 @@
mTransformLocalMatrix.set(Matrix.IDENTITY_MATRIX);
mNavigationBarView.transformMatrixToGlobal(mTransformGlobalMatrix);
mNavigationBarView.transformMatrixToLocal(mTransformLocalMatrix);
+ mRecentsAnimationStarted = false;
break;
}
}
- if (mStatusBar.isPresenterFullyCollapsed()
- && !mQuickScrubController.onInterceptTouchEvent(event)) {
+ boolean handledByQuickscrub = mQuickScrubController.onInterceptTouchEvent(event);
+ if (mStatusBar.isPresenterFullyCollapsed() && !handledByQuickscrub) {
+ // Proxy motion events until we start intercepting for quickscrub
proxyMotionEvents(event);
- return false;
}
- return (mDockWindowEnabled && interceptDockWindowEvent(event));
+
+ boolean result = handledByQuickscrub;
+ result |= mRecentsAnimationStarted;
+ if (mDockWindowEnabled) {
+ result |= interceptDockWindowEvent(event);
+ }
+ return result;
}
public boolean onTouchEvent(MotionEvent event) {
// The same down event was just sent on intercept and therefore can be ignored here
boolean ignoreProxyDownEvent = event.getAction() == MotionEvent.ACTION_DOWN
- && mOverviewEventSender.getProxy() != null;
+ && mOverviewProxyService.getProxy() != null;
boolean result = mStatusBar.isPresenterFullyCollapsed()
&& (mQuickScrubController.onTouchEvent(event)
|| ignoreProxyDownEvent
|| proxyMotionEvents(event));
+ result |= mRecentsAnimationStarted;
if (mDockWindowEnabled) {
result |= handleDockWindowEvent(event);
}
@@ -172,7 +189,7 @@
}
public void onDraw(Canvas canvas) {
- if (mOverviewEventSender.getProxy() != null) {
+ if (mOverviewProxyService.getProxy() != null) {
mQuickScrubController.onDraw(canvas);
}
}
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 ad5144e..af0afbd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -103,7 +103,6 @@
private DeadZone mDeadZone;
private final NavigationBarTransitions mBarTransitions;
private final OverviewProxyService mOverviewProxyService;
- private boolean mRecentsAnimationStarted;
// workaround for LayoutTransitions leaving the nav buttons in a weird state (bug 5549288)
final static boolean WORKAROUND_INVALID_LAYOUT = true;
@@ -263,7 +262,6 @@
}
public void setRecentsAnimationStarted(boolean started) {
- mRecentsAnimationStarted = started;
if (mRecentsOnboarding != null) {
mRecentsOnboarding.onRecentsAnimationStarted();
}
@@ -277,22 +275,7 @@
@Override
public boolean onInterceptTouchEvent(MotionEvent event) {
- int action = event.getActionMasked();
- if (action == MotionEvent.ACTION_DOWN) {
- mRecentsAnimationStarted = false;
- } else if (action == MotionEvent.ACTION_UP) {
- // If the overview proxy service has not started the recents animation then clean up
- // after it to ensure that the nav bar buttons still work
- if (mOverviewProxyService.getProxy() != null && !mRecentsAnimationStarted) {
- try {
- ActivityManager.getService().cancelRecentsAnimation();
- } catch (RemoteException e) {
- Log.e(TAG, "Could not cancel recents animation");
- }
- }
- }
-
- return mGestureHelper.onInterceptTouchEvent(event) || mRecentsAnimationStarted;
+ return mGestureHelper.onInterceptTouchEvent(event);
}
@Override
@@ -300,7 +283,7 @@
if (mGestureHelper.onTouchEvent(event)) {
return true;
}
- return mRecentsAnimationStarted || super.onTouchEvent(event);
+ return super.onTouchEvent(event);
}
public void abortCurrentGesture() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
index cd2e77a..52d005c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -68,14 +68,12 @@
import com.android.systemui.statusbar.NotificationShelf;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.notification.ActivityLaunchAnimator;
-import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.statusbar.policy.KeyguardUserSwitcher;
import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
import com.android.systemui.statusbar.stack.StackStateAnimator;
import java.util.List;
-import java.util.Collection;
public class NotificationPanelView extends PanelView implements
ExpandableView.OnHeightChangedListener,
@@ -1571,7 +1569,7 @@
private void updatePanelExpanded() {
boolean isExpanded = !isFullyCollapsed();
if (mPanelExpanded != isExpanded) {
- mHeadsUpManager.setIsExpanded(isExpanded);
+ mHeadsUpManager.setIsPanelExpanded(isExpanded);
mStatusBar.setPanelExpanded(isExpanded);
mPanelExpanded = isExpanded;
}
@@ -2338,7 +2336,7 @@
}
@Override
- public void setHeadsUpManager(HeadsUpManager headsUpManager) {
+ public void setHeadsUpManager(HeadsUpManagerPhone headsUpManager) {
super.setHeadsUpManager(headsUpManager);
mHeadsUpTouchHelper = new HeadsUpTouchHelper(headsUpManager, mNotificationStackScroller,
this);
@@ -2630,8 +2628,8 @@
}
}
- public void setPulsing(Collection<HeadsUpManager.HeadsUpEntry> pulsing) {
- mKeyguardStatusView.setPulsing(pulsing != null);
+ public void setPulsing(boolean pulsing) {
+ mKeyguardStatusView.setPulsing(pulsing);
positionClockAndNotifications();
mNotificationStackScroller.setPulsing(pulsing, mKeyguardStatusView.getLocationOnScreen()[1]
+ mKeyguardStatusView.getClockBottom());
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
index 2b7e474..6daabed 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
@@ -50,7 +50,7 @@
import com.android.systemui.doze.DozeLog;
import com.android.systemui.statusbar.FlingAnimationUtils;
import com.android.systemui.statusbar.StatusBarState;
-import com.android.systemui.statusbar.policy.HeadsUpManager;
+import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -75,7 +75,7 @@
}
protected StatusBar mStatusBar;
- protected HeadsUpManager mHeadsUpManager;
+ protected HeadsUpManagerPhone mHeadsUpManager;
private float mPeekHeight;
private float mHintDistance;
@@ -1252,7 +1252,7 @@
*/
protected abstract int getClearAllHeight();
- public void setHeadsUpManager(HeadsUpManager headsUpManager) {
+ public void setHeadsUpManager(HeadsUpManagerPhone headsUpManager) {
mHeadsUpManager = headsUpManager;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickScrubController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickScrubController.java
index 6bfaaf4..dc0835e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickScrubController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickScrubController.java
@@ -63,7 +63,7 @@
private static final String TAG = "QuickScrubController";
private static final int QUICK_SWITCH_FLING_VELOCITY = 0;
private static final int ANIM_DURATION_MS = 200;
- private static final long LONG_PRESS_DELAY_MS = 150;
+ private static final long LONG_PRESS_DELAY_MS = 225;
/**
* For quick step, set a damping value to allow the button to stick closer its origin position
@@ -76,6 +76,7 @@
private boolean mDraggingActive;
private boolean mQuickScrubActive;
+ private boolean mAllowQuickSwitch;
private float mDownOffset;
private float mTranslation;
private int mTouchDownX;
@@ -95,7 +96,6 @@
private final Paint mTrackPaint = new Paint();
private final int mScrollTouchSlop;
private final OverviewProxyService mOverviewEventSender;
- private final Display mDisplay;
private final int mTrackThickness;
private final int mTrackPadding;
private final ValueAnimator mTrackAnimator;
@@ -137,7 +137,8 @@
new GestureDetector.SimpleOnGestureListener() {
@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velX, float velY) {
- if (!isQuickScrubEnabled() || mQuickScrubActive) {
+ if (!isQuickScrubEnabled() || mQuickScrubActive || !mAllowQuickSwitch ||
+ !mHomeButtonRect.contains(mTouchDownX, mTouchDownY)) {
return false;
}
float velocityX = mIsRTL ? -velX : velX;
@@ -167,8 +168,6 @@
public QuickScrubController(Context context) {
mContext = context;
mScrollTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
- mDisplay = ((WindowManager) context.getSystemService(
- Context.WINDOW_SERVICE)).getDefaultDisplay();
mOverviewEventSender = Dependency.get(OverviewProxyService.class);
mGestureDetector = new GestureDetector(mContext, mGestureListener);
mTrackThickness = getDimensionPixelSize(mContext, R.dimen.nav_quick_scrub_track_thickness);
@@ -202,8 +201,24 @@
homeButton.setDelayTouchFeedback(false);
return false;
}
+
+ return handleTouchEvent(event);
+ }
+
+ /**
+ * @return true if we want to handle touch events for quick scrub/switch and prevent proxying
+ * the event to the overview service.
+ */
+ @Override
+ public boolean onTouchEvent(MotionEvent event) {
+ return handleTouchEvent(event);
+ }
+
+ private boolean handleTouchEvent(MotionEvent event) {
+ final IOverviewProxy overviewProxy = mOverviewEventSender.getProxy();
+ final ButtonDispatcher homeButton = mNavigationBarView.getHomeButton();
if (mGestureDetector.onTouchEvent(event)) {
- // If the fling has been handled, then skip proxying the UP
+ // If the fling has been handled on UP, then skip proxying the UP
return true;
}
int action = event.getAction();
@@ -220,6 +235,7 @@
homeButton.setDelayTouchFeedback(false);
mTouchDownX = mTouchDownY = -1;
}
+ mAllowQuickSwitch = true;
break;
}
case MotionEvent.ACTION_MOVE: {
@@ -304,22 +320,6 @@
return mDraggingActive || mQuickScrubActive;
}
- /**
- * @return true if we want to handle touch events for quick scrub/switch and prevent proxying
- * the event to the overview service.
- */
- @Override
- public boolean onTouchEvent(MotionEvent event) {
- if (mGestureDetector.onTouchEvent(event)) {
- // If the fling has been handled, then skip proxying the UP
- return true;
- }
- if (event.getAction() == MotionEvent.ACTION_UP) {
- endQuickScrub();
- }
- return mDraggingActive || mQuickScrubActive;
- }
-
@Override
public void onDraw(Canvas canvas) {
int color = (int) mTrackColorEvaluator.evaluate(mDarkIntensity, mLightTrackColor,
@@ -420,6 +420,11 @@
mDraggingActive = false;
}
+ public void cancelQuickSwitch() {
+ mAllowQuickSwitch = false;
+ mHandler.removeCallbacks(mLongPressRunnable);
+ }
+
private int getDimensionPixelSize(Context context, @DimenRes int resId) {
return context.getResources().getDimensionPixelSize(resId);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
index 2b16e74..1438763 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -166,7 +166,10 @@
private boolean mScreenBlankingCallbackCalled;
private Callback mCallback;
private boolean mWallpaperSupportsAmbientMode;
+
+ // Scrim blanking callbacks
private Choreographer.FrameCallback mPendingFrameCallback;
+ private Runnable mBlankingTransitionRunnable;
private final WakeLock mWakeLock;
private boolean mWakeLockHeld;
@@ -249,10 +252,15 @@
mCurrentInFrontAlpha = state.getFrontAlpha();
mCurrentBehindAlpha = state.getBehindAlpha();
+ // Cancel blanking transitions that were pending before we requested a new state
if (mPendingFrameCallback != null) {
Choreographer.getInstance().removeFrameCallback(mPendingFrameCallback);
mPendingFrameCallback = null;
}
+ if (getHandler().hasCallbacks(mBlankingTransitionRunnable)) {
+ getHandler().removeCallbacks(mBlankingTransitionRunnable);
+ mBlankingTransitionRunnable = null;
+ }
// Showing/hiding the keyguard means that scrim colors have to be switched, not necessary
// to do the same when you're just showing the brightness mirror.
@@ -767,7 +775,8 @@
mScreenBlankingCallbackCalled = true;
}
- Runnable blankingCallback = () -> {
+ mBlankingTransitionRunnable = () -> {
+ mBlankingTransitionRunnable = null;
mPendingFrameCallback = null;
mBlankScreen = false;
// Try again.
@@ -776,7 +785,10 @@
// Setting power states can happen after we push out the frame. Make sure we
// stay fully opaque until the power state request reaches the lower levels.
- getHandler().postDelayed(blankingCallback, 100);
+ if (DEBUG) {
+ Log.d(TAG, "Waiting for the screen to turn on...");
+ }
+ getHandler().postDelayed(mBlankingTransitionRunnable, 500);
};
doOnTheNextFrame(mPendingFrameCallback);
}
@@ -888,6 +900,20 @@
}
}
+ /**
+ * Interrupts blanking transitions once the display notifies that it's already on.
+ */
+ public void onScreenTurnedOn() {
+ final Handler handler = getHandler();
+ if (handler.hasCallbacks(mBlankingTransitionRunnable)) {
+ if (DEBUG) {
+ Log.d(TAG, "Shorter blanking because screen turned on. All good.");
+ }
+ handler.removeCallbacks(mBlankingTransitionRunnable);
+ mBlankingTransitionRunnable.run();
+ }
+ }
+
public interface Callback {
default void onStart() {
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index 1bf719a..458518c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -140,7 +140,6 @@
import com.android.keyguard.ViewMediatorCallback;
import com.android.systemui.ActivityStarterDelegate;
import com.android.systemui.AutoReinflateContainer;
-import com.android.systemui.charging.WirelessChargingAnimation;
import com.android.systemui.DemoMode;
import com.android.systemui.Dependency;
import com.android.systemui.EventLogTags;
@@ -152,6 +151,7 @@
import com.android.systemui.SystemUIFactory;
import com.android.systemui.UiOffloadThread;
import com.android.systemui.assist.AssistManager;
+import com.android.systemui.charging.WirelessChargingAnimation;
import com.android.systemui.classifier.FalsingLog;
import com.android.systemui.classifier.FalsingManager;
import com.android.systemui.colorextraction.SysuiColorExtractor;
@@ -181,6 +181,7 @@
import com.android.systemui.statusbar.ActivatableNotificationView;
import com.android.systemui.statusbar.BackDropView;
import com.android.systemui.statusbar.CommandQueue;
+import com.android.systemui.statusbar.CrossFadeHelper;
import com.android.systemui.statusbar.DismissView;
import com.android.systemui.statusbar.DragDownHelper;
import com.android.systemui.statusbar.EmptyShadeView;
@@ -208,6 +209,7 @@
import com.android.systemui.statusbar.notification.AboveShelfObserver;
import com.android.systemui.statusbar.notification.ActivityLaunchAnimator;
import com.android.systemui.statusbar.notification.VisualStabilityManager;
+import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
import com.android.systemui.statusbar.phone.UnlockMethodCache.OnUnlockMethodChangedListener;
import com.android.systemui.statusbar.policy.BatteryController;
import com.android.systemui.statusbar.policy.BatteryController.BatteryStateChangeCallback;
@@ -219,6 +221,7 @@
import com.android.systemui.statusbar.policy.DeviceProvisionedController.DeviceProvisionedListener;
import com.android.systemui.statusbar.policy.ExtensionController;
import com.android.systemui.statusbar.policy.HeadsUpManager;
+import com.android.systemui.statusbar.policy.HeadsUpUtil;
import com.android.systemui.statusbar.policy.KeyguardMonitor;
import com.android.systemui.statusbar.policy.KeyguardMonitorImpl;
import com.android.systemui.statusbar.policy.KeyguardUserSwitcher;
@@ -809,15 +812,14 @@
.commit();
mIconController = Dependency.get(StatusBarIconController.class);
- mHeadsUpManager = new HeadsUpManager(context, mStatusBarWindow, mGroupManager);
- mHeadsUpManager.setBar(this);
+ mHeadsUpManager = new HeadsUpManagerPhone(context, mStatusBarWindow, mGroupManager, this,
+ mVisualStabilityManager);
mHeadsUpManager.addListener(this);
mHeadsUpManager.addListener(mNotificationPanel);
mHeadsUpManager.addListener(mGroupManager);
mHeadsUpManager.addListener(mVisualStabilityManager);
mNotificationPanel.setHeadsUpManager(mHeadsUpManager);
mGroupManager.setHeadsUpManager(mHeadsUpManager);
- mHeadsUpManager.setVisualStabilityManager(mVisualStabilityManager);
putComponent(HeadsUpManager.class, mHeadsUpManager);
mEntryManager.setUpWithPresenter(this, mStackScroller, this, mHeadsUpManager);
@@ -1348,7 +1350,8 @@
@Override
public void onPerformRemoveNotification(StatusBarNotification n) {
- if (mStackScroller.hasPulsingNotifications() && mHeadsUpManager.getAllEntries().isEmpty()) {
+ if (mStackScroller.hasPulsingNotifications() &&
+ !mHeadsUpManager.hasHeadsUpNotifications()) {
// We were showing a pulse for a notification, but no notifications are pulsing anymore.
// Finish the pulse.
mDozeScrimController.pulseOutNow();
@@ -2097,9 +2100,8 @@
}
public void maybeEscalateHeadsUp() {
- Collection<HeadsUpManager.HeadsUpEntry> entries = mHeadsUpManager.getAllEntries();
- for (HeadsUpManager.HeadsUpEntry entry : entries) {
- final StatusBarNotification sbn = entry.entry.notification;
+ mHeadsUpManager.getAllEntries().forEach(entry -> {
+ final StatusBarNotification sbn = entry.notification;
final Notification notification = sbn.getNotification();
if (notification.fullScreenIntent != null) {
if (DEBUG) {
@@ -2109,11 +2111,11 @@
EventLog.writeEvent(EventLogTags.SYSUI_HEADS_UP_ESCALATION,
sbn.getKey());
notification.fullScreenIntent.send();
- entry.entry.notifyFullScreenIntentLaunched();
+ entry.notifyFullScreenIntentLaunched();
} catch (PendingIntent.CanceledException e) {
}
}
- }
+ });
mHeadsUpManager.releaseAllImmediately();
}
@@ -2459,14 +2461,25 @@
}
@Override
- public void showChargingAnimation(int batteryLevel) {
- if (mDozing) {
- // ambient
- } else if (mKeyguardManager.isKeyguardLocked()) {
- // lockscreen
- } else {
+ public void showWirelessChargingAnimation(int batteryLevel) {
+ if (mDozing || mKeyguardManager.isKeyguardLocked()) {
+ // on ambient or lockscreen, hide notification panel
WirelessChargingAnimation.makeWirelessChargingAnimation(mContext, null,
- batteryLevel).show();
+ batteryLevel, new WirelessChargingAnimation.Callback() {
+ @Override
+ public void onAnimationStarting() {
+ CrossFadeHelper.fadeOut(mNotificationPanel, 1);
+ }
+
+ @Override
+ public void onAnimationEnded() {
+ CrossFadeHelper.fadeIn(mNotificationPanel);
+ }
+ }).show();
+ } else {
+ // workspace
+ WirelessChargingAnimation.makeWirelessChargingAnimation(mContext, null,
+ batteryLevel, null).show();
}
}
@@ -3882,6 +3895,7 @@
private void instantCollapseNotificationPanel() {
mNotificationPanel.instantCollapse();
+ runPostCollapseRunnables();
}
@Override
@@ -4393,6 +4407,7 @@
@Override
public void onScreenTurnedOn() {
+ mScrimController.onScreenTurnedOn();
}
@Override
@@ -4658,24 +4673,22 @@
@Override
public void onPulseStarted() {
callback.onPulseStarted();
- Collection<HeadsUpManager.HeadsUpEntry> pulsingEntries =
- mHeadsUpManager.getAllEntries();
- if (!pulsingEntries.isEmpty()) {
+ if (mHeadsUpManager.hasHeadsUpNotifications()) {
// Only pulse the stack scroller if there's actually something to show.
// Otherwise just show the always-on screen.
- setPulsing(pulsingEntries);
+ setPulsing(true);
}
}
@Override
public void onPulseFinished() {
callback.onPulseFinished();
- setPulsing(null);
+ setPulsing(false);
}
- private void setPulsing(Collection<HeadsUpManager.HeadsUpEntry> pulsing) {
+ private void setPulsing(boolean pulsing) {
mNotificationPanel.setPulsing(pulsing);
- mVisualStabilityManager.setPulsing(pulsing != null);
+ mVisualStabilityManager.setPulsing(pulsing);
mIgnoreTouchWhilePulsing = false;
}
}, reason);
@@ -4823,7 +4836,7 @@
// for heads up notifications
- protected HeadsUpManager mHeadsUpManager;
+ protected HeadsUpManagerPhone mHeadsUpManager;
private AboveShelfObserver mAboveShelfObserver;
@@ -4926,7 +4939,7 @@
// Release the HUN notification to the shade.
if (isPresenterFullyCollapsed()) {
- HeadsUpManager.setIsClickedNotification(row, true);
+ HeadsUpUtil.setIsClickedHeadsUpNotification(row, true);
}
//
// In most cases, when FLAG_AUTO_CANCEL is set, the notification will
@@ -5045,6 +5058,8 @@
} else if (!isPresenterFullyCollapsed()) {
instantCollapseNotificationPanel();
visibilityChanged(false);
+ } else {
+ runPostCollapseRunnables();
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
index 53dfb24..040d7ec 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
@@ -16,119 +16,69 @@
package com.android.systemui.statusbar.policy;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.content.Context;
import android.content.res.Resources;
import android.database.ContentObserver;
+import android.os.SystemClock;
import android.os.Handler;
import android.os.Looper;
-import android.os.SystemClock;
-import android.provider.Settings;
-import android.support.v4.util.ArraySet;
import android.util.ArrayMap;
+import android.provider.Settings;
import android.util.Log;
-import android.util.Pools;
-import android.view.View;
-import android.view.ViewTreeObserver;
import android.view.accessibility.AccessibilityEvent;
import com.android.internal.logging.MetricsLogger;
import com.android.systemui.R;
import com.android.systemui.statusbar.ExpandableNotificationRow;
import com.android.systemui.statusbar.NotificationData;
-import com.android.systemui.statusbar.StatusBarState;
-import com.android.systemui.statusbar.notification.VisualStabilityManager;
-import com.android.systemui.statusbar.phone.NotificationGroupManager;
-import com.android.systemui.statusbar.phone.StatusBar;
import java.io.FileDescriptor;
import java.io.PrintWriter;
-import java.util.ArrayList;
-import java.util.Collection;
+import java.util.Iterator;
+import java.util.stream.Stream;
import java.util.HashMap;
import java.util.HashSet;
-import java.util.Stack;
/**
* A manager which handles heads up notifications which is a special mode where
* they simply peek from the top of the screen.
*/
-public class HeadsUpManager implements ViewTreeObserver.OnComputeInternalInsetsListener,
- VisualStabilityManager.Callback {
+public class HeadsUpManager {
private static final String TAG = "HeadsUpManager";
private static final boolean DEBUG = false;
private static final String SETTING_HEADS_UP_SNOOZE_LENGTH_MS = "heads_up_snooze_length_ms";
- private static final int TAG_CLICKED_NOTIFICATION = R.id.is_clicked_heads_up_tag;
- private final int mHeadsUpNotificationDecay;
- private final int mMinimumDisplayTime;
+ protected final Clock mClock = new Clock();
+ protected final HashSet<OnHeadsUpChangedListener> mListeners = new HashSet<>();
+ protected final Handler mHandler = new Handler(Looper.getMainLooper());
- private final int mTouchAcceptanceDelay;
+ protected final Context mContext;
+
+ protected int mHeadsUpNotificationDecay;
+ protected int mMinimumDisplayTime;
+ protected int mTouchAcceptanceDelay;
+ protected int mSnoozeLengthMs;
+ protected boolean mHasPinnedNotification;
+ protected int mUser;
+
+ private final HashMap<String, HeadsUpEntry> mHeadsUpEntries = new HashMap<>();
private final ArrayMap<String, Long> mSnoozedPackages;
- private final HashSet<OnHeadsUpChangedListener> mListeners = new HashSet<>();
- private final int mDefaultSnoozeLengthMs;
- private final Handler mHandler = new Handler(Looper.getMainLooper());
- private final Pools.Pool<HeadsUpEntry> mEntryPool = new Pools.Pool<HeadsUpEntry>() {
- private Stack<HeadsUpEntry> mPoolObjects = new Stack<>();
-
- @Override
- public HeadsUpEntry acquire() {
- if (!mPoolObjects.isEmpty()) {
- return mPoolObjects.pop();
- }
- return new HeadsUpEntry();
- }
-
- @Override
- public boolean release(HeadsUpEntry instance) {
- instance.reset();
- mPoolObjects.push(instance);
- return true;
- }
- };
-
- private final View mStatusBarWindowView;
- private final int mStatusBarHeight;
- private final Context mContext;
- private final NotificationGroupManager mGroupManager;
- private StatusBar mBar;
- private int mSnoozeLengthMs;
- private ContentObserver mSettingsObserver;
- private HashMap<String, HeadsUpEntry> mHeadsUpEntries = new HashMap<>();
- private HashSet<String> mSwipedOutKeys = new HashSet<>();
- private int mUser;
- private Clock mClock;
- private boolean mReleaseOnExpandFinish;
- private boolean mTrackingHeadsUp;
- private HashSet<NotificationData.Entry> mEntriesToRemoveAfterExpand = new HashSet<>();
- private ArraySet<NotificationData.Entry> mEntriesToRemoveWhenReorderingAllowed
- = new ArraySet<>();
- private boolean mIsExpanded;
- private boolean mHasPinnedNotification;
- private int[] mTmpTwoArray = new int[2];
- private boolean mHeadsUpGoingAway;
- private boolean mWaitingOnCollapseWhenGoingAway;
- private boolean mIsObserving;
- private boolean mRemoteInputActive;
- private float mExpandedHeight;
- private VisualStabilityManager mVisualStabilityManager;
- private int mStatusBarState;
-
- public HeadsUpManager(final Context context, View statusBarWindowView,
- NotificationGroupManager groupManager) {
+ public HeadsUpManager(@NonNull final Context context) {
mContext = context;
- Resources resources = mContext.getResources();
- mTouchAcceptanceDelay = resources.getInteger(R.integer.touch_acceptance_delay);
- mSnoozedPackages = new ArrayMap<>();
- mDefaultSnoozeLengthMs = resources.getInteger(R.integer.heads_up_default_snooze_length_ms);
- mSnoozeLengthMs = mDefaultSnoozeLengthMs;
+ Resources resources = context.getResources();
mMinimumDisplayTime = resources.getInteger(R.integer.heads_up_notification_minimum_time);
mHeadsUpNotificationDecay = resources.getInteger(R.integer.heads_up_notification_decay);
- mClock = new Clock();
+ mTouchAcceptanceDelay = resources.getInteger(R.integer.touch_acceptance_delay);
+ mSnoozedPackages = new ArrayMap<>();
+ int defaultSnoozeLengthMs =
+ resources.getInteger(R.integer.heads_up_default_snooze_length_ms);
mSnoozeLengthMs = Settings.Global.getInt(context.getContentResolver(),
- SETTING_HEADS_UP_SNOOZE_LENGTH_MS, mDefaultSnoozeLengthMs);
- mSettingsObserver = new ContentObserver(mHandler) {
+ SETTING_HEADS_UP_SNOOZE_LENGTH_MS, defaultSnoozeLengthMs);
+ ContentObserver settingsObserver = new ContentObserver(mHandler) {
@Override
public void onChange(boolean selfChange) {
final int packageSnoozeLengthMs = Settings.Global.getInt(
@@ -141,48 +91,27 @@
};
context.getContentResolver().registerContentObserver(
Settings.Global.getUriFor(SETTING_HEADS_UP_SNOOZE_LENGTH_MS), false,
- mSettingsObserver);
- mStatusBarWindowView = statusBarWindowView;
- mGroupManager = groupManager;
- mStatusBarHeight = resources.getDimensionPixelSize(
- com.android.internal.R.dimen.status_bar_height);
+ settingsObserver);
}
- private void updateTouchableRegionListener() {
- boolean shouldObserve = mHasPinnedNotification || mHeadsUpGoingAway
- || mWaitingOnCollapseWhenGoingAway;
- if (shouldObserve == mIsObserving) {
- return;
- }
- if (shouldObserve) {
- mStatusBarWindowView.getViewTreeObserver().addOnComputeInternalInsetsListener(this);
- mStatusBarWindowView.requestLayout();
- } else {
- mStatusBarWindowView.getViewTreeObserver().removeOnComputeInternalInsetsListener(this);
- }
- mIsObserving = shouldObserve;
- }
-
- public void setBar(StatusBar bar) {
- mBar = bar;
- }
-
- public void addListener(OnHeadsUpChangedListener listener) {
+ /**
+ * Adds an OnHeadUpChangedListener to observe events.
+ */
+ public void addListener(@NonNull OnHeadsUpChangedListener listener) {
mListeners.add(listener);
}
- public void removeListener(OnHeadsUpChangedListener listener) {
+ /**
+ * Removes the OnHeadUpChangedListener from the observer list.
+ */
+ public void removeListener(@NonNull OnHeadsUpChangedListener listener) {
mListeners.remove(listener);
}
- public StatusBar getBar() {
- return mBar;
- }
-
/**
* Called when posting a new notification to the heads up.
*/
- public void showNotification(NotificationData.Entry headsUp) {
+ public void showNotification(@NonNull NotificationData.Entry headsUp) {
if (DEBUG) Log.v(TAG, "showNotification");
addHeadsUpEntry(headsUp);
updateNotification(headsUp, true);
@@ -192,7 +121,7 @@
/**
* Called when updating or posting a notification to the heads up.
*/
- public void updateNotification(NotificationData.Entry headsUp, boolean alert) {
+ public void updateNotification(@NonNull NotificationData.Entry headsUp, boolean alert) {
if (DEBUG) Log.v(TAG, "updateNotification");
headsUp.row.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
@@ -204,14 +133,13 @@
// with the groupmanager
return;
}
- headsUpEntry.updateEntry();
+ headsUpEntry.updateEntry(true /* updatePostTime */);
setEntryPinned(headsUpEntry, shouldHeadsUpBecomePinned(headsUp));
}
}
- private void addHeadsUpEntry(NotificationData.Entry entry) {
- HeadsUpEntry headsUpEntry = mEntryPool.acquire();
-
+ private void addHeadsUpEntry(@NonNull NotificationData.Entry entry) {
+ HeadsUpEntry headsUpEntry = createHeadsUpEntry();
// This will also add the entry to the sortedList
headsUpEntry.setEntry(entry);
mHeadsUpEntries.put(entry.key, headsUpEntry);
@@ -223,16 +151,17 @@
entry.row.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
}
- private boolean shouldHeadsUpBecomePinned(NotificationData.Entry entry) {
- return mStatusBarState != StatusBarState.KEYGUARD
- && !mIsExpanded || hasFullScreenIntent(entry);
+ protected boolean shouldHeadsUpBecomePinned(@NonNull NotificationData.Entry entry) {
+ return hasFullScreenIntent(entry);
}
- private boolean hasFullScreenIntent(NotificationData.Entry entry) {
+ protected boolean hasFullScreenIntent(@NonNull NotificationData.Entry entry) {
return entry.notification.getNotification().fullScreenIntent != null;
}
- private void setEntryPinned(HeadsUpEntry headsUpEntry, boolean isPinned) {
+ protected void setEntryPinned(
+ @NonNull HeadsUpManager.HeadsUpEntry headsUpEntry, boolean isPinned) {
+ if (DEBUG) Log.v(TAG, "setEntryPinned: " + isPinned);
ExpandableNotificationRow row = headsUpEntry.entry.row;
if (row.isPinned() != isPinned) {
row.setPinned(isPinned);
@@ -247,33 +176,35 @@
}
}
- private void removeHeadsUpEntry(NotificationData.Entry entry) {
+ protected void removeHeadsUpEntry(@NonNull NotificationData.Entry entry) {
HeadsUpEntry remove = mHeadsUpEntries.remove(entry.key);
+ onHeadsUpEntryRemoved(remove);
+ }
+
+ protected void onHeadsUpEntryRemoved(@NonNull HeadsUpEntry remove) {
+ NotificationData.Entry entry = remove.entry;
entry.row.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
entry.row.setHeadsUp(false);
setEntryPinned(remove, false /* isPinned */);
for (OnHeadsUpChangedListener listener : mListeners) {
listener.onHeadsUpStateChanged(entry, false);
}
- mEntryPool.release(remove);
+ releaseHeadsUpEntry(remove);
}
- public void removeAllHeadsUpEntries() {
- for (String key : mHeadsUpEntries.keySet()) {
- removeHeadsUpEntry(mHeadsUpEntries.get(key).entry);
- }
- }
-
- private void updatePinnedMode() {
+ protected void updatePinnedMode() {
boolean hasPinnedNotification = hasPinnedNotificationInternal();
if (hasPinnedNotification == mHasPinnedNotification) {
return;
}
+ if (DEBUG) {
+ Log.v(TAG, "Pinned mode changed: " + mHasPinnedNotification + " -> " +
+ hasPinnedNotification);
+ }
mHasPinnedNotification = hasPinnedNotification;
if (mHasPinnedNotification) {
MetricsLogger.count(mContext, "note_peek", 1);
}
- updateTouchableRegionListener();
for (OnHeadsUpChangedListener listener : mListeners) {
listener.onHeadsUpPinnedModeChanged(hasPinnedNotification);
}
@@ -285,47 +216,36 @@
* @return true if the notification was removed and false if it still needs to be kept around
* for a bit since it wasn't shown long enough
*/
- public boolean removeNotification(String key, boolean ignoreEarliestRemovalTime) {
- if (DEBUG) Log.v(TAG, "remove");
- if (wasShownLongEnough(key) || ignoreEarliestRemovalTime) {
- releaseImmediately(key);
- return true;
- } else {
- getHeadsUpEntry(key).removeAsSoonAsPossible();
- return false;
- }
+ public boolean removeNotification(@NonNull String key, boolean ignoreEarliestRemovalTime) {
+ if (DEBUG) Log.v(TAG, "removeNotification");
+ releaseImmediately(key);
+ return true;
}
- private boolean wasShownLongEnough(String key) {
- HeadsUpEntry headsUpEntry = getHeadsUpEntry(key);
- HeadsUpEntry topEntry = getTopEntry();
- if (mSwipedOutKeys.contains(key)) {
- // We always instantly dismiss views being manually swiped out.
- mSwipedOutKeys.remove(key);
- return true;
- }
- if (headsUpEntry != topEntry) {
- return true;
- }
- return headsUpEntry.wasShownLongEnough();
- }
-
- public boolean isHeadsUp(String key) {
+ /**
+ * Returns if the given notification is in the Heads Up Notification list or not.
+ */
+ public boolean isHeadsUp(@NonNull String key) {
return mHeadsUpEntries.containsKey(key);
}
/**
- * Push any current Heads Up notification down into the shade.
+ * Pushes any current Heads Up notification down into the shade.
*/
public void releaseAllImmediately() {
if (DEBUG) Log.v(TAG, "releaseAllImmediately");
- ArrayList<String> keys = new ArrayList<>(mHeadsUpEntries.keySet());
- for (String key : keys) {
- releaseImmediately(key);
+ Iterator<HeadsUpEntry> iterator = mHeadsUpEntries.values().iterator();
+ while (iterator.hasNext()) {
+ HeadsUpEntry entry = iterator.next();
+ iterator.remove();
+ onHeadsUpEntryRemoved(entry);
}
}
- public void releaseImmediately(String key) {
+ /**
+ * Pushes the given Heads Up notification down into the shade.
+ */
+ public void releaseImmediately(@NonNull String key) {
HeadsUpEntry headsUpEntry = getHeadsUpEntry(key);
if (headsUpEntry == null) {
return;
@@ -334,11 +254,14 @@
removeHeadsUpEntry(shadeEntry);
}
- public boolean isSnoozed(String packageName) {
+ /**
+ * Returns if the given notification is snoozed or not.
+ */
+ public boolean isSnoozed(@NonNull String packageName) {
final String key = snoozeKey(packageName, mUser);
Long snoozedUntil = mSnoozedPackages.get(key);
if (snoozedUntil != null) {
- if (snoozedUntil > SystemClock.elapsedRealtime()) {
+ if (snoozedUntil > mClock.currentTimeMillis()) {
if (DEBUG) Log.v(TAG, key + " snoozed");
return true;
}
@@ -347,39 +270,71 @@
return false;
}
+ /**
+ * Snoozes all current Heads Up Notifications.
+ */
public void snooze() {
for (String key : mHeadsUpEntries.keySet()) {
HeadsUpEntry entry = mHeadsUpEntries.get(key);
String packageName = entry.entry.notification.getPackageName();
mSnoozedPackages.put(snoozeKey(packageName, mUser),
- SystemClock.elapsedRealtime() + mSnoozeLengthMs);
+ mClock.currentTimeMillis() + mSnoozeLengthMs);
}
- mReleaseOnExpandFinish = true;
}
- private static String snoozeKey(String packageName, int user) {
+ @NonNull
+ private static String snoozeKey(@NonNull String packageName, int user) {
return user + "," + packageName;
}
- private HeadsUpEntry getHeadsUpEntry(String key) {
+ @Nullable
+ protected HeadsUpEntry getHeadsUpEntry(@NonNull String key) {
return mHeadsUpEntries.get(key);
}
- public NotificationData.Entry getEntry(String key) {
- return mHeadsUpEntries.get(key).entry;
+ /**
+ * Returns the entry of given Heads Up Notification.
+ *
+ * @param key Key of heads up notification
+ */
+ @Nullable
+ public NotificationData.Entry getEntry(@NonNull String key) {
+ HeadsUpEntry entry = mHeadsUpEntries.get(key);
+ return entry != null ? entry.entry : null;
}
- public Collection<HeadsUpEntry> getAllEntries() {
- return mHeadsUpEntries.values();
+ /**
+ * Returns the stream of all current Heads Up Notifications.
+ */
+ @NonNull
+ public Stream<NotificationData.Entry> getAllEntries() {
+ return mHeadsUpEntries.values().stream().map(headsUpEntry -> headsUpEntry.entry);
}
- public HeadsUpEntry getTopEntry() {
+ /**
+ * Returns the top Heads Up Notification, which appeares to show at first.
+ */
+ @Nullable
+ public NotificationData.Entry getTopEntry() {
+ HeadsUpEntry topEntry = getTopHeadsUpEntry();
+ return (topEntry != null) ? topEntry.entry : null;
+ }
+
+ /**
+ * Returns if any heads up notification is available or not.
+ */
+ public boolean hasHeadsUpNotifications() {
+ return !mHeadsUpEntries.isEmpty();
+ }
+
+ @Nullable
+ protected HeadsUpEntry getTopHeadsUpEntry() {
if (mHeadsUpEntries.isEmpty()) {
return null;
}
HeadsUpEntry topEntry = null;
for (HeadsUpEntry entry: mHeadsUpEntries.values()) {
- if (topEntry == null || entry.compareTo(topEntry) == -1) {
+ if (topEntry == null || entry.compareTo(topEntry) < 0) {
topEntry = entry;
}
}
@@ -387,56 +342,22 @@
}
/**
- * Decides whether a click is invalid for a notification, i.e it has not been shown long enough
- * that a user might have consciously clicked on it.
- *
- * @param key the key of the touched notification
- * @return whether the touch is invalid and should be discarded
+ * Sets the current user.
*/
- public boolean shouldSwallowClick(String key) {
- HeadsUpEntry entry = mHeadsUpEntries.get(key);
- if (entry != null && mClock.currentTimeMillis() < entry.postTime) {
- return true;
- }
- return false;
- }
-
- public void onComputeInternalInsets(ViewTreeObserver.InternalInsetsInfo info) {
- if (mIsExpanded || mBar.isBouncerShowing()) {
- // The touchable region is always the full area when expanded
- return;
- }
- if (mHasPinnedNotification) {
- ExpandableNotificationRow topEntry = getTopEntry().entry.row;
- if (topEntry.isChildInGroup()) {
- final ExpandableNotificationRow groupSummary
- = mGroupManager.getGroupSummary(topEntry.getStatusBarNotification());
- if (groupSummary != null) {
- topEntry = groupSummary;
- }
- }
- topEntry.getLocationOnScreen(mTmpTwoArray);
- int minX = mTmpTwoArray[0];
- int maxX = mTmpTwoArray[0] + topEntry.getWidth();
- int maxY = topEntry.getIntrinsicHeight();
-
- info.setTouchableInsets(ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION);
- info.touchableRegion.set(minX, 0, maxX, maxY);
- } else if (mHeadsUpGoingAway || mWaitingOnCollapseWhenGoingAway) {
- info.setTouchableInsets(ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION);
- info.touchableRegion.set(0, 0, mStatusBarWindowView.getWidth(), mStatusBarHeight);
- }
- }
-
public void setUser(int user) {
mUser = user;
}
- public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw, @NonNull String[] args) {
pw.println("HeadsUpManager state:");
+ dumpInternal(fd, pw, args);
+ }
+
+ protected void dumpInternal(
+ @NonNull FileDescriptor fd, @NonNull PrintWriter pw, @NonNull String[] args) {
pw.print(" mTouchAcceptanceDelay="); pw.println(mTouchAcceptanceDelay);
pw.print(" mSnoozeLengthMs="); pw.println(mSnoozeLengthMs);
- pw.print(" now="); pw.println(SystemClock.elapsedRealtime());
+ pw.print(" now="); pw.println(mClock.currentTimeMillis());
pw.print(" mUser="); pw.println(mUser);
for (HeadsUpEntry entry: mHeadsUpEntries.values()) {
pw.print(" HeadsUpEntry="); pw.println(entry.entry);
@@ -449,6 +370,9 @@
}
}
+ /**
+ * Returns if there are any pinned Heads Up Notifications or not.
+ */
public boolean hasPinnedHeadsUp() {
return mHasPinnedNotification;
}
@@ -464,14 +388,8 @@
}
/**
- * Notifies that a notification was swiped out and will be removed.
- *
- * @param key the notification key
+ * Unpins all pinned Heads Up Notifications.
*/
- public void addSwipedOutNotification(String key) {
- mSwipedOutKeys.add(key);
- }
-
public void unpinAll() {
for (String key : mHeadsUpEntries.keySet()) {
HeadsUpEntry entry = mHeadsUpEntries.get(key);
@@ -481,60 +399,13 @@
}
}
- public void onExpandingFinished() {
- if (mReleaseOnExpandFinish) {
- releaseAllImmediately();
- mReleaseOnExpandFinish = false;
- } else {
- for (NotificationData.Entry entry : mEntriesToRemoveAfterExpand) {
- if (isHeadsUp(entry.key)) {
- // Maybe the heads-up was removed already
- removeHeadsUpEntry(entry);
- }
- }
- }
- mEntriesToRemoveAfterExpand.clear();
- }
-
- public void setTrackingHeadsUp(boolean trackingHeadsUp) {
- mTrackingHeadsUp = trackingHeadsUp;
- }
-
- public boolean isTrackingHeadsUp() {
- return mTrackingHeadsUp;
- }
-
- public void setIsExpanded(boolean isExpanded) {
- if (isExpanded != mIsExpanded) {
- mIsExpanded = isExpanded;
- if (isExpanded) {
- // make sure our state is sane
- mWaitingOnCollapseWhenGoingAway = false;
- mHeadsUpGoingAway = false;
- updateTouchableRegionListener();
- }
- }
- }
-
/**
- * @return the height of the top heads up notification when pinned. This is different from the
- * intrinsic height, which also includes whether the notification is system expanded and
- * is mainly used when dragging down from a heads up notification.
+ * Returns the value of the tracking-heads-up flag. See the doc of {@code setTrackingHeadsUp} as
+ * well.
*/
- public int getTopHeadsUpPinnedHeight() {
- HeadsUpEntry topEntry = getTopEntry();
- if (topEntry == null || topEntry.entry == null) {
- return 0;
- }
- ExpandableNotificationRow row = topEntry.entry.row;
- if (row.isChildInGroup()) {
- final ExpandableNotificationRow groupSummary
- = mGroupManager.getGroupSummary(row.getStatusBarNotification());
- if (groupSummary != null) {
- row = groupSummary;
- }
- }
- return row.getPinnedHeadsUpHeight();
+ public boolean isTrackingHeadsUp() {
+ // Might be implemented in subclass.
+ return false;
}
/**
@@ -543,7 +414,7 @@
* @return -1 if the first argument should be ranked higher than the second, 1 if the second
* one should be ranked higher and 0 if they are equal.
*/
- public int compare(NotificationData.Entry a, NotificationData.Entry b) {
+ public int compare(@NonNull NotificationData.Entry a, @NonNull NotificationData.Entry b) {
HeadsUpEntry aEntry = getHeadsUpEntry(a.key);
HeadsUpEntry bEntry = getHeadsUpEntry(b.key);
if (aEntry == null || bEntry == null) {
@@ -553,147 +424,62 @@
}
/**
- * Set that we are exiting the headsUp pinned mode, but some notifications might still be
- * animating out. This is used to keep the touchable regions in a sane state.
- */
- public void setHeadsUpGoingAway(boolean headsUpGoingAway) {
- if (headsUpGoingAway != mHeadsUpGoingAway) {
- mHeadsUpGoingAway = headsUpGoingAway;
- if (!headsUpGoingAway) {
- waitForStatusBarLayout();
- }
- updateTouchableRegionListener();
- }
- }
-
- /**
- * We need to wait on the whole panel to collapse, before we can remove the touchable region
- * listener.
- */
- private void waitForStatusBarLayout() {
- mWaitingOnCollapseWhenGoingAway = true;
- mStatusBarWindowView.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
- @Override
- public void onLayoutChange(View v, int left, int top, int right, int bottom,
- int oldLeft,
- int oldTop, int oldRight, int oldBottom) {
- if (mStatusBarWindowView.getHeight() <= mStatusBarHeight) {
- mStatusBarWindowView.removeOnLayoutChangeListener(this);
- mWaitingOnCollapseWhenGoingAway = false;
- updateTouchableRegionListener();
- }
- }
- });
- }
-
- public static void setIsClickedNotification(View child, boolean clicked) {
- child.setTag(TAG_CLICKED_NOTIFICATION, clicked ? true : null);
- }
-
- public static boolean isClickedHeadsUpNotification(View child) {
- Boolean clicked = (Boolean) child.getTag(TAG_CLICKED_NOTIFICATION);
- return clicked != null && clicked;
- }
-
- public void setRemoteInputActive(NotificationData.Entry entry, boolean remoteInputActive) {
- HeadsUpEntry headsUpEntry = mHeadsUpEntries.get(entry.key);
- if (headsUpEntry != null && headsUpEntry.remoteInputActive != remoteInputActive) {
- headsUpEntry.remoteInputActive = remoteInputActive;
- if (remoteInputActive) {
- headsUpEntry.removeAutoRemovalCallbacks();
- } else {
- headsUpEntry.updateEntry(false /* updatePostTime */);
- }
- }
- }
-
- /**
* Set an entry to be expanded and therefore stick in the heads up area if it's pinned
* until it's collapsed again.
*/
- public void setExpanded(NotificationData.Entry entry, boolean expanded) {
- HeadsUpEntry headsUpEntry = mHeadsUpEntries.get(entry.key);
- if (headsUpEntry != null && headsUpEntry.expanded != expanded && entry.row.isPinned()) {
- headsUpEntry.expanded = expanded;
- if (expanded) {
- headsUpEntry.removeAutoRemovalCallbacks();
- } else {
- headsUpEntry.updateEntry(false /* updatePostTime */);
- }
+ public void setExpanded(@NonNull NotificationData.Entry entry, boolean expanded) {
+ HeadsUpManager.HeadsUpEntry headsUpEntry = mHeadsUpEntries.get(entry.key);
+ if (headsUpEntry != null && entry.row.isPinned()) {
+ headsUpEntry.expanded(expanded);
}
}
- @Override
- public void onReorderingAllowed() {
- mBar.getNotificationScrollLayout().setHeadsUpGoingAwayAnimationsAllowed(false);
- for (NotificationData.Entry entry : mEntriesToRemoveWhenReorderingAllowed) {
- if (isHeadsUp(entry.key)) {
- // Maybe the heads-up was removed already
- removeHeadsUpEntry(entry);
- }
- }
- mEntriesToRemoveWhenReorderingAllowed.clear();
- mBar.getNotificationScrollLayout().setHeadsUpGoingAwayAnimationsAllowed(true);
+ @NonNull
+ protected HeadsUpEntry createHeadsUpEntry() {
+ return new HeadsUpEntry();
}
- public void setVisualStabilityManager(VisualStabilityManager visualStabilityManager) {
- mVisualStabilityManager = visualStabilityManager;
- }
-
- public void setStatusBarState(int statusBarState) {
- mStatusBarState = statusBarState;
+ protected void releaseHeadsUpEntry(@NonNull HeadsUpEntry entry) {
+ entry.reset();
}
/**
* This represents a notification and how long it is in a heads up mode. It also manages its
* lifecycle automatically when created.
*/
- public class HeadsUpEntry implements Comparable<HeadsUpEntry> {
- public NotificationData.Entry entry;
+ protected class HeadsUpEntry implements Comparable<HeadsUpEntry> {
+ @Nullable public NotificationData.Entry entry;
public long postTime;
- public long earliestRemovaltime;
- private Runnable mRemoveHeadsUpRunnable;
public boolean remoteInputActive;
+ public long earliestRemovaltime;
public boolean expanded;
- public void setEntry(final NotificationData.Entry entry) {
+ @Nullable private Runnable mRemoveHeadsUpRunnable;
+
+ public void setEntry(@Nullable final NotificationData.Entry entry) {
+ setEntry(entry, null);
+ }
+
+ public void setEntry(@Nullable final NotificationData.Entry entry,
+ @Nullable Runnable removeHeadsUpRunnable) {
this.entry = entry;
+ this.mRemoveHeadsUpRunnable = removeHeadsUpRunnable;
// The actual post time will be just after the heads-up really slided in
postTime = mClock.currentTimeMillis() + mTouchAcceptanceDelay;
- mRemoveHeadsUpRunnable = new Runnable() {
- @Override
- public void run() {
- if (!mVisualStabilityManager.isReorderingAllowed()) {
- mEntriesToRemoveWhenReorderingAllowed.add(entry);
- mVisualStabilityManager.addReorderingAllowedCallback(HeadsUpManager.this);
- } else if (!mTrackingHeadsUp) {
- removeHeadsUpEntry(entry);
- } else {
- mEntriesToRemoveAfterExpand.add(entry);
- }
- }
- };
- updateEntry();
- }
-
- public void updateEntry() {
- updateEntry(true);
+ updateEntry(true /* updatePostTime */);
}
public void updateEntry(boolean updatePostTime) {
+ if (DEBUG) Log.v(TAG, "updateEntry");
+
long currentTime = mClock.currentTimeMillis();
earliestRemovaltime = currentTime + mMinimumDisplayTime;
if (updatePostTime) {
postTime = Math.max(postTime, currentTime);
}
removeAutoRemovalCallbacks();
- if (mEntriesToRemoveAfterExpand.contains(entry)) {
- mEntriesToRemoveAfterExpand.remove(entry);
- }
- if (mEntriesToRemoveWhenReorderingAllowed.contains(entry)) {
- mEntriesToRemoveWhenReorderingAllowed.remove(entry);
- }
+
if (!isSticky()) {
long finishTime = postTime + mHeadsUpNotificationDecay;
long removeDelay = Math.max(finishTime - currentTime, mMinimumDisplayTime);
@@ -707,7 +493,7 @@
}
@Override
- public int compareTo(HeadsUpEntry o) {
+ public int compareTo(@NonNull HeadsUpEntry o) {
boolean isPinned = entry.row.isPinned();
boolean otherPinned = o.entry.row.isPinned();
if (isPinned && !otherPinned) {
@@ -734,26 +520,29 @@
: -1;
}
- public void removeAutoRemovalCallbacks() {
- mHandler.removeCallbacks(mRemoveHeadsUpRunnable);
- }
-
- public boolean wasShownLongEnough() {
- return earliestRemovaltime < mClock.currentTimeMillis();
- }
-
- public void removeAsSoonAsPossible() {
- removeAutoRemovalCallbacks();
- mHandler.postDelayed(mRemoveHeadsUpRunnable,
- earliestRemovaltime - mClock.currentTimeMillis());
+ public void expanded(boolean expanded) {
+ this.expanded = expanded;
}
public void reset() {
- removeAutoRemovalCallbacks();
entry = null;
- mRemoveHeadsUpRunnable = null;
expanded = false;
remoteInputActive = false;
+ removeAutoRemovalCallbacks();
+ mRemoveHeadsUpRunnable = null;
+ }
+
+ public void removeAutoRemovalCallbacks() {
+ if (mRemoveHeadsUpRunnable != null)
+ mHandler.removeCallbacks(mRemoveHeadsUpRunnable);
+ }
+
+ public void removeAsSoonAsPossible() {
+ if (mRemoveHeadsUpRunnable != null) {
+ removeAutoRemovalCallbacks();
+ mHandler.postDelayed(mRemoveHeadsUpRunnable,
+ earliestRemovaltime - mClock.currentTimeMillis());
+ }
}
}
@@ -762,5 +551,4 @@
return SystemClock.elapsedRealtime();
}
}
-
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpUtil.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpUtil.java
new file mode 100644
index 0000000..1e3c123c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpUtil.java
@@ -0,0 +1,47 @@
+/*
+ * 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.systemui.statusbar.policy;
+
+import android.view.View;
+
+import com.android.systemui.R;
+
+/**
+ * A class of utility static methods for heads up notifications.
+ */
+public final class HeadsUpUtil {
+ private static final int TAG_CLICKED_NOTIFICATION = R.id.is_clicked_heads_up_tag;
+
+ /**
+ * Set the given view as clicked or not-clicked.
+ * @param view The view to be set the flag to.
+ * @param clicked True to set as clicked. False to not-clicked.
+ */
+ public static void setIsClickedHeadsUpNotification(View view, boolean clicked) {
+ view.setTag(TAG_CLICKED_NOTIFICATION, clicked ? true : null);
+ }
+
+ /**
+ * Check if the given view has the flag of "clicked notification"
+ * @param view The view to be checked.
+ * @return True if the view has clicked. False othrewise.
+ */
+ public static boolean isClickedHeadsUpNotification(View view) {
+ Boolean clicked = (Boolean) view.getTag(TAG_CLICKED_NOTIFICATION);
+ return clicked != null && clicked;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java
index 077c6c3..9d1c1e8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java
@@ -27,8 +27,12 @@
import android.metrics.LogMaker;
import android.os.AsyncTask;
import android.os.Bundle;
+import android.os.RemoteException;
import android.os.SystemClock;
+import android.os.VibrationEffect;
+import android.os.Vibrator;
import android.util.AttributeSet;
+import android.util.Log;
import android.util.TypedValue;
import android.view.HapticFeedbackConstants;
import android.view.InputDevice;
@@ -45,6 +49,7 @@
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.systemui.Dependency;
+import com.android.systemui.OverviewProxyService;
import com.android.systemui.R;
import com.android.systemui.plugins.statusbar.phone.NavBarButtonProvider.ButtonInterface;
@@ -52,18 +57,23 @@
import static android.view.accessibility.AccessibilityNodeInfo.ACTION_LONG_CLICK;
public class KeyButtonView extends ImageView implements ButtonInterface {
+ private static final String TAG = KeyButtonView.class.getSimpleName();
private final boolean mPlaySounds;
private int mContentDescriptionRes;
private long mDownTime;
private int mCode;
private int mTouchSlop;
+ private int mTouchDownX;
+ private int mTouchDownY;
private boolean mSupportsLongpress = true;
private AudioManager mAudioManager;
private boolean mGestureAborted;
private boolean mLongClicked;
private OnClickListener mOnClickListener;
private final KeyButtonRipple mRipple;
+ private final OverviewProxyService mOverviewProxyService;
+ private final Vibrator mVibrator;
private final MetricsLogger mMetricsLogger = Dependency.get(MetricsLogger.class);
private final Runnable mCheckLongPress = new Runnable() {
@@ -110,6 +120,8 @@
mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
mRipple = new KeyButtonRipple(context, this);
+ mVibrator = mContext.getSystemService(Vibrator.class);
+ mOverviewProxyService = Dependency.get(OverviewProxyService.class);
setBackground(mRipple);
}
@@ -189,6 +201,7 @@
}
public boolean onTouchEvent(MotionEvent ev) {
+ final boolean isProxyConnected = mOverviewProxyService.getProxy() != null;
final int action = ev.getAction();
int x, y;
if (action == MotionEvent.ACTION_DOWN) {
@@ -203,23 +216,34 @@
mDownTime = SystemClock.uptimeMillis();
mLongClicked = false;
setPressed(true);
+ mTouchDownX = (int) ev.getX();
+ mTouchDownY = (int) ev.getY();
if (mCode != 0) {
sendEvent(KeyEvent.ACTION_DOWN, 0, mDownTime);
} else {
// Provide the same haptic feedback that the system offers for virtual keys.
performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY);
}
- playSoundEffect(SoundEffectConstants.CLICK);
+ if (isProxyConnected) {
+ // Provide small vibration for quick step or immediate down feedback
+ AsyncTask.execute(() ->
+ mVibrator.vibrate(VibrationEffect
+ .get(VibrationEffect.EFFECT_TICK, false)));
+ } else {
+ playSoundEffect(SoundEffectConstants.CLICK);
+ }
removeCallbacks(mCheckLongPress);
postDelayed(mCheckLongPress, ViewConfiguration.getLongPressTimeout());
break;
case MotionEvent.ACTION_MOVE:
x = (int)ev.getX();
y = (int)ev.getY();
- setPressed(x >= -mTouchSlop
- && x < getWidth() + mTouchSlop
- && y >= -mTouchSlop
- && y < getHeight() + mTouchSlop);
+ boolean exceededTouchSlopX = Math.abs(x - mTouchDownX) > mTouchSlop;
+ boolean exceededTouchSlopY = Math.abs(y - mTouchDownY) > mTouchSlop;
+ if (exceededTouchSlopX || exceededTouchSlopY) {
+ setPressed(false);
+ removeCallbacks(mCheckLongPress);
+ }
break;
case MotionEvent.ACTION_CANCEL:
setPressed(false);
@@ -231,13 +255,25 @@
case MotionEvent.ACTION_UP:
final boolean doIt = isPressed() && !mLongClicked;
setPressed(false);
- // Always send a release ourselves because it doesn't seem to be sent elsewhere
- // and it feels weird to sometimes get a release haptic and other times not.
- if ((SystemClock.uptimeMillis() - mDownTime) > 150 && !mLongClicked) {
+ if (isProxyConnected) {
+ if (doIt) {
+ performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY);
+ playSoundEffect(SoundEffectConstants.CLICK);
+ }
+ } else if ((SystemClock.uptimeMillis() - mDownTime) > 150 && !mLongClicked) {
+ // Always send a release ourselves because it doesn't seem to be sent elsewhere
+ // and it feels weird to sometimes get a release haptic and other times not.
performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY_RELEASE);
}
if (mCode != 0) {
if (doIt) {
+ // If there was a pending remote recents animation, then we need to
+ // cancel the animation now before we handle the button itself
+ try {
+ ActivityManager.getService().cancelRecentsAnimation();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Could not cancel recents animation", e);
+ }
sendEvent(KeyEvent.ACTION_UP, 0);
sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/AmbientState.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/AmbientState.java
index 424858a..d7a810e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/AmbientState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/AmbientState.java
@@ -64,7 +64,7 @@
private boolean mPanelTracking;
private boolean mExpansionChanging;
private boolean mPanelFullWidth;
- private Collection<HeadsUpManager.HeadsUpEntry> mPulsing;
+ private boolean mPulsing;
private boolean mUnlockHintRunning;
private boolean mQsCustomizerShowing;
private int mIntrinsicPadding;
@@ -315,23 +315,18 @@
}
public boolean hasPulsingNotifications() {
- return mPulsing != null;
+ return mPulsing;
}
- public void setPulsing(Collection<HeadsUpManager.HeadsUpEntry> hasPulsing) {
+ public void setPulsing(boolean hasPulsing) {
mPulsing = hasPulsing;
}
public boolean isPulsing(NotificationData.Entry entry) {
- if (mPulsing == null) {
+ if (!mPulsing || mHeadsUpManager == null) {
return false;
}
- for (HeadsUpManager.HeadsUpEntry e : mPulsing) {
- if (e.entry == entry) {
- return true;
- }
- }
- return false;
+ return mHeadsUpManager.getAllEntries().anyMatch(e -> (e == entry));
}
public boolean isPanelTracking() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
index c114a6f..1b55a5b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
@@ -92,10 +92,11 @@
import com.android.systemui.statusbar.notification.FakeShadowView;
import com.android.systemui.statusbar.notification.NotificationUtils;
import com.android.systemui.statusbar.notification.VisibilityLocationProvider;
+import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
import com.android.systemui.statusbar.phone.NotificationGroupManager;
import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.phone.ScrimController;
-import com.android.systemui.statusbar.policy.HeadsUpManager;
+import com.android.systemui.statusbar.policy.HeadsUpUtil;
import com.android.systemui.statusbar.policy.ScrollAdapter;
import android.support.v4.graphics.ColorUtils;
@@ -288,7 +289,7 @@
private HashSet<View> mClearOverlayViewsWhenFinished = new HashSet<>();
private HashSet<Pair<ExpandableNotificationRow, Boolean>> mHeadsUpChangeAnimations
= new HashSet<>();
- private HeadsUpManager mHeadsUpManager;
+ private HeadsUpManagerPhone mHeadsUpManager;
private boolean mTrackingHeadsUp;
private ScrimController mScrimController;
private boolean mForceNoOverlappingRendering;
@@ -358,7 +359,7 @@
}
};
private PorterDuffXfermode mSrcMode = new PorterDuffXfermode(PorterDuff.Mode.SRC);
- private Collection<HeadsUpManager.HeadsUpEntry> mPulsing;
+ private boolean mPulsing;
private boolean mDrawBackgroundAsSrc;
private boolean mFadingOut;
private boolean mParentNotFullyVisible;
@@ -690,7 +691,7 @@
}
private void updateAlgorithmHeightAndPadding() {
- if (mPulsing != null) {
+ if (mPulsing) {
mTopPadding = mClockBottom;
} else {
mTopPadding = mAmbientState.isDark() ? mDarkTopPadding : mRegularTopPadding;
@@ -920,6 +921,27 @@
}
/**
+ * @return the height of the top heads up notification when pinned. This is different from the
+ * intrinsic height, which also includes whether the notification is system expanded and
+ * is mainly used when dragging down from a heads up notification.
+ */
+ private int getTopHeadsUpPinnedHeight() {
+ NotificationData.Entry topEntry = mHeadsUpManager.getTopEntry();
+ if (topEntry == null) {
+ return 0;
+ }
+ ExpandableNotificationRow row = topEntry.row;
+ if (row.isChildInGroup()) {
+ final ExpandableNotificationRow groupSummary
+ = mGroupManager.getGroupSummary(row.getStatusBarNotification());
+ if (groupSummary != null) {
+ row = groupSummary;
+ }
+ }
+ return row.getPinnedHeadsUpHeight();
+ }
+
+ /**
* @return the position from where the appear transition ends when expanding.
* Measured in absolute height.
*/
@@ -930,7 +952,7 @@
int minNotificationsForShelf = 1;
if (mTrackingHeadsUp
|| (mHeadsUpManager.hasPinnedHeadsUp() && !mAmbientState.isDark())) {
- appearPosition = mHeadsUpManager.getTopHeadsUpPinnedHeight();
+ appearPosition = getTopHeadsUpPinnedHeight();
minNotificationsForShelf = 2;
} else {
appearPosition = 0;
@@ -1198,9 +1220,9 @@
if (slidingChild instanceof ExpandableNotificationRow) {
ExpandableNotificationRow row = (ExpandableNotificationRow) slidingChild;
if (!mIsExpanded && row.isHeadsUp() && row.isPinned()
- && mHeadsUpManager.getTopEntry().entry.row != row
+ && mHeadsUpManager.getTopEntry().row != row
&& mGroupManager.getGroupSummary(
- mHeadsUpManager.getTopEntry().entry.row.getStatusBarNotification())
+ mHeadsUpManager.getTopEntry().row.getStatusBarNotification())
!= row) {
continue;
}
@@ -2120,7 +2142,7 @@
@Override
public boolean hasPulsingNotifications() {
- return mPulsing != null;
+ return mPulsing;
}
private void updateScrollability() {
@@ -2753,7 +2775,7 @@
}
private boolean isClickedHeadsUp(View child) {
- return HeadsUpManager.isClickedHeadsUpNotification(child);
+ return HeadsUpUtil.isClickedHeadsUpNotification(child);
}
/**
@@ -4258,7 +4280,7 @@
mAnimationFinishedRunnables.add(runnable);
}
- public void setHeadsUpManager(HeadsUpManager headsUpManager) {
+ public void setHeadsUpManager(HeadsUpManagerPhone headsUpManager) {
mHeadsUpManager = headsUpManager;
mAmbientState.setHeadsUpManager(headsUpManager);
}
@@ -4326,8 +4348,8 @@
return mIsExpanded;
}
- public void setPulsing(Collection<HeadsUpManager.HeadsUpEntry> pulsing, int clockBottom) {
- if (mPulsing == null && pulsing == null) {
+ public void setPulsing(boolean pulsing, int clockBottom) {
+ if (!mPulsing && !pulsing) {
return;
}
mPulsing = pulsing;
@@ -4466,7 +4488,7 @@
pw.println(String.format("[%s: pulsing=%s qsCustomizerShowing=%s visibility=%s"
+ " alpha:%f scrollY:%d]",
this.getClass().getSimpleName(),
- mPulsing != null ?"T":"f",
+ mPulsing ? "T":"f",
mAmbientState.isQsCustomizerShowing() ? "T":"f",
getVisibility() == View.VISIBLE ? "visible"
: getVisibility() == View.GONE ? "gone"
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/ViewState.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/ViewState.java
index 682b849..04a7bd7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/ViewState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/ViewState.java
@@ -30,7 +30,7 @@
import com.android.systemui.statusbar.ExpandableView;
import com.android.systemui.statusbar.notification.AnimatableProperty;
import com.android.systemui.statusbar.notification.PropertyAnimator;
-import com.android.systemui.statusbar.policy.HeadsUpManager;
+import com.android.systemui.statusbar.policy.HeadsUpUtil;
/**
* A state of a view. This can be used to apply a set of view properties to a view with
@@ -582,7 +582,7 @@
animator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
- HeadsUpManager.setIsClickedNotification(child, false);
+ HeadsUpUtil.setIsClickedHeadsUpNotification(child, false);
child.setTag(TAG_ANIMATOR_TRANSLATION_Y, null);
child.setTag(TAG_START_TRANSLATION_Y, null);
child.setTag(TAG_END_TRANSLATION_Y, null);
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
index c622677..fb5c447 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
@@ -37,6 +37,7 @@
import android.content.res.ColorStateList;
import android.content.res.Resources;
import android.graphics.Color;
+import android.graphics.PixelFormat;
import android.graphics.drawable.ColorDrawable;
import android.media.AudioManager;
import android.media.AudioSystem;
@@ -54,6 +55,7 @@
import android.util.Slog;
import android.util.SparseBooleanArray;
import android.view.ContextThemeWrapper;
+import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.AccessibilityDelegate;
@@ -176,7 +178,15 @@
mWindow.setTitle(VolumeDialogImpl.class.getSimpleName());
mWindow.setType(WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY);
mWindow.setWindowAnimations(com.android.internal.R.style.Animation_Toast);
+ final WindowManager.LayoutParams lp = mWindow.getAttributes();
+ lp.format = PixelFormat.TRANSLUCENT;
+ lp.setTitle(VolumeDialogImpl.class.getSimpleName());
+ lp.gravity = Gravity.RIGHT | Gravity.CENTER_VERTICAL;
+ lp.windowAnimations = -1;
+ mWindow.setAttributes(lp);
+ mWindow.setLayout(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
+ mDialog.setCanceledOnTouchOutside(true);
mDialog.setContentView(R.layout.volume_dialog);
mDialog.setOnShowListener(dialog -> {
mDialogView.setTranslationX(mDialogView.getWidth() / 2);
@@ -199,8 +209,8 @@
rescheduleTimeoutH();
return true;
});
- VolumeUiLayout hardwareLayout = VolumeUiLayout.get(mDialogView);
- hardwareLayout.setOutsideTouchListener(view -> dismiss(DISMISS_REASON_TOUCH_OUTSIDE));
+ VolumeUiLayout uiLayout = VolumeUiLayout.get(mDialogView);
+ uiLayout.updateRotation();
mDialogRowsView = mDialog.findViewById(R.id.volume_dialog_rows);
mFooter = mDialog.findViewById(R.id.footer);
@@ -1015,15 +1025,21 @@
}
@Override
+ public boolean onTouchEvent(MotionEvent event) {
+ if (isShowing()) {
+ if (event.getAction() == MotionEvent.ACTION_OUTSIDE) {
+ dismissH(Events.DISMISS_REASON_TOUCH_OUTSIDE);
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @Override
public boolean dispatchPopulateAccessibilityEvent(@NonNull AccessibilityEvent event) {
event.setClassName(getClass().getSuperclass().getName());
event.setPackageName(mContext.getPackageName());
- ViewGroup.LayoutParams params = getWindow().getAttributes();
- boolean isFullScreen = (params.width == ViewGroup.LayoutParams.MATCH_PARENT) &&
- (params.height == ViewGroup.LayoutParams.MATCH_PARENT);
- event.setFullScreen(isFullScreen);
-
if (event.getEventType() == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED) {
if (mShowing) {
event.getText().add(mContext.getString(
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeUiLayout.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeUiLayout.java
index 3d44381..0a3a2ab 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeUiLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeUiLayout.java
@@ -37,10 +37,6 @@
public class VolumeUiLayout extends FrameLayout {
private View mChild;
- private int mOldHeight;
- private boolean mAnimating;
- private AnimatorSet mAnimation;
- private boolean mHasOutsideTouch;
private int mRotation = ROTATION_NONE;
@Nullable
private DisplayCutout mDisplayCutout;
@@ -52,13 +48,11 @@
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
- getViewTreeObserver().addOnComputeInternalInsetsListener(mInsetsListener);
}
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
- getViewTreeObserver().removeOnComputeInternalInsetsListener(mInsetsListener);
mDisplayCutout = null;
}
@@ -68,16 +62,11 @@
if (mChild == null) {
if (getChildCount() != 0) {
mChild = getChildAt(0);
- mOldHeight = mChild.getMeasuredHeight();
updateRotation();
} else {
return;
}
}
- int newHeight = mChild.getMeasuredHeight();
- if (newHeight != mOldHeight) {
- animateChild(mOldHeight, newHeight);
- }
}
@Override
@@ -95,8 +84,13 @@
}
}
- private void updateRotation() {
+ public void updateRotation() {
setDisplayCutout();
+ if (mChild == null) {
+ if (getChildCount() != 0) {
+ mChild = getChildAt(0);
+ }
+ }
int rotation = RotationUtils.getRotation(getContext());
if (rotation != mRotation) {
updateSafeInsets(rotation);
@@ -144,43 +138,11 @@
return r.bottom - r.top;
}
-
- private void animateChild(int oldHeight, int newHeight) {
- if (true) return;
- if (mAnimating) {
- mAnimation.cancel();
- }
- mAnimating = true;
- mAnimation = new AnimatorSet();
- mAnimation.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- mAnimating = false;
- }
- });
- int fromTop = mChild.getTop();
- int fromBottom = mChild.getBottom();
- int toTop = fromTop - ((newHeight - oldHeight) / 2);
- int toBottom = fromBottom + ((newHeight - oldHeight) / 2);
- ObjectAnimator top = ObjectAnimator.ofInt(mChild, "top", fromTop, toTop);
- mAnimation.playTogether(top,
- ObjectAnimator.ofInt(mChild, "bottom", fromBottom, toBottom));
- }
-
-
@Override
public ViewOutlineProvider getOutlineProvider() {
return super.getOutlineProvider();
}
- public void setOutsideTouchListener(OnClickListener onClickListener) {
- mHasOutsideTouch = true;
- requestLayout();
- setOnClickListener(onClickListener);
- setClickable(true);
- setFocusable(true);
- }
-
public static VolumeUiLayout get(View v) {
if (v instanceof VolumeUiLayout) return (VolumeUiLayout) v;
if (v.getParent() instanceof View) {
@@ -188,16 +150,4 @@
}
return null;
}
-
- private final ViewTreeObserver.OnComputeInternalInsetsListener mInsetsListener = inoutInfo -> {
- if (mHasOutsideTouch || (mChild == null)) {
- inoutInfo.setTouchableInsets(
- ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_FRAME);
- return;
- }
- inoutInfo.setTouchableInsets(
- ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_CONTENT);
- inoutInfo.contentInsets.set(mChild.getLeft(), mChild.getTop(),
- 0, getBottom() - mChild.getBottom());
- };
}
diff --git a/packages/SystemUI/tests/Android.mk b/packages/SystemUI/tests/Android.mk
index 936ff51..59a7da6 100644
--- a/packages/SystemUI/tests/Android.mk
+++ b/packages/SystemUI/tests/Android.mk
@@ -65,7 +65,6 @@
LOCAL_JAVA_LIBRARIES := \
android.test.runner \
telephony-common \
- android.car \
android.test.base \
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenStateTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenStateTest.java
index 521d2e3..8c4fd73 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenStateTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenStateTest.java
@@ -28,6 +28,7 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.when;
import android.os.Looper;
import android.support.test.filters.SmallTest;
@@ -35,11 +36,14 @@
import android.view.Display;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.statusbar.phone.DozeParameters;
import com.android.systemui.utils.os.FakeHandler;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
@RunWith(AndroidJUnit4.class)
@SmallTest
@@ -48,12 +52,16 @@
DozeServiceFake mServiceFake;
DozeScreenState mScreen;
FakeHandler mHandlerFake;
+ @Mock
+ DozeParameters mDozeParameters;
@Before
public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ when(mDozeParameters.getDisplayNeedsBlanking()).thenReturn(true);
mServiceFake = new DozeServiceFake();
mHandlerFake = new FakeHandler(Looper.getMainLooper());
- mScreen = new DozeScreenState(mServiceFake, mHandlerFake);
+ mScreen = new DozeScreenState(mServiceFake, mHandlerFake, mDozeParameters);
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/power/PowerNotificationWarningsTest.java b/packages/SystemUI/tests/src/com/android/systemui/power/PowerNotificationWarningsTest.java
index 3e37cfe..bf6cc53 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/power/PowerNotificationWarningsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/power/PowerNotificationWarningsTest.java
@@ -151,22 +151,4 @@
verify(mMockNotificationManager, times(1)).cancelAsUser(anyString(),
eq(SystemMessage.NOTE_THERMAL_SHUTDOWN), any());
}
-
- @Test
- public void testGetTimeRemainingFormatted_roundsDownTo15() {
- mPowerNotificationWarnings.updateEstimate(
- new Estimate(TimeUnit.MINUTES.toMillis(57), true));
- String time = mPowerNotificationWarnings.getTimeRemainingFormatted();
-
- assertTrue("time:" + time + ", expected: " + FORMATTED_45M, time.equals(FORMATTED_45M));
- }
-
- @Test
- public void testGetTimeRemainingFormatted_keepsMinutesWhenZero() {
- mPowerNotificationWarnings.updateEstimate(
- new Estimate(TimeUnit.MINUTES.toMillis(65), true));
- String time = mPowerNotificationWarnings.getTimeRemainingFormatted();
-
- assertTrue("time:" + time + ", expected: " + FORMATTED_HOUR, time.equals(FORMATTED_HOUR));
- }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/power/PowerUITest.java b/packages/SystemUI/tests/src/com/android/systemui/power/PowerUITest.java
index 0a51e5a..4b455ba 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/power/PowerUITest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/power/PowerUITest.java
@@ -155,7 +155,7 @@
// hybrid but the threshold has been overriden to be too low
boolean shouldShow =
mPowerUI.shouldShowLowBatteryWarning(UNPLUGGED, UNPLUGGED, ABOVE_WARNING_BUCKET,
- ABOVE_WARNING_BUCKET, Long.MAX_VALUE, BELOW_HYBRID_THRESHOLD,
+ ABOVE_WARNING_BUCKET, BELOW_HYBRID_THRESHOLD,
POWER_SAVER_OFF, BatteryManager.BATTERY_HEALTH_GOOD);
assertFalse(shouldShow);
}
@@ -172,7 +172,7 @@
// hybrid since the threshold has been overriden to be much higher
boolean shouldShow =
mPowerUI.shouldShowLowBatteryWarning(UNPLUGGED, UNPLUGGED, ABOVE_WARNING_BUCKET,
- ABOVE_WARNING_BUCKET, Long.MAX_VALUE, ABOVE_HYBRID_THRESHOLD,
+ ABOVE_WARNING_BUCKET, ABOVE_HYBRID_THRESHOLD,
POWER_SAVER_OFF, BatteryManager.BATTERY_HEALTH_GOOD);
assertTrue(shouldShow);
}
@@ -188,7 +188,7 @@
// hybrid
boolean shouldShow =
mPowerUI.shouldShowLowBatteryWarning(UNPLUGGED, UNPLUGGED, ABOVE_WARNING_BUCKET,
- ABOVE_WARNING_BUCKET, Long.MAX_VALUE, BELOW_HYBRID_THRESHOLD,
+ ABOVE_WARNING_BUCKET, BELOW_HYBRID_THRESHOLD,
POWER_SAVER_OFF, BatteryManager.BATTERY_HEALTH_GOOD);
assertTrue(shouldShow);
}
@@ -203,7 +203,7 @@
// unplugged device that would show the non-hybrid notification and the hybrid
boolean shouldShow =
mPowerUI.shouldShowLowBatteryWarning(UNPLUGGED, UNPLUGGED, ABOVE_WARNING_BUCKET,
- BELOW_WARNING_BUCKET, Long.MAX_VALUE, BELOW_HYBRID_THRESHOLD,
+ BELOW_WARNING_BUCKET, BELOW_HYBRID_THRESHOLD,
POWER_SAVER_OFF, BatteryManager.BATTERY_HEALTH_GOOD);
assertTrue(shouldShow);
}
@@ -218,7 +218,7 @@
// unplugged device that would show the non-hybrid but not the hybrid
boolean shouldShow =
mPowerUI.shouldShowLowBatteryWarning(UNPLUGGED, UNPLUGGED, ABOVE_WARNING_BUCKET,
- BELOW_WARNING_BUCKET, Long.MAX_VALUE, ABOVE_HYBRID_THRESHOLD,
+ BELOW_WARNING_BUCKET, ABOVE_HYBRID_THRESHOLD,
POWER_SAVER_OFF, BatteryManager.BATTERY_HEALTH_GOOD);
assertTrue(shouldShow);
}
@@ -233,7 +233,7 @@
// unplugged device that would show the neither due to battery level being good
boolean shouldShow =
mPowerUI.shouldShowLowBatteryWarning(UNPLUGGED, UNPLUGGED, ABOVE_WARNING_BUCKET,
- ABOVE_WARNING_BUCKET, ABOVE_HYBRID_THRESHOLD, ABOVE_HYBRID_THRESHOLD,
+ ABOVE_WARNING_BUCKET, ABOVE_HYBRID_THRESHOLD,
POWER_SAVER_OFF, BatteryManager.BATTERY_HEALTH_GOOD);
assertFalse(shouldShow);
}
@@ -248,7 +248,7 @@
// plugged device that would show the neither due to being plugged
boolean shouldShow =
mPowerUI.shouldShowLowBatteryWarning(!UNPLUGGED, UNPLUGGED, ABOVE_WARNING_BUCKET,
- BELOW_WARNING_BUCKET, ABOVE_HYBRID_THRESHOLD, BELOW_HYBRID_THRESHOLD,
+ BELOW_WARNING_BUCKET, BELOW_HYBRID_THRESHOLD,
POWER_SAVER_OFF, BatteryManager.BATTERY_HEALTH_GOOD);
assertFalse(shouldShow);
}
@@ -263,7 +263,7 @@
// Unknown battery status device that would show the neither due
boolean shouldShow =
mPowerUI.shouldShowLowBatteryWarning(UNPLUGGED, UNPLUGGED, ABOVE_WARNING_BUCKET,
- BELOW_WARNING_BUCKET, ABOVE_HYBRID_THRESHOLD, BELOW_HYBRID_THRESHOLD,
+ BELOW_WARNING_BUCKET, BELOW_HYBRID_THRESHOLD,
!POWER_SAVER_OFF, BatteryManager.BATTERY_STATUS_UNKNOWN);
assertFalse(shouldShow);
}
@@ -278,12 +278,31 @@
// BatterySaverEnabled device that would show the neither due to battery saver
boolean shouldShow =
mPowerUI.shouldShowLowBatteryWarning(UNPLUGGED, UNPLUGGED, ABOVE_WARNING_BUCKET,
- BELOW_WARNING_BUCKET, ABOVE_HYBRID_THRESHOLD, BELOW_HYBRID_THRESHOLD,
+ BELOW_WARNING_BUCKET, BELOW_HYBRID_THRESHOLD,
!POWER_SAVER_OFF, BatteryManager.BATTERY_HEALTH_GOOD);
assertFalse(shouldShow);
}
@Test
+ public void testShouldShowLowBatteryWarning_onlyShowsOncePerChargeCycle() {
+ mPowerUI.start();
+ when(mEnhancedEstimates.isHybridNotificationEnabled()).thenReturn(true);
+ when(mEnhancedEstimates.getLowWarningThreshold()).thenReturn(PowerUI.THREE_HOURS_IN_MILLIS);
+ when(mEnhancedEstimates.getSevereWarningThreshold()).thenReturn(ONE_HOUR_MILLIS);
+ when(mEnhancedEstimates.getEstimate())
+ .thenReturn(new Estimate(BELOW_HYBRID_THRESHOLD, true));
+ mPowerUI.mBatteryStatus = BatteryManager.BATTERY_HEALTH_GOOD;
+
+ mPowerUI.maybeShowBatteryWarning(UNPLUGGED, UNPLUGGED, ABOVE_WARNING_BUCKET,
+ ABOVE_WARNING_BUCKET);
+ boolean shouldShow =
+ mPowerUI.shouldShowLowBatteryWarning(UNPLUGGED, UNPLUGGED, ABOVE_WARNING_BUCKET,
+ ABOVE_WARNING_BUCKET, BELOW_HYBRID_THRESHOLD,
+ POWER_SAVER_OFF, BatteryManager.BATTERY_HEALTH_GOOD);
+ assertFalse(shouldShow);
+ }
+
+ @Test
public void testShouldDismissLowBatteryWarning_dismissWhenPowerSaverEnabled() {
mPowerUI.start();
when(mEnhancedEstimates.isHybridNotificationEnabled()).thenReturn(true);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java
index 6e7477f..f3c1171 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java
@@ -32,6 +32,7 @@
import com.android.systemui.statusbar.notification.AboveShelfObserver;
import com.android.systemui.statusbar.notification.InflationException;
import com.android.systemui.statusbar.notification.NotificationInflaterTest;
+import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
import com.android.systemui.statusbar.phone.NotificationGroupManager;
import com.android.systemui.statusbar.policy.HeadsUpManager;
@@ -51,7 +52,7 @@
public NotificationTestHelper(Context context) {
mContext = context;
mInstrumentation = InstrumentationRegistry.getInstrumentation();
- mHeadsUpManager = new HeadsUpManager(mContext, null, mGroupManager);
+ mHeadsUpManager = new HeadsUpManagerPhone(mContext, null, mGroupManager, null, null);
}
public ExpandableNotificationRow createRow() throws Exception {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java
new file mode 100644
index 0000000..aa991cb
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java
@@ -0,0 +1,216 @@
+/*
+ * Copyright (C) 2018 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.systemui.statusbar.phone;
+
+import android.app.ActivityManager;
+import android.app.Instrumentation;
+import android.app.Notification;
+import android.os.UserHandle;
+import android.view.View;
+import android.service.notification.StatusBarNotification;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+
+import com.android.systemui.R;
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.statusbar.ExpandableNotificationRow;
+import com.android.systemui.statusbar.NotificationData;
+import com.android.systemui.statusbar.StatusBarIconView;
+import com.android.systemui.statusbar.notification.VisualStabilityManager;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+import static junit.framework.Assert.assertNotNull;
+import static junit.framework.Assert.assertNull;
+import static junit.framework.Assert.assertTrue;
+import static junit.framework.Assert.assertFalse;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+public class HeadsUpManagerPhoneTest extends SysuiTestCase {
+ @Rule public MockitoRule rule = MockitoJUnit.rule();
+
+ private static final String TEST_PACKAGE_NAME = "test";
+ private static final int TEST_UID = 0;
+
+ private HeadsUpManagerPhone mHeadsUpManager;
+
+ private NotificationData.Entry mEntry;
+ private StatusBarNotification mSbn;
+
+ @Mock private NotificationGroupManager mGroupManager;
+ @Mock private View mStatusBarWindowView;
+ @Mock private StatusBar mBar;
+ @Mock private ExpandableNotificationRow mRow;
+ @Mock private VisualStabilityManager mVSManager;
+
+ @Before
+ public void setUp() {
+ when(mVSManager.isReorderingAllowed()).thenReturn(true);
+
+ mHeadsUpManager = new HeadsUpManagerPhone(
+ mContext, mStatusBarWindowView, mGroupManager, mBar, mVSManager);
+
+ Notification.Builder n = new Notification.Builder(mContext, "")
+ .setSmallIcon(R.drawable.ic_person)
+ .setContentTitle("Title")
+ .setContentText("Text");
+ mSbn = new StatusBarNotification(TEST_PACKAGE_NAME, TEST_PACKAGE_NAME, 0, null, TEST_UID,
+ 0, n.build(), new UserHandle(ActivityManager.getCurrentUser()), null, 0);
+
+ mEntry = new NotificationData.Entry(mSbn);
+ mEntry.row = mRow;
+ mEntry.expandedIcon = mock(StatusBarIconView.class);
+ }
+
+ @Test
+ public void testBasicOperations() {
+ // Check the initial state.
+ assertNull(mHeadsUpManager.getEntry(mEntry.key));
+ assertNull(mHeadsUpManager.getTopEntry());
+ assertEquals(0, mHeadsUpManager.getAllEntries().count());
+ assertFalse(mHeadsUpManager.hasHeadsUpNotifications());
+
+ // Add a notification.
+ mHeadsUpManager.showNotification(mEntry);
+
+ assertEquals(mEntry, mHeadsUpManager.getEntry(mEntry.key));
+ assertEquals(mEntry, mHeadsUpManager.getTopEntry());
+ assertEquals(1, mHeadsUpManager.getAllEntries().count());
+ assertTrue(mHeadsUpManager.hasHeadsUpNotifications());
+
+ // Update the notification.
+ mHeadsUpManager.updateNotification(mEntry, false);
+
+ assertEquals(mEntry, mHeadsUpManager.getEntry(mEntry.key));
+ assertEquals(mEntry, mHeadsUpManager.getTopEntry());
+ assertEquals(1, mHeadsUpManager.getAllEntries().count());
+ assertTrue(mHeadsUpManager.hasHeadsUpNotifications());
+
+ // Try to remove but defer, since the notification is currenlt visible on display.
+ mHeadsUpManager.removeNotification(mEntry.key, false /* ignoreEarliestRemovalTime */);
+
+ assertEquals(mEntry, mHeadsUpManager.getEntry(mEntry.key));
+ assertEquals(mEntry, mHeadsUpManager.getTopEntry());
+ assertEquals(1, mHeadsUpManager.getAllEntries().count());
+ assertTrue(mHeadsUpManager.hasHeadsUpNotifications());
+
+ // Remove forcibly with ignoreEarliestRemovalTime = true.
+ mHeadsUpManager.removeNotification(mEntry.key, true /* ignoreEarliestRemovalTime */);
+
+ // Check the initial state.
+ assertNull(mHeadsUpManager.getEntry(mEntry.key));
+ assertNull(mHeadsUpManager.getTopEntry());
+ assertEquals(0, mHeadsUpManager.getAllEntries().count());
+ assertFalse(mHeadsUpManager.hasHeadsUpNotifications());
+ }
+
+ @Test
+ public void testsTimeoutRemoval() {
+ mHeadsUpManager.removeMinimumDisplayTimeForTesting();
+
+ // Check the initial state.
+ assertNull(mHeadsUpManager.getEntry(mEntry.key));
+ assertNull(mHeadsUpManager.getTopEntry());
+ assertEquals(0, mHeadsUpManager.getAllEntries().count());
+ assertFalse(mHeadsUpManager.hasHeadsUpNotifications());
+
+ Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
+
+ // Run the code on the main thready, not to run an async operations.
+ instrumentation.runOnMainSync(() -> {
+ // Add a notification.
+ mHeadsUpManager.showNotification(mEntry);
+
+ // Ensure the head up is visible before timeout.
+ assertNotNull(mHeadsUpManager.getEntry(mEntry.key));
+ assertNotNull(mHeadsUpManager.getTopEntry());
+ assertEquals(1, mHeadsUpManager.getAllEntries().count());
+ assertTrue(mHeadsUpManager.hasHeadsUpNotifications());
+ });
+ // Wait for the async operations, which removes the heads up notification.
+ waitForIdleSync();
+
+ assertNull(mHeadsUpManager.getEntry(mEntry.key));
+ assertNull(mHeadsUpManager.getTopEntry());
+ assertEquals(0, mHeadsUpManager.getAllEntries().count());
+ assertFalse(mHeadsUpManager.hasHeadsUpNotifications());
+ }
+
+ @Test
+ public void releaseImmediately() {
+ // Check the initial state.
+ assertNull(mHeadsUpManager.getEntry(mEntry.key));
+ assertNull(mHeadsUpManager.getTopEntry());
+ assertEquals(0, mHeadsUpManager.getAllEntries().count());
+ assertFalse(mHeadsUpManager.hasHeadsUpNotifications());
+
+ // Add a notification.
+ mHeadsUpManager.showNotification(mEntry);
+
+ assertEquals(mEntry, mHeadsUpManager.getEntry(mEntry.key));
+ assertEquals(mEntry, mHeadsUpManager.getTopEntry());
+ assertEquals(1, mHeadsUpManager.getAllEntries().count());
+ assertTrue(mHeadsUpManager.hasHeadsUpNotifications());
+
+ // Remove but defer, since the notification is visible on display.
+ mHeadsUpManager.releaseImmediately(mEntry.key);
+
+ assertNull(mHeadsUpManager.getEntry(mEntry.key));
+ assertNull(mHeadsUpManager.getTopEntry());
+ assertEquals(0, mHeadsUpManager.getAllEntries().count());
+ assertFalse(mHeadsUpManager.hasHeadsUpNotifications());
+ }
+
+ @Test
+ public void releaseAllImmediately() {
+ // Check the initial state.
+ assertNull(mHeadsUpManager.getEntry(mEntry.key));
+ assertNull(mHeadsUpManager.getTopEntry());
+ assertEquals(0, mHeadsUpManager.getAllEntries().count());
+ assertFalse(mHeadsUpManager.hasHeadsUpNotifications());
+
+ // Add a notification.
+ mHeadsUpManager.showNotification(mEntry);
+
+ assertEquals(mEntry, mHeadsUpManager.getEntry(mEntry.key));
+ assertEquals(mEntry, mHeadsUpManager.getTopEntry());
+ assertEquals(1, mHeadsUpManager.getAllEntries().count());
+ assertTrue(mHeadsUpManager.hasHeadsUpNotifications());
+
+ // Remove but defer, since the notification is visible on display.
+ mHeadsUpManager.releaseAllImmediately();
+
+ assertNull(mHeadsUpManager.getEntry(mEntry.key));
+ assertNull(mHeadsUpManager.getTopEntry());
+ assertEquals(0, mHeadsUpManager.getAllEntries().count());
+ assertFalse(mHeadsUpManager.hasHeadsUpNotifications());
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
index bdf9b1f..31442af 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
@@ -86,8 +86,8 @@
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.notification.ActivityLaunchAnimator;
import com.android.systemui.statusbar.notification.VisualStabilityManager;
+import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
-import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.statusbar.policy.KeyguardMonitor;
import com.android.systemui.statusbar.policy.KeyguardMonitorImpl;
import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
@@ -110,7 +110,7 @@
@Mock private UnlockMethodCache mUnlockMethodCache;
@Mock private KeyguardIndicationController mKeyguardIndicationController;
@Mock private NotificationStackScrollLayout mStackScroller;
- @Mock private HeadsUpManager mHeadsUpManager;
+ @Mock private HeadsUpManagerPhone mHeadsUpManager;
@Mock private SystemServicesProxy mSystemServicesProxy;
@Mock private NotificationPanelView mNotificationPanelView;
@Mock private IStatusBarService mBarService;
@@ -588,7 +588,7 @@
static class TestableStatusBar extends StatusBar {
public TestableStatusBar(StatusBarKeyguardViewManager man,
UnlockMethodCache unlock, KeyguardIndicationController key,
- NotificationStackScrollLayout stack, HeadsUpManager hum,
+ NotificationStackScrollLayout stack, HeadsUpManagerPhone hum,
PowerManager pm, NotificationPanelView panelView,
IStatusBarService barService, NotificationListener notificationListener,
NotificationLogger notificationLogger,
@@ -650,7 +650,7 @@
public void setUpForTest(NotificationPresenter presenter,
NotificationListContainer listContainer,
Callback callback,
- HeadsUpManager headsUpManager,
+ HeadsUpManagerPhone headsUpManager,
NotificationData notificationData) {
super.setUpWithPresenter(presenter, listContainer, callback, headsUpManager);
mNotificationData = notificationData;
diff --git a/rs/jni/android_renderscript_RenderScript.cpp b/rs/jni/android_renderscript_RenderScript.cpp
index 52d0e08e..b32be73 100644
--- a/rs/jni/android_renderscript_RenderScript.cpp
+++ b/rs/jni/android_renderscript_RenderScript.cpp
@@ -24,9 +24,8 @@
#include <utils/misc.h>
#include <inttypes.h>
-#include <android-base/macros.h>
#include <androidfw/Asset.h>
-#include <androidfw/AssetManager2.h>
+#include <androidfw/AssetManager.h>
#include <androidfw/ResourceTypes.h>
#include <android-base/macros.h>
@@ -1665,22 +1664,18 @@
static jlong
nFileA3DCreateFromAsset(JNIEnv *_env, jobject _this, jlong con, jobject _assetMgr, jstring _path)
{
- Guarded<AssetManager2>* mgr = AssetManagerForJavaObject(_env, _assetMgr);
+ AssetManager* mgr = assetManagerForJavaObject(_env, _assetMgr);
if (mgr == nullptr) {
return 0;
}
AutoJavaStringToUTF8 str(_env, _path);
- std::unique_ptr<Asset> asset;
- {
- ScopedLock<AssetManager2> locked_mgr(*mgr);
- asset = locked_mgr->Open(str.c_str(), Asset::ACCESS_BUFFER);
- if (asset == nullptr) {
- return 0;
- }
+ Asset* asset = mgr->open(str.c_str(), Asset::ACCESS_BUFFER);
+ if (asset == nullptr) {
+ return 0;
}
- jlong id = (jlong)(uintptr_t)rsaFileA3DCreateFromAsset((RsContext)con, asset.release());
+ jlong id = (jlong)(uintptr_t)rsaFileA3DCreateFromAsset((RsContext)con, asset);
return id;
}
@@ -1757,25 +1752,22 @@
nFontCreateFromAsset(JNIEnv *_env, jobject _this, jlong con, jobject _assetMgr, jstring _path,
jfloat fontSize, jint dpi)
{
- Guarded<AssetManager2>* mgr = AssetManagerForJavaObject(_env, _assetMgr);
+ AssetManager* mgr = assetManagerForJavaObject(_env, _assetMgr);
if (mgr == nullptr) {
return 0;
}
AutoJavaStringToUTF8 str(_env, _path);
- std::unique_ptr<Asset> asset;
- {
- ScopedLock<AssetManager2> locked_mgr(*mgr);
- asset = locked_mgr->Open(str.c_str(), Asset::ACCESS_BUFFER);
- if (asset == nullptr) {
- return 0;
- }
+ Asset* asset = mgr->open(str.c_str(), Asset::ACCESS_BUFFER);
+ if (asset == nullptr) {
+ return 0;
}
jlong id = (jlong)(uintptr_t)rsFontCreateFromMemory((RsContext)con,
str.c_str(), str.length(),
fontSize, dpi,
asset->getBuffer(false), asset->getLength());
+ delete asset;
return id;
}
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index fc6058c..eba9830 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -1020,7 +1020,7 @@
// Disconnect from services for the old user.
UserState oldUserState = getCurrentUserStateLocked();
- oldUserState.onSwitchToAnotherUser();
+ oldUserState.onSwitchToAnotherUserLocked();
// Disable the local managers for the old user.
if (oldUserState.mUserClients.getRegisteredCallbackCount() > 0) {
@@ -1349,21 +1349,29 @@
private void updateRelevantEventsLocked(UserState userState) {
mMainHandler.post(() -> {
broadcastToClients(userState, ignoreRemoteException(client -> {
- int relevantEventTypes = computeRelevantEventTypes(userState, client);
+ int relevantEventTypes;
+ boolean changed = false;
+ synchronized (mLock) {
+ relevantEventTypes = computeRelevantEventTypesLocked(userState, client);
- if (client.mLastSentRelevantEventTypes != relevantEventTypes) {
- client.mLastSentRelevantEventTypes = relevantEventTypes;
+ if (client.mLastSentRelevantEventTypes != relevantEventTypes) {
+ client.mLastSentRelevantEventTypes = relevantEventTypes;
+ changed = true;
+ }
+ }
+ if (changed) {
client.mCallback.setRelevantEventTypes(relevantEventTypes);
}
}));
});
}
- private int computeRelevantEventTypes(UserState userState, Client client) {
+ private int computeRelevantEventTypesLocked(UserState userState, Client client) {
int relevantEventTypes = 0;
- // Use iterator for thread-safety
- for (AccessibilityServiceConnection service : userState.mBoundServices) {
+ int serviceCount = userState.mBoundServices.size();
+ for (int i = 0; i < serviceCount; i++) {
+ AccessibilityServiceConnection service = userState.mBoundServices.get(i);
relevantEventTypes |= isClientInPackageWhitelist(service.getServiceInfo(), client)
? service.getRelevantEventTypes()
: 0;
@@ -3616,7 +3624,9 @@
private Client(IAccessibilityManagerClient callback, int clientUid, UserState userState) {
mCallback = callback;
mPackageNames = mPackageManager.getPackagesForUid(clientUid);
- mLastSentRelevantEventTypes = computeRelevantEventTypes(userState, this);
+ synchronized (mLock) {
+ mLastSentRelevantEventTypes = computeRelevantEventTypesLocked(userState, this);
+ }
}
}
@@ -3635,8 +3645,7 @@
// Transient state.
- public final CopyOnWriteArrayList<AccessibilityServiceConnection> mBoundServices =
- new CopyOnWriteArrayList<>();
+ public final ArrayList<AccessibilityServiceConnection> mBoundServices = new ArrayList<>();
public final Map<ComponentName, AccessibilityServiceConnection> mComponentNameToServiceMap =
new HashMap<>();
@@ -3698,7 +3707,7 @@
return !mBoundServices.isEmpty() || !mBindingServices.isEmpty();
}
- public void onSwitchToAnotherUser() {
+ public void onSwitchToAnotherUserLocked() {
// Unbind all services.
unbindAllServicesLocked(this);
diff --git a/services/accessibility/java/com/android/server/accessibility/MagnificationController.java b/services/accessibility/java/com/android/server/accessibility/MagnificationController.java
index a70b88e..c36bb6d 100644
--- a/services/accessibility/java/com/android/server/accessibility/MagnificationController.java
+++ b/services/accessibility/java/com/android/server/accessibility/MagnificationController.java
@@ -901,6 +901,7 @@
}
}
+ @GuardedBy("mLock")
private void setMagnificationSpecLocked(MagnificationSpec spec) {
if (mEnabled) {
if (DEBUG_SET_MAGNIFICATION_SPEC) {
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
index 72c3c94..d5a722b 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
@@ -306,6 +306,7 @@
*
* @return service instance.
*/
+ @GuardedBy("mLock")
@NonNull
AutofillManagerServiceImpl getServiceForUserLocked(int userId) {
final int resolvedUserId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
@@ -325,6 +326,7 @@
*
* @return service instance or {@code null} if not already present
*/
+ @GuardedBy("mLock")
@Nullable
AutofillManagerServiceImpl peekServiceForUserLocked(int userId) {
final int resolvedUserId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
@@ -474,6 +476,7 @@
/**
* Removes a cached service for a given user.
*/
+ @GuardedBy("mLock")
private void removeCachedServiceLocked(int userId) {
final AutofillManagerServiceImpl service = peekServiceForUserLocked(userId);
if (service != null) {
@@ -485,6 +488,7 @@
/**
* Updates a cached service for a given user.
*/
+ @GuardedBy("mLock")
private void updateCachedServiceLocked(int userId) {
updateCachedServiceLocked(userId, mDisabledUsers.get(userId));
}
@@ -492,6 +496,7 @@
/**
* Updates a cached service for a given user.
*/
+ @GuardedBy("mLock")
private void updateCachedServiceLocked(int userId, boolean disabled) {
AutofillManagerServiceImpl service = getServiceForUserLocked(userId);
if (service != null) {
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
index 989a7b5..2dcc6da 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
@@ -211,6 +211,7 @@
}
}
+ @GuardedBy("mLock")
private int getServiceUidLocked() {
if (mInfo == null) {
Slog.w(TAG, "getServiceUidLocked(): no mInfo");
@@ -248,6 +249,7 @@
mContext.getContentResolver(), Settings.Secure.AUTOFILL_SERVICE, mUserId);
}
+ @GuardedBy("mLock")
void updateLocked(boolean disabled) {
final boolean wasEnabled = isEnabledLocked();
if (sVerbose) {
@@ -300,6 +302,7 @@
}
}
+ @GuardedBy("mLock")
boolean addClientLocked(IAutoFillManagerClient client) {
if (mClients == null) {
mClients = new RemoteCallbackList<>();
@@ -308,12 +311,14 @@
return isEnabledLocked();
}
+ @GuardedBy("mLock")
void removeClientLocked(IAutoFillManagerClient client) {
if (mClients != null) {
mClients.unregister(client);
}
}
+ @GuardedBy("mLock")
void setAuthenticationResultLocked(Bundle data, int sessionId, int authenticationId, int uid) {
if (!isEnabledLocked()) {
return;
@@ -336,6 +341,7 @@
}
}
+ @GuardedBy("mLock")
int startSessionLocked(@NonNull IBinder activityToken, int uid,
@NonNull IBinder appCallbackToken, @NonNull AutofillId autofillId,
@NonNull Rect virtualBounds, @Nullable AutofillValue value, boolean hasCallback,
@@ -389,6 +395,7 @@
/**
* Remove abandoned sessions if needed.
*/
+ @GuardedBy("mLock")
private void pruneAbandonedSessionsLocked() {
long now = System.currentTimeMillis();
if (mLastPrune < now - MAX_ABANDONED_SESSION_MILLIS) {
@@ -400,6 +407,7 @@
}
}
+ @GuardedBy("mLock")
void finishSessionLocked(int sessionId, int uid) {
if (!isEnabledLocked()) {
return;
@@ -423,6 +431,7 @@
}
}
+ @GuardedBy("mLock")
void cancelSessionLocked(int sessionId, int uid) {
if (!isEnabledLocked()) {
return;
@@ -436,6 +445,7 @@
session.removeSelfLocked();
}
+ @GuardedBy("mLock")
void disableOwnedAutofillServicesLocked(int uid) {
Slog.i(TAG, "disableOwnedServices(" + uid + "): " + mInfo);
if (mInfo == null) return;
@@ -468,6 +478,7 @@
}
}
+ @GuardedBy("mLock")
private Session createSessionByTokenLocked(@NonNull IBinder activityToken, int uid,
@NonNull IBinder appCallbackToken, boolean hasCallback,
@NonNull ComponentName componentName, int flags) {
@@ -546,6 +557,7 @@
/**
* Updates a session and returns whether it should be restarted.
*/
+ @GuardedBy("mLock")
boolean updateSessionLocked(int sessionId, int uid, AutofillId autofillId, Rect virtualBounds,
AutofillValue value, int action, int flags) {
final Session session = mSessions.get(sessionId);
@@ -568,6 +580,7 @@
return false;
}
+ @GuardedBy("mLock")
void removeSessionLocked(int sessionId) {
mSessions.remove(sessionId);
}
@@ -603,6 +616,7 @@
}
}
+ @GuardedBy("mLock")
void destroyLocked() {
if (sVerbose) Slog.v(TAG, "destroyLocked()");
@@ -655,6 +669,7 @@
}
}
+ @GuardedBy("mLock")
private boolean isValidEventLocked(String method, int sessionId) {
if (mEventHistory == null) {
Slog.w(TAG, method + ": not logging event because history is null");
@@ -726,6 +741,7 @@
/**
* Updates the last fill response when an autofill context is committed.
*/
+ @GuardedBy("mLock")
void logContextCommittedLocked(int sessionId, @Nullable Bundle clientState,
@Nullable ArrayList<String> selectedDatasets,
@Nullable ArraySet<String> ignoredDatasets,
@@ -739,6 +755,7 @@
manuallyFilledDatasetIds, null, null, appPackageName);
}
+ @GuardedBy("mLock")
void logContextCommittedLocked(int sessionId, @Nullable Bundle clientState,
@Nullable ArrayList<String> selectedDatasets,
@Nullable ArraySet<String> ignoredDatasets,
@@ -847,6 +864,7 @@
}
}
+ @GuardedBy("mLock")
private boolean isCalledByServiceLocked(String methodName, int callingUid) {
if (getServiceUidLocked() != callingUid) {
Slog.w(TAG, methodName + "() called by UID " + callingUid
@@ -856,6 +874,7 @@
return true;
}
+ @GuardedBy("mLock")
void dumpLocked(String prefix, PrintWriter pw) {
final String prefix2 = prefix + " ";
@@ -965,6 +984,7 @@
mFieldClassificationStrategy.dump(prefix2, pw);
}
+ @GuardedBy("mLock")
void destroySessionsLocked() {
if (mSessions.size() == 0) {
mUi.destroyAll(null, null, false);
@@ -976,6 +996,7 @@
}
// TODO(b/64940307): remove this method if SaveUI is refactored to be attached on activities
+ @GuardedBy("mLock")
void destroyFinishedSessionsLocked() {
final int sessionCount = mSessions.size();
for (int i = sessionCount - 1; i >= 0; i--) {
@@ -987,6 +1008,7 @@
}
}
+ @GuardedBy("mLock")
void listSessionsLocked(ArrayList<String> output) {
final int numSessions = mSessions.size();
for (int i = 0; i < numSessions; i++) {
@@ -995,6 +1017,7 @@
}
}
+ @GuardedBy("mLock")
boolean isCompatibilityModeRequestedLocked(@NonNull String packageName,
long versionCode) {
if (mInfo == null || !mInfo.isCompatibilityModeRequested(packageName, versionCode)) {
@@ -1060,6 +1083,7 @@
}
}
+ @GuardedBy("mLock")
private boolean isClientSessionDestroyedLocked(IAutoFillManagerClient client) {
final int sessionCount = mSessions.size();
for (int i = 0; i < sessionCount; i++) {
@@ -1071,6 +1095,7 @@
return true;
}
+ @GuardedBy("mLock")
boolean isEnabledLocked() {
return mSetupComplete && mInfo != null && !mDisabled;
}
@@ -1123,6 +1148,7 @@
/**
* Checks if autofill is disabled by service to the given activity.
*/
+ @GuardedBy("mLock")
private boolean isAutofillDisabledLocked(@NonNull ComponentName componentName) {
// Check activities first.
long elapsedTime = 0;
diff --git a/services/autofill/java/com/android/server/autofill/RemoteFillService.java b/services/autofill/java/com/android/server/autofill/RemoteFillService.java
index aea9ad0..fe6d4c4 100644
--- a/services/autofill/java/com/android/server/autofill/RemoteFillService.java
+++ b/services/autofill/java/com/android/server/autofill/RemoteFillService.java
@@ -475,6 +475,7 @@
return true;
}
+ @GuardedBy("mLock")
protected boolean isCancelledLocked() {
return mCancelled;
}
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 18f49ec..4a24704 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -300,6 +300,7 @@
/**
* Returns the ids of all entries in {@link #mViewStates} in the same order.
*/
+ @GuardedBy("mLock")
private AutofillId[] getIdsOfAllViewStatesLocked() {
final int numViewState = mViewStates.size();
final AutofillId[] ids = new AutofillId[numViewState];
@@ -346,6 +347,7 @@
* <p>Gets the value of a field, using either the {@code viewStates} or the {@code mContexts},
* or {@code null} when not found on either of them.
*/
+ @GuardedBy("mLock")
private AutofillValue findValueLocked(@NonNull AutofillId id) {
final ViewState state = mViewStates.get(id);
if (state == null) {
@@ -369,6 +371,7 @@
* @param fillContext The context to be filled
* @param flags The flags that started the session
*/
+ @GuardedBy("mLock")
private void fillContextWithAllowedValuesLocked(@NonNull FillContext fillContext, int flags) {
final ViewNode[] nodes = fillContext
.findViewNodesByAutofillIds(getIdsOfAllViewStatesLocked());
@@ -409,6 +412,7 @@
/**
* Cancels the last request sent to the {@link #mRemoteFillService}.
*/
+ @GuardedBy("mLock")
private void cancelCurrentRequestLocked() {
final int canceledRequest = mRemoteFillService.cancelCurrentRequest();
@@ -430,6 +434,7 @@
/**
* Reads a new structure and then request a new fill response from the fill service.
*/
+ @GuardedBy("mLock")
private void requestNewFillResponseLocked(int flags) {
int requestId;
@@ -497,6 +502,7 @@
*
* @return The activity token
*/
+ @GuardedBy("mLock")
@NonNull IBinder getActivityTokenLocked() {
return mActivityToken;
}
@@ -663,6 +669,7 @@
*
* @return The context or {@code null} if there is no context
*/
+ @GuardedBy("mLock")
@Nullable private FillContext getFillContextByRequestIdLocked(int requestId) {
if (mContexts == null) {
return null;
@@ -820,6 +827,7 @@
});
}
+ @GuardedBy("mLock")
void setAuthenticationResultLocked(Bundle data, int authenticationId) {
if (mDestroyed) {
Slog.w(TAG, "Call to Session#setAuthenticationResultLocked() rejected - session: "
@@ -882,6 +890,7 @@
}
}
+ @GuardedBy("mLock")
void setHasCallbackLocked(boolean hasIt) {
if (mDestroyed) {
Slog.w(TAG, "Call to Session#setHasCallbackLocked() rejected - session: "
@@ -891,6 +900,7 @@
mHasCallback = hasIt;
}
+ @GuardedBy("mLock")
@Nullable
private FillResponse getLastResponseLocked(@Nullable String logPrefix) {
if (mContexts == null) {
@@ -923,6 +933,7 @@
return response;
}
+ @GuardedBy("mLock")
@Nullable
private SaveInfo getSaveInfoLocked() {
final FillResponse response = getLastResponseLocked(null);
@@ -941,6 +952,7 @@
});
}
+ @GuardedBy("mLock")
private void logContextCommittedLocked() {
final FillResponse lastResponse = getLastResponseLocked("logContextCommited()");
if (lastResponse == null) return;
@@ -1241,6 +1253,7 @@
*
* @return {@code true} if session is done, or {@code false} if it's pending user action.
*/
+ @GuardedBy("mLock")
public boolean showSaveLocked() {
if (mDestroyed) {
Slog.w(TAG, "Call to Session#showSaveLocked() rejected - session: "
@@ -1510,6 +1523,7 @@
/**
* Returns whether the session is currently showing the save UI
*/
+ @GuardedBy("mLock")
boolean isSavingLocked() {
return mIsSaving;
}
@@ -1517,6 +1531,7 @@
/**
* Gets the latest non-empty value for the given id in the autofill contexts.
*/
+ @GuardedBy("mLock")
@Nullable
private AutofillValue getValueFromContextsLocked(AutofillId id) {
final int numContexts = mContexts.size();
@@ -1539,6 +1554,7 @@
/**
* Gets the latest autofill options for the given id in the autofill contexts.
*/
+ @GuardedBy("mLock")
@Nullable
private CharSequence[] getAutofillOptionsFromContextsLocked(AutofillId id) {
final int numContexts = mContexts.size();
@@ -1556,6 +1572,7 @@
/**
* Calls service when user requested save.
*/
+ @GuardedBy("mLock")
void callSaveLocked() {
if (mDestroyed) {
Slog.w(TAG, "Call to Session#callSaveLocked() rejected - session: "
@@ -1646,6 +1663,7 @@
* @param viewState The view that is entered.
* @param flags The flag that was passed by the AutofillManager.
*/
+ @GuardedBy("mLock")
private void requestNewFillResponseOnViewEnteredIfNecessaryLocked(@NonNull AutofillId id,
@NonNull ViewState viewState, int flags) {
if ((flags & FLAG_MANUAL_REQUEST) != 0) {
@@ -1673,6 +1691,7 @@
*
* @return {@code true} iff a new partition should be started
*/
+ @GuardedBy("mLock")
private boolean shouldStartNewPartitionLocked(@NonNull AutofillId id) {
if (mResponses == null) {
return true;
@@ -1721,6 +1740,7 @@
return true;
}
+ @GuardedBy("mLock")
void updateLocked(AutofillId id, Rect virtualBounds, AutofillValue value, int action,
int flags) {
if (mDestroyed) {
@@ -1830,6 +1850,7 @@
/**
* Checks whether a view should be ignored.
*/
+ @GuardedBy("mLock")
private boolean isIgnoredLocked(AutofillId id) {
// Always check the latest response only
final FillResponse response = getLastResponseLocked(null);
@@ -1910,6 +1931,7 @@
}
}
+ @GuardedBy("mLock")
private void updateTrackedIdsLocked() {
// Only track the views of the last response as only those are reported back to the
// service, see #showSaveLocked
@@ -1982,6 +2004,7 @@
}
}
+ @GuardedBy("mLock")
private void replaceResponseLocked(@NonNull FillResponse oldResponse,
@NonNull FillResponse newResponse, @Nullable Bundle newClientState) {
// Disassociate view states with the old response
@@ -2005,6 +2028,7 @@
removeSelf();
}
+ @GuardedBy("mLock")
private void processResponseLocked(@NonNull FillResponse newResponse,
@Nullable Bundle newClientState, int flags) {
// Make sure we are hiding the UI which will be shown
@@ -2042,6 +2066,7 @@
/**
* Sets the state of all views in the given response.
*/
+ @GuardedBy("mLock")
private void setViewStatesLocked(FillResponse response, int state, boolean clearResponse) {
final List<Dataset> datasets = response.getDatasets();
if (datasets != null) {
@@ -2090,6 +2115,7 @@
/**
* Sets the state of all views in the given dataset and response.
*/
+ @GuardedBy("mLock")
private void setViewStatesLocked(@Nullable FillResponse response, @NonNull Dataset dataset,
int state, boolean clearResponse) {
final ArrayList<AutofillId> ids = dataset.getFieldIds();
@@ -2110,6 +2136,7 @@
}
}
+ @GuardedBy("mLock")
private ViewState createOrUpdateViewStateLocked(@NonNull AutofillId id, int state,
@Nullable AutofillValue value) {
ViewState viewState = mViewStates.get(id);
@@ -2171,6 +2198,7 @@
}
// TODO: this should never be null, but we got at least one occurrence, probably due to a race.
+ @GuardedBy("mLock")
@Nullable
private Intent createAuthFillInIntentLocked(int requestId, Bundle extras) {
final Intent fillInIntent = new Intent();
@@ -2203,6 +2231,7 @@
return "Session: [id=" + id + ", component=" + mComponentName + "]";
}
+ @GuardedBy("mLock")
void dumpLocked(String prefix, PrintWriter pw) {
final String prefix2 = prefix + " ";
pw.print(prefix); pw.print("id: "); pw.println(id);
@@ -2332,6 +2361,7 @@
* disabled it).
* </ul>
*/
+ @GuardedBy("mLock")
RemoteFillService destroyLocked() {
if (mDestroyed) {
return null;
@@ -2347,6 +2377,7 @@
* Cleans up this session and remove it from the service always, even if it does have a pending
* Save UI.
*/
+ @GuardedBy("mLock")
void forceRemoveSelfLocked() {
if (sVerbose) Slog.v(TAG, "forceRemoveSelfLocked(): " + mPendingSaveUi);
@@ -2376,6 +2407,7 @@
* Cleans up this session and remove it from the service, but but only if it does not have a
* pending Save UI.
*/
+ @GuardedBy("mLock")
void removeSelfLocked() {
if (sVerbose) Slog.v(TAG, "removeSelfLocked(): " + mPendingSaveUi);
if (mDestroyed) {
@@ -2404,6 +2436,7 @@
* a specific {@code token} created by
* {@link PendingUi#PendingUi(IBinder, int, IAutoFillManagerClient)}.
*/
+ @GuardedBy("mLock")
boolean isSaveUiPendingForTokenLocked(@NonNull IBinder token) {
return isSaveUiPendingLocked() && token.equals(mPendingSaveUi.getToken());
}
@@ -2411,10 +2444,12 @@
/**
* Checks whether this session is hiding the Save UI to handle a custom description link.
*/
+ @GuardedBy("mLock")
private boolean isSaveUiPendingLocked() {
return mPendingSaveUi != null && mPendingSaveUi.getState() == PendingUi.STATE_PENDING;
}
+ @GuardedBy("mLock")
private int getLastResponseIndexLocked() {
// The response ids are monotonically increasing so
// we just find the largest id which is the last. We
diff --git a/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java
index 3b80f55..83367f3 100644
--- a/services/backup/java/com/android/server/backup/BackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/BackupManagerService.java
@@ -1931,6 +1931,7 @@
/**
* Remove a package from the full-data queue.
*/
+ @GuardedBy("mQueueLock")
private void dequeueFullBackupLocked(String packageName) {
final int N = mFullBackupQueue.size();
for (int i = N - 1; i >= 0; i--) {
diff --git a/services/core/java/com/android/server/AlarmManagerService.java b/services/core/java/com/android/server/AlarmManagerService.java
index 17c617c..10fad62 100644
--- a/services/core/java/com/android/server/AlarmManagerService.java
+++ b/services/core/java/com/android/server/AlarmManagerService.java
@@ -55,6 +55,7 @@
import android.os.UserHandle;
import android.os.WorkSource;
import android.provider.Settings;
+import android.system.Os;
import android.text.TextUtils;
import android.text.format.DateFormat;
import android.util.ArrayMap;
@@ -88,6 +89,7 @@
import java.util.function.Predicate;
import static android.app.AlarmManager.FLAG_ALLOW_WHILE_IDLE;
+import static android.app.AlarmManager.FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED;
import static android.app.AlarmManager.RTC_WAKEUP;
import static android.app.AlarmManager.RTC;
import static android.app.AlarmManager.ELAPSED_REALTIME_WAKEUP;
@@ -98,7 +100,8 @@
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.LocalLog;
-import com.android.server.ForceAppStandbyTracker.Listener;
+import com.android.internal.util.Preconditions;
+import com.android.server.AppStateTracker.Listener;
/**
* Alarm manager implementaion.
@@ -157,6 +160,7 @@
private long mNextNonWakeup;
private long mLastWakeupSet;
private long mLastWakeup;
+ private long mLastTrigger;
private long mLastTickSet;
private long mLastTickIssued; // elapsed
private long mLastTickReceived;
@@ -249,7 +253,7 @@
private final SparseArray<AlarmManager.AlarmClockInfo> mHandlerSparseAlarmClockArray =
new SparseArray<>();
- private final ForceAppStandbyTracker mForceAppStandbyTracker;
+ private AppStateTracker mAppStateTracker;
private boolean mAppStandbyParole;
private ArrayMap<Pair<String, Integer>, Long> mLastAlarmDeliveredForPackage = new ArrayMap<>();
@@ -707,9 +711,6 @@
super(context);
mConstants = new Constants(mHandler);
- mForceAppStandbyTracker = ForceAppStandbyTracker.getInstance(context);
- mForceAppStandbyTracker.addListener(mForceAppStandbyListener);
-
publishLocalService(AlarmManagerInternal.class, new LocalService());
}
@@ -1011,7 +1012,7 @@
// Recurring alarms may have passed several alarm intervals while the
// alarm was kept pending. Send the appropriate trigger count.
if (alarm.repeatInterval > 0) {
- alarm.count += (nowELAPSED - alarm.requestedWhenElapsed) / alarm.repeatInterval;
+ alarm.count += (nowELAPSED - alarm.expectedWhenElapsed) / alarm.repeatInterval;
// Also schedule its next recurrence
final long delta = alarm.count * alarm.repeatInterval;
final long nextElapsed = alarm.whenElapsed + delta;
@@ -1329,13 +1330,15 @@
@Override
public void onBootPhase(int phase) {
if (phase == PHASE_SYSTEM_SERVICES_READY) {
- mForceAppStandbyTracker.start();
mConstants.start(getContext().getContentResolver());
mAppOps = (AppOpsManager) getContext().getSystemService(Context.APP_OPS_SERVICE);
mLocalDeviceIdleController
= LocalServices.getService(DeviceIdleController.LocalService.class);
mUsageStatsManagerInternal = LocalServices.getService(UsageStatsManagerInternal.class);
mUsageStatsManagerInternal.addAppIdleStateChangeListener(new AppStandbyTracker());
+
+ mAppStateTracker = LocalServices.getService(AppStateTracker.class);
+ mAppStateTracker.addListener(mForceAppStandbyListener);
}
}
@@ -1507,24 +1510,19 @@
* Adjusts the alarm delivery time based on the current app standby bucket.
* @param alarm The alarm to adjust
* @return true if the alarm delivery time was updated.
- * TODO: Reduce the number of calls to getAppStandbyBucket by batching the calls per
- * {package, user} pairs
*/
private boolean adjustDeliveryTimeBasedOnStandbyBucketLocked(Alarm alarm) {
- if (alarm.alarmClock != null || UserHandle.isCore(alarm.creatorUid)) {
- return false;
- }
- // TODO: short term fix for b/72816079, remove after a proper fix is in place
- if ((alarm.flags & AlarmManager.FLAG_ALLOW_WHILE_IDLE) != 0) {
+ if (isExemptFromAppStandby(alarm)) {
return false;
}
if (mAppStandbyParole) {
- if (alarm.whenElapsed > alarm.requestedWhenElapsed) {
+ if (alarm.whenElapsed > alarm.expectedWhenElapsed) {
// We did defer this alarm earlier, restore original requirements
- alarm.whenElapsed = alarm.requestedWhenElapsed;
- alarm.maxWhenElapsed = alarm.requestedMaxWhenElapsed;
+ alarm.whenElapsed = alarm.expectedWhenElapsed;
+ alarm.maxWhenElapsed = alarm.expectedMaxWhenElapsed;
+ return true;
}
- return true;
+ return false;
}
final long oldWhenElapsed = alarm.whenElapsed;
final long oldMaxWhenElapsed = alarm.maxWhenElapsed;
@@ -1538,13 +1536,13 @@
final long lastElapsed = mLastAlarmDeliveredForPackage.getOrDefault(packageUser, 0L);
if (lastElapsed > 0) {
final long minElapsed = lastElapsed + getMinDelayForBucketLocked(standbyBucket);
- if (alarm.requestedWhenElapsed < minElapsed) {
+ if (alarm.expectedWhenElapsed < minElapsed) {
alarm.whenElapsed = alarm.maxWhenElapsed = minElapsed;
} else {
// app is now eligible to run alarms at the originally requested window.
// Restore original requirements in case they were changed earlier.
- alarm.whenElapsed = alarm.requestedWhenElapsed;
- alarm.maxWhenElapsed = alarm.requestedMaxWhenElapsed;
+ alarm.whenElapsed = alarm.expectedWhenElapsed;
+ alarm.maxWhenElapsed = alarm.expectedMaxWhenElapsed;
}
}
return (oldWhenElapsed != alarm.whenElapsed || oldMaxWhenElapsed != alarm.maxWhenElapsed);
@@ -1728,8 +1726,9 @@
// This means we will allow these alarms to go off as normal even while idle, with no
// timing restrictions.
} else if (workSource == null && (callingUid < Process.FIRST_APPLICATION_UID
- || callingUid == mSystemUiUid
- || mForceAppStandbyTracker.isUidPowerSaveWhitelisted(callingUid))) {
+ || UserHandle.isSameApp(callingUid, mSystemUiUid)
+ || ((mAppStateTracker != null)
+ && mAppStateTracker.isUidPowerSaveWhitelisted(callingUid)))) {
flags |= AlarmManager.FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED;
flags &= ~AlarmManager.FLAG_ALLOW_WHILE_IDLE;
}
@@ -1812,8 +1811,10 @@
mConstants.dump(pw);
pw.println();
- mForceAppStandbyTracker.dump(pw, " ");
- pw.println();
+ if (mAppStateTracker != null) {
+ mAppStateTracker.dump(pw, " ");
+ pw.println();
+ }
pw.println(" App Standby Parole: " + mAppStandbyParole);
pw.println();
@@ -1840,27 +1841,32 @@
pw.print(" Time since non-interactive: ");
TimeUtils.formatDuration(nowELAPSED - mNonInteractiveStartTime, pw);
pw.println();
- pw.print(" Max wakeup delay: ");
- TimeUtils.formatDuration(currentNonWakeupFuzzLocked(nowELAPSED), pw);
- pw.println();
- pw.print(" Time since last dispatch: ");
- TimeUtils.formatDuration(nowELAPSED - mLastAlarmDeliveryTime, pw);
- pw.println();
- pw.print(" Next non-wakeup delivery time: ");
- TimeUtils.formatDuration(nowELAPSED - mNextNonWakeupDeliveryTime, pw);
- pw.println();
}
+ pw.print(" Max wakeup delay: ");
+ TimeUtils.formatDuration(currentNonWakeupFuzzLocked(nowELAPSED), pw);
+ pw.println();
+ pw.print(" Time since last dispatch: ");
+ TimeUtils.formatDuration(nowELAPSED - mLastAlarmDeliveryTime, pw);
+ pw.println();
+ pw.print(" Next non-wakeup delivery time: ");
+ TimeUtils.formatDuration(nowELAPSED - mNextNonWakeupDeliveryTime, pw);
+ pw.println();
long nextWakeupRTC = mNextWakeup + (nowRTC - nowELAPSED);
long nextNonWakeupRTC = mNextNonWakeup + (nowRTC - nowELAPSED);
pw.print(" Next non-wakeup alarm: ");
TimeUtils.formatDuration(mNextNonWakeup, nowELAPSED, pw);
+ pw.print(" = "); pw.print(mNextNonWakeup);
pw.print(" = "); pw.println(sdf.format(new Date(nextNonWakeupRTC)));
- pw.print(" Next wakeup: "); TimeUtils.formatDuration(mNextWakeup, nowELAPSED, pw);
+ pw.print(" Next wakeup alarm: "); TimeUtils.formatDuration(mNextWakeup, nowELAPSED, pw);
+ pw.print(" = "); pw.print(mNextWakeup);
pw.print(" = "); pw.println(sdf.format(new Date(nextWakeupRTC)));
+ pw.print(" set at "); TimeUtils.formatDuration(mLastWakeupSet, nowELAPSED, pw);
+ pw.println();
pw.print(" Last wakeup: "); TimeUtils.formatDuration(mLastWakeup, nowELAPSED, pw);
- pw.print(" set at "); TimeUtils.formatDuration(mLastWakeupSet, nowELAPSED, pw);
- pw.println();
+ pw.print(" = "); pw.println(mLastWakeup);
+ pw.print(" Last trigger: "); TimeUtils.formatDuration(mLastTrigger, nowELAPSED, pw);
+ pw.print(" = "); pw.println(mLastTrigger);
pw.print(" Num time change events: "); pw.println(mNumTimeChanged);
pw.println();
@@ -2161,8 +2167,10 @@
mConstants.dumpProto(proto, AlarmManagerServiceProto.SETTINGS);
- mForceAppStandbyTracker.dumpProto(proto,
- AlarmManagerServiceProto.FORCE_APP_STANDBY_TRACKER);
+ if (mAppStateTracker != null) {
+ mAppStateTracker.dumpProto(proto,
+ AlarmManagerServiceProto.FORCE_APP_STANDBY_TRACKER);
+ }
proto.write(AlarmManagerServiceProto.IS_INTERACTIVE, mInteractive);
if (!mInteractive) {
@@ -2888,7 +2896,14 @@
alarmNanoseconds = (when % 1000) * 1000 * 1000;
}
- set(mNativeData, type, alarmSeconds, alarmNanoseconds);
+ final int result = set(mNativeData, type, alarmSeconds, alarmNanoseconds);
+ if (result != 0) {
+ final long nowElapsed = SystemClock.elapsedRealtime();
+ Slog.wtf(TAG, "Unable to set kernel alarm, now=" + nowElapsed
+ + " type=" + type + " when=" + when
+ + " @ (" + alarmSeconds + "," + alarmNanoseconds
+ + "), ret = " + result + " = " + Os.strerror(result));
+ }
} else {
Message msg = Message.obtain();
msg.what = ALARM_EVENT;
@@ -2942,20 +2957,21 @@
}
final String sourcePackage = alarm.sourcePackage;
final int sourceUid = alarm.creatorUid;
- return mForceAppStandbyTracker.areAlarmsRestricted(sourceUid, sourcePackage,
- allowWhileIdle);
+ return (mAppStateTracker != null) &&
+ mAppStateTracker.areAlarmsRestricted(sourceUid, sourcePackage, allowWhileIdle);
}
private native long init();
private native void close(long nativeData);
- private native void set(long nativeData, int type, long seconds, long nanoseconds);
+ private native int set(long nativeData, int type, long seconds, long nanoseconds);
private native int waitForAlarm(long nativeData);
private native int setKernelTime(long nativeData, long millis);
private native int setKernelTimezone(long nativeData, int minuteswest);
private long getWhileIdleMinIntervalLocked(int uid) {
final boolean dozing = mPendingIdleUntil != null;
- final boolean ebs = mForceAppStandbyTracker.isForceAllAppsStandbyEnabled();
+ final boolean ebs = (mAppStateTracker != null)
+ && mAppStateTracker.isForceAllAppsStandbyEnabled();
if (!dozing && !ebs) {
return mConstants.ALLOW_WHILE_IDLE_SHORT_TIME;
}
@@ -3000,10 +3016,11 @@
// Whoops, it hasn't been long enough since the last ALLOW_WHILE_IDLE
// alarm went off for this app. Reschedule the alarm to be in the
// correct time period.
- alarm.whenElapsed = minTime;
+ alarm.expectedWhenElapsed = alarm.whenElapsed = minTime;
if (alarm.maxWhenElapsed < minTime) {
alarm.maxWhenElapsed = minTime;
}
+ alarm.expectedMaxWhenElapsed = alarm.maxWhenElapsed;
if (RECORD_DEVICE_IDLE_ALARMS) {
IdleDispatchEntry ent = new IdleDispatchEntry();
ent.uid = alarm.uid;
@@ -3053,7 +3070,7 @@
if (alarm.repeatInterval > 0) {
// this adjustment will be zero if we're late by
// less than one full repeat interval
- alarm.count += (nowELAPSED - alarm.requestedWhenElapsed) / alarm.repeatInterval;
+ alarm.count += (nowELAPSED - alarm.expectedWhenElapsed) / alarm.repeatInterval;
// Also schedule its next recurrence
final long delta = alarm.count * alarm.repeatInterval;
@@ -3128,8 +3145,9 @@
public long windowLength;
public long whenElapsed; // 'when' in the elapsed time base
public long maxWhenElapsed; // also in the elapsed time base
- public final long requestedWhenElapsed; // original expiry time requested by the app
- public final long requestedMaxWhenElapsed;
+ // Expected alarm expiry time before app standby deferring is applied.
+ public long expectedWhenElapsed;
+ public long expectedMaxWhenElapsed;
public long repeatInterval;
public PriorityClass priorityClass;
@@ -3143,10 +3161,10 @@
|| _type == AlarmManager.RTC_WAKEUP;
when = _when;
whenElapsed = _whenElapsed;
- requestedWhenElapsed = _whenElapsed;
+ expectedWhenElapsed = _whenElapsed;
windowLength = _windowLength;
maxWhenElapsed = _maxWhen;
- requestedMaxWhenElapsed = _maxWhen;
+ expectedMaxWhenElapsed = _maxWhen;
repeatInterval = _interval;
operation = _op;
listener = _rec;
@@ -3205,8 +3223,10 @@
final boolean isRtc = (type == RTC || type == RTC_WAKEUP);
pw.print(prefix); pw.print("tag="); pw.println(statsTag);
pw.print(prefix); pw.print("type="); pw.print(type);
- pw.print(" requestedWhenElapsed="); TimeUtils.formatDuration(
- requestedWhenElapsed, nowELAPSED, pw);
+ pw.print(" expectedWhenElapsed="); TimeUtils.formatDuration(
+ expectedWhenElapsed, nowELAPSED, pw);
+ pw.print(" expectedMaxWhenElapsed="); TimeUtils.formatDuration(
+ expectedMaxWhenElapsed, nowELAPSED, pw);
pw.print(" whenElapsed="); TimeUtils.formatDuration(whenElapsed,
nowELAPSED, pw);
pw.print(" when=");
@@ -3344,6 +3364,11 @@
}
}
+ private boolean isExemptFromAppStandby(Alarm a) {
+ return a.alarmClock != null || UserHandle.isCore(a.creatorUid)
+ || (a.flags & FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED) != 0;
+ }
+
private class AlarmThread extends Thread
{
public AlarmThread()
@@ -3358,12 +3383,14 @@
while (true)
{
int result = waitForAlarm(mNativeData);
- mLastWakeup = SystemClock.elapsedRealtime();
-
- triggerList.clear();
final long nowRTC = System.currentTimeMillis();
final long nowELAPSED = SystemClock.elapsedRealtime();
+ synchronized (mLock) {
+ mLastWakeup = nowELAPSED;
+ }
+
+ triggerList.clear();
if ((result & TIME_CHANGED_MASK) != 0) {
// The kernel can give us spurious time change notifications due to
@@ -3429,6 +3456,7 @@
}
}
+ mLastTrigger = nowELAPSED;
boolean hasWakeup = triggerAlarmsLocked(triggerList, nowELAPSED, nowRTC);
if (!hasWakeup && checkAllowNonWakeupDelayLocked(nowELAPSED)) {
// if there are no wakeup alarms and the screen is off, we can
@@ -3462,7 +3490,7 @@
new ArraySet<>();
for (int i = 0; i < triggerList.size(); i++) {
final Alarm a = triggerList.get(i);
- if (!UserHandle.isCore(a.creatorUid)) {
+ if (!isExemptFromAppStandby(a)) {
triggerPackages.add(Pair.create(
a.sourcePackage, UserHandle.getUserId(a.creatorUid)));
}
@@ -4056,6 +4084,7 @@
/**
* Deliver an alarm and set up the post-delivery handling appropriately
*/
+ @GuardedBy("mLock")
public void deliverLocked(Alarm alarm, long nowELAPSED, boolean allowWhileIdle) {
if (alarm.operation != null) {
// PendingIntent alarm
@@ -4133,7 +4162,8 @@
if (allowWhileIdle) {
// Record the last time this uid handled an ALLOW_WHILE_IDLE alarm.
mLastAllowWhileIdleDispatch.put(alarm.creatorUid, nowELAPSED);
- if (mForceAppStandbyTracker.isUidInForeground(alarm.creatorUid)) {
+ if ((mAppStateTracker == null)
+ || mAppStateTracker.isUidInForeground(alarm.creatorUid)) {
mUseAllowWhileIdleShortTime.put(alarm.creatorUid, true);
} else {
mUseAllowWhileIdleShortTime.put(alarm.creatorUid, false);
@@ -4148,7 +4178,7 @@
mAllowWhileIdleDispatches.add(ent);
}
}
- if (!UserHandle.isCore(alarm.creatorUid)) {
+ if (!isExemptFromAppStandby(alarm)) {
final Pair<String, Integer> packageUser = Pair.create(alarm.sourcePackage,
UserHandle.getUserId(alarm.creatorUid));
mLastAlarmDeliveredForPackage.put(packageUser, nowELAPSED);
diff --git a/services/core/java/com/android/server/ForceAppStandbyTracker.java b/services/core/java/com/android/server/AppStateTracker.java
similarity index 95%
rename from services/core/java/com/android/server/ForceAppStandbyTracker.java
rename to services/core/java/com/android/server/AppStateTracker.java
index 100680d..9b29b32 100644
--- a/services/core/java/com/android/server/ForceAppStandbyTracker.java
+++ b/services/core/java/com/android/server/AppStateTracker.java
@@ -54,7 +54,6 @@
import com.android.internal.app.IAppOpsService;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.Preconditions;
-import com.android.server.DeviceIdleController.LocalService;
import com.android.server.ForceAppStandbyTrackerProto.ExemptedPackage;
import com.android.server.ForceAppStandbyTrackerProto.RunAnyInBackgroundRestrictedPackages;
@@ -73,14 +72,14 @@
* TODO: Make it a LocalService.
*
* Test:
- atest $ANDROID_BUILD_TOP/frameworks/base/services/tests/servicestests/src/com/android/server/ForceAppStandbyTrackerTest.java
+ atest $ANDROID_BUILD_TOP/frameworks/base/services/tests/servicestests/src/com/android/server/AppStateTrackerTest.java
*/
-public class ForceAppStandbyTracker {
+public class AppStateTracker {
private static final String TAG = "ForceAppStandbyTracker";
private static final boolean DEBUG = true;
- @GuardedBy("ForceAppStandbyTracker.class")
- private static ForceAppStandbyTracker sInstance;
+ @GuardedBy("AppStateTracker.class")
+ private static AppStateTracker sInstance;
private final Object mLock = new Object();
private final Context mContext;
@@ -89,6 +88,7 @@
static final int TARGET_OP = AppOpsManager.OP_RUN_ANY_IN_BACKGROUND;
IActivityManager mIActivityManager;
+ ActivityManagerInternal mActivityManagerInternal;
AppOpsManager mAppOpsManager;
IAppOpsService mAppOpsService;
PowerManagerInternal mPowerManagerInternal;
@@ -172,6 +172,9 @@
int EXEMPT_CHANGED = 6;
int FORCE_ALL_CHANGED = 7;
int FORCE_APP_STANDBY_FEATURE_FLAG_CHANGED = 8;
+
+ int IS_UID_ACTIVE_CACHED = 9;
+ int IS_UID_ACTIVE_RAW = 10;
}
private final StatLogger mStatLogger = new StatLogger(new String[] {
@@ -184,6 +187,9 @@
"EXEMPT_CHANGED",
"FORCE_ALL_CHANGED",
"FORCE_APP_STANDBY_FEATURE_FLAG_CHANGED",
+
+ "IS_UID_ACTIVE_CACHED",
+ "IS_UID_ACTIVE_RAW",
});
@VisibleForTesting
@@ -249,7 +255,7 @@
/**
* This is called when the OP_RUN_ANY_IN_BACKGROUND appops changed for a package.
*/
- private void onRunAnyAppOpsChanged(ForceAppStandbyTracker sender,
+ private void onRunAnyAppOpsChanged(AppStateTracker sender,
int uid, @NonNull String packageName) {
updateJobsForUidPackage(uid, packageName);
@@ -264,14 +270,14 @@
/**
* This is called when the foreground state changed for a UID.
*/
- private void onUidForegroundStateChanged(ForceAppStandbyTracker sender, int uid) {
+ private void onUidForegroundStateChanged(AppStateTracker sender, int uid) {
onUidForeground(uid, sender.isUidInForeground(uid));
}
/**
* This is called when the active/idle state changed for a UID.
*/
- private void onUidActiveStateChanged(ForceAppStandbyTracker sender, int uid) {
+ private void onUidActiveStateChanged(AppStateTracker sender, int uid) {
updateJobsForUid(uid);
if (sender.isUidActive(uid)) {
@@ -282,7 +288,7 @@
/**
* This is called when an app-id(s) is removed from the power save whitelist.
*/
- private void onPowerSaveUnwhitelisted(ForceAppStandbyTracker sender) {
+ private void onPowerSaveUnwhitelisted(AppStateTracker sender) {
updateAllJobs();
unblockAllUnrestrictedAlarms();
}
@@ -291,14 +297,14 @@
* This is called when the power save whitelist changes, excluding the
* {@link #onPowerSaveUnwhitelisted} case.
*/
- private void onPowerSaveWhitelistedChanged(ForceAppStandbyTracker sender) {
+ private void onPowerSaveWhitelistedChanged(AppStateTracker sender) {
updateAllJobs();
}
/**
* This is called when the temp whitelist changes.
*/
- private void onTempPowerSaveWhitelistChanged(ForceAppStandbyTracker sender) {
+ private void onTempPowerSaveWhitelistChanged(AppStateTracker sender) {
// TODO This case happens rather frequently; consider optimizing and update jobs
// only for affected app-ids.
@@ -311,7 +317,7 @@
/**
* This is called when the EXEMPT bucket is updated.
*/
- private void onExemptChanged(ForceAppStandbyTracker sender) {
+ private void onExemptChanged(AppStateTracker sender) {
// This doesn't happen very often, so just re-evaluate all jobs / alarms.
updateAllJobs();
unblockAllUnrestrictedAlarms();
@@ -320,7 +326,7 @@
/**
* This is called when the global "force all apps standby" flag changes.
*/
- private void onForceAllAppsStandbyChanged(ForceAppStandbyTracker sender) {
+ private void onForceAllAppsStandbyChanged(AppStateTracker sender) {
updateAllJobs();
if (!sender.isForceAllAppsStandbyEnabled()) {
@@ -377,30 +383,15 @@
}
}
- @VisibleForTesting
- ForceAppStandbyTracker(Context context, Looper looper) {
+ public AppStateTracker(Context context, Looper looper) {
mContext = context;
mHandler = new MyHandler(looper);
}
- private ForceAppStandbyTracker(Context context) {
- this(context, FgThread.get().getLooper());
- }
-
- /**
- * Get the singleton instance.
- */
- public static synchronized ForceAppStandbyTracker getInstance(Context context) {
- if (sInstance == null) {
- sInstance = new ForceAppStandbyTracker(context);
- }
- return sInstance;
- }
-
/**
* Call it when the system is ready.
*/
- public void start() {
+ public void onSystemServicesReady() {
synchronized (mLock) {
if (mStarted) {
return;
@@ -408,6 +399,7 @@
mStarted = true;
mIActivityManager = Preconditions.checkNotNull(injectIActivityManager());
+ mActivityManagerInternal = Preconditions.checkNotNull(injectActivityManagerInternal());
mAppOpsManager = Preconditions.checkNotNull(injectAppOpsManager());
mAppOpsService = Preconditions.checkNotNull(injectIAppOpsService());
mPowerManagerInternal = Preconditions.checkNotNull(injectPowerManagerInternal());
@@ -475,6 +467,11 @@
}
@VisibleForTesting
+ ActivityManagerInternal injectActivityManagerInternal() {
+ return LocalServices.getService(ActivityManagerInternal.class);
+ }
+
+ @VisibleForTesting
PowerManagerInternal injectPowerManagerInternal() {
return LocalServices.getService(PowerManagerInternal.class);
}
@@ -497,6 +494,7 @@
/**
* Update {@link #mRunAnyRestrictedPackages} with the current app ops state.
*/
+ @GuardedBy("mLock")
private void refreshForcedAppStandbyUidPackagesLocked() {
mRunAnyRestrictedPackages.clear();
final List<PackageOps> ops = mAppOpsManager.getPackagesForOps(
@@ -536,6 +534,7 @@
/**
* Update {@link #mForceAllAppsStandby} and notifies the listeners.
*/
+ @GuardedBy("mLock")
private void toggleForceAllAppsStandbyLocked(boolean enable) {
if (enable == mForceAllAppsStandby) {
return;
@@ -545,6 +544,7 @@
mHandler.notifyForceAllAppsStandbyChanged();
}
+ @GuardedBy("mLock")
private int findForcedAppStandbyUidPackageIndexLocked(int uid, @NonNull String packageName) {
final int size = mRunAnyRestrictedPackages.size();
if (size > 8) {
@@ -563,6 +563,7 @@
/**
* @return whether a uid package-name pair is in mRunAnyRestrictedPackages.
*/
+ @GuardedBy("mLock")
boolean isRunAnyRestrictedLocked(int uid, @NonNull String packageName) {
return findForcedAppStandbyUidPackageIndexLocked(uid, packageName) >= 0;
}
@@ -570,6 +571,7 @@
/**
* Add to / remove from {@link #mRunAnyRestrictedPackages}.
*/
+ @GuardedBy("mLock")
boolean updateForcedAppStandbyUidPackageLocked(int uid, @NonNull String packageName,
boolean restricted) {
final int index = findForcedAppStandbyUidPackageIndexLocked(uid, packageName);
@@ -799,7 +801,7 @@
return;
}
}
- final ForceAppStandbyTracker sender = ForceAppStandbyTracker.this;
+ final AppStateTracker sender = AppStateTracker.this;
long start = mStatLogger.getTime();
switch (msg.what) {
@@ -1089,11 +1091,11 @@
}
/**
- * @return whether a UID is in active or not.
+ * @return whether a UID is in active or not *based on cached information.*
*
* Note this information is based on the UID proc state callback, meaning it's updated
* asynchronously and may subtly be stale. If the fresh data is needed, use
- * {@link ActivityManagerInternal#getUidProcessState} instead.
+ * {@link #isUidActiveSynced} instead.
*/
public boolean isUidActive(int uid) {
if (UserHandle.isCore(uid)) {
@@ -1105,6 +1107,23 @@
}
/**
+ * @return whether a UID is in active or not *right now.*
+ *
+ * This gives the fresh information, but may access the activity manager so is slower.
+ */
+ public boolean isUidActiveSynced(int uid) {
+ if (isUidActive(uid)) { // Use the cached one first.
+ return true;
+ }
+ final long start = mStatLogger.getTime();
+
+ final boolean ret = mActivityManagerInternal.isUidActive(uid);
+ mStatLogger.logDurationStat(Stats.IS_UID_ACTIVE_RAW, start);
+
+ return ret;
+ }
+
+ /**
* @return whether a UID is in the foreground or not.
*
* Note this information is based on the UID proc state callback, meaning it's updated
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 3bfa31a..efad7d5 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -1402,7 +1402,8 @@
public boolean isActiveNetworkMetered() {
enforceAccessPermission();
- final NetworkCapabilities caps = getNetworkCapabilities(getActiveNetwork());
+ final int uid = Binder.getCallingUid();
+ final NetworkCapabilities caps = getUnfilteredActiveNetworkState(uid).networkCapabilities;
if (caps != null) {
return !caps.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED);
} else {
diff --git a/services/core/java/com/android/server/DeviceIdleController.java b/services/core/java/com/android/server/DeviceIdleController.java
index 44974ff..2b3c585 100644
--- a/services/core/java/com/android/server/DeviceIdleController.java
+++ b/services/core/java/com/android/server/DeviceIdleController.java
@@ -82,6 +82,7 @@
import com.android.internal.os.BackgroundThread;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.FastXmlSerializer;
+import com.android.internal.util.Preconditions;
import com.android.internal.util.XmlUtils;
import com.android.server.am.BatteryStatsService;
import com.android.server.net.NetworkPolicyManagerInternal;
@@ -128,6 +129,7 @@
private Intent mIdleIntent;
private Intent mLightIdleIntent;
private AnyMotionDetector mAnyMotionDetector;
+ private final AppStateTracker mAppStateTracker;
private boolean mLightEnabled;
private boolean mDeepEnabled;
private boolean mForceIdle;
@@ -1371,6 +1373,8 @@
super(context);
mConfigFile = new AtomicFile(new File(getSystemDir(), "deviceidle.xml"));
mHandler = new MyHandler(BackgroundThread.getHandler().getLooper());
+ mAppStateTracker = new AppStateTracker(context, FgThread.get().getLooper());
+ LocalServices.addService(AppStateTracker.class, mAppStateTracker);
}
boolean isAppOnWhitelistInternal(int appid) {
@@ -1501,6 +1505,8 @@
(PowerManager) getContext().getSystemService(Context.POWER_SERVICE),
mHandler, mSensorManager, this, angleThreshold);
+ mAppStateTracker.onSystemServicesReady();
+
mIdleIntent = new Intent(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED);
mIdleIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY
| Intent.FLAG_RECEIVER_FOREGROUND);
@@ -2615,7 +2621,7 @@
}
private void passWhiteListToForceAppStandbyTrackerLocked() {
- ForceAppStandbyTracker.getInstance(getContext()).setPowerSaveWhitelistAppIds(
+ mAppStateTracker.setPowerSaveWhitelistAppIds(
mPowerSaveWhitelistExceptIdleAppIdArray,
mTempWhitelistAppIdArray);
}
diff --git a/services/core/java/com/android/server/InputMethodManagerService.java b/services/core/java/com/android/server/InputMethodManagerService.java
index 93f7f1d..bdeb231 100644
--- a/services/core/java/com/android/server/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/InputMethodManagerService.java
@@ -1166,6 +1166,7 @@
mImePackageAppeared = false;
}
+ @GuardedBy("mMethodMap")
private boolean shouldRebuildInputMethodListLocked() {
// This method is guaranteed to be called only by getRegisteredHandler().
@@ -1467,6 +1468,7 @@
setSelectedInputMethodAndSubtypeLocked(defIm, NOT_A_SUBTYPE_ID, false);
}
+ @GuardedBy("mMethodMap")
private void switchUserLocked(int newUserId) {
if (DEBUG) Slog.d(TAG, "Switching user stage 1/3. newUserId=" + newUserId
+ " currentUserId=" + mSettings.getCurrentUserId());
@@ -1817,6 +1819,7 @@
return flags;
}
+ @GuardedBy("mMethodMap")
@NonNull
InputBindResult attachNewInputLocked(
/* @InputMethodClient.StartInputReason */ final int startInputReason, boolean initial) {
@@ -1846,6 +1849,7 @@
mCurId, mCurSeq, mCurUserActionNotificationSequenceNumber);
}
+ @GuardedBy("mMethodMap")
@NonNull
InputBindResult startInputLocked(
/* @InputMethodClient.StartInputReason */ final int startInputReason,
@@ -1889,6 +1893,7 @@
controlFlags, startInputReason);
}
+ @GuardedBy("mMethodMap")
@NonNull
InputBindResult startInputUncheckedLocked(@NonNull ClientState cs, IInputContext inputContext,
/* @InputConnectionInspector.missingMethods */ final int missingMethods,
@@ -3642,6 +3647,7 @@
return false;
}
+ @GuardedBy("mMethodMap")
void buildInputMethodListLocked(boolean resetDefaultEnabledIme) {
if (DEBUG) {
Slog.d(TAG, "--- re-buildInputMethodList reset = " + resetDefaultEnabledIme
diff --git a/services/core/java/com/android/server/IpSecService.java b/services/core/java/com/android/server/IpSecService.java
index a07a982..45a4dfb9 100644
--- a/services/core/java/com/android/server/IpSecService.java
+++ b/services/core/java/com/android/server/IpSecService.java
@@ -1438,7 +1438,9 @@
switch (config.getMode()) {
case IpSecTransform.MODE_TRANSPORT:
+ break;
case IpSecTransform.MODE_TUNNEL:
+ enforceNetworkStackPermission();
break;
default:
throw new IllegalArgumentException(
@@ -1446,6 +1448,11 @@
}
}
+ private void enforceNetworkStackPermission() {
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.NETWORK_STACK,
+ "IpSecService");
+ }
+
private void createOrUpdateTransform(
IpSecConfig c, int resourceId, SpiRecord spiRecord, EncapSocketRecord socketRecord)
throws RemoteException {
@@ -1615,6 +1622,7 @@
@Override
public synchronized void applyTunnelModeTransform(
int tunnelResourceId, int direction, int transformResourceId) throws RemoteException {
+ enforceNetworkStackPermission();
checkDirection(direction);
UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
diff --git a/services/core/java/com/android/server/ServiceWatcher.java b/services/core/java/com/android/server/ServiceWatcher.java
index f4238f2..42c836e 100644
--- a/services/core/java/com/android/server/ServiceWatcher.java
+++ b/services/core/java/com/android/server/ServiceWatcher.java
@@ -199,6 +199,7 @@
* bound.
* @returns {@code true} if a valid package was found to bind to.
*/
+ @GuardedBy("mLock")
private boolean bindBestPackageLocked(String justCheckThisPackage, boolean forceRebind) {
Intent intent = new Intent(mAction);
if (justCheckThisPackage != null) {
@@ -273,6 +274,7 @@
return true;
}
+ @GuardedBy("mLock")
private void unbindLocked() {
ComponentName component;
component = mBoundComponent;
@@ -287,6 +289,7 @@
}
}
+ @GuardedBy("mLock")
private void bindToPackageLocked(ComponentName component, int version, int userId) {
Intent intent = new Intent(mAction);
intent.setComponent(component);
diff --git a/services/core/java/com/android/server/StatLogger.java b/services/core/java/com/android/server/StatLogger.java
index f211731..0e6f5e2 100644
--- a/services/core/java/com/android/server/StatLogger.java
+++ b/services/core/java/com/android/server/StatLogger.java
@@ -17,6 +17,7 @@
package com.android.server;
import android.os.SystemClock;
+import android.util.Slog;
import android.util.proto.ProtoOutputStream;
import com.android.internal.annotations.GuardedBy;
@@ -33,6 +34,8 @@
* @hide
*/
public class StatLogger {
+ private static final String TAG = "StatLogger";
+
private final Object mLock = new Object();
private final int SIZE;
@@ -66,8 +69,12 @@
*/
public void logDurationStat(int eventId, long start) {
synchronized (mLock) {
- mCountStats[eventId]++;
- mDurationStats[eventId] += (getTime() - start);
+ if (eventId >= 0 && eventId < SIZE) {
+ mCountStats[eventId]++;
+ mDurationStats[eventId] += (getTime() - start);
+ } else {
+ Slog.wtf(TAG, "Invalid event ID: " + eventId);
+ }
}
}
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index 84b93e3..1a0068d 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -809,6 +809,7 @@
}
}
+ @GuardedBy("mLock")
private void addInternalVolumeLocked() {
// Create a stub volume that represents internal storage
final VolumeInfo internal = new VolumeInfo(VolumeInfo.ID_PRIVATE_INTERNAL,
@@ -1109,6 +1110,7 @@
}
};
+ @GuardedBy("mLock")
private void onDiskScannedLocked(DiskInfo disk) {
int volumeCount = 0;
for (int i = 0; i < mVolumes.size(); i++) {
@@ -1134,6 +1136,7 @@
mCallbacks.notifyDiskScanned(disk, volumeCount);
}
+ @GuardedBy("mLock")
private void onVolumeCreatedLocked(VolumeInfo vol) {
if (mPms.isOnlyCoreApps()) {
Slog.d(TAG, "System booted in core-only mode; ignoring volume " + vol.getId());
@@ -1209,6 +1212,7 @@
return true;
}
+ @GuardedBy("mLock")
private void onVolumeStateChangedLocked(VolumeInfo vol, int oldState, int newState) {
// Remember that we saw this volume so we're ready to accept user
// metadata, or so we can annoy them when a private volume is ejected
@@ -1299,6 +1303,7 @@
}
}
+ @GuardedBy("mLock")
private void onMoveStatusLocked(int status) {
if (mMoveCallback == null) {
Slog.w(TAG, "Odd, status but no move requested");
@@ -1515,6 +1520,7 @@
}
}
+ @GuardedBy("mLock")
private void readSettingsLocked() {
mRecords.clear();
mPrimaryStorageUuid = getDefaultPrimaryStorageUuid();
@@ -1559,6 +1565,7 @@
}
}
+ @GuardedBy("mLock")
private void writeSettingsLocked() {
FileOutputStream fos = null;
try {
diff --git a/services/core/java/com/android/server/VibratorService.java b/services/core/java/com/android/server/VibratorService.java
index dac1f4f..7fe5d7c 100644
--- a/services/core/java/com/android/server/VibratorService.java
+++ b/services/core/java/com/android/server/VibratorService.java
@@ -55,6 +55,7 @@
import android.view.InputDevice;
import android.media.AudioAttributes;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.app.IAppOpsService;
import com.android.internal.app.IBatteryStats;
import com.android.internal.util.DumpUtils;
@@ -111,6 +112,7 @@
private boolean mVibrateInputDevicesSetting; // guarded by mInputDeviceVibrators
private boolean mInputDeviceListenerRegistered; // guarded by mInputDeviceVibrators
+ @GuardedBy("mLock")
private Vibration mCurrentVibration;
private int mCurVibUid = -1;
private boolean mLowPowerMode;
@@ -435,45 +437,45 @@
// If our current vibration is longer than the new vibration and is the same amplitude,
// then just let the current one finish.
- if (effect instanceof VibrationEffect.OneShot
- && mCurrentVibration != null
- && mCurrentVibration.effect instanceof VibrationEffect.OneShot) {
- VibrationEffect.OneShot newOneShot = (VibrationEffect.OneShot) effect;
- VibrationEffect.OneShot currentOneShot =
- (VibrationEffect.OneShot) mCurrentVibration.effect;
- if (mCurrentVibration.hasTimeoutLongerThan(newOneShot.getDuration())
- && newOneShot.getAmplitude() == currentOneShot.getAmplitude()) {
+ synchronized (mLock) {
+ if (effect instanceof VibrationEffect.OneShot
+ && mCurrentVibration != null
+ && mCurrentVibration.effect instanceof VibrationEffect.OneShot) {
+ VibrationEffect.OneShot newOneShot = (VibrationEffect.OneShot) effect;
+ VibrationEffect.OneShot currentOneShot =
+ (VibrationEffect.OneShot) mCurrentVibration.effect;
+ if (mCurrentVibration.hasTimeoutLongerThan(newOneShot.getDuration())
+ && newOneShot.getAmplitude() == currentOneShot.getAmplitude()) {
+ if (DEBUG) {
+ Slog.d(TAG, "Ignoring incoming vibration in favor of current vibration");
+ }
+ return;
+ }
+ }
+
+ // If the current vibration is repeating and the incoming one is non-repeating, then
+ // ignore the non-repeating vibration. This is so that we don't cancel vibrations that
+ // are meant to grab the attention of the user, like ringtones and alarms, in favor of
+ // one-shot vibrations that are likely quite short.
+ if (!isRepeatingVibration(effect)
+ && mCurrentVibration != null
+ && isRepeatingVibration(mCurrentVibration.effect)) {
if (DEBUG) {
- Slog.d(TAG, "Ignoring incoming vibration in favor of current vibration");
+ Slog.d(TAG, "Ignoring incoming vibration in favor of alarm vibration");
}
return;
}
- }
- // If the current vibration is repeating and the incoming one is non-repeating, then ignore
- // the non-repeating vibration. This is so that we don't cancel vibrations that are meant
- // to grab the attention of the user, like ringtones and alarms, in favor of one-shot
- // vibrations that are likely quite short.
- if (!isRepeatingVibration(effect)
- && mCurrentVibration != null && isRepeatingVibration(mCurrentVibration.effect)) {
- if (DEBUG) {
- Slog.d(TAG, "Ignoring incoming vibration in favor of alarm vibration");
- }
- return;
- }
-
- Vibration vib = new Vibration(token, effect, usageHint, uid, opPkg);
- linkVibration(vib);
-
- long ident = Binder.clearCallingIdentity();
- try {
- synchronized (mLock) {
+ Vibration vib = new Vibration(token, effect, usageHint, uid, opPkg);
+ linkVibration(vib);
+ long ident = Binder.clearCallingIdentity();
+ try {
doCancelVibrateLocked();
startVibrationLocked(vib);
addToPreviousVibrationsLocked(vib);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
}
- } finally {
- Binder.restoreCallingIdentity(ident);
}
}
@@ -516,6 +518,7 @@
}
};
+ @GuardedBy("mLock")
private void doCancelVibrateLocked() {
mH.removeCallbacks(mVibrationEndRunnable);
if (mThread != null) {
@@ -538,6 +541,7 @@
}
}
+ @GuardedBy("mLock")
private void startVibrationLocked(final Vibration vib) {
if (!isAllowedToVibrateLocked(vib)) {
return;
@@ -568,6 +572,7 @@
startVibrationInnerLocked(vib);
}
+ @GuardedBy("mLock")
private void startVibrationInnerLocked(Vibration vib) {
mCurrentVibration = vib;
if (vib.effect instanceof VibrationEffect.OneShot) {
@@ -701,6 +706,7 @@
return mode;
}
+ @GuardedBy("mLock")
private void reportFinishVibrationLocked() {
if (mCurrentVibration != null) {
try {
@@ -880,6 +886,7 @@
}
}
+ @GuardedBy("mLock")
private long doVibratorPrebakedEffectLocked(Vibration vib) {
final VibrationEffect.Prebaked prebaked = (VibrationEffect.Prebaked) vib.effect;
final boolean usingInputDeviceVibrators;
diff --git a/services/core/java/com/android/server/Watchdog.java b/services/core/java/com/android/server/Watchdog.java
index 5215b6f..c8e0a5e 100644
--- a/services/core/java/com/android/server/Watchdog.java
+++ b/services/core/java/com/android/server/Watchdog.java
@@ -558,14 +558,7 @@
Slog.w(TAG, "Restart not allowed: Watchdog is *not* killing the system process");
} else {
Slog.w(TAG, "*** WATCHDOG KILLING SYSTEM PROCESS: " + subject);
- for (int i=0; i<blockedCheckers.size(); i++) {
- Slog.w(TAG, blockedCheckers.get(i).getName() + " stack trace:");
- StackTraceElement[] stackTrace
- = blockedCheckers.get(i).getThread().getStackTrace();
- for (StackTraceElement element: stackTrace) {
- Slog.w(TAG, " at " + element);
- }
- }
+ WatchdogDiagnostics.diagnoseCheckers(blockedCheckers);
Slog.w(TAG, "*** GOODBYE!");
Process.killProcess(Process.myPid());
System.exit(10);
diff --git a/services/core/java/com/android/server/WatchdogDiagnostics.java b/services/core/java/com/android/server/WatchdogDiagnostics.java
new file mode 100644
index 0000000..01db2d3
--- /dev/null
+++ b/services/core/java/com/android/server/WatchdogDiagnostics.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2018 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;
+
+import android.util.Log;
+import android.util.LogWriter;
+import android.util.Slog;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.Watchdog.HandlerChecker;
+
+import dalvik.system.AnnotatedStackTraceElement;
+import dalvik.system.VMStack;
+
+import java.io.PrintWriter;
+import java.util.List;
+
+/**
+ * Class to give diagnostic messages for Watchdogs.
+ */
+class WatchdogDiagnostics {
+ private static String getBlockedOnString(Object blockedOn) {
+ return String.format("- waiting to lock <0x%08x> (a %s)",
+ System.identityHashCode(blockedOn), blockedOn.getClass().getName());
+ }
+
+ private static String getLockedString(Object heldLock) {
+ return String.format("- locked <0x%08x> (a %s)", System.identityHashCode(heldLock),
+ heldLock.getClass().getName());
+ }
+
+ /**
+ * Print the annotated stack for the given thread. If the annotated stack cannot be retrieved,
+ * returns false.
+ */
+ @VisibleForTesting
+ public static boolean printAnnotatedStack(Thread thread, PrintWriter out) {
+ AnnotatedStackTraceElement stack[] = VMStack.getAnnotatedThreadStackTrace(thread);
+ if (stack == null) {
+ return false;
+ }
+ out.println(thread.getName() + " annotated stack trace:");
+ for (AnnotatedStackTraceElement element : stack) {
+ out.println(" at " + element.getStackTraceElement());
+ if (element.getBlockedOn() != null) {
+ out.println(" " + getBlockedOnString(element.getBlockedOn()));
+ }
+ if (element.getHeldLocks() != null) {
+ for (Object held : element.getHeldLocks()) {
+ out.println(" " + getLockedString(held));
+ }
+ }
+ }
+ return true;
+ }
+
+ public static void diagnoseCheckers(final List<HandlerChecker> blockedCheckers) {
+ PrintWriter out = new PrintWriter(new LogWriter(Log.WARN, Watchdog.TAG, Log.LOG_ID_SYSTEM),
+ true);
+ for (int i=0; i<blockedCheckers.size(); i++) {
+ Thread blockedThread = blockedCheckers.get(i).getThread();
+ if (printAnnotatedStack(blockedThread, out)) {
+ continue;
+ }
+
+ // Fall back to "regular" stack trace, if necessary.
+ Slog.w(Watchdog.TAG, blockedThread.getName() + " stack trace:");
+ StackTraceElement[] stackTrace = blockedThread.getStackTrace();
+ for (StackTraceElement element : stackTrace) {
+ Slog.w(Watchdog.TAG, " at " + element);
+ }
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 14404f5..230f69d 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -1043,20 +1043,14 @@
throw new SecurityException("Instant app " + r.appInfo.packageName
+ " does not have permission to create foreground services");
default:
- try {
- if (AppGlobals.getPackageManager().checkPermission(
- android.Manifest.permission.INSTANT_APP_FOREGROUND_SERVICE,
- r.appInfo.packageName, UserHandle.getUserId(r.appInfo.uid))
- != PackageManager.PERMISSION_GRANTED) {
- throw new SecurityException("Instant app " + r.appInfo.packageName
- + " does not have permission to create foreground"
- + "services");
- }
- } catch (RemoteException e) {
- throw new SecurityException("Failed to check instant app permission." ,
- e);
- }
+ mAm.enforcePermission(
+ android.Manifest.permission.INSTANT_APP_FOREGROUND_SERVICE,
+ r.app.pid, r.appInfo.uid, "startForeground");
}
+ } else if (r.appInfo.targetSdkVersion >= Build.VERSION_CODES.P) {
+ mAm.enforcePermission(
+ android.Manifest.permission.FOREGROUND_SERVICE,
+ r.app.pid, r.appInfo.uid, "startForeground");
}
if (r.fgRequired) {
if (DEBUG_SERVICE || DEBUG_BACKGROUND_CHECK) {
diff --git a/services/core/java/com/android/server/am/ActivityDisplay.java b/services/core/java/com/android/server/am/ActivityDisplay.java
index 220014f..608352b 100644
--- a/services/core/java/com/android/server/am/ActivityDisplay.java
+++ b/services/core/java/com/android/server/am/ActivityDisplay.java
@@ -657,6 +657,26 @@
return false;
}
+ void remove() {
+ final boolean destroyContentOnRemoval = shouldDestroyContentOnRemove();
+ while (getChildCount() > 0) {
+ final ActivityStack stack = getChildAt(0);
+ if (destroyContentOnRemoval) {
+ mSupervisor.moveStackToDisplayLocked(stack.mStackId, DEFAULT_DISPLAY,
+ false /* onTop */);
+ stack.finishAllActivitiesLocked(true /* immediately */);
+ } else {
+ // Moving all tasks to fullscreen stack, because it's guaranteed to be
+ // a valid launch stack for all activities. This way the task history from
+ // external display will be preserved on primary after move.
+ mSupervisor.moveTasksToFullscreenStackLocked(stack, true /* onTop */);
+ }
+ }
+
+ mWindowContainerController.removeContainer();
+ mWindowContainerController = null;
+ }
+
/** Update and get all UIDs that are present on the display and have access to it. */
IntArray getPresentUIDs() {
mDisplayAccessUIDs.clear();
@@ -666,7 +686,7 @@
return mDisplayAccessUIDs;
}
- boolean shouldDestroyContentOnRemove() {
+ private boolean shouldDestroyContentOnRemove() {
return mDisplay.getRemoveMode() == REMOVE_MODE_DESTROY_CONTENT;
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 2d443a3..cd8b6d7 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -3004,6 +3004,16 @@
Watchdog.getInstance().addMonitor(this);
Watchdog.getInstance().addThread(mHandler);
+
+ // bind background thread to little cores
+ // this is expected to fail inside of framework tests because apps can't touch cpusets directly
+ try {
+ Process.setThreadGroupAndCpuset(BackgroundThread.get().getThreadId(),
+ Process.THREAD_GROUP_BG_NONINTERACTIVE);
+ } catch (Exception e) {
+ Slog.w(TAG, "Setting background thread cpuset failed");
+ }
+
}
protected ActivityStackSupervisor createStackSupervisor() {
@@ -3282,6 +3292,7 @@
* Update AMS states when an activity is resumed. This should only be called by
* {@link ActivityStack#setResumedActivityLocked} when an activity is resumed.
*/
+ @GuardedBy("this")
void setResumedActivityUncheckLocked(ActivityRecord r, String reason) {
final TaskRecord task = r.getTask();
if (task.isActivityTypeStandard()) {
@@ -3807,6 +3818,7 @@
info.className = entryPoint;
info.packageName = "android";
info.seInfoUser = SELinuxUtil.COMPLETE_STR;
+ info.targetSdkVersion = Build.VERSION.SDK_INT;
ProcessRecord proc = startProcessLocked(processName, info /* info */,
false /* knownToBeDead */, 0 /* intentFlags */, "" /* hostingType */,
null /* hostingName */, true /* allowWhileBooting */, true /* isolated */,
@@ -3816,6 +3828,7 @@
}
}
+ @GuardedBy("this")
final ProcessRecord startProcessLocked(String processName,
ApplicationInfo info, boolean knownToBeDead, int intentFlags,
String hostingType, ComponentName hostingName, boolean allowWhileBooting,
@@ -3826,6 +3839,7 @@
null /* crashHandler */);
}
+ @GuardedBy("this")
final ProcessRecord startProcessLocked(String processName, ApplicationInfo info,
boolean knownToBeDead, int intentFlags, String hostingType, ComponentName hostingName,
boolean allowWhileBooting, boolean isolated, int isolatedUid, boolean keepIfLarge,
@@ -3942,6 +3956,7 @@
return (ai.flags&ApplicationInfo.FLAG_PERSISTENT) != 0;
}
+ @GuardedBy("this")
private final void startProcessLocked(ProcessRecord app,
String hostingType, String hostingNameStr) {
startProcessLocked(app, hostingType, hostingNameStr, null /* abiOverride */);
@@ -3950,6 +3965,7 @@
/**
* @return {@code true} if process start is successful, false otherwise.
*/
+ @GuardedBy("this")
private final boolean startProcessLocked(ProcessRecord app, String hostingType,
String hostingNameStr, String abiOverride) {
if (app.pendingStart) {
@@ -5124,6 +5140,7 @@
.supportsLocalVoiceInteraction();
}
+ @GuardedBy("this")
void onLocalVoiceInteractionStartedLocked(IBinder activity,
IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor) {
ActivityRecord activityToCallback = ActivityRecord.forTokenLocked(activity);
@@ -5622,6 +5639,7 @@
* as a result of that process going away. Clears out all connections
* to the process.
*/
+ @GuardedBy("this")
private final void handleAppDiedLocked(ProcessRecord app,
boolean restarting, boolean allowRestart) {
int pid = app.pid;
@@ -5768,10 +5786,12 @@
}
}
+ @GuardedBy("this")
final void appDiedLocked(ProcessRecord app) {
appDiedLocked(app, app.pid, app.thread, false);
}
+ @GuardedBy("this")
final void appDiedLocked(ProcessRecord app, int pid, IApplicationThread thread,
boolean fromBinderDied) {
// First check if this ProcessRecord is actually active for the pid.
@@ -6229,6 +6249,7 @@
}
}
+ @GuardedBy("this")
final void showLaunchWarningLocked(final ActivityRecord cur, final ActivityRecord next) {
if (!mLaunchWarningShown) {
mLaunchWarningShown = true;
@@ -6655,6 +6676,7 @@
}
}
+ @GuardedBy("this")
void closeSystemDialogsLocked(String reason) {
Intent intent = new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY
@@ -6761,11 +6783,13 @@
}
}
+ @GuardedBy("this")
private void forceStopPackageLocked(final String packageName, int uid, String reason) {
forceStopPackageLocked(packageName, UserHandle.getAppId(uid), false,
false, true, false, false, UserHandle.getUserId(uid), reason);
}
+ @GuardedBy("this")
private void finishForceStopPackageLocked(final String packageName, int uid) {
Intent intent = new Intent(Intent.ACTION_PACKAGE_RESTARTED,
Uri.fromParts("package", packageName, null));
@@ -6781,6 +6805,7 @@
}
+ @GuardedBy("this")
private final boolean killPackageProcessesLocked(String packageName, int appId,
int userId, int minOomAdj, boolean callerWillRestart, boolean allowRestart,
boolean doit, boolean evenPersistent, String reason) {
@@ -6952,6 +6977,7 @@
return didSomething;
}
+ @GuardedBy("this")
final boolean forceStopPackageLocked(String packageName, int appId,
boolean callerWillRestart, boolean purgeCache, boolean doit,
boolean evenPersistent, boolean uninstalling, int userId, String reason) {
@@ -7163,6 +7189,7 @@
}
}
+ @GuardedBy("this")
boolean removeProcessLocked(ProcessRecord app,
boolean callerWillRestart, boolean allowRestart, String reason) {
final String name = app.processName;
@@ -7217,6 +7244,7 @@
return needRestart;
}
+ @GuardedBy("this")
private final void processContentProviderPublishTimedOutLocked(ProcessRecord app) {
cleanupAppInLaunchingProvidersLocked(app, true);
removeProcessLocked(app, false, true, "timeout publishing content providers");
@@ -7277,6 +7305,7 @@
}
}
+ @GuardedBy("this")
private final boolean attachApplicationLocked(IApplicationThread thread,
int pid, int callingUid, long startSeq) {
@@ -8860,6 +8889,20 @@
/**
* This can be called with or without the global lock held.
*/
+ void enforcePermission(String permission, int pid, int uid, String func) {
+ if (checkPermission(permission, pid, uid) == PackageManager.PERMISSION_GRANTED) {
+ return;
+ }
+
+ String msg = "Permission Denial: " + func + " from pid=" + pid + ", uid=" + uid
+ + " requires " + permission;
+ Slog.w(TAG, msg);
+ throw new SecurityException(msg);
+ }
+
+ /**
+ * This can be called with or without the global lock held.
+ */
void enforceCallerIsRecentsOrHasPermission(String permission, String func) {
if (!mRecentTasks.isCallerRecents(Binder.getCallingUid())) {
enforceCallingPermission(permission, func);
@@ -9130,6 +9173,7 @@
grantEphemeralAccess(userId, intent, targetAppId, ephemeralAppId);
}
+ @GuardedBy("this")
private UriPermission findUriPermissionLocked(int targetUid, GrantUri grantUri) {
final ArrayMap<GrantUri, UriPermission> targetUris = mGrantedUriPermissions.get(targetUid);
if (targetUris != null) {
@@ -9138,6 +9182,7 @@
return null;
}
+ @GuardedBy("this")
private UriPermission findOrCreateUriPermissionLocked(String sourcePkg,
String targetPkg, int targetUid, GrantUri grantUri) {
ArrayMap<GrantUri, UriPermission> targetUris = mGrantedUriPermissions.get(targetUid);
@@ -9155,6 +9200,7 @@
return perm;
}
+ @GuardedBy("this")
private final boolean checkUriPermissionLocked(GrantUri grantUri, int uid,
final int modeFlags) {
final boolean persistable = (modeFlags & Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION) != 0;
@@ -9225,6 +9271,7 @@
* If you already know the uid of the target, you can supply it in
* lastTargetUid else set that to -1.
*/
+ @GuardedBy("this")
int checkGrantUriPermissionLocked(int callingUid, String targetPkg, GrantUri grantUri,
final int modeFlags, int lastTargetUid) {
if (!Intent.isAccessUriMode(modeFlags)) {
@@ -9387,6 +9434,7 @@
}
}
+ @GuardedBy("this")
void grantUriPermissionUncheckedLocked(int targetUid, String targetPkg, GrantUri grantUri,
final int modeFlags, UriPermissionOwner owner) {
if (!Intent.isAccessUriMode(modeFlags)) {
@@ -9416,6 +9464,7 @@
perm.grantModes(modeFlags, owner);
}
+ @GuardedBy("this")
void grantUriPermissionLocked(int callingUid, String targetPkg, GrantUri grantUri,
final int modeFlags, UriPermissionOwner owner, int targetUserId) {
if (targetPkg == null) {
@@ -9467,6 +9516,7 @@
/**
* Like checkGrantUriPermissionLocked, but takes an Intent.
*/
+ @GuardedBy("this")
NeededUriGrants checkGrantUriPermissionFromIntentLocked(int callingUid,
String targetPkg, Intent intent, int mode, NeededUriGrants needed, int targetUserId) {
if (DEBUG_URI_PERMISSION) Slog.v(TAG_URI_PERMISSION,
@@ -9553,6 +9603,7 @@
/**
* Like grantUriPermissionUncheckedLocked, but takes an Intent.
*/
+ @GuardedBy("this")
void grantUriPermissionUncheckedFromIntentLocked(NeededUriGrants needed,
UriPermissionOwner owner) {
if (needed != null) {
@@ -9564,6 +9615,7 @@
}
}
+ @GuardedBy("this")
void grantUriPermissionFromIntentLocked(int callingUid,
String targetPkg, Intent intent, UriPermissionOwner owner, int targetUserId) {
NeededUriGrants needed = checkGrantUriPermissionFromIntentLocked(callingUid, targetPkg,
@@ -9608,6 +9660,7 @@
}
}
+ @GuardedBy("this")
void removeUriPermissionIfNeededLocked(UriPermission perm) {
if (perm.modeFlags == 0) {
final ArrayMap<GrantUri, UriPermission> perms = mGrantedUriPermissions.get(
@@ -9624,6 +9677,7 @@
}
}
+ @GuardedBy("this")
private void revokeUriPermissionLocked(String targetPackage, int callingUid, GrantUri grantUri,
final int modeFlags) {
if (DEBUG_URI_PERMISSION) Slog.v(TAG_URI_PERMISSION,
@@ -9758,6 +9812,7 @@
* @param targetOnly When {@code true}, only remove permissions where the app is the target,
* not source.
*/
+ @GuardedBy("this")
private void removeUriPermissionsForPackageLocked(
String packageName, int userHandle, boolean persistable, boolean targetOnly) {
if (userHandle == UserHandle.USER_ALL && packageName == null) {
@@ -9944,6 +9999,7 @@
}
}
+ @GuardedBy("this")
private void readGrantedUriPermissionsLocked() {
if (DEBUG_URI_PERMISSION) Slog.v(TAG_URI_PERMISSION, "readGrantedUriPermissions()");
@@ -10106,6 +10162,7 @@
*
* @return if any mutations occured that require persisting.
*/
+ @GuardedBy("this")
private boolean maybePrunePersistedUriGrantsLocked(int uid) {
final ArrayMap<GrantUri, UriPermission> perms = mGrantedUriPermissions.get(uid);
if (perms == null) return false;
@@ -12606,6 +12663,7 @@
// GLOBAL MANAGEMENT
// =========================================================
+ @GuardedBy("this")
final ProcessRecord newProcessRecordLocked(ApplicationInfo info, String customProcess,
boolean isolated, int isolatedUid) {
String proc = customProcess != null ? customProcess : info.processName;
@@ -12650,6 +12708,9 @@
if (!mBooted && !mBooting
&& userId == UserHandle.USER_SYSTEM
&& (info.flags & PERSISTENT_MASK) == PERSISTENT_MASK) {
+ // The system process is initialized to SCHED_GROUP_DEFAULT in init.rc.
+ r.curSchedGroup = ProcessList.SCHED_GROUP_DEFAULT;
+ r.setSchedGroup = ProcessList.SCHED_GROUP_DEFAULT;
r.persistent = true;
r.maxAdj = ProcessList.PERSISTENT_PROC_ADJ;
}
@@ -12692,6 +12753,7 @@
}
}
+ @GuardedBy("this")
final ProcessRecord addAppLocked(ApplicationInfo info, String customProcess, boolean isolated,
String abiOverride) {
ProcessRecord app;
@@ -12808,6 +12870,7 @@
}
}
+ @GuardedBy("this")
void finishRunningVoiceLocked() {
if (mRunningVoice != null) {
mRunningVoice = null;
@@ -12823,6 +12886,7 @@
}
}
+ @GuardedBy("this")
void updateSleepIfNeededLocked() {
final boolean shouldSleep = !mStackSupervisor.hasAwakeDisplay();
final boolean wasSleeping = mSleeping;
@@ -12927,6 +12991,7 @@
Binder.restoreCallingIdentity(origId);
}
+ @GuardedBy("this")
void startRunningVoiceLocked(IVoiceInteractionSession session, int targetUid) {
Slog.d(TAG, "<<< startRunningVoiceLocked()");
mVoiceWakeLock.setWorkSource(new WorkSource(targetUid));
@@ -14785,8 +14850,6 @@
mUserController.sendUserSwitchBroadcasts(-1, currentUserId);
BinderInternal.nSetBinderProxyCountEnabled(true);
- //STOPSHIP: Temporary BinderProxy Threshold for b/71353150
- BinderInternal.nSetBinderProxyCountWatermarks(1500, 1200);
BinderInternal.setBinderProxyCountCallback(
new BinderInternal.BinderProxyLimitListener() {
@Override
@@ -16164,6 +16227,7 @@
return false;
}
+ @GuardedBy("this")
void dumpProcessesLocked(FileDescriptor fd, PrintWriter pw, String[] args,
int opti, boolean dumpAll, String dumpPackage, int dumpAppId) {
boolean needSep = false;
@@ -16617,6 +16681,7 @@
pw.println(" mForceBackgroundCheck=" + mForceBackgroundCheck);
}
+ @GuardedBy("this")
void writeProcessesToProtoLocked(ProtoOutputStream proto, String dumpPackage) {
int numPers = 0;
@@ -17444,6 +17509,7 @@
}
}
+ @GuardedBy("this")
void dumpPermissionsLocked(FileDescriptor fd, PrintWriter pw, String[] args,
int opti, boolean dumpAll, String dumpPackage) {
boolean needSep = false;
@@ -19516,6 +19582,7 @@
* @return Returns true if the given process has been restarted, so the
* app that was passed in must remain on the process lists.
*/
+ @GuardedBy("this")
private final boolean cleanUpApplicationRecordLocked(ProcessRecord app,
boolean restarting, boolean allowRestart, int index, boolean replacingPid) {
if (index >= 0) {
@@ -20605,6 +20672,7 @@
}
}
+ @GuardedBy("this")
final int broadcastIntentLocked(ProcessRecord callerApp,
String callerPackage, Intent intent, String resolvedType,
IIntentReceiver resultTo, int resultCode, String resultData,
@@ -21595,6 +21663,7 @@
}
}
+ @GuardedBy("this")
void finishInstrumentationLocked(ProcessRecord app, int resultCode, Bundle results) {
if (app.instr == null) {
Slog.w(TAG, "finishInstrumentation called on non-instrumented: " + app);
@@ -21879,7 +21948,7 @@
}
try {
if (values != null) {
- changes = updateGlobalConfiguration(values, initLocale, persistent, userId,
+ changes = updateGlobalConfigurationLocked(values, initLocale, persistent, userId,
deferResume);
}
@@ -21897,8 +21966,16 @@
return kept;
}
+ /**
+ * Returns true if this configuration change is interesting enough to send an
+ * {@link Intent#ACTION_SPLIT_CONFIGURATION_CHANGED} broadcast.
+ */
+ private static boolean isSplitConfigurationChange(int configDiff) {
+ return (configDiff & (ActivityInfo.CONFIG_LOCALE | ActivityInfo.CONFIG_DENSITY)) != 0;
+ }
+
/** Update default (global) configuration and notify listeners about changes. */
- private int updateGlobalConfiguration(@NonNull Configuration values, boolean initLocale,
+ private int updateGlobalConfigurationLocked(@NonNull Configuration values, boolean initLocale,
boolean persistent, int userId, boolean deferResume) {
mTempConfig.setTo(getGlobalConfiguration());
final int changes = mTempConfig.updateFrom(values);
@@ -22001,6 +22078,19 @@
UserHandle.USER_ALL);
}
+ // Send a broadcast to PackageInstallers if the configuration change is interesting
+ // for the purposes of installing additional splits.
+ if (!initLocale && isSplitConfigurationChange(changes)) {
+ intent = new Intent(Intent.ACTION_SPLIT_CONFIGURATION_CHANGED);
+ intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING
+ | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
+
+ // Typically only app stores will have this permission.
+ String[] permissions = new String[] { android.Manifest.permission.INSTALL_PACKAGES };
+ broadcastIntentLocked(null, null, intent, null, null, 0, null, null, permissions,
+ OP_NONE, null, false, false, MY_PID, SYSTEM_UID, UserHandle.USER_ALL);
+ }
+
// Override configuration of the default display duplicates global config, so we need to
// update it also. This will also notify WindowManager about changes.
performDisplayOverrideConfigUpdate(mStackSupervisor.getConfiguration(), deferResume,
@@ -22073,7 +22163,7 @@
// Override configuration of the default display duplicates global config, so
// we're calling global config update instead for default display. It will also
// apply the correct override config.
- changes = updateGlobalConfiguration(values, false /* initLocale */,
+ changes = updateGlobalConfigurationLocked(values, false /* initLocale */,
false /* persistent */, UserHandle.USER_NULL /* userId */, deferResume);
} else {
changes = performDisplayOverrideConfigUpdate(values, deferResume, displayId);
@@ -23993,6 +24083,7 @@
return applyOomAdjLocked(app, doingAll, now, SystemClock.elapsedRealtime());
}
+ @GuardedBy("this")
final void updateProcessForegroundLocked(ProcessRecord proc, boolean isForeground,
boolean oomAdj) {
if (isForeground != proc.foregroundServices) {
@@ -24062,6 +24153,7 @@
* if necessary, or skip.
* @return whether updateOomAdjLocked(app) was successful.
*/
+ @GuardedBy("this")
final boolean updateOomAdjLocked(ProcessRecord app, boolean oomAdjAll) {
final ActivityRecord TOP_ACT = resumedAppLocked();
final ProcessRecord TOP_APP = TOP_ACT != null ? TOP_ACT.app : null;
@@ -24086,6 +24178,7 @@
return success;
}
+ @GuardedBy("this")
final void updateOomAdjLocked() {
final ActivityRecord TOP_ACT = resumedAppLocked();
final ProcessRecord TOP_APP = TOP_ACT != null ? TOP_ACT.app : null;
@@ -24806,6 +24899,7 @@
/**
* Whitelists {@code targetUid} to temporarily bypass Power Save mode.
*/
+ @GuardedBy("this")
void tempWhitelistForPendingIntentLocked(int callerPid, int callerUid, int targetUid,
long duration, String tag) {
if (DEBUG_WHITELISTS) {
@@ -24838,6 +24932,7 @@
/**
* Whitelists {@code targetUid} to temporarily bypass Power Save mode.
*/
+ @GuardedBy("this")
void tempWhitelistUidLocked(int targetUid, long duration, String tag) {
mPendingTempWhitelist.put(targetUid, new PendingTempWhitelist(targetUid, duration, tag));
setUidTempWhitelistStateLocked(targetUid, true);
@@ -24877,6 +24972,7 @@
}
}
+ @GuardedBy("this")
final void setAppIdTempWhitelistStateLocked(int appId, boolean onWhitelist) {
boolean changed = false;
for (int i=mActiveUids.size()-1; i>=0; i--) {
@@ -24891,6 +24987,7 @@
}
}
+ @GuardedBy("this")
final void setUidTempWhitelistStateLocked(int uid, boolean onWhitelist) {
boolean changed = false;
final UidRecord uidRec = mActiveUids.get(uid);
@@ -25869,6 +25966,14 @@
public boolean isCallerRecents(int callingUid) {
return getRecentTasks().isCallerRecents(callingUid);
}
+
+ @Override
+ public boolean isUidActive(int uid) {
+ synchronized (ActivityManagerService.this) {
+ final UidRecord uidRec = mActiveUids.get(uid);
+ return (uidRec != null) && !uidRec.idle;
+ }
+ }
}
/**
diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java
index cae0d2b..9838851 100644
--- a/services/core/java/com/android/server/am/ActivityRecord.java
+++ b/services/core/java/com/android/server/am/ActivityRecord.java
@@ -133,11 +133,16 @@
import android.app.PendingIntent;
import android.app.PictureInPictureParams;
import android.app.ResultInfo;
+import android.app.servertransaction.ActivityLifecycleItem;
+import android.app.servertransaction.ActivityRelaunchItem;
+import android.app.servertransaction.ClientTransaction;
+import android.app.servertransaction.ClientTransactionItem;
import android.app.servertransaction.MoveToDisplayItem;
import android.app.servertransaction.MultiWindowModeChangeItem;
import android.app.servertransaction.NewIntentItem;
import android.app.servertransaction.PauseActivityItem;
import android.app.servertransaction.PipModeChangeItem;
+import android.app.servertransaction.ResumeActivityItem;
import android.app.servertransaction.WindowVisibilityItem;
import android.app.servertransaction.ActivityConfigurationChangeItem;
import android.content.ComponentName;
@@ -375,7 +380,8 @@
}
String getLifecycleDescription(String reason) {
- return "packageName=" + packageName + ", state=" + state + ", reason=" + reason;
+ return "component:" + intent.getComponent().flattenToShortString() + ", state=" + state
+ + ", reason=" + reason + ", time=" + System.currentTimeMillis();
}
void dump(PrintWriter pw, String prefix) {
@@ -2370,6 +2376,15 @@
setLastReportedConfiguration(service.getGlobalConfiguration(), newMergedOverrideConfig);
+ if (state == INITIALIZING) {
+ // No need to relaunch or schedule new config for activity that hasn't been launched
+ // yet. We do, however, return after applying the config to activity record, so that
+ // it will use it for launch transaction.
+ if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION,
+ "Skipping config check for initializing activity: " + this);
+ return true;
+ }
+
if (changes == 0 && !forceNewConfig) {
if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION,
"Configuration no differences in " + this);
@@ -2559,12 +2574,23 @@
+ " callers=" + Debug.getCallers(6));
forceNewConfig = false;
mStackSupervisor.activityRelaunchingLocked(this);
- app.thread.scheduleRelaunchActivity(appToken, pendingResults, pendingNewIntents,
- configChangeFlags, !andResume,
- new Configuration(service.getGlobalConfiguration()),
- new Configuration(getMergedOverrideConfiguration()), preserveWindow);
+ final ClientTransactionItem callbackItem = ActivityRelaunchItem.obtain(pendingResults,
+ pendingNewIntents, configChangeFlags,
+ new MergedConfiguration(service.getGlobalConfiguration(),
+ getMergedOverrideConfiguration()),
+ preserveWindow);
+ final ActivityLifecycleItem lifecycleItem;
+ if (andResume) {
+ lifecycleItem = ResumeActivityItem.obtain(service.isNextTransitionForward());
+ } else {
+ lifecycleItem = PauseActivityItem.obtain();
+ }
+ final ClientTransaction transaction = ClientTransaction.obtain(app.thread, appToken);
+ transaction.addCallback(callbackItem);
+ transaction.setLifecycleStateRequest(lifecycleItem);
+ service.mLifecycleManager.scheduleTransaction(transaction);
// Note: don't need to call pauseIfSleepingLocked() here, because the caller will only
- // pass in 'andResume' if this activity is currently resumed, which implies we aren't
+ // request resume if this activity is currently resumed, which implies we aren't
// sleeping.
} catch (RemoteException e) {
if (DEBUG_SWITCH || DEBUG_STATES) Slog.i(TAG_SWITCH, "Relaunch failed", e);
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index ab2dc36..055a1aa 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -138,6 +138,7 @@
import android.util.proto.ProtoOutputStream;
import android.view.Display;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.IVoiceInteractor;
import com.android.internal.os.BatteryStatsImpl;
@@ -2211,6 +2212,7 @@
* Use {@link ActivityStackSupervisor#resumeFocusedStackTopActivityLocked} to resume the
* right activity for the current system state.
*/
+ @GuardedBy("mService")
boolean resumeTopActivityUncheckedLocked(ActivityRecord prev, ActivityOptions options) {
if (mStackSupervisor.inResumeTopActivity) {
// Don't even start recursing.
@@ -2250,6 +2252,7 @@
mStackSupervisor.mRecentTasks.add(r.getTask());
}
+ @GuardedBy("mService")
private boolean resumeTopActivityInnerLocked(ActivityRecord prev, ActivityOptions options) {
if (!mService.mBooting && !mService.mBooted) {
// Not ready yet!
@@ -4214,7 +4217,8 @@
if (DEBUG_SWITCH) Slog.i(TAG_SWITCH, "Destroying: " + r);
mService.mLifecycleManager.scheduleTransaction(r.app.thread, r.appToken,
DestroyActivityItem.obtain(r.finishing, r.configChangeFlags)
- .setDescription(r.getLifecycleDescription("destroyActivityLocked")));
+ .setDescription(
+ r.getLifecycleDescription("destroyActivityLocked:" + reason)));
} catch (Exception e) {
// We can just ignore exceptions here... if the process
// has crashed, our death notification will clean things
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index a0f31cd..4928e90 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -166,6 +166,7 @@
import android.view.Display;
import android.view.RemoteAnimationAdapter;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.content.ReferrerIntent;
import com.android.internal.os.logging.MetricsLoggerWrapper;
@@ -1508,6 +1509,8 @@
Slog.w(TAG, "Activity " + r + " being launched, but already in LRU list");
}
+ // TODO(lifecycler): Resume or pause requests are done as part of launch transaction,
+ // so updating the state should be done accordingly.
if (andResume && readyToResume()) {
// As part of the process of launching, ActivityThread also performs
// a resume.
@@ -1858,6 +1861,7 @@
* Called when the frontmost task is idle.
* @return the state of mService.mBooting before this was called.
*/
+ @GuardedBy("mService")
private boolean checkFinishBootingLocked() {
final boolean booting = mService.mBooting;
boolean enableScreen = false;
@@ -1873,6 +1877,7 @@
}
// Checked.
+ @GuardedBy("mService")
final ActivityRecord activityIdleInternalLocked(final IBinder token, boolean fromTimeout,
boolean processPausingActivities, Configuration config) {
if (DEBUG_ALL) Slog.v(TAG, "Activity idle: " + token);
@@ -4087,25 +4092,12 @@
if (activityDisplay == null) {
return;
}
- final boolean destroyContentOnRemoval
- = activityDisplay.shouldDestroyContentOnRemove();
- while (activityDisplay.getChildCount() > 0) {
- final ActivityStack stack = activityDisplay.getChildAt(0);
- if (destroyContentOnRemoval) {
- moveStackToDisplayLocked(stack.mStackId, DEFAULT_DISPLAY, false /* onTop */);
- stack.finishAllActivitiesLocked(true /* immediately */);
- } else {
- // Moving all tasks to fullscreen stack, because it's guaranteed to be
- // a valid launch stack for all activities. This way the task history from
- // external display will be preserved on primary after move.
- moveTasksToFullscreenStackLocked(stack, true /* onTop */);
- }
- }
+
+ activityDisplay.remove();
releaseSleepTokens(activityDisplay);
mActivityDisplays.remove(displayId);
- mWindowManager.onDisplayRemoved(displayId);
}
}
diff --git a/services/core/java/com/android/server/am/ActivityStarter.java b/services/core/java/com/android/server/am/ActivityStarter.java
index b061ba4..055b89b 100644
--- a/services/core/java/com/android/server/am/ActivityStarter.java
+++ b/services/core/java/com/android/server/am/ActivityStarter.java
@@ -790,7 +790,7 @@
// Instead, launch the ephemeral installer. Once the installer is finished, it
// starts either the intent we resolved here [on install error] or the ephemeral
// app [on install success].
- if (rInfo != null && rInfo.isInstantAppAvailable) {
+ if (rInfo != null && rInfo.auxiliaryInfo != null) {
intent = createLaunchIntent(rInfo.auxiliaryInfo, ephemeralIntent,
callingPackage, verificationBundle, resolvedType, userId);
resolvedType = null;
diff --git a/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java b/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
index 927b72c..ef82f36 100644
--- a/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
+++ b/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
@@ -182,6 +182,7 @@
mExecutorService.shutdownNow();
}
+ @GuardedBy("this")
private Future<?> scheduleSyncLocked(String reason, int flags) {
if (mExecutorService.isShutdown()) {
return CompletableFuture.failedFuture(new IllegalStateException("worker shutdown"));
@@ -248,6 +249,7 @@
}
};
+ @GuardedBy("mWorkerLock")
private void updateExternalStatsLocked(final String reason, int updateFlags) {
// We will request data from external processes asynchronously, and wait on a timeout.
SynchronousResultReceiver wifiReceiver = null;
@@ -372,6 +374,7 @@
return null;
}
+ @GuardedBy("mWorkerLock")
private WifiActivityEnergyInfo extractDeltaLocked(WifiActivityEnergyInfo latest) {
final long timePeriodMs = latest.mTimestamp - mLastInfo.mTimestamp;
final long lastScanMs = mLastInfo.mControllerScanTimeMs;
diff --git a/services/core/java/com/android/server/am/GlobalSettingsToPropertiesMapper.java b/services/core/java/com/android/server/am/GlobalSettingsToPropertiesMapper.java
index 9033b55..71fd71b 100644
--- a/services/core/java/com/android/server/am/GlobalSettingsToPropertiesMapper.java
+++ b/services/core/java/com/android/server/am/GlobalSettingsToPropertiesMapper.java
@@ -93,7 +93,7 @@
}
try {
systemPropertiesSet(key, value);
- } catch (IllegalArgumentException e) {
+ } catch (Exception e) {
Slog.e(TAG, "Unable to set property " + key + " value '" + value + "'", e);
}
}
diff --git a/services/core/java/com/android/server/am/PersistentConnection.java b/services/core/java/com/android/server/am/PersistentConnection.java
index 52eaca1..c5edb26 100644
--- a/services/core/java/com/android/server/am/PersistentConnection.java
+++ b/services/core/java/com/android/server/am/PersistentConnection.java
@@ -220,6 +220,7 @@
}
}
+ @GuardedBy("mLock")
public final void bindInnerLocked(boolean resetBackoff) {
unscheduleRebindLocked();
@@ -260,6 +261,7 @@
}
}
+ @GuardedBy("mLock")
private void cleanUpConnectionLocked() {
mIsConnected = false;
mService = null;
@@ -276,6 +278,7 @@
}
}
+ @GuardedBy("mLock")
private final void unbindLocked() {
unscheduleRebindLocked();
@@ -289,11 +292,13 @@
cleanUpConnectionLocked();
}
+ @GuardedBy("mLock")
void unscheduleRebindLocked() {
injectRemoveCallbacks(mBindForBackoffRunnable);
mRebindScheduled = false;
}
+ @GuardedBy("mLock")
void scheduleRebindLocked() {
unbindLocked();
diff --git a/services/core/java/com/android/server/am/ProcessStatsService.java b/services/core/java/com/android/server/am/ProcessStatsService.java
index edf565a..c10d81b 100644
--- a/services/core/java/com/android/server/am/ProcessStatsService.java
+++ b/services/core/java/com/android/server/am/ProcessStatsService.java
@@ -135,6 +135,7 @@
return mMemFactorLowered;
}
+ @GuardedBy("mAm")
public boolean setMemFactorLocked(int memFactor, boolean screenOn, long now) {
mMemFactorLowered = memFactor < mLastMemOnlyState;
mLastMemOnlyState = memFactor;
diff --git a/services/core/java/com/android/server/am/RecentsAnimation.java b/services/core/java/com/android/server/am/RecentsAnimation.java
index e7b067b..db4e09f 100644
--- a/services/core/java/com/android/server/am/RecentsAnimation.java
+++ b/services/core/java/com/android/server/am/RecentsAnimation.java
@@ -20,6 +20,7 @@
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
import static android.content.Intent.FLAG_ACTIVITY_NO_ANIMATION;
+import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
import static android.view.WindowManager.TRANSIT_NONE;
import static com.android.server.am.ActivityStackSupervisor.PRESERVE_WINDOWS;
@@ -27,6 +28,7 @@
import android.content.ComponentName;
import android.content.Intent;
import android.os.Handler;
+import android.os.Trace;
import android.view.IRecentsAnimationRunner;
import com.android.server.wm.RecentsAnimationController.RecentsAnimationCallbacks;
import com.android.server.wm.WindowManagerService;
@@ -70,6 +72,7 @@
void startRecentsActivity(Intent intent, IRecentsAnimationRunner recentsAnimationRunner,
ComponentName recentsComponent, int recentsUid) {
+ Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "RecentsAnimation#startRecentsActivity");
mWindowManager.deferSurfaceLayout();
try {
// Cancel the previous recents animation if necessary
@@ -124,6 +127,7 @@
mHandler.postDelayed(mCancelAnimationRunnable, RECENTS_ANIMATION_TIMEOUT);
} finally {
mWindowManager.continueSurfaceLayout();
+ Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
}
}
@@ -134,6 +138,8 @@
if (mWindowManager.getRecentsAnimationController() == null) return;
mWindowManager.inSurfaceTransaction(() -> {
+ Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER,
+ "RecentsAnimation#onAnimationFinished_inSurfaceTransaction");
mWindowManager.deferSurfaceLayout();
try {
mWindowManager.cleanupRecentsAnimation();
@@ -167,6 +173,7 @@
mWindowManager.executeAppTransition();
} finally {
mWindowManager.continueSurfaceLayout();
+ Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
}
});
}
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 8635cff..e95608f 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -285,7 +285,7 @@
private final int[][] SOUND_EFFECT_FILES_MAP = new int[AudioManager.NUM_SOUND_EFFECTS][2];
/** Maximum volume index values for audio streams */
- private static int[] MAX_STREAM_VOLUME = new int[] {
+ protected static int[] MAX_STREAM_VOLUME = new int[] {
5, // STREAM_VOICE_CALL
7, // STREAM_SYSTEM
7, // STREAM_RING
@@ -300,7 +300,7 @@
};
/** Minimum volume index values for audio streams */
- private static int[] MIN_STREAM_VOLUME = new int[] {
+ protected static int[] MIN_STREAM_VOLUME = new int[] {
1, // STREAM_VOICE_CALL
0, // STREAM_SYSTEM
0, // STREAM_RING
@@ -1410,7 +1410,7 @@
Binder.getCallingUid());
}
- private void adjustStreamVolume(int streamType, int direction, int flags,
+ protected void adjustStreamVolume(int streamType, int direction, int flags,
String callingPackage, String caller, int uid) {
if (mUseFixedVolume) {
return;
diff --git a/services/core/java/com/android/server/content/ContentService.java b/services/core/java/com/android/server/content/ContentService.java
index 1ee0548..c3f020a 100644
--- a/services/core/java/com/android/server/content/ContentService.java
+++ b/services/core/java/com/android/server/content/ContentService.java
@@ -1116,6 +1116,7 @@
return (pi != null) ? pi.packageName : null;
}
+ @GuardedBy("mCache")
private ArrayMap<Pair<String, Uri>, Bundle> findOrCreateCacheLocked(int userId,
String providerPackageName) {
ArrayMap<String, ArrayMap<Pair<String, Uri>, Bundle>> userCache = mCache.get(userId);
@@ -1131,6 +1132,7 @@
return packageCache;
}
+ @GuardedBy("mCache")
private void invalidateCacheLocked(int userId, String providerPackageName, Uri uri) {
ArrayMap<String, ArrayMap<Pair<String, Uri>, Bundle>> userCache = mCache.get(userId);
if (userCache == null) return;
diff --git a/services/core/java/com/android/server/content/SyncLogger.java b/services/core/java/com/android/server/content/SyncLogger.java
index 75c0181..20aec7e 100644
--- a/services/core/java/com/android/server/content/SyncLogger.java
+++ b/services/core/java/com/android/server/content/SyncLogger.java
@@ -194,6 +194,7 @@
}
}
+ @GuardedBy("mLock")
private void openLogLocked(long now) {
// If we already have a log file opened and the date has't changed, just use it.
final long day = now % DateUtils.DAY_IN_MILLIS;
@@ -219,6 +220,7 @@
}
}
+ @GuardedBy("mLock")
private void closeCurrentLogLocked() {
IoUtils.closeQuietly(mLogWriter);
mLogWriter = null;
diff --git a/services/core/java/com/android/server/display/BrightnessTracker.java b/services/core/java/com/android/server/display/BrightnessTracker.java
index 6b4666a..524de91 100644
--- a/services/core/java/com/android/server/display/BrightnessTracker.java
+++ b/services/core/java/com/android/server/display/BrightnessTracker.java
@@ -115,12 +115,10 @@
= new RingBuffer<>(BrightnessChangeEvent.class, MAX_EVENTS);
@GuardedBy("mEventsLock")
private boolean mEventsDirty;
- private final Runnable mEventsWriter = () -> writeEvents();
- private volatile boolean mWriteEventsScheduled;
+
+ private volatile boolean mWriteBrightnessTrackerStateScheduled;
private AmbientBrightnessStatsTracker mAmbientBrightnessStatsTracker;
- private final Runnable mAmbientBrightnessStatsWriter = () -> writeAmbientBrightnessStats();
- private volatile boolean mWriteBrightnessStatsScheduled;
private UserManager mUserManager;
private final Context mContext;
@@ -167,13 +165,7 @@
}
mBgHandler = new TrackerHandler(mInjector.getBackgroundHandler().getLooper());
mUserManager = mContext.getSystemService(UserManager.class);
- try {
- final ActivityManager.StackInfo focusedStack = mInjector.getFocusedStack();
- mCurrentUserId = focusedStack.userId;
- } catch (RemoteException e) {
- // Really shouldn't be possible.
- return;
- }
+ mCurrentUserId = ActivityManager.getCurrentUser();
mBgHandler.obtainMessage(MSG_BACKGROUND_START, (Float) initialBrightness).sendToTarget();
}
@@ -355,18 +347,17 @@
}
private void scheduleWriteBrightnessTrackerState() {
- if (!mWriteEventsScheduled) {
- mBgHandler.post(mEventsWriter);
- mWriteEventsScheduled = true;
- }
- if (!mWriteBrightnessStatsScheduled) {
- mBgHandler.post(mAmbientBrightnessStatsWriter);
- mWriteBrightnessStatsScheduled = true;
+ if (!mWriteBrightnessTrackerStateScheduled) {
+ mBgHandler.post(() -> {
+ mWriteBrightnessTrackerStateScheduled = false;
+ writeEvents();
+ writeAmbientBrightnessStats();
+ });
+ mWriteBrightnessTrackerStateScheduled = true;
}
}
private void writeEvents() {
- mWriteEventsScheduled = false;
synchronized (mEventsLock) {
if (!mEventsDirty) {
// Nothing to write
@@ -398,7 +389,6 @@
}
private void writeAmbientBrightnessStats() {
- mWriteBrightnessStatsScheduled = false;
final AtomicFile writeTo = mInjector.getFile(AMBIENT_BRIGHTNESS_STATS_FILE);
if (writeTo == null) {
return;
@@ -642,6 +632,7 @@
}
}
if (mAmbientBrightnessStatsTracker != null) {
+ pw.println();
mAmbientBrightnessStatsTracker.dump(pw);
}
}
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index b27f1ec..23de592 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -51,7 +51,6 @@
import android.provider.Settings;
import android.util.MathUtils;
import android.util.Slog;
-import android.util.Spline;
import android.util.TimeUtils;
import android.view.Display;
diff --git a/services/core/java/com/android/server/fingerprint/FingerprintsUserState.java b/services/core/java/com/android/server/fingerprint/FingerprintsUserState.java
index 0976a22..b0cde2d 100644
--- a/services/core/java/com/android/server/fingerprint/FingerprintsUserState.java
+++ b/services/core/java/com/android/server/fingerprint/FingerprintsUserState.java
@@ -202,6 +202,7 @@
}
}
+ @GuardedBy("this")
private void readStateSyncLocked() {
FileInputStream in;
if (!mFile.exists()) {
@@ -226,6 +227,7 @@
}
}
+ @GuardedBy("this")
private void parseStateLocked(XmlPullParser parser)
throws IOException, XmlPullParserException {
final int outerDepth = parser.getDepth();
@@ -243,6 +245,7 @@
}
}
+ @GuardedBy("this")
private void parseFingerprintsLocked(XmlPullParser parser)
throws IOException, XmlPullParserException {
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
index 1e09383..de0f298 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
@@ -1299,6 +1299,7 @@
/**
* Return external input devices.
*/
+ @GuardedBy("mLock")
List<HdmiDeviceInfo> getSafeExternalInputsLocked() {
return mSafeExternalInputs;
}
@@ -1421,6 +1422,7 @@
}
}
+ @GuardedBy("mLock")
List<HdmiDeviceInfo> getSafeCecDevicesLocked() {
ArrayList<HdmiDeviceInfo> infoList = new ArrayList<>();
for (HdmiDeviceInfo info : mSafeAllDeviceInfos) {
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index 3d079cc..b06dba9 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -1097,6 +1097,7 @@
}
}
+ @GuardedBy("mLock")
private List<HdmiDeviceInfo> getMhlDevicesLocked() {
return mMhlDevices;
}
diff --git a/services/core/java/com/android/server/job/JobSchedulerService.java b/services/core/java/com/android/server/job/JobSchedulerService.java
index 401c05e..47a4fb2 100644
--- a/services/core/java/com/android/server/job/JobSchedulerService.java
+++ b/services/core/java/com/android/server/job/JobSchedulerService.java
@@ -40,7 +40,6 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
-import android.content.Intent.UriFlags;
import android.content.pm.IPackageManager;
import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
@@ -79,7 +78,7 @@
import com.android.internal.util.Preconditions;
import com.android.server.DeviceIdleController;
import com.android.server.FgThread;
-import com.android.server.ForceAppStandbyTracker;
+import com.android.server.AppStateTracker;
import com.android.server.LocalServices;
import com.android.server.job.JobSchedulerServiceDumpProto.ActiveJob;
import com.android.server.job.JobSchedulerServiceDumpProto.PendingJob;
@@ -184,7 +183,7 @@
ActivityManagerInternal mActivityManagerInternal;
IBatteryStats mBatteryStats;
DeviceIdleController.LocalService mLocalDeviceIdleController;
- final ForceAppStandbyTracker mForceAppStandbyTracker;
+ AppStateTracker mAppStateTracker;
/**
* Set to true once we are allowed to run third party apps.
@@ -787,20 +786,13 @@
}
/**
- * Return whether an UID is in the foreground or not.
+ * Return whether an UID is active or idle.
*/
- private boolean isUidInForeground(int uid) {
- synchronized (mLock) {
- if (mUidPriorityOverride.get(uid, 0) > 0) {
- return true;
- }
- }
- // Note UID observer may not be called in time, so we always check with the AM.
- return mActivityManagerInternal.getUidProcessState(uid)
- <= ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE;
+ private boolean isUidActive(int uid) {
+ return mAppStateTracker.isUidActiveSynced(uid);
}
- private final Predicate<Integer> mIsUidInForegroundPredicate = this::isUidInForeground;
+ private final Predicate<Integer> mIsUidActivePredicate = this::isUidActive;
public int scheduleAsPackage(JobInfo job, JobWorkItem work, int uId, String packageName,
int userId, String tag) {
@@ -826,7 +818,7 @@
// If any of work item is enqueued when the source is in the foreground,
// exempt the entire job.
- toCancel.maybeAddForegroundExemption(mIsUidInForegroundPredicate);
+ toCancel.maybeAddForegroundExemption(mIsUidActivePredicate);
return JobScheduler.RESULT_SUCCESS;
}
@@ -838,7 +830,7 @@
// Note if it's a sync job, this method is called on the handler so it's not exactly
// the state when requestSync() was called, but that should be fine because of the
// 1 minute foreground grace period.
- jobStatus.maybeAddForegroundExemption(mIsUidInForegroundPredicate);
+ jobStatus.maybeAddForegroundExemption(mIsUidActivePredicate);
if (DEBUG) Slog.d(TAG, "SCHEDULE: " + jobStatus.toShortString());
// Jobs on behalf of others don't apply to the per-app job cap
@@ -1123,8 +1115,6 @@
mDeviceIdleJobsController = DeviceIdleJobsController.get(this);
mControllers.add(mDeviceIdleJobsController);
- mForceAppStandbyTracker = ForceAppStandbyTracker.getInstance(context);
-
// If the job store determined that it can't yet reschedule persisted jobs,
// we need to start watching the clock.
if (!mJobs.jobTimesInflatedValid()) {
@@ -1185,7 +1175,8 @@
if (PHASE_SYSTEM_SERVICES_READY == phase) {
mConstants.start(getContext().getContentResolver());
- mForceAppStandbyTracker.start();
+ mAppStateTracker = Preconditions.checkNotNull(
+ LocalServices.getService(AppStateTracker.class));
// Register br for package removals and user removals.
final IntentFilter filter = new IntentFilter();
diff --git a/services/core/java/com/android/server/job/JobServiceContext.java b/services/core/java/com/android/server/job/JobServiceContext.java
index 37b3990..4988974 100644
--- a/services/core/java/com/android/server/job/JobServiceContext.java
+++ b/services/core/java/com/android/server/job/JobServiceContext.java
@@ -295,10 +295,12 @@
}
/** Called externally when a job that was scheduled for execution should be cancelled. */
+ @GuardedBy("mLock")
void cancelExecutingJobLocked(int reason, String debugReason) {
doCancelLocked(reason, debugReason);
}
+ @GuardedBy("mLock")
void preemptExecutingJobLocked() {
doCancelLocked(JobParameters.REASON_PREEMPT, "cancelled due to preemption");
}
@@ -319,6 +321,7 @@
return mTimeoutElapsed;
}
+ @GuardedBy("mLock")
boolean timeoutIfExecutingLocked(String pkgName, int userId, boolean matchJobId, int jobId,
String reason) {
final JobStatus executing = getRunningJobLocked();
@@ -512,6 +515,7 @@
}
}
+ @GuardedBy("mLock")
void doServiceBoundLocked() {
removeOpTimeOutLocked();
handleServiceBoundLocked();
@@ -531,6 +535,7 @@
}
}
+ @GuardedBy("mLock")
void doCallbackLocked(boolean reschedule, String reason) {
if (DEBUG) {
Slog.d(TAG, "doCallback of : " + mRunningJob
@@ -550,6 +555,7 @@
}
}
+ @GuardedBy("mLock")
void doCancelLocked(int arg1, String debugReason) {
if (mVerb == VERB_FINISHED) {
if (DEBUG) {
@@ -567,6 +573,7 @@
}
/** Start the job on the service. */
+ @GuardedBy("mLock")
private void handleServiceBoundLocked() {
if (DEBUG) {
Slog.d(TAG, "handleServiceBound for " + getRunningJobNameLocked());
@@ -605,6 +612,7 @@
* _EXECUTING -> Error
* _STOPPING -> Error
*/
+ @GuardedBy("mLock")
private void handleStartedLocked(boolean workOngoing) {
switch (mVerb) {
case VERB_STARTING:
@@ -637,6 +645,7 @@
* _STARTING -> Error
* _PENDING -> Error
*/
+ @GuardedBy("mLock")
private void handleFinishedLocked(boolean reschedule, String reason) {
switch (mVerb) {
case VERB_EXECUTING:
@@ -659,6 +668,7 @@
* in the message queue.
* _ENDING -> No point in doing anything here, so we ignore.
*/
+ @GuardedBy("mLock")
private void handleCancelLocked(String reason) {
if (JobSchedulerService.DEBUG) {
Slog.d(TAG, "Handling cancel for: " + mRunningJob.getJobId() + " "
@@ -683,6 +693,7 @@
}
/** Process MSG_TIMEOUT here. */
+ @GuardedBy("mLock")
private void handleOpTimeoutLocked() {
switch (mVerb) {
case VERB_BINDING:
@@ -722,6 +733,7 @@
* Already running, need to stop. Will switch {@link #mVerb} from VERB_EXECUTING ->
* VERB_STOPPING.
*/
+ @GuardedBy("mLock")
private void sendStopMessageLocked(String reason) {
removeOpTimeOutLocked();
if (mVerb != VERB_EXECUTING) {
@@ -747,6 +759,7 @@
* or from acknowledging the stop message we sent. Either way, we're done tracking it and
* we want to clean up internally.
*/
+ @GuardedBy("mLock")
private void closeAndCleanupJobLocked(boolean reschedule, String reason) {
final JobStatus completedJob;
if (mVerb == VERB_FINISHED) {
diff --git a/services/core/java/com/android/server/job/controllers/BackgroundJobsController.java b/services/core/java/com/android/server/job/controllers/BackgroundJobsController.java
index 5eb7700..e8057fb 100644
--- a/services/core/java/com/android/server/job/controllers/BackgroundJobsController.java
+++ b/services/core/java/com/android/server/job/controllers/BackgroundJobsController.java
@@ -22,8 +22,10 @@
import android.util.Slog;
import android.util.proto.ProtoOutputStream;
-import com.android.server.ForceAppStandbyTracker;
-import com.android.server.ForceAppStandbyTracker.Listener;
+import com.android.internal.util.Preconditions;
+import com.android.server.AppStateTracker;
+import com.android.server.AppStateTracker.Listener;
+import com.android.server.LocalServices;
import com.android.server.job.JobSchedulerService;
import com.android.server.job.JobStore;
import com.android.server.job.StateControllerProto;
@@ -42,8 +44,7 @@
private final JobSchedulerService mJobSchedulerService;
- private final ForceAppStandbyTracker mForceAppStandbyTracker;
-
+ private final AppStateTracker mAppStateTracker;
public static BackgroundJobsController get(JobSchedulerService service) {
synchronized (sCreationLock) {
@@ -59,10 +60,9 @@
super(service, context, lock);
mJobSchedulerService = service;
- mForceAppStandbyTracker = ForceAppStandbyTracker.getInstance(context);
-
- mForceAppStandbyTracker.addListener(mForceAppStandbyListener);
- mForceAppStandbyTracker.start();
+ mAppStateTracker = Preconditions.checkNotNull(
+ LocalServices.getService(AppStateTracker.class));
+ mAppStateTracker.addListener(mForceAppStandbyListener);
}
@Override
@@ -79,7 +79,7 @@
public void dumpControllerStateLocked(final PrintWriter pw, final int filterUid) {
pw.println("BackgroundJobsController");
- mForceAppStandbyTracker.dump(pw, "");
+ mAppStateTracker.dump(pw, "");
pw.println("Job state:");
mJobSchedulerService.getJobStore().forEachJob((jobStatus) -> {
@@ -92,16 +92,16 @@
jobStatus.printUniqueId(pw);
pw.print(" from ");
UserHandle.formatUid(pw, uid);
- pw.print(mForceAppStandbyTracker.isUidActive(uid) ? " active" : " idle");
- if (mForceAppStandbyTracker.isUidPowerSaveWhitelisted(uid) ||
- mForceAppStandbyTracker.isUidTempPowerSaveWhitelisted(uid)) {
+ pw.print(mAppStateTracker.isUidActive(uid) ? " active" : " idle");
+ if (mAppStateTracker.isUidPowerSaveWhitelisted(uid) ||
+ mAppStateTracker.isUidTempPowerSaveWhitelisted(uid)) {
pw.print(", whitelisted");
}
pw.print(": ");
pw.print(sourcePkg);
pw.print(" [RUN_ANY_IN_BACKGROUND ");
- pw.print(mForceAppStandbyTracker.isRunAnyInBackgroundAppOpsAllowed(uid, sourcePkg)
+ pw.print(mAppStateTracker.isRunAnyInBackgroundAppOpsAllowed(uid, sourcePkg)
? "allowed]" : "disallowed]");
if ((jobStatus.satisfiedConstraints
@@ -118,7 +118,7 @@
final long token = proto.start(fieldId);
final long mToken = proto.start(StateControllerProto.BACKGROUND);
- mForceAppStandbyTracker.dumpProto(proto,
+ mAppStateTracker.dumpProto(proto,
StateControllerProto.BackgroundJobsController.FORCE_APP_STANDBY_TRACKER);
mJobSchedulerService.getJobStore().forEachJob((jobStatus) -> {
@@ -136,14 +136,14 @@
proto.write(TrackedJob.SOURCE_PACKAGE_NAME, sourcePkg);
proto.write(TrackedJob.IS_IN_FOREGROUND,
- mForceAppStandbyTracker.isUidActive(sourceUid));
+ mAppStateTracker.isUidActive(sourceUid));
proto.write(TrackedJob.IS_WHITELISTED,
- mForceAppStandbyTracker.isUidPowerSaveWhitelisted(sourceUid) ||
- mForceAppStandbyTracker.isUidTempPowerSaveWhitelisted(sourceUid));
+ mAppStateTracker.isUidPowerSaveWhitelisted(sourceUid) ||
+ mAppStateTracker.isUidTempPowerSaveWhitelisted(sourceUid));
proto.write(
TrackedJob.CAN_RUN_ANY_IN_BACKGROUND,
- mForceAppStandbyTracker.isRunAnyInBackgroundAppOpsAllowed(
+ mAppStateTracker.isRunAnyInBackgroundAppOpsAllowed(
sourceUid, sourcePkg));
proto.write(
@@ -197,7 +197,7 @@
final int uid = jobStatus.getSourceUid();
final String packageName = jobStatus.getSourcePackageName();
- final boolean canRun = !mForceAppStandbyTracker.areJobsRestricted(uid, packageName,
+ final boolean canRun = !mAppStateTracker.areJobsRestricted(uid, packageName,
(jobStatus.getInternalFlags() & JobStatus.INTERNAL_FLAG_HAS_FOREGROUND_EXEMPTION)
!= 0);
diff --git a/services/core/java/com/android/server/job/controllers/ConnectivityController.java b/services/core/java/com/android/server/job/controllers/ConnectivityController.java
index 13873e4..77e813e 100644
--- a/services/core/java/com/android/server/job/controllers/ConnectivityController.java
+++ b/services/core/java/com/android/server/job/controllers/ConnectivityController.java
@@ -92,6 +92,7 @@
mNetPolicyManager.registerListener(mNetPolicyListener);
}
+ @GuardedBy("mLock")
@Override
public void maybeStartTrackingJobLocked(JobStatus jobStatus, JobStatus lastJob) {
if (jobStatus.hasConnectivityConstraint()) {
@@ -101,6 +102,7 @@
}
}
+ @GuardedBy("mLock")
@Override
public void maybeStopTrackingJobLocked(JobStatus jobStatus, JobStatus incomingJob,
boolean forUpdate) {
@@ -325,6 +327,7 @@
}
};
+ @GuardedBy("mLock")
@Override
public void dumpControllerStateLocked(PrintWriter pw, int filterUid) {
pw.print("Connectivity: connected=");
@@ -348,6 +351,7 @@
}
}
+ @GuardedBy("mLock")
@Override
public void dumpControllerStateLocked(ProtoOutputStream proto, long fieldId, int filterUid) {
final long token = proto.start(fieldId);
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java
index e715724..6deff36 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsService.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java
@@ -2236,9 +2236,10 @@
* This happens during a normal migration case when the user currently has password.
*
* 2. credentialhash == null and credential == null
- * A new SP blob and a new SID will be created, while the user has no credentials.
+ * A new SP blob and will be created, while the user has no credentials.
* This can happens when we are activating an escrow token on a unsecured device, during
* which we want to create the SP structure with an empty user credential.
+ * This could also happen during an untrusted reset to clear password.
*
* 3. credentialhash == null and credential != null
* This is the untrusted credential reset, OR the user sets a new lockscreen password
@@ -2250,16 +2251,8 @@
String credential, int credentialType, int requestedQuality,
int userId) throws RemoteException {
Slog.i(TAG, "Initialize SyntheticPassword for user: " + userId);
- // Load from the cache or a make a new one
- AuthenticationToken auth = mSpCache.get(userId);
- if (auth != null) {
- // If the synthetic password has been cached, we can only be in case 3., described
- // above, for an untrusted credential reset so a new SID is still needed.
- mSpManager.newSidForUser(getGateKeeperService(), auth, userId);
- } else {
- auth = mSpManager.newSyntheticPasswordAndSid(getGateKeeperService(),
- credentialHash, credential, userId);
- }
+ final AuthenticationToken auth = mSpManager.newSyntheticPasswordAndSid(
+ getGateKeeperService(), credentialHash, credential, userId);
onAuthTokenKnownForUser(userId, auth);
if (auth == null) {
Slog.wtf(TAG, "initializeSyntheticPasswordLocked returns null auth token");
@@ -2473,36 +2466,41 @@
: "pattern"));
}
+ boolean untrustedReset = false;
if (auth != null) {
- // We are performing a trusted credential change i.e. a correct existing credential
- // is provided
- setLockCredentialWithAuthTokenLocked(credential, credentialType, auth, requestedQuality,
- userId);
- mSpManager.destroyPasswordBasedSyntheticPassword(handle, userId);
onAuthTokenKnownForUser(userId, auth);
} else if (response != null
&& response.getResponseCode() == VerifyCredentialResponse.RESPONSE_ERROR) {
- // We are performing an untrusted credential change i.e. by DevicePolicyManager.
- // So provision a new SP and SID. This would invalidate existing escrow tokens.
- // Still support this for now but this flow will be removed in the next release.
+ // We are performing an untrusted credential change, by DevicePolicyManager or other
+ // internal callers that don't provide the existing credential
Slog.w(TAG, "Untrusted credential change invoked");
-
- if (mSpCache.get(userId) == null) {
- throw new IllegalStateException(
- "Untrusted credential reset not possible without cached SP");
- }
-
- initializeSyntheticPasswordLocked(null, credential, credentialType, requestedQuality,
- userId);
- synchronizeUnifiedWorkChallengeForProfiles(userId, null);
- mSpManager.destroyPasswordBasedSyntheticPassword(handle, userId);
-
- notifyActivePasswordMetricsAvailable(credential, userId);
+ // Try to get a cached auth token, so we can keep SP unchanged.
+ auth = mSpCache.get(userId);
+ untrustedReset = true;
} else /* response == null || responseCode == VerifyCredentialResponse.RESPONSE_RETRY */ {
Slog.w(TAG, "spBasedSetLockCredentialInternalLocked: " +
(response != null ? "rate limit exceeded" : "failed"));
return;
}
+
+ if (auth != null) {
+ if (untrustedReset) {
+ // Force change the current SID to mantain existing behaviour that an untrusted
+ // reset leads to a change of SID. If the untrusted reset is for clearing the
+ // current password, the nuking of the SID will be done in
+ // setLockCredentialWithAuthTokenLocked next
+ mSpManager.newSidForUser(getGateKeeperService(), auth, userId);
+ }
+ setLockCredentialWithAuthTokenLocked(credential, credentialType, auth, requestedQuality,
+ userId);
+ mSpManager.destroyPasswordBasedSyntheticPassword(handle, userId);
+ } else {
+ throw new IllegalStateException(
+ "Untrusted credential reset not possible without cached SP");
+ // Could call initializeSyntheticPasswordLocked(null, credential, credentialType,
+ // requestedQuality, userId) instead if we still allow untrusted reset that changes
+ // synthetic password. That would invalidate existing escrow tokens though.
+ }
mRecoverableKeyStoreManager.lockScreenSecretChanged(credentialType, credential, userId);
}
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncTask.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncTask.java
index 662ffc8..dee24c7 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncTask.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncTask.java
@@ -44,6 +44,7 @@
import java.security.PublicKey;
import java.security.SecureRandom;
import java.security.UnrecoverableKeyException;
+import java.security.cert.CertPath;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
@@ -176,8 +177,17 @@
return;
}
- PublicKey publicKey = mRecoverableKeyStoreDb.getRecoveryServicePublicKey(mUserId,
+ PublicKey publicKey;
+ CertPath certPath = mRecoverableKeyStoreDb.getRecoveryServiceCertPath(mUserId,
recoveryAgentUid);
+ if (certPath != null) {
+ Log.d(TAG, "Using the public key in stored CertPath for syncing");
+ publicKey = certPath.getCertificates().get(0).getPublicKey();
+ } else {
+ Log.d(TAG, "Using the stored raw public key for syncing");
+ publicKey = mRecoverableKeyStoreDb.getRecoveryServicePublicKey(mUserId,
+ recoveryAgentUid);
+ }
if (publicKey == null) {
Log.w(TAG, "Not initialized for KeySync: no public key set. Cancelling task.");
return;
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java
index 33e767fe..9f82268 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java
@@ -23,16 +23,15 @@
import static android.security.keystore.RecoveryController.ERROR_SERVICE_INTERNAL_ERROR;
import static android.security.keystore.RecoveryController.ERROR_SESSION_EXPIRED;
+import android.Manifest;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.PendingIntent;
import android.content.Context;
-import android.Manifest;
import android.os.Binder;
import android.os.RemoteException;
import android.os.ServiceSpecificException;
import android.os.UserHandle;
-
import android.security.keystore.recovery.KeyChainProtectionParams;
import android.security.keystore.recovery.KeyChainSnapshot;
import android.security.keystore.recovery.RecoveryController;
@@ -43,6 +42,10 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.HexDump;
import com.android.server.locksettings.recoverablekeystore.storage.ApplicationKeyStorage;
+import com.android.server.locksettings.recoverablekeystore.certificate.CertParsingException;
+import com.android.server.locksettings.recoverablekeystore.certificate.CertValidationException;
+import com.android.server.locksettings.recoverablekeystore.certificate.CertXml;
+import com.android.server.locksettings.recoverablekeystore.certificate.TrustedRootCert;
import com.android.server.locksettings.recoverablekeystore.storage.RecoverableKeyStoreDb;
import com.android.server.locksettings.recoverablekeystore.storage.RecoverySessionStorage;
import com.android.server.locksettings.recoverablekeystore.storage.RecoverySnapshotStorage;
@@ -52,8 +55,10 @@
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.PublicKey;
-import java.security.spec.InvalidKeySpecException;
import java.security.UnrecoverableKeyException;
+import java.security.cert.CertPath;
+import java.security.cert.CertificateEncodingException;
+import java.security.spec.InvalidKeySpecException;
import java.security.spec.X509EncodedKeySpec;
import java.util.Arrays;
import java.util.HashMap;
@@ -152,18 +157,67 @@
}
public void initRecoveryService(
- @NonNull String rootCertificateAlias, @NonNull byte[] signedPublicKeyList)
+ @NonNull String rootCertificateAlias, @NonNull byte[] recoveryServiceCertFile)
throws RemoteException {
checkRecoverKeyStorePermission();
int userId = UserHandle.getCallingUserId();
int uid = Binder.getCallingUid();
- // TODO: open /system/etc/security/... cert file, and check the signature on the public keys
- PublicKey publicKey;
+
+ // TODO: Check the public-key signature on the whole file before parsing it
+
+ CertXml certXml;
+ try {
+ certXml = CertXml.parse(recoveryServiceCertFile);
+ } catch (CertParsingException e) {
+ // TODO: Do not use raw key bytes anymore once the other components are updated
+ Log.d(TAG, "Failed to parse the cert file", e);
+ PublicKey publicKey = parseEcPublicKey(recoveryServiceCertFile);
+ if (mDatabase.setRecoveryServicePublicKey(userId, uid, publicKey) > 0) {
+ mDatabase.setShouldCreateSnapshot(userId, uid, true);
+ }
+ return;
+ }
+
+ // Check serial number
+ long newSerial = certXml.getSerial();
+ Long oldSerial = mDatabase.getRecoveryServiceCertSerial(userId, uid);
+ if (oldSerial != null && oldSerial >= newSerial) {
+ if (oldSerial == newSerial) {
+ Log.i(TAG, "The cert file serial number is the same, so skip updating.");
+ } else {
+ Log.e(TAG, "The cert file serial number is older than the one in database.");
+ }
+ return;
+ }
+ Log.i(TAG, "Updating the certificate with the new serial number " + newSerial);
+
+ CertPath certPath;
+ try {
+ Log.d(TAG, "Getting and validating a random endpoint certificate");
+ certPath = certXml.getRandomEndpointCert(TrustedRootCert.TRUSTED_ROOT_CERT);
+ } catch (CertValidationException e) {
+ Log.e(TAG, "Invalid endpoint cert", e);
+ throw new ServiceSpecificException(
+ ERROR_BAD_CERTIFICATE_FORMAT, "Failed to validate certificate.");
+ }
+ try {
+ Log.d(TAG, "Saving the randomly chosen endpoint certificate to database");
+ if (mDatabase.setRecoveryServiceCertPath(userId, uid, certPath) > 0) {
+ mDatabase.setRecoveryServiceCertSerial(userId, uid, newSerial);
+ mDatabase.setShouldCreateSnapshot(userId, uid, true);
+ }
+ } catch (CertificateEncodingException e) {
+ Log.e(TAG, "Failed to encode CertPath", e);
+ throw new ServiceSpecificException(
+ ERROR_BAD_CERTIFICATE_FORMAT, "Failed to encode CertPath.");
+ }
+ }
+
+ private PublicKey parseEcPublicKey(@NonNull byte[] bytes) throws ServiceSpecificException {
try {
KeyFactory kf = KeyFactory.getInstance("EC");
- // TODO: Randomly choose a key from the list -- right now we just use the whole input
- X509EncodedKeySpec pkSpec = new X509EncodedKeySpec(signedPublicKeyList);
- publicKey = kf.generatePublic(pkSpec);
+ X509EncodedKeySpec pkSpec = new X509EncodedKeySpec(bytes);
+ return kf.generatePublic(pkSpec);
} catch (NoSuchAlgorithmException e) {
Log.wtf(TAG, "EC algorithm not available. AOSP must support this.", e);
throw new ServiceSpecificException(ERROR_SERVICE_INTERNAL_ERROR, e.getMessage());
@@ -171,10 +225,6 @@
throw new ServiceSpecificException(
ERROR_BAD_CERTIFICATE_FORMAT, "Not a valid X509 certificate.");
}
- long updatedRows = mDatabase.setRecoveryServicePublicKey(userId, uid, publicKey);
- if (updatedRows > 0) {
- mDatabase.setShouldCreateSnapshot(userId, uid, true);
- }
}
/**
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/certificate/CertUtils.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/certificate/CertUtils.java
index fea6733..09ec5ad 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/certificate/CertUtils.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/certificate/CertUtils.java
@@ -68,7 +68,7 @@
import org.xml.sax.SAXException;
/** Utility functions related to parsing and validating public-key certificates. */
-final class CertUtils {
+public final class CertUtils {
private static final String CERT_FORMAT = "X.509";
private static final String CERT_PATH_ALG = "PKIX";
@@ -217,7 +217,7 @@
* @return the decoding decoding result
* @throws CertParsingException if the input string is not a properly base64-encoded string
*/
- static byte[] decodeBase64(String str) throws CertParsingException {
+ public static byte[] decodeBase64(String str) throws CertParsingException {
try {
return Base64.getDecoder().decode(str);
} catch (IllegalArgumentException e) {
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/certificate/TrustedRootCert.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/certificate/TrustedRootCert.java
new file mode 100644
index 0000000..7195d62
--- /dev/null
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/certificate/TrustedRootCert.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 com.android.server.locksettings.recoverablekeystore.certificate;
+
+import java.security.cert.X509Certificate;
+
+/**
+ * Holds the X509 certificate of the trusted root CA cert for the recoverable key store service.
+ *
+ * TODO: Read the certificate from a PEM file directly and remove this class.
+ */
+public final class TrustedRootCert {
+
+ private static final String TRUSTED_ROOT_CERT_BASE64 = ""
+ + "MIIFJjCCAw6gAwIBAgIJAIobXsJlzhNdMA0GCSqGSIb3DQEBDQUAMCAxHjAcBgNV"
+ + "BAMMFUdvb2dsZSBDcnlwdEF1dGhWYXVsdDAeFw0xODAyMDIxOTM5MTRaFw0zODAx"
+ + "MjgxOTM5MTRaMCAxHjAcBgNVBAMMFUdvb2dsZSBDcnlwdEF1dGhWYXVsdDCCAiIw"
+ + "DQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK2OT5i40/H7LINg/lq/0G0hR65P"
+ + "Q4Mud3OnuVt6UIYV2T18+v6qW1yJd5FcnND/ZKPau4aUAYklqJuSVjOXQD0BjgS2"
+ + "98Xa4dSn8Ci1rUR+5tdmrxqbYUdT2ZvJIUMMR6fRoqi+LlAbKECrV+zYQTyLU68w"
+ + "V66hQpAButjJKiZzkXjmKLfJ5IWrNEn17XM988rk6qAQn/BYCCQGf3rQuJeksGmA"
+ + "N1lJOwNYxmWUyouVwqwZthNEWqTuEyBFMkAT+99PXW7oVDc7oU5cevuihxQWNTYq"
+ + "viGB8cck6RW3cmqrDSaJF/E+N0cXFKyYC7FDcggt6k3UrxNKTuySdDEa8+2RTQqU"
+ + "Y9npxBlQE+x9Ig56OI1BG3bSBsGdPgjpyHadZeh2tgk+oqlGsSsum24YxaxuSysT"
+ + "Qfcu/XhyfUXavfmGrBOXerTzIl5oBh/F5aHTV85M2tYEG0qsPPvSpZAWtdJ/2rca"
+ + "OxvhwOL+leZKr8McjXVR00lBsRuKXX4nTUMwya09CO3QHFPFZtZvqjy2HaMOnVLQ"
+ + "I6b6dHEfmsHybzVOe3yPEoFQSU9UhUdmi71kwwoanPD3j9fJHmXTx4PzYYBRf1ZE"
+ + "o+uPgMPk7CDKQFZLjnR40z1uzu3O8aZ3AKZzP+j7T4XQKJLQLmllKtPgLgNdJyib"
+ + "2Glg7QhXH/jBTL6hAgMBAAGjYzBhMB0GA1UdDgQWBBSbZfrqOYH54EJpkdKMZjMc"
+ + "z/Hp+DAfBgNVHSMEGDAWgBSbZfrqOYH54EJpkdKMZjMcz/Hp+DAPBgNVHRMBAf8E"
+ + "BTADAQH/MA4GA1UdDwEB/wQEAwIBhjANBgkqhkiG9w0BAQ0FAAOCAgEAKh9nm/vW"
+ + "glMWp3vcCwWwJW286ecREDlI+CjGh5h+f2N4QRrXd/tKE3qQJWCqGx8sFfIUjmI7"
+ + "KYdsC2gyQ2cA2zl0w7pB2QkuqE6zVbnh1D17Hwl19IMyAakFaM9ad4/EoH7oQmqX"
+ + "nF/f5QXGZw4kf1HcgKgoCHWXjqR8MqHOcXR8n6WFqxjzJf1jxzi6Yo2dZ7PJbnE6"
+ + "+kHIJuiCpiHL75v5g1HM41gT3ddFFSrn88ThNPWItT5Z8WpFjryVzank2Yt02LLl"
+ + "WqZg9IC375QULc5B58NMnaiVJIDJQ8zoNgj1yaxqtUMnJX570lotO2OXe4ec9aCQ"
+ + "DIJ84YLM/qStFdeZ9416E80dchskbDG04GuVJKlzWjxAQNMRFhyaPUSBTLLg+kwP"
+ + "t9+AMmc+A7xjtFQLZ9fBYHOBsndJOmeSQeYeckl+z/1WQf7DdwXn/yijon7mxz4z"
+ + "cCczfKwTJTwBh3wR5SQr2vQm7qaXM87qxF8PCAZrdZaw5I80QwkgTj0WTZ2/GdSw"
+ + "d3o5SyzzBAjpwtG+4bO/BD9h9wlTsHpT6yWOZs4OYAKU5ykQrncI8OyavMggArh3"
+ + "/oM58v0orUWINtIc2hBlka36PhATYQiLf+AiWKnwhCaaHExoYKfQlMtXBodNvOK8"
+ + "xqx69x05q/qbHKEcTHrsss630vxrp1niXvA=";
+
+ /**
+ * The X509 certificate of the trusted root CA cert for the recoverable key store service.
+ *
+ * TODO: Change it to the production certificate root CA before the final launch.
+ */
+ public static final X509Certificate TRUSTED_ROOT_CERT;
+
+ static {
+ try {
+ TRUSTED_ROOT_CERT = CertUtils.decodeCert(
+ CertUtils.decodeBase64(TRUSTED_ROOT_CERT_BASE64));
+ } catch (CertParsingException e) {
+ // Should not happen
+ throw new RuntimeException(e);
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDb.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDb.java
index b96208d..89ddb6c 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDb.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDb.java
@@ -31,9 +31,14 @@
import com.android.server.locksettings.recoverablekeystore.storage.RecoverableKeyStoreDbContract.RecoveryServiceMetadataEntry;
import com.android.server.locksettings.recoverablekeystore.storage.RecoverableKeyStoreDbContract.UserMetadataEntry;
+import java.io.ByteArrayInputStream;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.PublicKey;
+import java.security.cert.CertPath;
+import java.security.cert.CertificateEncodingException;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateFactory;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.X509EncodedKeySpec;
import java.util.ArrayList;
@@ -53,6 +58,7 @@
private static final String TAG = "RecoverableKeyStoreDb";
private static final int IDLE_TIMEOUT_SECONDS = 30;
private static final int LAST_SYNCED_AT_UNSYNCED = -1;
+ private static final String CERT_PATH_ENCODING = "PkiPath";
private final RecoverableKeyStoreDbHelper mKeyStoreDbHelper;
@@ -361,6 +367,79 @@
}
/**
+ * Returns the serial number of the XML file containing certificates of the recovery service.
+ *
+ * @param userId The userId of the profile the application is running under.
+ * @param uid The uid of the application who initializes the local recovery components.
+ * @return The value that were previously set, or null if there's none.
+ *
+ * @hide
+ */
+ @Nullable
+ public Long getRecoveryServiceCertSerial(int userId, int uid) {
+ return getLong(userId, uid, RecoveryServiceMetadataEntry.COLUMN_NAME_CERT_SERIAL);
+ }
+
+ /**
+ * Records the serial number of the XML file containing certificates of the recovery service.
+ *
+ * @param userId The userId of the profile the application is running under.
+ * @param uid The uid of the application who initializes the local recovery components.
+ * @param serial The serial number contained in the XML file for recovery service certificates.
+ * @return The primary key of the inserted row, or -1 if failed.
+ *
+ * @hide
+ */
+ public long setRecoveryServiceCertSerial(int userId, int uid, long serial) {
+ return setLong(userId, uid, RecoveryServiceMetadataEntry.COLUMN_NAME_CERT_SERIAL, serial);
+ }
+
+ /**
+ * Returns the {@code CertPath} of the recovery service.
+ *
+ * @param userId The userId of the profile the application is running under.
+ * @param uid The uid of the application who initializes the local recovery components.
+ * @return The value that were previously set, or null if there's none.
+ *
+ * @hide
+ */
+ @Nullable
+ public CertPath getRecoveryServiceCertPath(int userId, int uid) {
+ byte[] bytes = getBytes(userId, uid, RecoveryServiceMetadataEntry.COLUMN_NAME_CERT_PATH);
+ if (bytes == null) {
+ return null;
+ }
+ try {
+ return decodeCertPath(bytes);
+ } catch (CertificateException e) {
+ Log.wtf(TAG,
+ String.format(Locale.US,
+ "Recovery service CertPath entry cannot be decoded for "
+ + "userId=%d uid=%d.",
+ userId, uid), e);
+ return null;
+ }
+ }
+
+ /**
+ * Sets the {@code CertPath} of the recovery service.
+ *
+ * @param userId The userId of the profile the application is running under.
+ * @param uid The uid of the application who initializes the local recovery components.
+ * @param certPath The certificate path of the recovery service.
+ * @return The primary key of the inserted row, or -1 if failed.
+ * @hide
+ */
+ public long setRecoveryServiceCertPath(int userId, int uid, CertPath certPath) throws
+ CertificateEncodingException {
+ if (certPath.getCertificates().size() == 0) {
+ throw new CertificateEncodingException("No certificate contained in the cert path.");
+ }
+ return setBytes(userId, uid, RecoveryServiceMetadataEntry.COLUMN_NAME_CERT_PATH,
+ certPath.getEncoded(CERT_PATH_ENCODING));
+ }
+
+ /**
* Returns the list of recovery agents initialized for given {@code userId}
* @param userId The userId of the profile the application is running under.
* @return The list of recovery agents
@@ -515,48 +594,6 @@
}
/**
- * Returns the first (and only?) public key for {@code userId}.
- *
- * @param userId The userId of the profile whose keys are to be synced.
- * @return The public key, or null if none exists.
- */
- @Nullable
- public PublicKey getRecoveryServicePublicKey(int userId) {
- SQLiteDatabase db = mKeyStoreDbHelper.getReadableDatabase();
-
- String[] projection = { RecoveryServiceMetadataEntry.COLUMN_NAME_PUBLIC_KEY };
- String selection =
- RecoveryServiceMetadataEntry.COLUMN_NAME_USER_ID + " = ?";
- String[] selectionArguments = { Integer.toString(userId) };
-
- try (
- Cursor cursor = db.query(
- RecoveryServiceMetadataEntry.TABLE_NAME,
- projection,
- selection,
- selectionArguments,
- /*groupBy=*/ null,
- /*having=*/ null,
- /*orderBy=*/ null)
- ) {
- if (cursor.getCount() < 1) {
- return null;
- }
-
- cursor.moveToFirst();
- byte[] keyBytes = cursor.getBlob(cursor.getColumnIndexOrThrow(
- RecoveryServiceMetadataEntry.COLUMN_NAME_PUBLIC_KEY));
-
- try {
- return decodeX509Key(keyBytes);
- } catch (InvalidKeySpecException e) {
- Log.wtf(TAG, "Could not decode public key for " + userId);
- return null;
- }
- }
- }
-
- /**
* Updates the counterId
*
* @param userId The userId of the profile the application is running under.
@@ -585,7 +622,6 @@
return getLong(userId, uid, RecoveryServiceMetadataEntry.COLUMN_NAME_COUNTER_ID);
}
-
/**
* Updates the server parameters given by the application initializing the local recovery
* components.
@@ -869,4 +905,16 @@
throw new RuntimeException(e);
}
}
+
+ @Nullable
+ private static CertPath decodeCertPath(byte[] bytes) throws CertificateException {
+ CertificateFactory certFactory;
+ try {
+ certFactory = CertificateFactory.getInstance("X.509");
+ } catch (CertificateException e) {
+ // Should not happen, as X.509 is mandatory for all providers.
+ throw new RuntimeException(e);
+ }
+ return certFactory.generateCertPath(new ByteArrayInputStream(bytes), CERT_PATH_ENCODING);
+ }
}
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbContract.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbContract.java
index 4ee282b..1cb5d91 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbContract.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbContract.java
@@ -104,9 +104,10 @@
static final String COLUMN_NAME_UID = "uid";
/**
- * Version of the latest recovery snapshot
+ * Version of the latest recovery snapshot.
*/
static final String COLUMN_NAME_SNAPSHOT_VERSION = "snapshot_version";
+
/**
* Flag to generate new snapshot.
*/
@@ -118,6 +119,16 @@
static final String COLUMN_NAME_PUBLIC_KEY = "public_key";
/**
+ * The certificate path of the recovery service.
+ */
+ static final String COLUMN_NAME_CERT_PATH = "cert_path";
+
+ /**
+ * The serial number contained in the certificate XML file of the recovery service.
+ */
+ static final String COLUMN_NAME_CERT_SERIAL = "cert_serial";
+
+ /**
* Secret types used for end-to-end encryption.
*/
static final String COLUMN_NAME_SECRET_TYPES = "secret_types";
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbHelper.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbHelper.java
index 79fd496..8a89f2d 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbHelper.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbHelper.java
@@ -19,6 +19,7 @@
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
+import android.util.Log;
import com.android.server.locksettings.recoverablekeystore.storage.RecoverableKeyStoreDbContract.KeysEntry;
import com.android.server.locksettings.recoverablekeystore.storage.RecoverableKeyStoreDbContract.RecoveryServiceMetadataEntry;
@@ -28,7 +29,9 @@
* Helper for creating the recoverable key database.
*/
class RecoverableKeyStoreDbHelper extends SQLiteOpenHelper {
- private static final int DATABASE_VERSION = 2;
+ private static final String TAG = "RecoverableKeyStoreDbHp";
+
+ static final int DATABASE_VERSION = 3;
private static final String DATABASE_NAME = "recoverablekeystore.db";
private static final String SQL_CREATE_KEYS_ENTRY =
@@ -59,6 +62,8 @@
+ RecoveryServiceMetadataEntry.COLUMN_NAME_SNAPSHOT_VERSION + " INTEGER,"
+ RecoveryServiceMetadataEntry.COLUMN_NAME_SHOULD_CREATE_SNAPSHOT + " INTEGER,"
+ RecoveryServiceMetadataEntry.COLUMN_NAME_PUBLIC_KEY + " BLOB,"
+ + RecoveryServiceMetadataEntry.COLUMN_NAME_CERT_PATH + " BLOB,"
+ + RecoveryServiceMetadataEntry.COLUMN_NAME_CERT_SERIAL + " INTEGER,"
+ RecoveryServiceMetadataEntry.COLUMN_NAME_SECRET_TYPES + " TEXT,"
+ RecoveryServiceMetadataEntry.COLUMN_NAME_COUNTER_ID + " INTEGER,"
+ RecoveryServiceMetadataEntry.COLUMN_NAME_SERVER_PARAMS + " BLOB,"
@@ -88,9 +93,39 @@
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
- db.execSQL(SQL_DELETE_KEYS_ENTRY);
- db.execSQL(SQL_DELETE_USER_METADATA_ENTRY);
- db.execSQL(SQL_DELETE_RECOVERY_SERVICE_METADATA_ENTRY);
- onCreate(db);
+ if (oldVersion < 2) {
+ db.execSQL(SQL_DELETE_KEYS_ENTRY);
+ db.execSQL(SQL_DELETE_USER_METADATA_ENTRY);
+ db.execSQL(SQL_DELETE_RECOVERY_SERVICE_METADATA_ENTRY);
+ onCreate(db);
+ return;
+ }
+
+ if (oldVersion < 3) {
+ upgradeDbForVersion3(db);
+ }
+ }
+
+ private void upgradeDbForVersion3(SQLiteDatabase db) {
+ // Add the two columns for cert path and cert serial number
+ addColumnToTable(db, RecoveryServiceMetadataEntry.TABLE_NAME,
+ RecoveryServiceMetadataEntry.COLUMN_NAME_CERT_PATH, "BLOB", /*defaultStr=*/ null);
+ addColumnToTable(db, RecoveryServiceMetadataEntry.TABLE_NAME,
+ RecoveryServiceMetadataEntry.COLUMN_NAME_CERT_SERIAL, "INTEGER", /*defaultStr=*/
+ null);
+ }
+
+ private static void addColumnToTable(
+ SQLiteDatabase db, String tableName, String column, String columnType,
+ String defaultStr) {
+ Log.d(TAG, "Adding column " + column + " to " + tableName + ".");
+
+ String alterStr = "ALTER TABLE " + tableName + " ADD COLUMN " + column + " " + columnType;
+ if (defaultStr != null && !defaultStr.isEmpty()) {
+ alterStr += " DEFAULT " + defaultStr;
+ }
+
+ db.execSQL(alterStr + ";");
}
}
+
diff --git a/services/core/java/com/android/server/media/AudioPlayerStateMonitor.java b/services/core/java/com/android/server/media/AudioPlayerStateMonitor.java
index 7881a95..648c782 100644
--- a/services/core/java/com/android/server/media/AudioPlayerStateMonitor.java
+++ b/services/core/java/com/android/server/media/AudioPlayerStateMonitor.java
@@ -289,6 +289,7 @@
}
}
+ @GuardedBy("mLock")
private void sendAudioPlayerActiveStateChangedMessageLocked(
final AudioPlaybackConfiguration config, final boolean isRemoved) {
for (MessageHandler messageHandler : mListenerMap.values()) {
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index a6f049e..f346629 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -1722,7 +1722,7 @@
final long totalBytes = getTotalBytes(
NetworkTemplate.buildTemplateMobileAll(state.subscriberId), start, end);
final long remainingBytes = limitBytes - totalBytes;
- final long remainingDays = Math.min(1, (end - currentTimeMillis())
+ final long remainingDays = Math.max(1, (end - currentTimeMillis())
/ TimeUnit.DAYS.toMillis(1));
if (remainingBytes > 0) {
quotaBytes = (remainingBytes / remainingDays) / 10;
@@ -4678,10 +4678,12 @@
return subId;
}
+ @GuardedBy("mNetworkPoliciesSecondLock")
private int getSubIdLocked(Network network) {
return mNetIdToSubId.get(network.netId, INVALID_SUBSCRIPTION_ID);
}
+ @GuardedBy("mNetworkPoliciesSecondLock")
private SubscriptionPlan getPrimarySubscriptionPlanLocked(int subId) {
final SubscriptionPlan[] plans = mSubscriptionPlans.get(subId);
return ArrayUtils.isEmpty(plans) ? null : plans[0];
diff --git a/services/core/java/com/android/server/net/NetworkStatsCollection.java b/services/core/java/com/android/server/net/NetworkStatsCollection.java
index 961a451..3cc4d83 100644
--- a/services/core/java/com/android/server/net/NetworkStatsCollection.java
+++ b/services/core/java/com/android/server/net/NetworkStatsCollection.java
@@ -187,6 +187,7 @@
*/
@VisibleForTesting
public static long multiplySafe(long value, long num, long den) {
+ if (den == 0) den = 1;
long x = value;
long y = num;
diff --git a/services/core/java/com/android/server/net/NetworkStatsService.java b/services/core/java/com/android/server/net/NetworkStatsService.java
index bfc150e..76c4db1 100644
--- a/services/core/java/com/android/server/net/NetworkStatsService.java
+++ b/services/core/java/com/android/server/net/NetworkStatsService.java
@@ -405,6 +405,7 @@
mNonMonotonicObserver, dropBox, prefix, config.bucketDuration, includeTags);
}
+ @GuardedBy("mStatsLock")
private void shutdownLocked() {
mContext.unregisterReceiver(mTetherReceiver);
mContext.unregisterReceiver(mPollReceiver);
@@ -431,6 +432,7 @@
mSystemReady = false;
}
+ @GuardedBy("mStatsLock")
private void maybeUpgradeLegacyStatsLocked() {
File file;
try {
@@ -909,6 +911,7 @@
* reflect current {@link #mPersistThreshold} value. Always defers to
* {@link Global} values when defined.
*/
+ @GuardedBy("mStatsLock")
private void updatePersistThresholdsLocked() {
mDevRecorder.setPersistThreshold(mSettings.getDevPersistBytes(mPersistThreshold));
mXtRecorder.setPersistThreshold(mSettings.getXtPersistBytes(mPersistThreshold));
@@ -1029,6 +1032,7 @@
* are active on a single {@code iface}, they are combined under a single
* {@link NetworkIdentitySet}.
*/
+ @GuardedBy("mStatsLock")
private void updateIfacesLocked(Network[] defaultNetworks) {
if (!mSystemReady) return;
if (LOGV) Slog.v(TAG, "updateIfacesLocked()");
@@ -1128,6 +1132,7 @@
return ident;
}
+ @GuardedBy("mStatsLock")
private void recordSnapshotLocked(long currentTime) throws RemoteException {
// snapshot and record current counters; read UID stats first to
// avoid over counting dev stats.
@@ -1163,6 +1168,7 @@
* Bootstrap initial stats snapshot, usually during {@link #systemReady()}
* so we have baseline values without double-counting.
*/
+ @GuardedBy("mStatsLock")
private void bootstrapStatsLocked() {
final long currentTime = mTime.hasCache() ? mTime.currentTimeMillis()
: System.currentTimeMillis();
@@ -1197,6 +1203,7 @@
* Periodic poll operation, reading current statistics and recording into
* {@link NetworkStatsHistory}.
*/
+ @GuardedBy("mStatsLock")
private void performPollLocked(int flags) {
if (!mSystemReady) return;
if (LOGV) Slog.v(TAG, "performPollLocked(flags=0x" + Integer.toHexString(flags) + ")");
@@ -1258,6 +1265,7 @@
/**
* Sample recent statistics summary into {@link EventLog}.
*/
+ @GuardedBy("mStatsLock")
private void performSampleLocked() {
// TODO: migrate trustedtime fixes to separate binary log events
final long trustedTime = mTime.hasCache() ? mTime.currentTimeMillis() : -1;
@@ -1295,6 +1303,7 @@
/**
* Clean up {@link #mUidRecorder} after UID is removed.
*/
+ @GuardedBy("mStatsLock")
private void removeUidsLocked(int... uids) {
if (LOGV) Slog.v(TAG, "removeUidsLocked() for UIDs " + Arrays.toString(uids));
@@ -1313,6 +1322,7 @@
/**
* Clean up {@link #mUidRecorder} after user is removed.
*/
+ @GuardedBy("mStatsLock")
private void removeUserLocked(int userId) {
if (LOGV) Slog.v(TAG, "removeUserLocked() for userId=" + userId);
@@ -1434,6 +1444,7 @@
}
}
+ @GuardedBy("mStatsLock")
private void dumpProtoLocked(FileDescriptor fd) {
final ProtoOutputStream proto = new ProtoOutputStream(fd);
diff --git a/services/core/java/com/android/server/net/watchlist/WatchlistLoggingHandler.java b/services/core/java/com/android/server/net/watchlist/WatchlistLoggingHandler.java
index 81fe641..e8b39c0 100644
--- a/services/core/java/com/android/server/net/watchlist/WatchlistLoggingHandler.java
+++ b/services/core/java/com/android/server/net/watchlist/WatchlistLoggingHandler.java
@@ -22,12 +22,14 @@
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.UserInfo;
import android.os.Bundle;
import android.os.DropBoxManager;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.os.UserHandle;
+import android.os.UserManager;
import android.provider.Settings;
import android.text.TextUtils;
import android.util.Slog;
@@ -238,20 +240,34 @@
* if an app is really visited C&C site.
* (2) App digests that previously recorded in database.
*/
- private List<String> getAllDigestsForReport(WatchlistReportDbHelper.AggregatedResult record) {
+ @VisibleForTesting
+ List<String> getAllDigestsForReport(WatchlistReportDbHelper.AggregatedResult record) {
// Step 1: Get all installed application digests.
+ final List<UserInfo> users = ((UserManager) mContext.getSystemService(
+ Context.USER_SERVICE)).getUsers();
+ final int totalUsers = users.size();
final List<ApplicationInfo> apps = mContext.getPackageManager().getInstalledApplications(
PackageManager.MATCH_ANY_USER | PackageManager.MATCH_ALL);
final HashSet<String> result = new HashSet<>(apps.size() + record.appDigestCNCList.size());
final int size = apps.size();
for (int i = 0; i < size; i++) {
- byte[] digest = getDigestFromUid(apps.get(i).uid);
- if (digest == null) {
+ final int appUid = apps.get(i).uid;
+ boolean added = false;
+ // As the uid returned by getInstalledApplications() is for primary user only, it
+ // may exist in secondary users but not primary user, so we need to loop and see if
+ // that user has the app enabled.
+ for (int j = 0; j < totalUsers && !added; j++) {
+ int uid = UserHandle.getUid(users.get(j).id, appUid);
+ byte[] digest = getDigestFromUid(uid);
+ if (digest != null) {
+ result.add(HexDump.toHexString(digest));
+ added = true;
+ }
+ }
+ if (!added) {
Slog.e(TAG, "Cannot get digest from uid: " + apps.get(i).uid
+ ",pkg: " + apps.get(i).packageName);
- continue;
}
- result.add(HexDump.toHexString(digest));
}
// Step 2: Add all digests from records
result.addAll(record.appDigestCNCList.keySet());
@@ -288,9 +304,9 @@
return null;
}
}
- } else {
- Slog.e(TAG, "Should not happen");
}
+ // Not able to find a package name for this uid, possibly the package is installed on
+ // another user.
return null;
});
}
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index ada002c..3800017 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -686,6 +686,7 @@
sbn.getId(), Notification.FLAG_AUTO_CANCEL,
Notification.FLAG_FOREGROUND_SERVICE, false, r.getUserId(),
REASON_CLICK, null);
+ reportUserInteraction(r);
}
}
@@ -706,7 +707,7 @@
.setSubtype(actionIndex));
EventLogTags.writeNotificationActionClicked(key, actionIndex,
r.getLifespanMs(now), r.getFreshnessMs(now), r.getExposureMs(now));
- // TODO: Log action click via UsageStats.
+ reportUserInteraction(r);
}
}
@@ -827,6 +828,7 @@
NotificationRecord r = mNotificationsByKey.get(key);
if (r != null) {
r.recordDirectReplied();
+ reportUserInteraction(r);
}
}
}
@@ -1758,6 +1760,10 @@
return INotificationManager.Stub.asInterface(mService);
}
+ /**
+ * Report to usage stats that the notification was seen.
+ * @param r notification record
+ */
protected void reportSeen(NotificationRecord r) {
final int userId = r.sbn.getUserId();
mAppUsageStats.reportEvent(r.sbn.getPackageName(),
@@ -1766,6 +1772,17 @@
UsageEvents.Event.NOTIFICATION_SEEN);
}
+ /**
+ * Report to usage stats that the notification was clicked.
+ * @param r notification record
+ */
+ protected void reportUserInteraction(NotificationRecord r) {
+ final int userId = r.sbn.getUserId();
+ mAppUsageStats.reportEvent(r.sbn.getPackageName(),
+ userId == UserHandle.USER_ALL ? UserHandle.USER_SYSTEM : userId,
+ UsageEvents.Event.USER_INTERACTION);
+ }
+
@VisibleForTesting
NotificationManagerInternal getInternalService() {
return mInternalService;
@@ -3909,6 +3926,7 @@
return true;
}
+ @GuardedBy("mNotificationLock")
protected int getNotificationCountLocked(String pkg, int userId, int excludedId,
String excludedTag) {
int count = 0;
diff --git a/services/core/java/com/android/server/pm/InstantAppRegistry.java b/services/core/java/com/android/server/pm/InstantAppRegistry.java
index 30088dd..fb81ebf 100644
--- a/services/core/java/com/android/server/pm/InstantAppRegistry.java
+++ b/services/core/java/com/android/server/pm/InstantAppRegistry.java
@@ -274,7 +274,7 @@
}
// Propagate permissions before removing any state
- propagateInstantAppPermissionsIfNeeded(pkg.packageName, userId);
+ propagateInstantAppPermissionsIfNeeded(pkg, userId);
// Track instant apps
if (ps.getInstantApp(userId)) {
@@ -869,10 +869,10 @@
return uninstalledApps;
}
- private void propagateInstantAppPermissionsIfNeeded(@NonNull String packageName,
+ private void propagateInstantAppPermissionsIfNeeded(@NonNull PackageParser.Package pkg,
@UserIdInt int userId) {
InstantAppInfo appInfo = peekOrParseUninstalledInstantAppInfo(
- packageName, userId);
+ pkg.packageName, userId);
if (appInfo == null) {
return;
}
@@ -884,8 +884,8 @@
for (String grantedPermission : appInfo.getGrantedPermissions()) {
final boolean propagatePermission =
mService.mSettings.canPropagatePermissionToInstantApp(grantedPermission);
- if (propagatePermission) {
- mService.grantRuntimePermission(packageName, grantedPermission, userId);
+ if (propagatePermission && pkg.requestedPermissions.contains(grantedPermission)) {
+ mService.grantRuntimePermission(pkg.packageName, grantedPermission, userId);
}
}
} finally {
diff --git a/services/core/java/com/android/server/pm/InstantAppResolverConnection.java b/services/core/java/com/android/server/pm/InstantAppResolverConnection.java
index a9ee411..98f421e 100644
--- a/services/core/java/com/android/server/pm/InstantAppResolverConnection.java
+++ b/services/core/java/com/android/server/pm/InstantAppResolverConnection.java
@@ -141,6 +141,7 @@
}
}
+ @GuardedBy("mLock")
private void waitForBindLocked(String token) throws TimeoutException, InterruptedException {
final long startMillis = SystemClock.uptimeMillis();
while (mBindState != STATE_IDLE) {
@@ -250,6 +251,7 @@
}
}
+ @GuardedBy("mLock")
private void handleBinderDiedLocked() {
if (mRemoteInstance != null) {
try {
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index 59f9dae..0b32d1a 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -226,6 +226,7 @@
}
}
+ @GuardedBy("mSessions")
private void reconcileStagesLocked(String volumeUuid, boolean isEphemeral) {
final File stagingDir = buildStagingDir(volumeUuid, isEphemeral);
final ArraySet<File> unclaimedStages = newArraySet(
@@ -283,6 +284,7 @@
}
}
+ @GuardedBy("mSessions")
private void readSessionsLocked() {
if (LOGD) Slog.v(TAG, "readSessionsLocked()");
@@ -340,6 +342,7 @@
}
}
+ @GuardedBy("mSessions")
private void addHistoricalSessionLocked(PackageInstallerSession session) {
CharArrayWriter writer = new CharArrayWriter();
IndentingPrintWriter pw = new IndentingPrintWriter(writer, " ");
@@ -352,6 +355,7 @@
mHistoricalSessionsByInstaller.get(installerUid) + 1);
}
+ @GuardedBy("mSessions")
private void writeSessionsLocked() {
if (LOGD) Slog.v(TAG, "writeSessionsLocked()");
@@ -612,6 +616,7 @@
}
}
+ @GuardedBy("mSessions")
private int allocateSessionIdLocked() {
int n = 0;
int sessionId;
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 3dd5a34..9c69281 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -318,6 +318,7 @@
/**
* @return {@code true} iff the installing is app an device owner or affiliated profile owner.
*/
+ @GuardedBy("mLock")
private boolean isInstallerDeviceOwnerOrAffiliatedProfileOwnerLocked() {
DevicePolicyManagerInternal dpmi =
LocalServices.getService(DevicePolicyManagerInternal.class);
@@ -334,6 +335,7 @@
*
* @return {@code true} iff we need to ask to confirm the permissions?
*/
+ @GuardedBy("mLock")
private boolean needToAskForPermissionsLocked() {
if (mPermissionsManuallyAccepted) {
return false;
@@ -456,6 +458,7 @@
}
}
+ @GuardedBy("mLock")
private void assertPreparedAndNotSealedLocked(String cookie) {
assertPreparedAndNotCommittedOrDestroyedLocked(cookie);
if (mSealed) {
@@ -463,6 +466,7 @@
}
}
+ @GuardedBy("mLock")
private void assertPreparedAndNotCommittedOrDestroyedLocked(String cookie) {
assertPreparedAndNotDestroyedLocked(cookie);
if (mCommitted) {
@@ -470,6 +474,7 @@
}
}
+ @GuardedBy("mLock")
private void assertPreparedAndNotDestroyedLocked(String cookie) {
if (!mPrepared) {
throw new IllegalStateException(cookie + " before prepared");
@@ -484,6 +489,7 @@
* might point at an ASEC mount point, which is why we delay path resolution
* until someone actively works with the session.
*/
+ @GuardedBy("mLock")
private File resolveStageDirLocked() throws IOException {
if (mResolvedStageDir == null) {
if (stageDir != null) {
@@ -516,6 +522,7 @@
}
}
+ @GuardedBy("mLock")
private void computeProgressLocked(boolean forcePublish) {
mProgress = MathUtils.constrain(mClientProgress * 0.8f, 0f, 0.8f)
+ MathUtils.constrain(mInternalProgress * 0.2f, 0f, 0.2f);
@@ -728,6 +735,7 @@
* Check if the caller is the owner of this session. Otherwise throw a
* {@link SecurityException}.
*/
+ @GuardedBy("mLock")
private void assertCallerIsOwnerOrRootLocked() {
final int callingUid = Binder.getCallingUid();
if (callingUid != Process.ROOT_UID && callingUid != mInstallerUid) {
@@ -738,6 +746,7 @@
/**
* If anybody is reading or writing data of the session, throw an {@link SecurityException}.
*/
+ @GuardedBy("mLock")
private void assertNoWriteFileTransfersOpenLocked() {
// Verify that all writers are hands-off
for (RevocableFileDescriptor fd : mFds) {
@@ -820,6 +829,7 @@
* @throws PackageManagerException if the session was sealed but something went wrong. If the
* session was sealed this is the only possible exception.
*/
+ @GuardedBy("mLock")
private void sealAndValidateLocked() throws PackageManagerException, IOException {
assertNoWriteFileTransfersOpenLocked();
assertPreparedAndNotDestroyedLocked("sealing of session");
@@ -901,6 +911,7 @@
mCallback.onSessionSealedBlocking(this);
}
+ @GuardedBy("mLock")
private void commitLocked()
throws PackageManagerException {
if (mDestroyed) {
@@ -1016,6 +1027,7 @@
* Note that upgrade compatibility is still performed by
* {@link PackageManagerService}.
*/
+ @GuardedBy("mLock")
private void validateInstallLocked(@Nullable PackageInfo pkgInfo)
throws PackageManagerException {
mPackageName = null;
@@ -1228,6 +1240,7 @@
}
}
+ @GuardedBy("mLock")
private void assertApkConsistentLocked(String tag, ApkLite apk)
throws PackageManagerException {
if (!mPackageName.equals(apk.packageName)) {
@@ -1511,6 +1524,7 @@
}
}
+ @GuardedBy("mLock")
private void dumpLocked(IndentingPrintWriter pw) {
pw.println("Session " + sessionId + ":");
pw.increaseIndent();
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 2816bbd..61effb8 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -797,6 +797,7 @@
return overlayPackages;
}
+ @GuardedBy("mInstallLock")
final String[] getStaticOverlayPathsLocked(Collection<PackageParser.Package> allPackages,
String targetPackageName, String targetPath) {
if ("android".equals(targetPackageName)) {
@@ -964,7 +965,7 @@
volatile boolean mSystemReady;
volatile boolean mSafeMode;
volatile boolean mHasSystemUidErrors;
- private volatile boolean mEphemeralAppsDisabled;
+ private volatile boolean mWebInstantAppsDisabled;
ApplicationInfo mAndroidApplication;
final ActivityInfo mResolveActivity = new ActivityInfo();
@@ -5216,7 +5217,13 @@
@Override
public int checkUidPermission(String permName, int uid) {
- return mPermissionManager.checkUidPermission(permName, uid, getCallingUid());
+ synchronized (mPackages) {
+ final String[] packageNames = getPackagesForUid(uid);
+ final PackageParser.Package pkg = (packageNames != null && packageNames.length > 0)
+ ? mPackages.get(packageNames[0])
+ : null;
+ return mPermissionManager.checkUidPermission(permName, pkg, uid, getCallingUid());
+ }
}
@Override
@@ -5950,11 +5957,11 @@
/**
* Returns whether or not instant apps have been disabled remotely.
*/
- private boolean isEphemeralDisabled() {
- return mEphemeralAppsDisabled;
+ private boolean areWebInstantAppsDisabled() {
+ return mWebInstantAppsDisabled;
}
- private boolean isInstantAppAllowed(
+ private boolean isInstantAppResolutionAllowed(
Intent intent, List<ResolveInfo> resolvedActivities, int userId,
boolean skipPackageCheck) {
if (mInstantAppResolverConnection == null) {
@@ -5979,8 +5986,12 @@
|| (intent.getFlags() & Intent.FLAG_ACTIVITY_MATCH_EXTERNAL) == 0) {
return false;
}
- } else if (intent.getData() == null || TextUtils.isEmpty(intent.getData().getHost())) {
- return false;
+ } else {
+ if (intent.getData() == null || TextUtils.isEmpty(intent.getData().getHost())) {
+ return false;
+ } else if (areWebInstantAppsDisabled()) {
+ return false;
+ }
}
// Deny ephemeral apps if the user chose _ALWAYS or _ALWAYS_ASK for intent resolution.
// Or if there's already an ephemeral app installed that handles the action
@@ -6511,14 +6522,13 @@
}
}
return applyPostResolutionFilter(
- list, instantAppPkgName, allowDynamicSplits, filterCallingUid, userId);
+ list, instantAppPkgName, allowDynamicSplits, filterCallingUid, userId, intent);
}
// reader
boolean sortResult = false;
- boolean addEphemeral = false;
+ boolean addInstant = false;
List<ResolveInfo> result;
- final boolean ephemeralDisabled = isEphemeralDisabled();
synchronized (mPackages) {
if (pkgName == null) {
List<CrossProfileIntentFilter> matchingFilters =
@@ -6531,14 +6541,14 @@
xpResult.add(xpResolveInfo);
return applyPostResolutionFilter(
filterIfNotSystemUser(xpResult, userId), instantAppPkgName,
- allowDynamicSplits, filterCallingUid, userId);
+ allowDynamicSplits, filterCallingUid, userId, intent);
}
// Check for results in the current profile.
result = filterIfNotSystemUser(mActivities.queryIntent(
intent, resolvedType, flags, userId), userId);
- addEphemeral = !ephemeralDisabled
- && isInstantAppAllowed(intent, result, userId, false /*skipPackageCheck*/);
+ addInstant = isInstantAppResolutionAllowed(intent, result, userId,
+ false /*skipPackageCheck*/);
// Check for cross profile results.
boolean hasNonNegativePriorityResult = hasNonNegativePriority(result);
xpResolveInfo = queryCrossProfileIntents(
@@ -6565,20 +6575,20 @@
// in the result.
result.remove(xpResolveInfo);
}
- if (result.size() == 0 && !addEphemeral) {
+ if (result.size() == 0 && !addInstant) {
// No result in current profile, but found candidate in parent user.
// And we are not going to add emphemeral app, so we can return the
// result straight away.
result.add(xpDomainInfo.resolveInfo);
return applyPostResolutionFilter(result, instantAppPkgName,
- allowDynamicSplits, filterCallingUid, userId);
+ allowDynamicSplits, filterCallingUid, userId, intent);
}
- } else if (result.size() <= 1 && !addEphemeral) {
+ } else if (result.size() <= 1 && !addInstant) {
// No result in parent user and <= 1 result in current profile, and we
// are not going to add emphemeral app, so we can return the result without
// further processing.
return applyPostResolutionFilter(result, instantAppPkgName,
- allowDynamicSplits, filterCallingUid, userId);
+ allowDynamicSplits, filterCallingUid, userId, intent);
}
// We have more than one candidate (combining results from current and parent
// profile), so we need filtering and sorting.
@@ -6598,8 +6608,7 @@
if (result == null || result.size() == 0) {
// the caller wants to resolve for a particular package; however, there
// were no installed results, so, try to find an ephemeral result
- addEphemeral = !ephemeralDisabled
- && isInstantAppAllowed(
+ addInstant = isInstantAppResolutionAllowed(
intent, null /*result*/, userId, true /*skipPackageCheck*/);
if (result == null) {
result = new ArrayList<>();
@@ -6607,7 +6616,7 @@
}
}
}
- if (addEphemeral) {
+ if (addInstant) {
result = maybeAddInstantAppInstaller(
result, intent, resolvedType, flags, userId, resolveForStart);
}
@@ -6615,7 +6624,7 @@
Collections.sort(result, mResolvePrioritySorter);
}
return applyPostResolutionFilter(
- result, instantAppPkgName, allowDynamicSplits, filterCallingUid, userId);
+ result, instantAppPkgName, allowDynamicSplits, filterCallingUid, userId, intent);
}
private List<ResolveInfo> maybeAddInstantAppInstaller(List<ResolveInfo> result, Intent intent,
@@ -6819,12 +6828,20 @@
* @param resolveInfos The pre-filtered list of resolved activities
* @param ephemeralPkgName The ephemeral package name. If {@code null}, no filtering
* is performed.
+ * @param intent
* @return A filtered list of resolved activities.
*/
private List<ResolveInfo> applyPostResolutionFilter(List<ResolveInfo> resolveInfos,
- String ephemeralPkgName, boolean allowDynamicSplits, int filterCallingUid, int userId) {
+ String ephemeralPkgName, boolean allowDynamicSplits, int filterCallingUid, int userId,
+ Intent intent) {
+ final boolean blockInstant = intent.isWebIntent() && areWebInstantAppsDisabled();
for (int i = resolveInfos.size() - 1; i >= 0; i--) {
final ResolveInfo info = resolveInfos.get(i);
+ // remove locally resolved instant app web results when disabled
+ if (info.isInstantAppAvailable && blockInstant) {
+ resolveInfos.remove(i);
+ continue;
+ }
// allow activities that are defined in the provided package
if (allowDynamicSplits
&& info.activityInfo != null
@@ -7456,7 +7473,7 @@
}
}
return applyPostResolutionFilter(
- list, instantAppPkgName, allowDynamicSplits, callingUid, userId);
+ list, instantAppPkgName, allowDynamicSplits, callingUid, userId, intent);
}
// reader
@@ -7466,14 +7483,14 @@
final List<ResolveInfo> result =
mReceivers.queryIntent(intent, resolvedType, flags, userId);
return applyPostResolutionFilter(
- result, instantAppPkgName, allowDynamicSplits, callingUid, userId);
+ result, instantAppPkgName, allowDynamicSplits, callingUid, userId, intent);
}
final PackageParser.Package pkg = mPackages.get(pkgName);
if (pkg != null) {
final List<ResolveInfo> result = mReceivers.queryIntentForPackage(
intent, resolvedType, flags, pkg.receivers, userId);
return applyPostResolutionFilter(
- result, instantAppPkgName, allowDynamicSplits, callingUid, userId);
+ result, instantAppPkgName, allowDynamicSplits, callingUid, userId, intent);
}
return Collections.emptyList();
}
@@ -7943,7 +7960,7 @@
@Override
public ParceledListSlice<InstantAppInfo> getInstantApps(int userId) {
- if (HIDE_EPHEMERAL_APIS || isEphemeralDisabled()) {
+ if (HIDE_EPHEMERAL_APIS) {
return null;
}
if (!canViewInstantApps(Binder.getCallingUid(), userId)) {
@@ -7968,7 +7985,7 @@
mPermissionManager.enforceCrossUserPermission(Binder.getCallingUid(), userId,
true /* requireFullPermission */, false /* checkShell */,
"isInstantApp");
- if (HIDE_EPHEMERAL_APIS || isEphemeralDisabled()) {
+ if (HIDE_EPHEMERAL_APIS) {
return false;
}
@@ -7994,7 +8011,7 @@
@Override
public byte[] getInstantAppCookie(String packageName, int userId) {
- if (HIDE_EPHEMERAL_APIS || isEphemeralDisabled()) {
+ if (HIDE_EPHEMERAL_APIS) {
return null;
}
@@ -8012,7 +8029,7 @@
@Override
public boolean setInstantAppCookie(String packageName, byte[] cookie, int userId) {
- if (HIDE_EPHEMERAL_APIS || isEphemeralDisabled()) {
+ if (HIDE_EPHEMERAL_APIS) {
return true;
}
@@ -8030,7 +8047,7 @@
@Override
public Bitmap getInstantAppIcon(String packageName, int userId) {
- if (HIDE_EPHEMERAL_APIS || isEphemeralDisabled()) {
+ if (HIDE_EPHEMERAL_APIS) {
return null;
}
@@ -9015,6 +9032,7 @@
}
}
+ @GuardedBy("mPackages")
private void notifyPackageUseLocked(String packageName, int reason) {
final PackageParser.Package p = mPackages.get(packageName);
if (p == null) {
@@ -10625,8 +10643,6 @@
~ApplicationInfo.PRIVATE_FLAG_DEFAULT_TO_DEVICE_PROTECTED_STORAGE;
pkg.applicationInfo.privateFlags &=
~ApplicationInfo.PRIVATE_FLAG_DIRECT_BOOT_AWARE;
- // clear protected broadcasts
- pkg.protectedBroadcasts = null;
// cap permission priorities
if (pkg.permissionGroups != null && pkg.permissionGroups.size() > 0) {
for (int i = pkg.permissionGroups.size() - 1; i >= 0; --i) {
@@ -10635,6 +10651,8 @@
}
}
if ((scanFlags & SCAN_AS_PRIVILEGED) == 0) {
+ // clear protected broadcasts
+ pkg.protectedBroadcasts = null;
// ignore export request for single user receivers
if (pkg.receivers != null) {
for (int i = pkg.receivers.size() - 1; i >= 0; --i) {
@@ -13954,6 +13972,7 @@
}
}
+ @GuardedBy("mPackages")
private boolean canSuspendPackageForUserLocked(String packageName, int userId) {
if (isPackageDeviceAdmin(packageName, userId)) {
Slog.w(TAG, "Cannot suspend/un-suspend package \"" + packageName
@@ -16610,6 +16629,12 @@
if (userId != UserHandle.USER_ALL) {
ps.setInstalled(true, userId);
ps.setEnabled(COMPONENT_ENABLED_STATE_DEFAULT, userId, installerPackageName);
+ } else {
+ for (int currentUserId : sUserManager.getUserIds()) {
+ ps.setInstalled(true, currentUserId);
+ ps.setEnabled(COMPONENT_ENABLED_STATE_DEFAULT, currentUserId,
+ installerPackageName);
+ }
}
// When replacing an existing package, preserve the original install reason for all
@@ -20696,7 +20721,7 @@
ContentObserver co = new ContentObserver(mHandler) {
@Override
public void onChange(boolean selfChange) {
- mEphemeralAppsDisabled =
+ mWebInstantAppsDisabled =
(Global.getInt(resolver, Global.ENABLE_EPHEMERAL_FEATURE, 1) == 0) ||
(Secure.getInt(resolver, Secure.INSTANT_APPS_ENABLED, 1) == 0);
}
@@ -20704,7 +20729,7 @@
mContext.getContentResolver().registerContentObserver(android.provider.Settings.Global
.getUriFor(Global.ENABLE_EPHEMERAL_FEATURE),
false, co, UserHandle.USER_SYSTEM);
- mContext.getContentResolver().registerContentObserver(android.provider.Settings.Global
+ mContext.getContentResolver().registerContentObserver(android.provider.Settings.Secure
.getUriFor(Secure.INSTANT_APPS_ENABLED), false, co, UserHandle.USER_SYSTEM);
co.onChange(true);
diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java
index a85d6d8..034fd23 100644
--- a/services/core/java/com/android/server/pm/ShortcutService.java
+++ b/services/core/java/com/android/server/pm/ShortcutService.java
@@ -530,6 +530,7 @@
return processState <= PROCESS_STATE_FOREGROUND_THRESHOLD;
}
+ @GuardedBy("mLock")
boolean isUidForegroundLocked(int uid) {
if (uid == Process.SYSTEM_UID) {
// IUidObserver doesn't report the state of SYSTEM, but it always has bound services,
@@ -545,6 +546,7 @@
return isProcessStateForeground(mActivityManagerInternal.getUidProcessState(uid));
}
+ @GuardedBy("mLock")
long getUidLastForegroundElapsedTimeLocked(int uid) {
return mUidLastForegroundElapsedTime.get(uid);
}
@@ -638,6 +640,7 @@
}
}
+ @GuardedBy("mLock")
private void unloadUserLocked(int userId) {
if (DEBUG) {
Slog.d(TAG, "unloadUserLocked: user=" + userId);
@@ -864,6 +867,7 @@
writeAttr(out, name, intent.toUri(/* flags =*/ 0));
}
+ @GuardedBy("mLock")
@VisibleForTesting
void saveBaseStateLocked() {
final AtomicFile file = getBaseStateFile();
@@ -896,6 +900,7 @@
}
}
+ @GuardedBy("mLock")
private void loadBaseStateLocked() {
mRawLastResetTime = 0;
@@ -948,6 +953,7 @@
return new File(injectUserDataPath(userId), FILENAME_USER_PACKAGES);
}
+ @GuardedBy("mLock")
private void saveUserLocked(@UserIdInt int userId) {
final File path = getUserFile(userId);
if (DEBUG) {
@@ -974,6 +980,7 @@
}
}
+ @GuardedBy("mLock")
private void saveUserInternalLocked(@UserIdInt int userId, OutputStream os,
boolean forBackup) throws IOException, XmlPullParserException {
@@ -1107,12 +1114,14 @@
}
/** Return the last reset time. */
+ @GuardedBy("mLock")
long getLastResetTimeLocked() {
updateTimesLocked();
return mRawLastResetTime;
}
/** Return the next reset time. */
+ @GuardedBy("mLock")
long getNextResetTimeLocked() {
updateTimesLocked();
return mRawLastResetTime + mResetInterval;
@@ -1125,6 +1134,7 @@
/**
* Update the last reset time.
*/
+ @GuardedBy("mLock")
private void updateTimesLocked() {
final long now = injectCurrentTimeMillis();
@@ -1220,6 +1230,7 @@
return ret;
}
+ @GuardedBy("mLock")
void forEachLoadedUserLocked(@NonNull Consumer<ShortcutUser> c) {
for (int i = mUsers.size() - 1; i >= 0; i--) {
c.accept(mUsers.valueAt(i));
@@ -1279,6 +1290,7 @@
* {@link ShortcutBitmapSaver#waitForAllSavesLocked()} to make sure there's no pending bitmap
* saves are going on.
*/
+ @GuardedBy("mLock")
private void cleanupDanglingBitmapDirectoriesLocked(@UserIdInt int userId) {
if (DEBUG) {
Slog.d(TAG, "cleanupDanglingBitmaps: userId=" + userId);
@@ -2108,6 +2120,7 @@
}
}
+ @GuardedBy("mLock")
private ParceledListSlice<ShortcutInfo> getShortcutsWithQueryLocked(@NonNull String packageName,
@UserIdInt int userId, int cloneFlags, @NonNull Predicate<ShortcutInfo> query) {
@@ -2418,6 +2431,7 @@
*
* This is called when an app is uninstalled, or an app gets "clear data"ed.
*/
+ @GuardedBy("mLock")
@VisibleForTesting
void cleanUpPackageLocked(String packageName, int owningUserId, int packageUserId,
boolean appStillExists) {
@@ -2508,6 +2522,7 @@
return setReturnedByServer(ret);
}
+ @GuardedBy("ShortcutService.this.mLock")
private void getShortcutsInnerLocked(int launcherUserId, @NonNull String callingPackage,
@Nullable String packageName, @Nullable List<String> shortcutIds, long changedSince,
@Nullable ComponentName componentName, int queryFlags,
@@ -2579,6 +2594,7 @@
}
}
+ @GuardedBy("ShortcutService.this.mLock")
private ShortcutInfo getShortcutInfoLocked(
int launcherUserId, @NonNull String callingPackage,
@NonNull String packageName, @NonNull String shortcutId, int userId,
@@ -2940,6 +2956,7 @@
verifyStates();
}
+ @GuardedBy("mLock")
private void rescanUpdatedPackagesLocked(@UserIdInt int userId, long lastScanTime) {
final ShortcutUser user = getUserShortcutsLocked(userId);
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index b53d83b..a0577b1 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -1547,6 +1547,7 @@
return result;
}
+ @GuardedBy("mRestrictionsLock")
private EnforcingUser getEnforcingUserLocked(@UserIdInt int userId) {
int source = mDeviceOwnerUserId == userId ? UserManager.RESTRICTION_SOURCE_DEVICE_OWNER
: UserManager.RESTRICTION_SOURCE_PROFILE_OWNER;
@@ -2896,6 +2897,7 @@
}
}
+ @GuardedBy("mUsersLock")
@VisibleForTesting
void addRemovingUserIdLocked(int userId) {
// We remember deleted user IDs to prevent them from being
@@ -3405,6 +3407,7 @@
return nextId;
}
+ @GuardedBy("mUsersLock")
private int scanNextAvailableIdLocked() {
for (int i = MIN_USER_ID; i < MAX_USER_ID; i++) {
if (mUsers.indexOfKey(i) < 0 && !mRemovingUserIds.get(i)) {
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerInternal.java b/services/core/java/com/android/server/pm/permission/PermissionManagerInternal.java
index 60c118b..859bbe3 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerInternal.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerInternal.java
@@ -146,7 +146,8 @@
public abstract int checkPermission(@NonNull String permName, @NonNull String packageName,
int callingUid, int userId);
- public abstract int checkUidPermission(String permName, int uid, int callingUid);
+ public abstract int checkUidPermission(@NonNull String permName,
+ @Nullable PackageParser.Package pkg, int uid, int callingUid);
/**
* Enforces the request is from the system or an app that has INTERACT_ACROSS_USERS
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
index 08f8bbd..afaafbc 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -242,7 +242,8 @@
return PackageManager.PERMISSION_DENIED;
}
- private int checkUidPermission(String permName, int uid, int callingUid) {
+ private int checkUidPermission(String permName, PackageParser.Package pkg, int uid,
+ int callingUid) {
final int callingUserId = UserHandle.getUserId(callingUid);
final boolean isCallerInstantApp =
mPackageManagerInt.getInstantAppPackageName(callingUid) != null;
@@ -253,28 +254,13 @@
return PackageManager.PERMISSION_DENIED;
}
- final String[] packages = mContext.getPackageManager().getPackagesForUid(uid);
- if (packages != null && packages.length > 0) {
- PackageParser.Package pkg = null;
- for (String packageName : packages) {
- pkg = mPackageManagerInt.getPackage(packageName);
- if (pkg != null) {
- break;
- }
- }
- if (pkg == null) {
-Slog.e(TAG, "TODD: No package not found; UID: " + uid);
-Slog.e(TAG, "TODD: Packages: " + Arrays.toString(packages));
- return PackageManager.PERMISSION_DENIED;
- }
+ if (pkg != null) {
if (pkg.mSharedUserId != null) {
if (isCallerInstantApp) {
return PackageManager.PERMISSION_DENIED;
}
- } else {
- if (mPackageManagerInt.filterAppAccess(pkg, callingUid, callingUserId)) {
- return PackageManager.PERMISSION_DENIED;
- }
+ } else if (mPackageManagerInt.filterAppAccess(pkg, callingUid, callingUserId)) {
+ return PackageManager.PERMISSION_DENIED;
}
final PermissionsState permissionsState =
((PackageSetting) pkg.mExtras).getPermissionsState();
@@ -1210,6 +1196,7 @@
return false;
}
+ @GuardedBy("mLock")
private void grantRuntimePermissionsGrantedToDisabledPackageLocked(
PackageParser.Package pkg, int callingUid, PermissionCallback callback) {
if (pkg.parentPackage == null) {
@@ -1499,6 +1486,7 @@
}
}
+ @GuardedBy("mLock")
private int[] revokeUnusedSharedUserPermissionsLocked(
SharedUserSetting suSetting, int[] allUserIds) {
// Collect all used permissions in the UID
@@ -2066,8 +2054,9 @@
permName, packageName, callingUid, userId);
}
@Override
- public int checkUidPermission(String permName, int uid, int callingUid) {
- return PermissionManagerService.this.checkUidPermission(permName, uid, callingUid);
+ public int checkUidPermission(String permName, PackageParser.Package pkg, int uid,
+ int callingUid) {
+ return PermissionManagerService.this.checkUidPermission(permName, pkg, uid, callingUid);
}
@Override
public PermissionGroupInfo getPermissionGroupInfo(String groupName, int flags,
diff --git a/services/core/java/com/android/server/pm/permission/PermissionSettings.java b/services/core/java/com/android/server/pm/permission/PermissionSettings.java
index f6c4990..b3f2833 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionSettings.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionSettings.java
@@ -201,34 +201,42 @@
}
}
+ @GuardedBy("mLock")
@Nullable BasePermission getPermissionLocked(@NonNull String permName) {
return mPermissions.get(permName);
}
+ @GuardedBy("mLock")
@Nullable BasePermission getPermissionTreeLocked(@NonNull String permName) {
return mPermissionTrees.get(permName);
}
+ @GuardedBy("mLock")
void putPermissionLocked(@NonNull String permName, @NonNull BasePermission permission) {
mPermissions.put(permName, permission);
}
+ @GuardedBy("mLock")
void putPermissionTreeLocked(@NonNull String permName, @NonNull BasePermission permission) {
mPermissionTrees.put(permName, permission);
}
+ @GuardedBy("mLock")
void removePermissionLocked(@NonNull String permName) {
mPermissions.remove(permName);
}
+ @GuardedBy("mLock")
void removePermissionTreeLocked(@NonNull String permName) {
mPermissionTrees.remove(permName);
}
+ @GuardedBy("mLock")
@NonNull Collection<BasePermission> getAllPermissionsLocked() {
return mPermissions.values();
}
+ @GuardedBy("mLock")
@NonNull Collection<BasePermission> getAllPermissionTreesLocked() {
return mPermissionTrees.values();
}
diff --git a/services/core/java/com/android/server/power/BatterySaverPolicy.java b/services/core/java/com/android/server/power/BatterySaverPolicy.java
index 847c90a..08dc97e 100644
--- a/services/core/java/com/android/server/power/BatterySaverPolicy.java
+++ b/services/core/java/com/android/server/power/BatterySaverPolicy.java
@@ -306,6 +306,7 @@
}
}
+ @GuardedBy("mLock")
@VisibleForTesting
void updateConstantsLocked(final String setting, final String deviceSpecificSetting) {
mSettings = setting;
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index 1bb85c4..38dc820 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -120,9 +120,6 @@
private static final boolean DEBUG = false;
private static final boolean DEBUG_SPEW = DEBUG && true;
- // if DEBUG_WIRELESS=true, plays wireless charging animation w/ sound on every plug + unplug
- private static final boolean DEBUG_WIRELESS = false;
-
// Message: Sent when a user activity timeout occurs to update the power state.
private static final int MSG_USER_ACTIVITY_TIMEOUT = 1;
// Message: Sent when the device enters or exits a dreaming or dozing state.
@@ -1743,14 +1740,15 @@
userActivityNoUpdateLocked(
now, PowerManager.USER_ACTIVITY_EVENT_OTHER, 0, Process.SYSTEM_UID);
- // Tell the notifier whether wireless charging has started so that
- // it can provide feedback to the user.
- if (dockedOnWirelessCharger || DEBUG_WIRELESS) {
- mNotifier.onWirelessChargingStarted(mBatteryLevel);
- } else if (mIsPowered && !wasPowered
- && (mPlugType == BatteryManager.BATTERY_PLUGGED_AC
- || mPlugType == BatteryManager.BATTERY_PLUGGED_USB)) {
- mNotifier.onWiredChargingStarted();
+ // only play charging sounds if boot is completed so charging sounds don't play
+ // with potential notification sounds
+ if (mBootCompleted) {
+ if (mIsPowered && !BatteryManager.isPlugWired(oldPlugType)
+ && BatteryManager.isPlugWired(mPlugType)) {
+ mNotifier.onWiredChargingStarted();
+ } else if (dockedOnWirelessCharger) {
+ mNotifier.onWirelessChargingStarted(mBatteryLevel);
+ }
}
}
diff --git a/services/core/java/com/android/server/power/batterysaver/BatterySavingStats.java b/services/core/java/com/android/server/power/batterysaver/BatterySavingStats.java
index b0b07ea..37df94f 100644
--- a/services/core/java/com/android/server/power/batterysaver/BatterySavingStats.java
+++ b/services/core/java/com/android/server/power/batterysaver/BatterySavingStats.java
@@ -285,6 +285,7 @@
}
}
+ @GuardedBy("mLock")
private void transitionStateLocked(int newState) {
if (mCurrentState == newState) {
return;
@@ -298,6 +299,7 @@
mMetricsLoggerHelper.transitionState(newState, now, batteryLevel, batteryPercent);
}
+ @GuardedBy("mLock")
private void endLastStateLocked(long now, int batteryLevel, int batteryPercent) {
if (mCurrentState < 0) {
return;
@@ -338,6 +340,7 @@
}
+ @GuardedBy("mLock")
private void startNewStateLocked(int newState, long now, int batteryLevel, int batteryPercent) {
if (DEBUG) {
Slog.d(TAG, "New state: " + stateToString(newState));
@@ -363,7 +366,7 @@
indent = indent + " ";
pw.print(indent);
- pw.println("Battery Saver: Off On");
+ pw.println("Battery Saver: w/Off w/On");
dumpLineLocked(pw, indent, InteractiveState.NON_INTERACTIVE, "NonIntr",
DozeState.NOT_DOZING, "NonDoze");
dumpLineLocked(pw, indent, InteractiveState.INTERACTIVE, " Intr",
diff --git a/services/core/java/com/android/server/power/batterysaver/FileUpdater.java b/services/core/java/com/android/server/power/batterysaver/FileUpdater.java
index e0ab9e9..c08b610 100644
--- a/services/core/java/com/android/server/power/batterysaver/FileUpdater.java
+++ b/services/core/java/com/android/server/power/batterysaver/FileUpdater.java
@@ -306,6 +306,7 @@
}
}
+ @GuardedBy("mLock")
private void saveDefaultValuesLocked() {
final AtomicFile file = new AtomicFile(injectDefaultValuesFilename());
@@ -334,6 +335,7 @@
}
}
+ @GuardedBy("mLock")
@VisibleForTesting
boolean loadDefaultValuesLocked() {
final AtomicFile file = new AtomicFile(injectDefaultValuesFilename());
diff --git a/services/core/java/com/android/server/stats/StatsCompanionService.java b/services/core/java/com/android/server/stats/StatsCompanionService.java
index a87ae1e..ef6067a 100644
--- a/services/core/java/com/android/server/stats/StatsCompanionService.java
+++ b/services/core/java/com/android/server/stats/StatsCompanionService.java
@@ -85,11 +85,9 @@
public static final String RESULT_RECEIVER_CONTROLLER_KEY = "controller_activity";
static final String TAG = "StatsCompanionService";
- static final boolean DEBUG = true;
+ static final boolean DEBUG = false;
- public static final String ACTION_TRIGGER_COLLECTION =
- "com.android.server.stats.action.TRIGGER_COLLECTION";
-
+ public static final int CODE_DATA_BROADCAST = 1;
public static final int CODE_SUBSCRIBER_BROADCAST = 1;
private final Context mContext;
@@ -110,9 +108,9 @@
private TelephonyManager mTelephony = null;
private final StatFs mStatFsData = new StatFs(Environment.getDataDirectory().getAbsolutePath());
private final StatFs mStatFsSystem =
- new StatFs(Environment.getRootDirectory().getAbsolutePath());
+ new StatFs(Environment.getRootDirectory().getAbsolutePath());
private final StatFs mStatFsTemp =
- new StatFs(Environment.getDownloadCacheDirectory().getAbsolutePath());
+ new StatFs(Environment.getDownloadCacheDirectory().getAbsolutePath());
public StatsCompanionService(Context context) {
super();
@@ -122,7 +120,7 @@
mAnomalyAlarmIntent = PendingIntent.getBroadcast(mContext, 0,
new Intent(mContext, AnomalyAlarmReceiver.class), 0);
mPullingAlarmIntent = PendingIntent.getBroadcast(
- mContext, 0, new Intent(mContext, PullingAlarmReceiver.class), 0);
+ mContext, 0, new Intent(mContext, PullingAlarmReceiver.class), 0);
mAppUpdateReceiver = new AppUpdateReceiver();
mUserUpdateReceiver = new BroadcastReceiver() {
@Override
@@ -153,17 +151,21 @@
for (int i = 0; i < numClusters; i++) {
final int numSpeedSteps = powerProfile.getNumSpeedStepsInCpuCluster(i);
mKernelCpuSpeedReaders[i] = new KernelCpuSpeedReader(firstCpuOfCluster,
- numSpeedSteps);
+ numSpeedSteps);
firstCpuOfCluster += powerProfile.getNumCoresInCpuCluster(i);
}
}
@Override
- public void sendBroadcast(String pkg, String cls) {
- // TODO: Use a pending intent.
+ public void sendDataBroadcast(IBinder intentSenderBinder) {
enforceCallingPermission();
- mContext.sendBroadcastAsUser(new Intent(ACTION_TRIGGER_COLLECTION).setClassName(pkg, cls),
- UserHandle.SYSTEM);
+ IntentSender intentSender = new IntentSender(intentSenderBinder);
+ Intent intent = new Intent();
+ try {
+ intentSender.sendIntent(mContext, CODE_DATA_BROADCAST, intent, null, null);
+ } catch (IntentSender.SendIntentException e) {
+ Slog.w(TAG, "Unable to send using IntentSender");
+ }
}
@Override
@@ -209,6 +211,7 @@
}
// Assumes that sStatsdLock is held.
+ @GuardedBy("sStatsdLock")
private final void informAllUidsLocked(Context context) throws RemoteException {
UserManager um = (UserManager) context.getSystemService(Context.USER_SERVICE);
PackageManager pm = context.getPackageManager();
@@ -305,24 +308,23 @@
}
public final static class PullingAlarmReceiver extends BroadcastReceiver {
- @Override
- public void onReceive(Context context, Intent intent) {
- if (DEBUG)
- Slog.d(TAG, "Time to poll something.");
- synchronized (sStatsdLock) {
- if (sStatsd == null) {
- Slog.w(TAG, "Could not access statsd to inform it of pulling alarm firing.");
- return;
- }
- try {
- // Two-way call to statsd to retain AlarmManager wakelock
- sStatsd.informPollAlarmFired();
- } catch (RemoteException e) {
- Slog.w(TAG, "Failed to inform statsd of pulling alarm firing.", e);
- }
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (DEBUG)
+ Slog.d(TAG, "Time to poll something.");
+ synchronized (sStatsdLock) {
+ if (sStatsd == null) {
+ Slog.w(TAG, "Could not access statsd to inform it of pulling alarm firing.");
+ return;
+ }
+ try {
+ // Two-way call to statsd to retain AlarmManager wakelock
+ sStatsd.informPollAlarmFired();
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Failed to inform statsd of pulling alarm firing.", e);
+ }
+ }
}
- // AlarmManager releases its own wakelock here.
- }
}
private final static class ShutdownEventReceiver extends BroadcastReceiver {
@@ -332,9 +334,9 @@
* Skip immediately if intent is not relevant to device shutdown.
*/
if (!intent.getAction().equals(Intent.ACTION_REBOOT)
- && !(intent.getAction().equals(Intent.ACTION_SHUTDOWN)
- && (intent.getFlags() & Intent.FLAG_RECEIVER_FOREGROUND) != 0)) {
- return;
+ && !(intent.getAction().equals(Intent.ACTION_SHUTDOWN)
+ && (intent.getFlags() & Intent.FLAG_RECEIVER_FOREGROUND) != 0)) {
+ return;
}
Slog.i(TAG, "StatsCompanionService noticed a shutdown.");
@@ -381,50 +383,50 @@
@Override // Binder call
public void setPullingAlarms(long timestampMs, long intervalMs) {
- enforceCallingPermission();
- if (DEBUG)
- Slog.d(TAG, "Setting pulling alarm for " + timestampMs + " every " + intervalMs + "ms");
- final long callingToken = Binder.clearCallingIdentity();
- try {
- // using RTC, not RTC_WAKEUP, so if device is asleep, will only fire when it awakens.
- // This alarm is inexact, leaving its exactness completely up to the OS optimizations.
- // TODO: totally inexact means that stats per bucket could be quite off. Is this okay?
- mAlarmManager.setRepeating(AlarmManager.RTC, timestampMs, intervalMs, mPullingAlarmIntent);
- } finally {
- Binder.restoreCallingIdentity(callingToken);
- }
+ enforceCallingPermission();
+ if (DEBUG)
+ Slog.d(TAG, "Setting pulling alarm for " + timestampMs + " every " + intervalMs + "ms");
+ final long callingToken = Binder.clearCallingIdentity();
+ try {
+ // using RTC, not RTC_WAKEUP, so if device is asleep, will only fire when it awakens.
+ // This alarm is inexact, leaving its exactness completely up to the OS optimizations.
+ // TODO: totally inexact means that stats per bucket could be quite off. Is this okay?
+ mAlarmManager.setRepeating(AlarmManager.RTC, timestampMs, intervalMs, mPullingAlarmIntent);
+ } finally {
+ Binder.restoreCallingIdentity(callingToken);
+ }
}
@Override // Binder call
public void cancelPullingAlarms() {
- enforceCallingPermission();
- if (DEBUG)
- Slog.d(TAG, "Cancelling pulling alarm");
- final long callingToken = Binder.clearCallingIdentity();
- try {
- mAlarmManager.cancel(mPullingAlarmIntent);
- } finally {
- Binder.restoreCallingIdentity(callingToken);
- }
+ enforceCallingPermission();
+ if (DEBUG)
+ Slog.d(TAG, "Cancelling pulling alarm");
+ final long callingToken = Binder.clearCallingIdentity();
+ try {
+ mAlarmManager.cancel(mPullingAlarmIntent);
+ } finally {
+ Binder.restoreCallingIdentity(callingToken);
+ }
}
private void addNetworkStats(
- int tag, List<StatsLogEventWrapper> ret, NetworkStats stats, boolean withFGBG) {
- int size = stats.size();
- NetworkStats.Entry entry = new NetworkStats.Entry(); // For recycling
- for (int j = 0; j < size; j++) {
- stats.getValues(j, entry);
- StatsLogEventWrapper e = new StatsLogEventWrapper(tag, withFGBG ? 6 : 5);
- e.writeInt(entry.uid);
- if (withFGBG) {
- e.writeInt(entry.set);
+ int tag, List<StatsLogEventWrapper> ret, NetworkStats stats, boolean withFGBG) {
+ int size = stats.size();
+ NetworkStats.Entry entry = new NetworkStats.Entry(); // For recycling
+ for (int j = 0; j < size; j++) {
+ stats.getValues(j, entry);
+ StatsLogEventWrapper e = new StatsLogEventWrapper(tag, withFGBG ? 6 : 5);
+ e.writeInt(entry.uid);
+ if (withFGBG) {
+ e.writeInt(entry.set);
+ }
+ e.writeLong(entry.rxBytes);
+ e.writeLong(entry.rxPackets);
+ e.writeLong(entry.txBytes);
+ e.writeLong(entry.txPackets);
+ ret.add(e);
}
- e.writeLong(entry.rxBytes);
- e.writeLong(entry.rxPackets);
- e.writeLong(entry.txBytes);
- e.writeLong(entry.txPackets);
- ret.add(e);
- }
}
/**
@@ -491,220 +493,220 @@
}
private void pullKernelWakelock(int tagId, List<StatsLogEventWrapper> pulledData) {
- final KernelWakelockStats wakelockStats =
- mKernelWakelockReader.readKernelWakelockStats(mTmpWakelockStats);
- for (Map.Entry<String, KernelWakelockStats.Entry> ent : wakelockStats.entrySet()) {
- String name = ent.getKey();
- KernelWakelockStats.Entry kws = ent.getValue();
- StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, 4);
- e.writeString(name);
- e.writeInt(kws.mCount);
- e.writeInt(kws.mVersion);
- e.writeLong(kws.mTotalTime);
- pulledData.add(e);
- }
+ final KernelWakelockStats wakelockStats =
+ mKernelWakelockReader.readKernelWakelockStats(mTmpWakelockStats);
+ for (Map.Entry<String, KernelWakelockStats.Entry> ent : wakelockStats.entrySet()) {
+ String name = ent.getKey();
+ KernelWakelockStats.Entry kws = ent.getValue();
+ StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, 4);
+ e.writeString(name);
+ e.writeInt(kws.mCount);
+ e.writeInt(kws.mVersion);
+ e.writeLong(kws.mTotalTime);
+ pulledData.add(e);
+ }
}
private void pullWifiBytesTransfer(int tagId, List<StatsLogEventWrapper> pulledData) {
- long token = Binder.clearCallingIdentity();
- try {
- // TODO: Consider caching the following call to get BatteryStatsInternal.
- BatteryStatsInternal bs = LocalServices.getService(BatteryStatsInternal.class);
- String[] ifaces = bs.getWifiIfaces();
- if (ifaces.length == 0) {
- return;
+ long token = Binder.clearCallingIdentity();
+ try {
+ // TODO: Consider caching the following call to get BatteryStatsInternal.
+ BatteryStatsInternal bs = LocalServices.getService(BatteryStatsInternal.class);
+ String[] ifaces = bs.getWifiIfaces();
+ if (ifaces.length == 0) {
+ return;
+ }
+ NetworkStatsFactory nsf = new NetworkStatsFactory();
+ // Combine all the metrics per Uid into one record.
+ NetworkStats stats =
+ nsf.readNetworkStatsDetail(NetworkStats.UID_ALL, ifaces, NetworkStats.TAG_NONE, null)
+ .groupedByUid();
+ addNetworkStats(tagId, pulledData, stats, false);
+ } catch (java.io.IOException e) {
+ Slog.e(TAG, "Pulling netstats for wifi bytes has error", e);
+ } finally {
+ Binder.restoreCallingIdentity(token);
}
- NetworkStatsFactory nsf = new NetworkStatsFactory();
- // Combine all the metrics per Uid into one record.
- NetworkStats stats =
- nsf.readNetworkStatsDetail(NetworkStats.UID_ALL, ifaces, NetworkStats.TAG_NONE, null)
- .groupedByUid();
- addNetworkStats(tagId, pulledData, stats, false);
- } catch (java.io.IOException e) {
- Slog.e(TAG, "Pulling netstats for wifi bytes has error", e);
- } finally {
- Binder.restoreCallingIdentity(token);
- }
}
private void pullWifiBytesTransferByFgBg(int tagId, List<StatsLogEventWrapper> pulledData) {
- long token = Binder.clearCallingIdentity();
- try {
- BatteryStatsInternal bs = LocalServices.getService(BatteryStatsInternal.class);
- String[] ifaces = bs.getWifiIfaces();
- if (ifaces.length == 0) {
- return;
+ long token = Binder.clearCallingIdentity();
+ try {
+ BatteryStatsInternal bs = LocalServices.getService(BatteryStatsInternal.class);
+ String[] ifaces = bs.getWifiIfaces();
+ if (ifaces.length == 0) {
+ return;
+ }
+ NetworkStatsFactory nsf = new NetworkStatsFactory();
+ NetworkStats stats = rollupNetworkStatsByFGBG(
+ nsf.readNetworkStatsDetail(NetworkStats.UID_ALL, ifaces, NetworkStats.TAG_NONE, null));
+ addNetworkStats(tagId, pulledData, stats, true);
+ } catch (java.io.IOException e) {
+ Slog.e(TAG, "Pulling netstats for wifi bytes w/ fg/bg has error", e);
+ } finally {
+ Binder.restoreCallingIdentity(token);
}
- NetworkStatsFactory nsf = new NetworkStatsFactory();
- NetworkStats stats = rollupNetworkStatsByFGBG(
- nsf.readNetworkStatsDetail(NetworkStats.UID_ALL, ifaces, NetworkStats.TAG_NONE, null));
- addNetworkStats(tagId, pulledData, stats, true);
- } catch (java.io.IOException e) {
- Slog.e(TAG, "Pulling netstats for wifi bytes w/ fg/bg has error", e);
- } finally {
- Binder.restoreCallingIdentity(token);
- }
}
private void pullMobileBytesTransfer(int tagId, List<StatsLogEventWrapper> pulledData) {
- long token = Binder.clearCallingIdentity();
- try {
- BatteryStatsInternal bs = LocalServices.getService(BatteryStatsInternal.class);
- String[] ifaces = bs.getMobileIfaces();
- if (ifaces.length == 0) {
- return;
+ long token = Binder.clearCallingIdentity();
+ try {
+ BatteryStatsInternal bs = LocalServices.getService(BatteryStatsInternal.class);
+ String[] ifaces = bs.getMobileIfaces();
+ if (ifaces.length == 0) {
+ return;
+ }
+ NetworkStatsFactory nsf = new NetworkStatsFactory();
+ // Combine all the metrics per Uid into one record.
+ NetworkStats stats =
+ nsf.readNetworkStatsDetail(NetworkStats.UID_ALL, ifaces, NetworkStats.TAG_NONE, null)
+ .groupedByUid();
+ addNetworkStats(tagId, pulledData, stats, false);
+ } catch (java.io.IOException e) {
+ Slog.e(TAG, "Pulling netstats for mobile bytes has error", e);
+ } finally {
+ Binder.restoreCallingIdentity(token);
}
- NetworkStatsFactory nsf = new NetworkStatsFactory();
- // Combine all the metrics per Uid into one record.
- NetworkStats stats =
- nsf.readNetworkStatsDetail(NetworkStats.UID_ALL, ifaces, NetworkStats.TAG_NONE, null)
- .groupedByUid();
- addNetworkStats(tagId, pulledData, stats, false);
- } catch (java.io.IOException e) {
- Slog.e(TAG, "Pulling netstats for mobile bytes has error", e);
- } finally {
- Binder.restoreCallingIdentity(token);
- }
}
private void pullBluetoothBytesTransfer(int tagId, List<StatsLogEventWrapper> pulledData) {
- BluetoothActivityEnergyInfo info = pullBluetoothData();
- for (UidTraffic traffic : info.getUidTraffic()) {
- StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, 3);
- e.writeInt(traffic.getUid());
- e.writeLong(traffic.getRxBytes());
- e.writeLong(traffic.getTxBytes());
- pulledData.add(e);
- }
+ BluetoothActivityEnergyInfo info = pullBluetoothData();
+ for (UidTraffic traffic : info.getUidTraffic()) {
+ StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, 3);
+ e.writeInt(traffic.getUid());
+ e.writeLong(traffic.getRxBytes());
+ e.writeLong(traffic.getTxBytes());
+ pulledData.add(e);
+ }
}
private void pullMobileBytesTransferByFgBg(int tagId, List<StatsLogEventWrapper> pulledData) {
- long token = Binder.clearCallingIdentity();
- try {
- BatteryStatsInternal bs = LocalServices.getService(BatteryStatsInternal.class);
- String[] ifaces = bs.getMobileIfaces();
- if (ifaces.length == 0) {
- return;
+ long token = Binder.clearCallingIdentity();
+ try {
+ BatteryStatsInternal bs = LocalServices.getService(BatteryStatsInternal.class);
+ String[] ifaces = bs.getMobileIfaces();
+ if (ifaces.length == 0) {
+ return;
+ }
+ NetworkStatsFactory nsf = new NetworkStatsFactory();
+ NetworkStats stats = rollupNetworkStatsByFGBG(
+ nsf.readNetworkStatsDetail(NetworkStats.UID_ALL, ifaces, NetworkStats.TAG_NONE, null));
+ addNetworkStats(tagId, pulledData, stats, true);
+ } catch (java.io.IOException e) {
+ Slog.e(TAG, "Pulling netstats for mobile bytes w/ fg/bg has error", e);
+ } finally {
+ Binder.restoreCallingIdentity(token);
}
- NetworkStatsFactory nsf = new NetworkStatsFactory();
- NetworkStats stats = rollupNetworkStatsByFGBG(
- nsf.readNetworkStatsDetail(NetworkStats.UID_ALL, ifaces, NetworkStats.TAG_NONE, null));
- addNetworkStats(tagId, pulledData, stats, true);
- } catch (java.io.IOException e) {
- Slog.e(TAG, "Pulling netstats for mobile bytes w/ fg/bg has error", e);
- } finally {
- Binder.restoreCallingIdentity(token);
- }
}
private void pullCpuTimePerFreq(int tagId, List<StatsLogEventWrapper> pulledData) {
- for (int cluster = 0; cluster < mKernelCpuSpeedReaders.length; cluster++) {
- long[] clusterTimeMs = mKernelCpuSpeedReaders[cluster].readAbsolute();
- if (clusterTimeMs != null) {
- for (int speed = clusterTimeMs.length - 1; speed >= 0; --speed) {
- StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, 3);
- e.writeInt(cluster);
- e.writeInt(speed);
- e.writeLong(clusterTimeMs[speed]);
- pulledData.add(e);
- }
+ for (int cluster = 0; cluster < mKernelCpuSpeedReaders.length; cluster++) {
+ long[] clusterTimeMs = mKernelCpuSpeedReaders[cluster].readAbsolute();
+ if (clusterTimeMs != null) {
+ for (int speed = clusterTimeMs.length - 1; speed >= 0; --speed) {
+ StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, 3);
+ e.writeInt(cluster);
+ e.writeInt(speed);
+ e.writeLong(clusterTimeMs[speed]);
+ pulledData.add(e);
+ }
+ }
}
- }
}
private void pullWifiActivityEnergyInfo(int tagId, List<StatsLogEventWrapper> pulledData) {
- long token = Binder.clearCallingIdentity();
- if (mWifiManager == null) {
- mWifiManager =
- IWifiManager.Stub.asInterface(ServiceManager.getService(Context.WIFI_SERVICE));
- }
- if (mWifiManager != null) {
- try {
- SynchronousResultReceiver wifiReceiver = new SynchronousResultReceiver("wifi");
- mWifiManager.requestActivityInfo(wifiReceiver);
- final WifiActivityEnergyInfo wifiInfo = awaitControllerInfo(wifiReceiver);
- StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, 6);
- e.writeLong(wifiInfo.getTimeStamp());
- e.writeInt(wifiInfo.getStackState());
- e.writeLong(wifiInfo.getControllerTxTimeMillis());
- e.writeLong(wifiInfo.getControllerRxTimeMillis());
- e.writeLong(wifiInfo.getControllerIdleTimeMillis());
- e.writeLong(wifiInfo.getControllerEnergyUsed());
- pulledData.add(e);
- } catch (RemoteException e) {
- Slog.e(TAG, "Pulling wifiManager for wifi controller activity energy info has error", e);
- } finally {
- Binder.restoreCallingIdentity(token);
+ long token = Binder.clearCallingIdentity();
+ if (mWifiManager == null) {
+ mWifiManager =
+ IWifiManager.Stub.asInterface(ServiceManager.getService(Context.WIFI_SERVICE));
}
- }
+ if (mWifiManager != null) {
+ try {
+ SynchronousResultReceiver wifiReceiver = new SynchronousResultReceiver("wifi");
+ mWifiManager.requestActivityInfo(wifiReceiver);
+ final WifiActivityEnergyInfo wifiInfo = awaitControllerInfo(wifiReceiver);
+ StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, 6);
+ e.writeLong(wifiInfo.getTimeStamp());
+ e.writeInt(wifiInfo.getStackState());
+ e.writeLong(wifiInfo.getControllerTxTimeMillis());
+ e.writeLong(wifiInfo.getControllerRxTimeMillis());
+ e.writeLong(wifiInfo.getControllerIdleTimeMillis());
+ e.writeLong(wifiInfo.getControllerEnergyUsed());
+ pulledData.add(e);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Pulling wifiManager for wifi controller activity energy info has error", e);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
}
private void pullModemActivityInfo(int tagId, List<StatsLogEventWrapper> pulledData) {
- long token = Binder.clearCallingIdentity();
- if (mTelephony == null) {
- mTelephony = TelephonyManager.from(mContext);
- }
- if (mTelephony != null) {
- SynchronousResultReceiver modemReceiver = new SynchronousResultReceiver("telephony");
- mTelephony.requestModemActivityInfo(modemReceiver);
- final ModemActivityInfo modemInfo = awaitControllerInfo(modemReceiver);
- StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, 6);
- e.writeLong(modemInfo.getTimestamp());
- e.writeLong(modemInfo.getSleepTimeMillis());
- e.writeLong(modemInfo.getIdleTimeMillis());
- e.writeLong(modemInfo.getTxTimeMillis()[0]);
- e.writeLong(modemInfo.getTxTimeMillis()[1]);
- e.writeLong(modemInfo.getTxTimeMillis()[2]);
- e.writeLong(modemInfo.getTxTimeMillis()[3]);
- e.writeLong(modemInfo.getTxTimeMillis()[4]);
- e.writeLong(modemInfo.getRxTimeMillis());
- e.writeLong(modemInfo.getEnergyUsed());
- pulledData.add(e);
- }
+ long token = Binder.clearCallingIdentity();
+ if (mTelephony == null) {
+ mTelephony = TelephonyManager.from(mContext);
+ }
+ if (mTelephony != null) {
+ SynchronousResultReceiver modemReceiver = new SynchronousResultReceiver("telephony");
+ mTelephony.requestModemActivityInfo(modemReceiver);
+ final ModemActivityInfo modemInfo = awaitControllerInfo(modemReceiver);
+ StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, 6);
+ e.writeLong(modemInfo.getTimestamp());
+ e.writeLong(modemInfo.getSleepTimeMillis());
+ e.writeLong(modemInfo.getIdleTimeMillis());
+ e.writeLong(modemInfo.getTxTimeMillis()[0]);
+ e.writeLong(modemInfo.getTxTimeMillis()[1]);
+ e.writeLong(modemInfo.getTxTimeMillis()[2]);
+ e.writeLong(modemInfo.getTxTimeMillis()[3]);
+ e.writeLong(modemInfo.getTxTimeMillis()[4]);
+ e.writeLong(modemInfo.getRxTimeMillis());
+ e.writeLong(modemInfo.getEnergyUsed());
+ pulledData.add(e);
+ }
}
private void pullBluetoothActivityInfo(int tagId, List<StatsLogEventWrapper> pulledData) {
- BluetoothActivityEnergyInfo info = pullBluetoothData();
- StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, 6);
- e.writeLong(info.getTimeStamp());
- e.writeInt(info.getBluetoothStackState());
- e.writeLong(info.getControllerTxTimeMillis());
- e.writeLong(info.getControllerRxTimeMillis());
- e.writeLong(info.getControllerIdleTimeMillis());
- e.writeLong(info.getControllerEnergyUsed());
- pulledData.add(e);
+ BluetoothActivityEnergyInfo info = pullBluetoothData();
+ StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, 6);
+ e.writeLong(info.getTimeStamp());
+ e.writeInt(info.getBluetoothStackState());
+ e.writeLong(info.getControllerTxTimeMillis());
+ e.writeLong(info.getControllerRxTimeMillis());
+ e.writeLong(info.getControllerIdleTimeMillis());
+ e.writeLong(info.getControllerEnergyUsed());
+ pulledData.add(e);
}
private synchronized BluetoothActivityEnergyInfo pullBluetoothData() {
final BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
- if (adapter != null) {
- SynchronousResultReceiver bluetoothReceiver = new SynchronousResultReceiver("bluetooth");
- adapter.requestControllerActivityEnergyInfo(bluetoothReceiver);
- return awaitControllerInfo(bluetoothReceiver);
- } else {
- Slog.e(TAG, "Failed to get bluetooth adapter!");
- return null;
- }
+ if (adapter != null) {
+ SynchronousResultReceiver bluetoothReceiver = new SynchronousResultReceiver("bluetooth");
+ adapter.requestControllerActivityEnergyInfo(bluetoothReceiver);
+ return awaitControllerInfo(bluetoothReceiver);
+ } else {
+ Slog.e(TAG, "Failed to get bluetooth adapter!");
+ return null;
+ }
}
private void pullSystemElapsedRealtime(int tagId, List<StatsLogEventWrapper> pulledData) {
- StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, 1);
- e.writeLong(SystemClock.elapsedRealtime());
- pulledData.add(e);
+ StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, 1);
+ e.writeLong(SystemClock.elapsedRealtime());
+ pulledData.add(e);
}
private void pullDiskSpace(int tagId, List<StatsLogEventWrapper> pulledData) {
- StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, 3);
- e.writeLong(mStatFsData.getAvailableBytes());
- e.writeLong(mStatFsSystem.getAvailableBytes());
- e.writeLong(mStatFsTemp.getAvailableBytes());
- pulledData.add(e);
+ StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, 3);
+ e.writeLong(mStatFsData.getAvailableBytes());
+ e.writeLong(mStatFsSystem.getAvailableBytes());
+ e.writeLong(mStatFsTemp.getAvailableBytes());
+ pulledData.add(e);
}
private void pullSystemUpTime(int tagId, List<StatsLogEventWrapper> pulledData) {
- StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, 1);
- e.writeLong(SystemClock.uptimeMillis());
- pulledData.add(e);
+ StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, 1);
+ e.writeLong(SystemClock.uptimeMillis());
+ pulledData.add(e);
}
/**
@@ -718,56 +720,56 @@
List<StatsLogEventWrapper> ret = new ArrayList();
switch (tagId) {
case StatsLog.WIFI_BYTES_TRANSFER: {
- pullWifiBytesTransfer(tagId, ret);
- break;
+ pullWifiBytesTransfer(tagId, ret);
+ break;
}
case StatsLog.MOBILE_BYTES_TRANSFER: {
- pullMobileBytesTransfer(tagId, ret);
- break;
+ pullMobileBytesTransfer(tagId, ret);
+ break;
}
case StatsLog.WIFI_BYTES_TRANSFER_BY_FG_BG: {
- pullWifiBytesTransferByFgBg(tagId, ret);
- break;
+ pullWifiBytesTransferByFgBg(tagId, ret);
+ break;
}
case StatsLog.MOBILE_BYTES_TRANSFER_BY_FG_BG: {
- pullMobileBytesTransferByFgBg(tagId, ret);
- break;
+ pullMobileBytesTransferByFgBg(tagId, ret);
+ break;
}
case StatsLog.BLUETOOTH_BYTES_TRANSFER: {
- pullBluetoothBytesTransfer(tagId, ret);
- break;
+ pullBluetoothBytesTransfer(tagId, ret);
+ break;
}
case StatsLog.KERNEL_WAKELOCK: {
- pullKernelWakelock(tagId, ret);
- break;
+ pullKernelWakelock(tagId, ret);
+ break;
}
case StatsLog.CPU_TIME_PER_FREQ: {
- pullCpuTimePerFreq(tagId, ret);
- break;
+ pullCpuTimePerFreq(tagId, ret);
+ break;
}
case StatsLog.WIFI_ACTIVITY_ENERGY_INFO: {
- pullWifiActivityEnergyInfo(tagId, ret);
- break;
+ pullWifiActivityEnergyInfo(tagId, ret);
+ break;
}
case StatsLog.MODEM_ACTIVITY_INFO: {
- pullModemActivityInfo(tagId, ret);
- break;
+ pullModemActivityInfo(tagId, ret);
+ break;
}
case StatsLog.BLUETOOTH_ACTIVITY_INFO: {
- pullBluetoothActivityInfo(tagId, ret);
- break;
+ pullBluetoothActivityInfo(tagId, ret);
+ break;
}
case StatsLog.SYSTEM_UPTIME: {
- pullSystemUpTime(tagId, ret);
- break;
+ pullSystemUpTime(tagId, ret);
+ break;
}
case StatsLog.SYSTEM_ELAPSED_REALTIME: {
- pullSystemElapsedRealtime(tagId, ret);
- break;
+ pullSystemElapsedRealtime(tagId, ret);
+ break;
}
case StatsLog.DISK_SPACE: {
- pullDiskSpace(tagId, ret);
- break;
+ pullDiskSpace(tagId, ret);
+ break;
}
default:
Slog.w(TAG, "No such tagId data as " + tagId);
@@ -788,14 +790,14 @@
@Override
public void triggerUidSnapshot() {
- enforceCallingPermission();
- synchronized (sStatsdLock) {
- try {
- informAllUidsLocked(mContext);
- } catch (RemoteException e) {
- Slog.e(TAG, "Failed to trigger uid snapshot.", e);
+ enforceCallingPermission();
+ synchronized (sStatsdLock) {
+ try {
+ informAllUidsLocked(mContext);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to trigger uid snapshot.", e);
+ }
}
- }
}
private void enforceCallingPermission() {
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
index 7c170ae..343fb91 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
@@ -321,7 +321,7 @@
public void showChargingAnimation(int batteryLevel) {
if (mBar != null) {
try {
- mBar.showChargingAnimation(batteryLevel);
+ mBar.showWirelessChargingAnimation(batteryLevel);
} catch (RemoteException ex){
}
}
diff --git a/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java b/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java
index 853c7eb..0ac853b 100644
--- a/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java
+++ b/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java
@@ -22,7 +22,6 @@
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Binder;
-import android.os.Handler;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Slog;
@@ -43,7 +42,6 @@
import java.util.LinkedList;
import java.util.Queue;
import java.util.concurrent.Callable;
-import java.util.concurrent.TimeUnit;
/**
* A manager for TextClassifier services.
@@ -54,9 +52,6 @@
private static final String LOG_TAG = "TextClassificationManagerService";
- // How long after the last interaction with the service we would unbind
- private static final long TIMEOUT_IDLE_BIND_MILLIS = TimeUnit.MINUTES.toMillis(1);
-
public static final class Lifecycle extends SystemService {
private final TextClassificationManagerService mManagerService;
@@ -79,10 +74,8 @@
}
private final Context mContext;
- private final Handler mHandler;
private final Intent mServiceIntent;
private final ServiceConnection mConnection;
- private final Runnable mUnbind;
private final Object mLock;
@GuardedBy("mLock")
private final Queue<PendingRequest> mPendingRequests;
@@ -94,7 +87,6 @@
private TextClassificationManagerService(Context context) {
mContext = Preconditions.checkNotNull(context);
- mHandler = new Handler();
mServiceIntent = new Intent(TextClassifierService.SERVICE_INTERFACE)
.setComponent(TextClassifierService.getServiceComponentName(mContext));
mConnection = new ServiceConnection() {
@@ -131,7 +123,6 @@
}
};
mPendingRequests = new LinkedList<>();
- mUnbind = this::unbind;
mLock = new Object();
}
@@ -152,7 +143,6 @@
if (isBoundLocked()) {
mService.onSuggestSelection(
text, selectionStartIndex, selectionEndIndex, options, callback);
- scheduleUnbindLocked();
} else {
final Callable<Void> request = () -> {
onSuggestSelection(
@@ -184,7 +174,6 @@
synchronized (mLock) {
if (isBoundLocked()) {
mService.onClassifyText(text, startIndex, endIndex, options, callback);
- scheduleUnbindLocked();
} else {
final Callable<Void> request = () -> {
onClassifyText(text, startIndex, endIndex, options, callback);
@@ -213,7 +202,6 @@
synchronized (mLock) {
if (isBoundLocked()) {
mService.onGenerateLinks(text, options, callback);
- scheduleUnbindLocked();
} else {
final Callable<Void> request = () -> {
onGenerateLinks(text, options, callback);
@@ -270,27 +258,6 @@
mBinding = binding;
}
- private void unbind() {
- synchronized (mLock) {
- if (!isBoundLocked()) {
- return;
- }
-
- Slog.d(LOG_TAG, "Unbinding from " + mServiceIntent.getComponent());
- mContext.unbindService(mConnection);
-
- synchronized (mLock) {
- mService = null;
- }
- }
- }
-
- @GuardedBy("mLock")
- private void scheduleUnbindLocked() {
- mHandler.removeCallbacks(mUnbind);
- mHandler.postDelayed(mUnbind, TIMEOUT_IDLE_BIND_MILLIS);
- }
-
@GuardedBy("mLock")
private void enqueueRequestLocked(
Callable<Void> request, Callable<Void> onServiceFailure, IBinder binder) {
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 2512dbd..41a6e2b 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -725,8 +725,9 @@
* wallpaper windows in the window list.
*/
DisplayContent(Display display, WindowManagerService service,
- WallpaperController wallpaperController) {
+ WallpaperController wallpaperController, DisplayWindowController controller) {
super(service);
+ setController(controller);
if (service.mRoot.getDisplayContent(display.getDisplayId()) != null) {
throw new IllegalArgumentException("Display with ID=" + display.getDisplayId()
+ " already exists=" + service.mRoot.getDisplayContent(display.getDisplayId())
@@ -1941,6 +1942,8 @@
} finally {
mRemovingDisplay = false;
}
+
+ mService.onDisplayRemoved(mDisplayId);
}
/** Returns true if a removal action is still being deferred. */
@@ -1950,7 +1953,6 @@
if (!stillDeferringRemoval && mDeferredRemoval) {
removeImmediately();
- mService.onDisplayRemoved(mDisplayId);
return false;
}
return true;
diff --git a/services/core/java/com/android/server/wm/DisplayWindowController.java b/services/core/java/com/android/server/wm/DisplayWindowController.java
index ad4957e..0e12838 100644
--- a/services/core/java/com/android/server/wm/DisplayWindowController.java
+++ b/services/core/java/com/android/server/wm/DisplayWindowController.java
@@ -16,11 +16,14 @@
package com.android.server.wm;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_DISPLAY;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STACK;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
import android.content.res.Configuration;
+import android.os.Binder;
import android.util.Slog;
+import android.view.Display;
/**
* Controller for the display container. This is created by activity manager to link activity
@@ -36,9 +39,16 @@
mDisplayId = displayId;
synchronized (mWindowMap) {
- // TODO: Convert to setContainer() from DisplayContent once everything is hooked up.
- // Currently we are not setup to register for config changes.
- mContainer = mRoot.getDisplayContentOrCreate(displayId);
+ final Display display = mService.mDisplayManager.getDisplay(displayId);
+ if (display != null) {
+ final long callingIdentity = Binder.clearCallingIdentity();
+ try {
+ mRoot.createDisplayContent(display, this /* controller */);
+ } finally {
+ Binder.restoreCallingIdentity(callingIdentity);
+ }
+ }
+
if (mContainer == null) {
throw new IllegalArgumentException("Trying to add displayId=" + displayId);
}
@@ -47,14 +57,22 @@
@Override
public void removeContainer() {
- // TODO: Pipe through from ActivityDisplay to remove the display
- throw new UnsupportedOperationException("To be implemented");
+ synchronized (mWindowMap) {
+ if(mContainer == null) {
+ if (DEBUG_DISPLAY) Slog.i(TAG_WM, "removeDisplay: could not find displayId="
+ + mDisplayId);
+ return;
+ }
+ mContainer.removeIfPossible();
+ super.removeContainer();
+ }
}
@Override
public void onOverrideConfigurationChanged(Configuration overrideConfiguration) {
- // TODO: Pipe through from ActivityDisplay to update the configuration for the display
- throw new UnsupportedOperationException("To be implemented");
+ // TODO: The container receives override configuration changes through other means. enabling
+ // callbacks through the controller causes layout issues. Investigate consolidating
+ // override configuration propagation to just here.
}
/**
diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java
index fe5b65c..e869f58 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimationController.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java
@@ -62,6 +62,7 @@
// The recents component app token that is shown behind the visibile tasks
private AppWindowToken mHomeAppToken;
+ private Rect mMinimizedHomeBounds = new Rect();
// We start the RecentsAnimationController in a pending-start state since we need to wait for
// the wallpaper/activity to draw before we can give control to the handler to start animating
@@ -105,7 +106,7 @@
final AppWindowToken topChild = task.getTopChild();
final WindowState mainWindow = topChild.findMainWindow();
return new TaskSnapshot(buffer, topChild.getConfiguration().orientation,
- mainWindow.mStableInsets,
+ mainWindow.mContentInsets,
ActivityManager.isLowRamDeviceStatic() /* reduced */,
1.0f /* scale */);
}
@@ -163,8 +164,6 @@
* @param remoteAnimationRunner The remote runner which should be notified when the animation is
* ready to start or has been canceled
* @param callbacks Callbacks to be made when the animation finishes
- * @param restoreHomeBehindStackId The stack id to restore the home stack behind once the
- * animation is complete. Will be passed to the callback.
*/
RecentsAnimationController(WindowManagerService service,
IRecentsAnimationRunner remoteAnimationRunner, RecentsAnimationCallbacks callbacks,
@@ -200,13 +199,15 @@
if (recentsComponentAppToken != null) {
if (DEBUG) Log.d(TAG, "setHomeApp(" + recentsComponentAppToken.getName() + ")");
mHomeAppToken = recentsComponentAppToken;
- final WallpaperController wc = dc.mWallpaperController;
if (recentsComponentAppToken.windowsCanBeWallpaperTarget()) {
dc.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
dc.setLayoutNeeded();
}
}
+ // Save the minimized home height
+ dc.getDockedDividerController().getHomeStackBoundsInDockedMode(mMinimizedHomeBounds);
+
mService.mWindowPlacerLocked.performSurfacePlacement();
}
@@ -232,7 +233,15 @@
appAnimations[i] = mPendingAnimations.get(i).createRemoteAnimationApp();
}
mPendingStart = false;
- mRunner.onAnimationStart(mController, appAnimations);
+
+ final Rect minimizedHomeBounds =
+ mHomeAppToken != null && mHomeAppToken.inSplitScreenSecondaryWindowingMode()
+ ? mMinimizedHomeBounds : null;
+ final Rect contentInsets =
+ mHomeAppToken != null && mHomeAppToken.findMainWindow() != null
+ ? mHomeAppToken.findMainWindow().mContentInsets : null;
+ mRunner.onAnimationStart_New(mController, appAnimations, contentInsets,
+ minimizedHomeBounds);
} catch (RemoteException e) {
Slog.e(TAG, "Failed to start recents animation", e);
}
@@ -334,11 +343,15 @@
}
RemoteAnimationTarget createRemoteAnimationApp() {
- // TODO: Do we need position and stack bounds?
+ final Point position = new Point();
+ final Rect bounds = new Rect();
+ final WindowContainer container = mTask.getParent();
+ container.getRelativePosition(position);
+ container.getBounds(bounds);
+ final WindowState mainWindow = mTask.getTopVisibleAppMainWindow();
return new RemoteAnimationTarget(mTask.mTaskId, MODE_CLOSING, mCapturedLeash,
- !mTask.fillsParent(),
- mTask.getTopVisibleAppMainWindow().mWinAnimator.mLastClipRect,
- mTask.getPrefixOrderIndex(), new Point(), new Rect(),
+ !mTask.fillsParent(), mainWindow.mWinAnimator.mLastClipRect,
+ mainWindow.mContentInsets, mTask.getPrefixOrderIndex(), position, bounds,
mTask.getWindowConfiguration());
}
diff --git a/services/core/java/com/android/server/wm/RemoteAnimationController.java b/services/core/java/com/android/server/wm/RemoteAnimationController.java
index 9251993..c353c1d 100644
--- a/services/core/java/com/android/server/wm/RemoteAnimationController.java
+++ b/services/core/java/com/android/server/wm/RemoteAnimationController.java
@@ -166,7 +166,7 @@
}
return new RemoteAnimationTarget(task.mTaskId, getMode(),
mCapturedLeash, !mAppWindowToken.fillsParent(),
- mainWindow.mWinAnimator.mLastClipRect,
+ mainWindow.mWinAnimator.mLastClipRect, mainWindow.mContentInsets,
mAppWindowToken.getPrefixOrderIndex(), mPosition, mStackBounds,
task.getWindowConfiguration());
}
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index c535fe5..f5760e5 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -193,30 +193,6 @@
}
}
- /**
- * Retrieve the DisplayContent for the specified displayId. Will create a new DisplayContent if
- * there is a Display for the displayId.
- *
- * @param displayId The display the caller is interested in.
- * @return The DisplayContent associated with displayId or null if there is no Display for it.
- */
- DisplayContent getDisplayContentOrCreate(int displayId) {
- DisplayContent dc = getDisplayContent(displayId);
-
- if (dc == null) {
- final Display display = mService.mDisplayManager.getDisplay(displayId);
- if (display != null) {
- final long callingIdentity = Binder.clearCallingIdentity();
- try {
- dc = createDisplayContent(display);
- } finally {
- Binder.restoreCallingIdentity(callingIdentity);
- }
- }
- }
- return dc;
- }
-
DisplayContent getDisplayContent(int displayId) {
for (int i = mChildren.size() - 1; i >= 0; --i) {
final DisplayContent current = mChildren.get(i);
@@ -227,9 +203,9 @@
return null;
}
- private DisplayContent createDisplayContent(final Display display) {
- final DisplayContent dc = new DisplayContent(display, mService,
- mWallpaperController);
+ DisplayContent createDisplayContent(final Display display, DisplayWindowController controller) {
+ final DisplayContent dc =
+ new DisplayContent(display, mService, mWallpaperController, controller);
final int displayId = display.getDisplayId();
if (DEBUG_DISPLAY) Slog.v(TAG_WM, "Adding display=" + display);
@@ -748,19 +724,6 @@
(mSustainedPerformanceModeEnabled ? 1 : 0));
}
- if (mService.mTurnOnScreen) {
- if (mService.mAllowTheaterModeWakeFromLayout
- || Settings.Global.getInt(mService.mContext.getContentResolver(),
- Settings.Global.THEATER_MODE_ON, 0) == 0) {
- if (DEBUG_VISIBILITY || DEBUG_POWER) {
- Slog.v(TAG, "Turning screen on after layout!");
- }
- mService.mPowerManager.wakeUp(SystemClock.uptimeMillis(),
- "android.server.wm:TURN_ON");
- }
- mService.mTurnOnScreen = false;
- }
-
if (mUpdateRotation) {
if (DEBUG_ORIENTATION) Slog.d(TAG, "Performing post-rotate rotation");
// TODO(multi-display): Update rotation for different displays separately.
diff --git a/services/core/java/com/android/server/wm/SurfaceAnimationRunner.java b/services/core/java/com/android/server/wm/SurfaceAnimationRunner.java
index dc62cc8..1b2f954 100644
--- a/services/core/java/com/android/server/wm/SurfaceAnimationRunner.java
+++ b/services/core/java/com/android/server/wm/SurfaceAnimationRunner.java
@@ -151,6 +151,7 @@
}
}
+ @GuardedBy("mLock")
private void startPendingAnimationsLocked() {
for (int i = mPendingAnimations.size() - 1; i >= 0; i--) {
startAnimationLocked(mPendingAnimations.valueAt(i));
@@ -158,6 +159,7 @@
mPendingAnimations.clear();
}
+ @GuardedBy("mLock")
private void startAnimationLocked(RunningAnimation a) {
final ValueAnimator anim = mAnimatorFactory.makeAnimator();
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotPersister.java b/services/core/java/com/android/server/wm/TaskSnapshotPersister.java
index 7b047a8..621bee7 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotPersister.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotPersister.java
@@ -281,11 +281,13 @@
mSnapshot = snapshot;
}
+ @GuardedBy("mLock")
@Override
void onQueuedLocked() {
mStoreQueueItems.offer(this);
}
+ @GuardedBy("mLock")
@Override
void onDequeuedLocked() {
mStoreQueueItems.remove(this);
diff --git a/services/core/java/com/android/server/wm/WindowAnimator.java b/services/core/java/com/android/server/wm/WindowAnimator.java
index cec13ab..49a30d5 100644
--- a/services/core/java/com/android/server/wm/WindowAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowAnimator.java
@@ -161,7 +161,7 @@
final int numDisplays = mDisplayContentsAnimators.size();
for (int i = 0; i < numDisplays; i++) {
final int displayId = mDisplayContentsAnimators.keyAt(i);
- final DisplayContent dc = mService.mRoot.getDisplayContentOrCreate(displayId);
+ final DisplayContent dc = mService.mRoot.getDisplayContent(displayId);
DisplayContentsAnimator displayAnimator = mDisplayContentsAnimators.valueAt(i);
final ScreenRotationAnimation screenRotationAnimation =
@@ -195,7 +195,7 @@
for (int i = 0; i < numDisplays; i++) {
final int displayId = mDisplayContentsAnimators.keyAt(i);
- final DisplayContent dc = mService.mRoot.getDisplayContentOrCreate(displayId);
+ final DisplayContent dc = mService.mRoot.getDisplayContent(displayId);
dc.checkAppWindowsReadyToShow();
@@ -228,7 +228,7 @@
final int numDisplays = mDisplayContentsAnimators.size();
for (int i = 0; i < numDisplays; i++) {
final int displayId = mDisplayContentsAnimators.keyAt(i);
- final DisplayContent dc = mService.mRoot.getDisplayContentOrCreate(displayId);
+ final DisplayContent dc = mService.mRoot.getDisplayContent(displayId);
dc.onPendingTransactionApplied();
}
@@ -305,7 +305,7 @@
pw.println(":");
final DisplayContentsAnimator displayAnimator = mDisplayContentsAnimators.valueAt(i);
final DisplayContent dc =
- mService.mRoot.getDisplayContentOrCreate(mDisplayContentsAnimators.keyAt(i));
+ mService.mRoot.getDisplayContent(mDisplayContentsAnimators.keyAt(i));
dc.dumpWindowAnimators(pw, subPrefix);
if (displayAnimator.mScreenRotationAnimation != null) {
pw.print(subPrefix); pw.println("mScreenRotationAnimation:");
@@ -339,7 +339,7 @@
if (displayId < 0) {
return 0;
}
- final DisplayContent displayContent = mService.mRoot.getDisplayContentOrCreate(displayId);
+ final DisplayContent displayContent = mService.mRoot.getDisplayContent(displayId);
return (displayContent != null) ? displayContent.pendingLayoutChanges : 0;
}
@@ -347,7 +347,7 @@
if (displayId < 0) {
return;
}
- final DisplayContent displayContent = mService.mRoot.getDisplayContentOrCreate(displayId);
+ final DisplayContent displayContent = mService.mRoot.getDisplayContent(displayId);
if (displayContent != null) {
displayContent.pendingLayoutChanges |= changes;
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 676fb9f..c2ed2ae 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -737,7 +737,6 @@
final InputManagerService mInputManager;
final DisplayManagerInternal mDisplayManagerInternal;
final DisplayManager mDisplayManager;
- private final Display[] mDisplays;
// Indicates whether this device supports wide color gamut rendering
private boolean mHasWideColorGamutSupport;
@@ -746,8 +745,6 @@
private Session mHoldingScreenOn;
private PowerManager.WakeLock mHoldingScreenWakeLock;
- boolean mTurnOnScreen;
-
// Whether or not a layout can cause a wake up when theater mode is enabled.
boolean mAllowTheaterModeWakeFromLayout;
@@ -915,7 +912,6 @@
@Override
public void run() {
WindowManagerPolicyThread.set(Thread.currentThread(), Looper.myLooper());
-
mPolicy.init(mContext, WindowManagerService.this, WindowManagerService.this);
}
}, 0);
@@ -974,10 +970,6 @@
}
mDisplayManager = (DisplayManager)context.getSystemService(Context.DISPLAY_SERVICE);
- mDisplays = mDisplayManager.getDisplays();
- for (Display display : mDisplays) {
- createDisplayContentLocked(display);
- }
mKeyguardDisableHandler = new KeyguardDisableHandler(mContext, mPolicy);
@@ -1066,6 +1058,13 @@
mDragDropController = new DragDropController(this, mH.getLooper());
LocalServices.addService(WindowManagerInternal.class, new LocalService());
+ }
+
+ /**
+ * Called after all entities (such as the {@link ActivityManagerService}) have been set up and
+ * associated with the {@link WindowManagerService}.
+ */
+ public void onInitReady() {
initPolicy();
// Add ourself to the Watchdog monitors.
@@ -1081,6 +1080,7 @@
showEmulatorDisplayOverlayIfNeeded();
}
+
public InputMonitor getInputMonitor() {
return mInputMonitor;
}
@@ -1131,7 +1131,7 @@
throw new IllegalStateException("Display has not been initialialized");
}
- final DisplayContent displayContent = mRoot.getDisplayContentOrCreate(displayId);
+ final DisplayContent displayContent = mRoot.getDisplayContent(displayId);
if (displayContent == null) {
Slog.w(TAG_WM, "Attempted to add window to a display that does not exist: "
+ displayId + ". Aborting.");
@@ -2277,7 +2277,7 @@
}
synchronized(mWindowMap) {
- final DisplayContent dc = mRoot.getDisplayContentOrCreate(displayId);
+ final DisplayContent dc = mRoot.getDisplayContent(displayId);
WindowToken token = dc.getWindowToken(binder);
if (token != null) {
Slog.w(TAG_WM, "addWindowToken: Attempted to add binder token: " + binder
@@ -3666,7 +3666,7 @@
boolean wallpaperOnly) {
final DisplayContent displayContent;
synchronized(mWindowMap) {
- displayContent = mRoot.getDisplayContentOrCreate(displayId);
+ displayContent = mRoot.getDisplayContent(displayId);
if (displayContent == null) {
if (DEBUG_SCREENSHOT) Slog.i(TAG_WM, "Screenshot returning null. No Display for "
+ "displayId=" + displayId);
@@ -3885,7 +3885,7 @@
public boolean registerWallpaperVisibilityListener(IWallpaperVisibilityListener listener,
int displayId) {
synchronized (mWindowMap) {
- final DisplayContent displayContent = mRoot.getDisplayContentOrCreate(displayId);
+ final DisplayContent displayContent = mRoot.getDisplayContent(displayId);
if (displayContent == null) {
throw new IllegalArgumentException("Trying to register visibility event "
+ "for invalid display: " + displayId);
@@ -4442,10 +4442,13 @@
}
public void displayReady() {
- for (Display display : mDisplays) {
+ final int displayCount = mRoot.mChildren.size();
+ for (int i = 0; i < displayCount; ++i) {
+ final DisplayContent display = mRoot.mChildren.get(i);
displayReady(display.getDisplayId());
}
+
synchronized(mWindowMap) {
final DisplayContent displayContent = getDefaultDisplayContentLocked();
if (mMaxUiWidth > 0) {
@@ -4476,7 +4479,7 @@
private void displayReady(int displayId) {
synchronized(mWindowMap) {
- final DisplayContent displayContent = mRoot.getDisplayContentOrCreate(displayId);
+ final DisplayContent displayContent = mRoot.getDisplayContent(displayId);
if (displayContent != null) {
mAnimator.addDisplayLocked(displayId);
displayContent.initializeDisplayBaseInfo();
@@ -5025,7 +5028,7 @@
@Override
public void getInitialDisplaySize(int displayId, Point size) {
synchronized (mWindowMap) {
- final DisplayContent displayContent = mRoot.getDisplayContentOrCreate(displayId);
+ final DisplayContent displayContent = mRoot.getDisplayContent(displayId);
if (displayContent != null && displayContent.hasAccess(Binder.getCallingUid())) {
size.x = displayContent.mInitialDisplayWidth;
size.y = displayContent.mInitialDisplayHeight;
@@ -5036,7 +5039,7 @@
@Override
public void getBaseDisplaySize(int displayId, Point size) {
synchronized (mWindowMap) {
- final DisplayContent displayContent = mRoot.getDisplayContentOrCreate(displayId);
+ final DisplayContent displayContent = mRoot.getDisplayContent(displayId);
if (displayContent != null && displayContent.hasAccess(Binder.getCallingUid())) {
size.x = displayContent.mBaseDisplayWidth;
size.y = displayContent.mBaseDisplayHeight;
@@ -5063,7 +5066,7 @@
final int MIN_WIDTH = 200;
final int MIN_HEIGHT = 200;
final int MAX_SCALE = 2;
- final DisplayContent displayContent = mRoot.getDisplayContentOrCreate(displayId);
+ final DisplayContent displayContent = mRoot.getDisplayContent(displayId);
if (displayContent != null) {
width = Math.min(Math.max(width, MIN_WIDTH),
displayContent.mInitialDisplayWidth * MAX_SCALE);
@@ -5093,7 +5096,7 @@
final long ident = Binder.clearCallingIdentity();
try {
synchronized(mWindowMap) {
- final DisplayContent displayContent = mRoot.getDisplayContentOrCreate(displayId);
+ final DisplayContent displayContent = mRoot.getDisplayContent(displayId);
if (displayContent != null) {
if (mode < 0 || mode > 1) {
mode = 0;
@@ -5175,7 +5178,7 @@
final long ident = Binder.clearCallingIdentity();
try {
synchronized(mWindowMap) {
- final DisplayContent displayContent = mRoot.getDisplayContentOrCreate(displayId);
+ final DisplayContent displayContent = mRoot.getDisplayContent(displayId);
if (displayContent != null) {
setForcedDisplaySizeLocked(displayContent, displayContent.mInitialDisplayWidth,
displayContent.mInitialDisplayHeight);
@@ -5191,7 +5194,7 @@
@Override
public int getInitialDisplayDensity(int displayId) {
synchronized (mWindowMap) {
- final DisplayContent displayContent = mRoot.getDisplayContentOrCreate(displayId);
+ final DisplayContent displayContent = mRoot.getDisplayContent(displayId);
if (displayContent != null && displayContent.hasAccess(Binder.getCallingUid())) {
return displayContent.mInitialDisplayDensity;
}
@@ -5202,7 +5205,7 @@
@Override
public int getBaseDisplayDensity(int displayId) {
synchronized (mWindowMap) {
- final DisplayContent displayContent = mRoot.getDisplayContentOrCreate(displayId);
+ final DisplayContent displayContent = mRoot.getDisplayContent(displayId);
if (displayContent != null && displayContent.hasAccess(Binder.getCallingUid())) {
return displayContent.mBaseDisplayDensity;
}
@@ -5228,7 +5231,7 @@
final long ident = Binder.clearCallingIdentity();
try {
synchronized(mWindowMap) {
- final DisplayContent displayContent = mRoot.getDisplayContentOrCreate(displayId);
+ final DisplayContent displayContent = mRoot.getDisplayContent(displayId);
if (displayContent != null && mCurrentUserId == targetUserId) {
setForcedDisplayDensityLocked(displayContent, density);
}
@@ -5259,7 +5262,7 @@
final long ident = Binder.clearCallingIdentity();
try {
synchronized(mWindowMap) {
- final DisplayContent displayContent = mRoot.getDisplayContentOrCreate(displayId);
+ final DisplayContent displayContent = mRoot.getDisplayContent(displayId);
if (displayContent != null && mCurrentUserId == callingUserId) {
setForcedDisplayDensityLocked(displayContent,
displayContent.mInitialDisplayDensity);
@@ -5351,7 +5354,7 @@
final long ident = Binder.clearCallingIdentity();
try {
synchronized(mWindowMap) {
- DisplayContent displayContent = mRoot.getDisplayContentOrCreate(displayId);
+ DisplayContent displayContent = mRoot.getDisplayContent(displayId);
if (displayContent != null) {
setOverscanLocked(displayContent, left, top, right, bottom);
}
@@ -6614,19 +6617,11 @@
synchronized (mWindowMap) { }
}
- // TODO: All the display method below should probably be moved into the RootWindowContainer...
- private void createDisplayContentLocked(final Display display) {
- if (display == null) {
- throw new IllegalArgumentException("getDisplayContent: display must not be null");
- }
- mRoot.getDisplayContentOrCreate(display.getDisplayId());
- }
-
// There is an inherent assumption that this will never return null.
// TODO(multi-display): Inspect all the call-points of this method to see if they make sense to
// support non-default display.
DisplayContent getDefaultDisplayContentLocked() {
- return mRoot.getDisplayContentOrCreate(DEFAULT_DISPLAY);
+ return mRoot.getDisplayContent(DEFAULT_DISPLAY);
}
public void onDisplayAdded(int displayId) {
@@ -6641,10 +6636,6 @@
public void onDisplayRemoved(int displayId) {
synchronized (mWindowMap) {
- final DisplayContent displayContent = mRoot.getDisplayContentOrCreate(displayId);
- if (displayContent != null) {
- displayContent.removeIfPossible();
- }
mAnimator.removeDisplayLocked(displayId);
mWindowPlacerLocked.requestTraversal();
}
@@ -6660,7 +6651,7 @@
public void onDisplayChanged(int displayId) {
synchronized (mWindowMap) {
- final DisplayContent displayContent = mRoot.getDisplayContentOrCreate(displayId);
+ final DisplayContent displayContent = mRoot.getDisplayContent(displayId);
if (displayContent != null) {
displayContent.updateDisplayInfo();
}
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 240e7fd..21b4361 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -17,6 +17,7 @@
package com.android.server.wm;
import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
+import static android.os.PowerManager.DRAW_WAKE_LOCK;
import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.SurfaceControl.Transaction;
@@ -167,6 +168,7 @@
import android.os.Trace;
import android.os.UserHandle;
import android.os.WorkSource;
+import android.provider.Settings;
import android.text.TextUtils;
import android.util.DisplayMetrics;
import android.util.MergedConfiguration;
@@ -265,7 +267,7 @@
// This is a non-system overlay window that is currently force hidden.
private boolean mForceHideNonSystemOverlayWindow;
boolean mAppFreezing;
- boolean mHidden; // Used to determine if to show child windows.
+ boolean mHidden = true; // Used to determine if to show child windows.
boolean mWallpaperVisible; // for wallpaper, what was last vis report?
private boolean mDragResizing;
private boolean mDragResizingChangeReported = true;
@@ -285,7 +287,6 @@
int mLayer;
boolean mHaveFrame;
boolean mObscured;
- boolean mTurnOnScreen;
int mLayoutSeq = -1;
@@ -635,6 +636,11 @@
private TapExcludeRegionHolder mTapExcludeRegionHolder;
/**
+ * Used for testing because the real PowerManager is final.
+ */
+ private PowerManagerWrapper mPowerManagerWrapper;
+
+ /**
* Compares two window sub-layers and returns -1 if the first is lesser than the second in terms
* of z-order and 1 otherwise.
*/
@@ -663,9 +669,34 @@
private static final float DEFAULT_DIM_AMOUNT_DEAD_WINDOW = 0.5f;
+ interface PowerManagerWrapper {
+ void wakeUp(long time, String reason);
+
+ boolean isInteractive();
+
+ }
+
WindowState(WindowManagerService service, Session s, IWindow c, WindowToken token,
- WindowState parentWindow, int appOp, int seq, WindowManager.LayoutParams a,
- int viewVisibility, int ownerId, boolean ownerCanAddInternalSystemWindow) {
+ WindowState parentWindow, int appOp, int seq, WindowManager.LayoutParams a,
+ int viewVisibility, int ownerId, boolean ownerCanAddInternalSystemWindow) {
+ this(service, s, c, token, parentWindow, appOp, seq, a, viewVisibility, ownerId,
+ ownerCanAddInternalSystemWindow, new PowerManagerWrapper() {
+ @Override
+ public void wakeUp(long time, String reason) {
+ service.mPowerManager.wakeUp(time, reason);
+ }
+
+ @Override
+ public boolean isInteractive() {
+ return service.mPowerManager.isInteractive();
+ }
+ });
+ }
+
+ WindowState(WindowManagerService service, Session s, IWindow c, WindowToken token,
+ WindowState parentWindow, int appOp, int seq, WindowManager.LayoutParams a,
+ int viewVisibility, int ownerId, boolean ownerCanAddInternalSystemWindow,
+ PowerManagerWrapper powerManagerWrapper) {
super(service);
mSession = s;
mClient = c;
@@ -682,6 +713,7 @@
DeathRecipient deathRecipient = new DeathRecipient();
mSeq = seq;
mEnforceSizeCompat = (mAttrs.privateFlags & PRIVATE_FLAG_COMPATIBLE_WINDOW) != 0;
+ mPowerManagerWrapper = powerManagerWrapper;
if (localLOGV) Slog.v(
TAG, "Window " + this + " client=" + c.asBinder()
+ " token=" + token + " (" + mAttrs.token + ")" + " params=" + a);
@@ -2275,9 +2307,34 @@
void prepareWindowToDisplayDuringRelayout(boolean wasVisible) {
// We need to turn on screen regardless of visibility.
- if ((mAttrs.flags & FLAG_TURN_SCREEN_ON) != 0) {
- if (DEBUG_VISIBILITY) Slog.v(TAG, "Relayout window turning screen on: " + this);
- mTurnOnScreen = true;
+ boolean hasTurnScreenOnFlag = (mAttrs.flags & FLAG_TURN_SCREEN_ON) != 0;
+ boolean allowTheaterMode =
+ mService.mAllowTheaterModeWakeFromLayout || Settings.Global.getInt(
+ mService.mContext.getContentResolver(), Settings.Global.THEATER_MODE_ON, 0)
+ == 0;
+ boolean canTurnScreenOn = mAppToken == null || mAppToken.canTurnScreenOn();
+
+ // The screen will turn on if the following conditions are met
+ // 1. The window has the flag FLAG_TURN_SCREEN_ON
+ // 2. The WMS allows theater mode.
+ // 3. No AWT or the AWT allows the screen to be turned on. This should only be true once
+ // per resume to prevent the screen getting getting turned on for each relayout. Set
+ // canTurnScreenOn will be set to false so the window doesn't turn the screen on again
+ // during this resume.
+ // 4. When the screen is not interactive. This is because when the screen is already
+ // interactive, the value may persist until the next animation, which could potentially
+ // be occurring while turning off the screen. This would lead to the screen incorrectly
+ // turning back on.
+ if (hasTurnScreenOnFlag && allowTheaterMode && canTurnScreenOn
+ && !mPowerManagerWrapper.isInteractive()) {
+ if (DEBUG_VISIBILITY || DEBUG_POWER) {
+ Slog.v(TAG, "Relayout window turning screen on: " + this);
+ }
+ mPowerManagerWrapper.wakeUp(SystemClock.uptimeMillis(),
+ "android.server.wm:TURN_ON");
+ }
+ if (mAppToken != null) {
+ mAppToken.setCanTurnScreenOn(false);
}
// If we were already visible, skip rest of preparation.
@@ -2571,8 +2628,7 @@
// in wake lock statistics. So in particular, we don't want to include the
// window's hash code as in toString().
final CharSequence tag = getWindowTag();
- mDrawLock = mService.mPowerManager.newWakeLock(
- PowerManager.DRAW_WAKE_LOCK, "Window:" + tag);
+ mDrawLock = mService.mPowerManager.newWakeLock(DRAW_WAKE_LOCK, "Window:" + tag);
mDrawLock.setReferenceCounted(false);
mDrawLock.setWorkSource(new WorkSource(mOwnerUid, mAttrs.packageName));
}
@@ -3327,15 +3383,13 @@
pw.print(" mDestroying="); pw.print(mDestroying);
pw.print(" mRemoved="); pw.println(mRemoved);
}
- if (getOrientationChanging() || mAppFreezing || mTurnOnScreen
- || mReportOrientationChanged) {
+ if (getOrientationChanging() || mAppFreezing || mReportOrientationChanged) {
pw.print(prefix); pw.print("mOrientationChanging=");
pw.print(mOrientationChanging);
pw.print(" configOrientationChanging=");
pw.print(getLastReportedConfiguration().orientation
!= getConfiguration().orientation);
pw.print(" mAppFreezing="); pw.print(mAppFreezing);
- pw.print(" mTurnOnScreen="); pw.print(mTurnOnScreen);
pw.print(" mReportOrientationChanged="); pw.println(mReportOrientationChanged);
}
if (mLastFreezeDuration != 0) {
@@ -4505,13 +4559,12 @@
if (!mAnimatingExit && mAppDied) {
mIsDimming = true;
dimmer.dimAbove(getPendingTransaction(), this, DEFAULT_DIM_AMOUNT_DEAD_WINDOW);
- } else if ((mAttrs.flags & FLAG_DIM_BEHIND) != 0 && isVisibleNow()
- && !mWinAnimator.mLastHidden) {
+ } else if ((mAttrs.flags & FLAG_DIM_BEHIND) != 0 && isVisibleNow() && !mHidden) {
// Only show a dim behind when the following is satisfied:
// 1. The window has the flag FLAG_DIM_BEHIND
// 2. The WindowToken is not hidden so dims aren't shown when the window is exiting.
// 3. The WS is considered visible according to the isVisible() method
- // 4. The WSA is not hidden.
+ // 4. The WS is not hidden.
mIsDimming = true;
dimmer.dimBelow(getPendingTransaction(), this, mAttrs.dimAmount);
}
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index dd23b6f..9621ee5 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -1108,31 +1108,6 @@
w.setOrientationChanging(false);
}
}
- // We process mTurnOnScreen even for windows which have already
- // been shown, to handle cases where windows are not necessarily
- // hidden while the screen is turning off.
- // TODO(b/63773439): These cases should be eliminated, though we probably still
- // want to process mTurnOnScreen in this way for clarity.
- if (mWin.mTurnOnScreen &&
- (mWin.mAppToken == null || mWin.mAppToken.canTurnScreenOn())) {
- if (DEBUG_VISIBILITY) Slog.v(TAG, "Show surface turning screen on: " + mWin);
- mWin.mTurnOnScreen = false;
-
- // The window should only turn the screen on once per resume, but
- // prepareSurfaceLocked can be called multiple times. Set canTurnScreenOn to
- // false so the window doesn't turn the screen on again during this resume.
- if (mWin.mAppToken != null) {
- mWin.mAppToken.setCanTurnScreenOn(false);
- }
-
- // We do not add {@code SET_TURN_ON_SCREEN} when the screen is already
- // interactive as the value may persist until the next animation, which could
- // potentially occurring while turning off the screen. This would lead to the
- // screen incorrectly turning back on.
- if (!mService.mPowerManager.isInteractive()) {
- mService.mTurnOnScreen = true;
- }
- }
}
if (hasSurface()) {
w.mToken.hasVisible = true;
diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp
index fb4e85c..4045b72 100644
--- a/services/core/jni/Android.bp
+++ b/services/core/jni/Android.bp
@@ -24,6 +24,7 @@
"com_android_server_connectivity_Vpn.cpp",
"com_android_server_connectivity_tethering_OffloadHardwareInterface.cpp",
"com_android_server_ConsumerIrService.cpp",
+ "com_android_server_devicepolicy_CryptoTestHelper.cpp",
"com_android_server_HardwarePropertiesManagerService.cpp",
"com_android_server_hdmi_HdmiCecController.cpp",
"com_android_server_input_InputApplicationHandle.cpp",
diff --git a/services/core/jni/com_android_server_AlarmManagerService.cpp b/services/core/jni/com_android_server_AlarmManagerService.cpp
index bcb0b4f..47350c1 100644
--- a/services/core/jni/com_android_server_AlarmManagerService.cpp
+++ b/services/core/jni/com_android_server_AlarmManagerService.cpp
@@ -385,20 +385,21 @@
delete impl;
}
-static void android_server_AlarmManagerService_set(JNIEnv*, jobject, jlong nativeData, jint type, jlong seconds, jlong nanoseconds)
+static jint android_server_AlarmManagerService_set(JNIEnv*, jobject, jlong nativeData, jint type, jlong seconds, jlong nanoseconds)
{
AlarmImpl *impl = reinterpret_cast<AlarmImpl *>(nativeData);
struct timespec ts;
ts.tv_sec = seconds;
ts.tv_nsec = nanoseconds;
- int result = impl->set(type, &ts);
+ const int result = impl->set(type, &ts);
if (result < 0)
{
ALOGE("Unable to set alarm to %lld.%09lld: %s\n",
static_cast<long long>(seconds),
static_cast<long long>(nanoseconds), strerror(errno));
}
+ return result >= 0 ? 0 : errno;
}
static jint android_server_AlarmManagerService_waitForAlarm(JNIEnv*, jobject, jlong nativeData)
@@ -424,7 +425,7 @@
/* name, signature, funcPtr */
{"init", "()J", (void*)android_server_AlarmManagerService_init},
{"close", "(J)V", (void*)android_server_AlarmManagerService_close},
- {"set", "(JIJJ)V", (void*)android_server_AlarmManagerService_set},
+ {"set", "(JIJJ)I", (void*)android_server_AlarmManagerService_set},
{"waitForAlarm", "(J)I", (void*)android_server_AlarmManagerService_waitForAlarm},
{"setKernelTime", "(JJ)I", (void*)android_server_AlarmManagerService_setKernelTime},
{"setKernelTimezone", "(JI)I", (void*)android_server_AlarmManagerService_setKernelTimezone},
diff --git a/services/core/jni/com_android_server_ArcVideoService.cpp b/services/core/jni/com_android_server_ArcVideoService.cpp
index 7df8276..f93cd90 100644
--- a/services/core/jni/com_android_server_ArcVideoService.cpp
+++ b/services/core/jni/com_android_server_ArcVideoService.cpp
@@ -32,7 +32,7 @@
#include <arc/Future.h>
#include <arc/IArcBridgeService.h>
#include <arc/MojoProcessSupport.h>
-#include <video.mojom.h>
+#include <components/arc/common/video.mojom.h>
namespace {
diff --git a/services/core/jni/com_android_server_devicepolicy_CryptoTestHelper.cpp b/services/core/jni/com_android_server_devicepolicy_CryptoTestHelper.cpp
new file mode 100644
index 0000000..b53ea92
--- /dev/null
+++ b/services/core/jni/com_android_server_devicepolicy_CryptoTestHelper.cpp
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "jni.h"
+#include "core_jni_helpers.h"
+
+#include <openssl/crypto.h>
+
+namespace {
+
+static jint runSelfTest(JNIEnv* env, jobject /* clazz */) {
+ return BORINGSSL_self_test();
+}
+
+static const JNINativeMethod methods[] = {
+ /* name, signature, funcPtr */
+ {"runSelfTest", "()I", (void*) runSelfTest}
+};
+
+} // anonymous namespace
+
+namespace android {
+
+int register_android_server_devicepolicy_CryptoTestHelper(JNIEnv *env) {
+ return jniRegisterNativeMethods(
+ env, "com/android/server/devicepolicy/CryptoTestHelper", methods, NELEM(methods));
+}
+
+} // namespace android
\ No newline at end of file
diff --git a/services/core/jni/onload.cpp b/services/core/jni/onload.cpp
index 07ddb05..bf2a637 100644
--- a/services/core/jni/onload.cpp
+++ b/services/core/jni/onload.cpp
@@ -42,6 +42,7 @@
int register_android_server_location_GnssLocationProvider(JNIEnv* env);
int register_android_server_connectivity_Vpn(JNIEnv* env);
int register_android_server_connectivity_tethering_OffloadHardwareInterface(JNIEnv*);
+int register_android_server_devicepolicy_CryptoTestHelper(JNIEnv*);
int register_android_server_hdmi_HdmiCecController(JNIEnv* env);
int register_android_server_tv_TvUinputBridge(JNIEnv* env);
int register_android_server_tv_TvInputHal(JNIEnv* env);
@@ -88,6 +89,7 @@
register_android_server_location_GnssLocationProvider(env);
register_android_server_connectivity_Vpn(env);
register_android_server_connectivity_tethering_OffloadHardwareInterface(env);
+ register_android_server_devicepolicy_CryptoTestHelper(env);
register_android_server_ConsumerIrService(env);
register_android_server_BatteryStatsService(env);
register_android_server_hdmi_HdmiCecController(env);
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/CryptoTestHelper.java b/services/devicepolicy/java/com/android/server/devicepolicy/CryptoTestHelper.java
new file mode 100644
index 0000000..a20758e
--- /dev/null
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/CryptoTestHelper.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2018 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.devicepolicy;
+
+import android.app.admin.SecurityLog;
+
+/**
+ * Helper to call native BoringSSL self test.
+ */
+public class CryptoTestHelper {
+ public static void runAndLogSelfTest() {
+ final int result = runSelfTest();
+ SecurityLog.writeEvent(SecurityLog.TAG_CRYPTO_SELF_TEST_COMPLETED, result);
+ }
+ private static native int runSelfTest();
+}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DeviceAdminServiceController.java b/services/devicepolicy/java/com/android/server/devicepolicy/DeviceAdminServiceController.java
index 60f204d..0c0ce8d 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DeviceAdminServiceController.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DeviceAdminServiceController.java
@@ -193,6 +193,7 @@
}
}
+ @GuardedBy("mLock")
private void disconnectServiceOnUserLocked(int userId, @NonNull String actionForLog) {
final DevicePolicyServiceConnection conn = mConnections.get(userId);
if (conn != null) {
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 953a79f..8753344 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -2044,6 +2044,10 @@
public TransferOwnershipMetadataManager newTransferOwnershipMetadataManager() {
return new TransferOwnershipMetadataManager();
}
+
+ public void runCryptoSelfTest() {
+ CryptoTestHelper.runAndLogSelfTest();
+ }
}
/**
@@ -2296,6 +2300,7 @@
if (hasDeviceOwner && mInjector.securityLogGetLoggingEnabledProperty()) {
mSecurityLogMonitor.start();
+ mInjector.runCryptoSelfTest();
maybePauseDeviceWideLoggingLocked();
}
}
@@ -10221,6 +10226,7 @@
mInjector.registerContentObserver(mDefaultImeChanged, false, this, UserHandle.USER_ALL);
}
+ @GuardedBy("DevicePolicyManagerService.this")
private void addPendingChangeByOwnerLocked(int userId) {
mUserIdsWithPendingChangesByOwner.add(userId);
}
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index c361434..75bb5e4 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -724,7 +724,6 @@
MmsServiceBroker mmsService = null;
HardwarePropertiesManagerService hardwarePropertiesService = null;
- boolean disableRtt = SystemProperties.getBoolean("config.disable_rtt", false);
boolean disableSystemTextClassifier = SystemProperties.getBoolean(
"config.disable_systemtextclassifier", false);
boolean disableCameraService = SystemProperties.getBoolean("config.disable_cameraservice",
@@ -843,6 +842,14 @@
ServiceManager.addService(Context.INPUT_SERVICE, inputManager);
traceEnd();
+ traceBeginAndSlog("SetWindowManagerService");
+ mActivityManagerService.setWindowManager(wm);
+ traceEnd();
+
+ traceBeginAndSlog("WindowManagerServiceOnInitReady");
+ wm.onInitReady();
+ traceEnd();
+
// Start receiving calls from HIDL services. Start in in a separate thread
// because it need to connect to SensorManager. This have to start
// after START_SENSOR_SERVICE is done.
@@ -860,10 +867,6 @@
traceEnd();
}
- traceBeginAndSlog("SetWindowManagerService");
- mActivityManagerService.setWindowManager(wm);
- traceEnd();
-
traceBeginAndSlog("StartInputManager");
inputManager.setWindowManagerCallbacks(wm.getInputMonitor());
inputManager.start();
@@ -1105,18 +1108,12 @@
traceEnd();
}
- if (!disableRtt) {
- traceBeginAndSlog("StartWifiRtt");
- mSystemServiceManager.startService("com.android.server.wifi.RttService");
+ if (context.getPackageManager().hasSystemFeature(
+ PackageManager.FEATURE_WIFI_RTT)) {
+ traceBeginAndSlog("StartRttService");
+ mSystemServiceManager.startService(
+ "com.android.server.wifi.rtt.RttService");
traceEnd();
-
- if (context.getPackageManager().hasSystemFeature(
- PackageManager.FEATURE_WIFI_RTT)) {
- traceBeginAndSlog("StartRttService");
- mSystemServiceManager.startService(
- "com.android.server.wifi.rtt.RttService");
- traceEnd();
- }
}
if (context.getPackageManager().hasSystemFeature(
diff --git a/services/net/java/android/net/apf/ApfFilter.java b/services/net/java/android/net/apf/ApfFilter.java
index 7d9736e..d190432 100644
--- a/services/net/java/android/net/apf/ApfFilter.java
+++ b/services/net/java/android/net/apf/ApfFilter.java
@@ -1068,6 +1068,7 @@
mLastInstallEvent.flags = ApfProgramEvent.flagsFor(mIPv4Address != null, mMulticastFilter);
}
+ @GuardedBy("this")
private void logApfProgramEventLocked(long now) {
if (mLastInstallEvent == null) {
return;
diff --git a/services/net/java/android/net/ip/RouterAdvertisementDaemon.java b/services/net/java/android/net/ip/RouterAdvertisementDaemon.java
index 49a1e79..8fbc01e 100644
--- a/services/net/java/android/net/ip/RouterAdvertisementDaemon.java
+++ b/services/net/java/android/net/ip/RouterAdvertisementDaemon.java
@@ -268,6 +268,7 @@
mUnicastResponder = null;
}
+ @GuardedBy("mLock")
private void assembleRaLocked() {
final ByteBuffer ra = ByteBuffer.wrap(mRA);
ra.order(ByteOrder.BIG_ENDIAN);
diff --git a/services/print/java/com/android/server/print/RemotePrintSpooler.java b/services/print/java/com/android/server/print/RemotePrintSpooler.java
index ba5dde0..c1c32c2 100644
--- a/services/print/java/com/android/server/print/RemotePrintSpooler.java
+++ b/services/print/java/com/android/server/print/RemotePrintSpooler.java
@@ -596,6 +596,7 @@
}
}
+ @GuardedBy("mLock")
private void bindLocked() throws TimeoutException, InterruptedException {
while (mIsBinding) {
mLock.wait();
diff --git a/services/robotests/src/com/android/server/backup/PerformBackupTaskTest.java b/services/robotests/src/com/android/server/backup/PerformBackupTaskTest.java
index e103464..cd9311f 100644
--- a/services/robotests/src/com/android/server/backup/PerformBackupTaskTest.java
+++ b/services/robotests/src/com/android/server/backup/PerformBackupTaskTest.java
@@ -26,6 +26,8 @@
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
@@ -40,7 +42,9 @@
import android.app.IActivityManager;
import android.app.IBackupAgent;
import android.app.backup.BackupAgent;
+import android.app.backup.BackupDataInput;
import android.app.backup.BackupDataOutput;
+import android.app.backup.BackupTransport;
import android.app.backup.FullBackupDataOutput;
import android.app.backup.IBackupManager;
import android.app.backup.IBackupManagerMonitor;
@@ -53,6 +57,7 @@
import android.os.HandlerThread;
import android.os.Looper;
import android.os.Message;
+import android.os.ParcelFileDescriptor;
import android.os.PowerManager;
import android.os.RemoteException;
import android.platform.test.annotations.Presubmit;
@@ -80,6 +85,8 @@
import org.mockito.ArgumentMatcher;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
import org.robolectric.shadow.api.Shadow;
@@ -88,6 +95,7 @@
import org.robolectric.shadows.ShadowQueuedWork;
import java.io.File;
+import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Stream;
@@ -184,32 +192,33 @@
@Test
public void testRunTask_whenTransportProvidesFlags_passesThemToTheAgent() throws Exception {
- BackupAgent agent = setUpAgent(PACKAGE_1);
+ AgentMock agentMock = setUpAgent(PACKAGE_1);
int flags = BackupAgent.FLAG_CLIENT_SIDE_ENCRYPTION_ENABLED;
when(mTransportBinder.getTransportFlags()).thenReturn(flags);
PerformBackupTask task = createPerformBackupTask(emptyList(), false, true, PACKAGE_1);
runTask(task);
- verify(agent).onBackup(any(), argThat(dataOutputWithTransportFlags(flags)), any());
+ verify(agentMock.agent)
+ .onBackup(any(), argThat(dataOutputWithTransportFlags(flags)), any());
}
@Test
public void testRunTask_whenTransportDoesNotProvidesFlags() throws Exception {
- BackupAgent agent = setUpAgent(PACKAGE_1);
+ AgentMock agentMock = setUpAgent(PACKAGE_1);
PerformBackupTask task = createPerformBackupTask(emptyList(), false, true, PACKAGE_1);
runTask(task);
- verify(agent).onBackup(any(), argThat(dataOutputWithTransportFlags(0)), any());
+ verify(agentMock.agent).onBackup(any(), argThat(dataOutputWithTransportFlags(0)), any());
}
@Test
public void testRunTask_whenTransportProvidesFlagsAndMultipleAgents_passesToAll()
throws Exception {
- List<BackupAgent> agents = setUpAgents(PACKAGE_1, PACKAGE_2);
- BackupAgent agent1 = agents.get(0);
- BackupAgent agent2 = agents.get(1);
+ List<AgentMock> agentMocks = setUpAgents(PACKAGE_1, PACKAGE_2);
+ BackupAgent agent1 = agentMocks.get(0).agent;
+ BackupAgent agent2 = agentMocks.get(1).agent;
int flags = BackupAgent.FLAG_CLIENT_SIDE_ENCRYPTION_ENABLED;
when(mTransportBinder.getTransportFlags()).thenReturn(flags);
PerformBackupTask task =
@@ -223,14 +232,103 @@
@Test
public void testRunTask_whenTransportChangeFlagsAfterTaskCreation() throws Exception {
- BackupAgent agent = setUpAgent(PACKAGE_1);
+ AgentMock agentMock = setUpAgent(PACKAGE_1);
PerformBackupTask task = createPerformBackupTask(emptyList(), false, true, PACKAGE_1);
int flags = BackupAgent.FLAG_CLIENT_SIDE_ENCRYPTION_ENABLED;
when(mTransportBinder.getTransportFlags()).thenReturn(flags);
runTask(task);
- verify(agent).onBackup(any(), argThat(dataOutputWithTransportFlags(flags)), any());
+ verify(agentMock.agent)
+ .onBackup(any(), argThat(dataOutputWithTransportFlags(flags)), any());
+ }
+
+ @Test
+ public void testRunTask_callsListenerOnTaskFinished() throws Exception {
+ setUpAgent(PACKAGE_1);
+ PerformBackupTask task = createPerformBackupTask(emptyList(), false, true, PACKAGE_1);
+
+ runTask(task);
+
+ verify(mListener).onFinished(any());
+ }
+
+ @Test
+ public void testRunTask_callsTransportPerformBackup() throws Exception {
+ AgentMock agentMock = setUpAgent(PACKAGE_1);
+ agentOnBackupDo(
+ agentMock.agent,
+ (oldState, dataOutput, newState) -> {
+ writeData(dataOutput, "key1", "foo".getBytes());
+ writeData(dataOutput, "key2", "bar".getBytes());
+ });
+ PerformBackupTask task = createPerformBackupTask(emptyList(), false, true, PACKAGE_1);
+ // We need to verify at call time because the file is deleted right after
+ when(mTransportBinder.performBackup(argThat(packageInfo(PACKAGE_1)), any(), anyInt()))
+ .then(this::mockAndVerifyTransportPerformBackupData);
+
+ runTask(task);
+
+ // Already verified data in mockAndVerifyPerformBackupData
+ verify(mTransportBinder).performBackup(argThat(packageInfo(PACKAGE_1)), any(), anyInt());
+ }
+
+ private int mockAndVerifyTransportPerformBackupData(InvocationOnMock invocation)
+ throws IOException {
+ ParcelFileDescriptor data = invocation.getArgument(1);
+
+ // Verifying that what we passed to the transport is what the agent wrote
+ BackupDataInput dataInput = new BackupDataInput(data.getFileDescriptor());
+
+ // "key1" => "foo"
+ assertThat(dataInput.readNextHeader()).isTrue();
+ assertThat(dataInput.getKey()).isEqualTo("key1");
+ int size1 = dataInput.getDataSize();
+ byte[] data1 = new byte[size1];
+ dataInput.readEntityData(data1, 0, size1);
+ assertThat(data1).isEqualTo("foo".getBytes());
+
+ // "key2" => "bar"
+ assertThat(dataInput.readNextHeader()).isTrue();
+ assertThat(dataInput.getKey()).isEqualTo("key2");
+ int size2 = dataInput.getDataSize();
+ byte[] data2 = new byte[size2];
+ dataInput.readEntityData(data2, 0, size2);
+ assertThat(data2).isEqualTo("bar".getBytes());
+
+ // No more
+ assertThat(dataInput.readNextHeader()).isFalse();
+
+ return BackupTransport.TRANSPORT_OK;
+ }
+
+ @Test
+ public void testRunTask_whenPerformBackupSucceeds_callsTransportFinishBackup()
+ throws Exception {
+ setUpAgent(PACKAGE_1);
+ PerformBackupTask task = createPerformBackupTask(emptyList(), false, true, PACKAGE_1);
+ when(mTransportBinder.performBackup(argThat(packageInfo(PACKAGE_1)), any(), anyInt()))
+ .thenReturn(BackupTransport.TRANSPORT_OK);
+
+ runTask(task);
+
+ verify(mTransportBinder).finishBackup();
+ }
+
+ @Test
+ public void testRunTask_whenProhibitedKey_failsAgent() throws Exception {
+ AgentMock agentMock = setUpAgent(PACKAGE_1);
+ agentOnBackupDo(
+ agentMock.agent,
+ (oldState, dataOutput, newState) -> {
+ char prohibitedChar = 0xff00;
+ writeData(dataOutput, prohibitedChar + "key", "foo".getBytes());
+ });
+ PerformBackupTask task = createPerformBackupTask(emptyList(), false, true, PACKAGE_1);
+
+ runTask(task);
+
+ verify(agentMock.agentBinder).fail(any());
}
private void runTask(PerformBackupTask task) {
@@ -241,26 +339,34 @@
}
}
- private List<BackupAgent> setUpAgents(String... packageNames) {
+ private List<AgentMock> setUpAgents(String... packageNames) {
return Stream.of(packageNames).map(this::setUpAgent).collect(toList());
}
- private BackupAgent setUpAgent(String packageName) {
- PackageInfo packageInfo = new PackageInfo();
- packageInfo.packageName = packageName;
- packageInfo.applicationInfo = new ApplicationInfo();
- packageInfo.applicationInfo.flags = ApplicationInfo.FLAG_ALLOW_BACKUP;
- packageInfo.applicationInfo.backupAgentName = "BackupAgent" + packageName;
- packageInfo.applicationInfo.packageName = packageName;
- mShadowPackageManager.setApplicationEnabledSetting(
- packageName, PackageManager.COMPONENT_ENABLED_STATE_ENABLED, 0);
- mShadowPackageManager.addPackage(packageInfo);
- BackupAgent backupAgent = spy(BackupAgent.class);
- IBackupAgent backupAgentBinder = IBackupAgent.Stub.asInterface(backupAgent.onBind());
- when(mBackupManagerService.bindToAgentSynchronous(
- eq(packageInfo.applicationInfo), anyInt()))
- .thenReturn(backupAgentBinder);
- return backupAgent;
+ private AgentMock setUpAgent(String packageName) {
+ try {
+ PackageInfo packageInfo = new PackageInfo();
+ packageInfo.packageName = packageName;
+ packageInfo.applicationInfo = new ApplicationInfo();
+ packageInfo.applicationInfo.flags = ApplicationInfo.FLAG_ALLOW_BACKUP;
+ packageInfo.applicationInfo.backupAgentName = "BackupAgent" + packageName;
+ packageInfo.applicationInfo.packageName = packageName;
+ mShadowPackageManager.setApplicationEnabledSetting(
+ packageName, PackageManager.COMPONENT_ENABLED_STATE_ENABLED, 0);
+ mShadowPackageManager.addPackage(packageInfo);
+ BackupAgent backupAgent = spy(BackupAgent.class);
+ IBackupAgent backupAgentBinder =
+ spy(IBackupAgent.Stub.asInterface(backupAgent.onBind()));
+ // Don't crash our only process (in production code this would crash the app, not us)
+ doNothing().when(backupAgentBinder).fail(any());
+ when(mBackupManagerService.bindToAgentSynchronous(
+ eq(packageInfo.applicationInfo), anyInt()))
+ .thenReturn(backupAgentBinder);
+ return new AgentMock(backupAgentBinder, backupAgent);
+ } catch (RemoteException e) {
+ // Never happens, compiler happy
+ throw new AssertionError(e);
+ }
}
private PerformBackupTask createPerformBackupTask(
@@ -288,10 +394,53 @@
return task;
}
- private ArgumentMatcher<BackupDataOutput> dataOutputWithTransportFlags(int flags) {
+ private static ArgumentMatcher<PackageInfo> packageInfo(String packageName) {
+ return packageInfo -> packageName.equals(packageInfo.packageName);
+ }
+
+ private static ArgumentMatcher<BackupDataOutput> dataOutputWithTransportFlags(int flags) {
return dataOutput -> dataOutput.getTransportFlags() == flags;
}
+ private static void writeData(BackupDataOutput dataOutput, String key, byte[] data)
+ throws IOException {
+ dataOutput.writeEntityHeader(key, data.length);
+ dataOutput.writeEntityData(data, data.length);
+ }
+
+ private static void agentOnBackupDo(BackupAgent agent, BackupAgentOnBackup function)
+ throws Exception {
+ doAnswer(function).when(agent).onBackup(any(), any(), any());
+ }
+
+ @FunctionalInterface
+ private interface BackupAgentOnBackup extends Answer<Void> {
+ void onBackup(
+ ParcelFileDescriptor oldState,
+ BackupDataOutput dataOutput,
+ ParcelFileDescriptor newState)
+ throws IOException;
+
+ @Override
+ default Void answer(InvocationOnMock invocation) throws Throwable {
+ onBackup(
+ invocation.getArgument(0),
+ invocation.getArgument(1),
+ invocation.getArgument(2));
+ return null;
+ }
+ }
+
+ private static class AgentMock {
+ private final IBackupAgent agentBinder;
+ private final BackupAgent agent;
+
+ public AgentMock(IBackupAgent agentBinder, BackupAgent agent) {
+ this.agentBinder = agentBinder;
+ this.agent = agent;
+ }
+ }
+
private abstract static class FakeIBackupManager extends IBackupManager.Stub {
private Handler mBackupHandler;
private BackupRestoreTask mTask;
diff --git a/services/robotests/src/com/android/server/testing/shadows/ShadowBackupDataInput.java b/services/robotests/src/com/android/server/testing/shadows/ShadowBackupDataInput.java
index 28489af..8016a8b 100644
--- a/services/robotests/src/com/android/server/testing/shadows/ShadowBackupDataInput.java
+++ b/services/robotests/src/com/android/server/testing/shadows/ShadowBackupDataInput.java
@@ -21,41 +21,76 @@
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
+import java.io.EOFException;
import java.io.FileDescriptor;
+import java.io.FileInputStream;
import java.io.IOException;
+import java.io.ObjectInputStream;
+/**
+ * Shadow for {@link BackupDataInput}. Format read does NOT match implementation. To write data to
+ * be read by this shadow, you should also declare shadow {@link ShadowBackupDataOutput}.
+ */
@Implements(BackupDataInput.class)
public class ShadowBackupDataInput {
- @Implementation
- public void __constructor__(FileDescriptor fd) {
- }
+ private ObjectInputStream mInput;
+ private int mSize;
+ private String mKey;
+ private boolean mHeaderReady;
@Implementation
- protected void finalize() throws Throwable {
+ public void __constructor__(FileDescriptor fd) {
+ try {
+ mInput = new ObjectInputStream(new FileInputStream(fd));
+ } catch (IOException e) {
+ throw new AssertionError(e);
+ }
}
@Implementation
public boolean readNextHeader() throws IOException {
- return false;
+ mHeaderReady = false;
+ try {
+ mSize = mInput.readInt();
+ } catch (EOFException e) {
+ return false;
+ }
+ mKey = mInput.readUTF();
+ mHeaderReady = true;
+ return true;
}
@Implementation
public String getKey() {
- throw new AssertionError("Can't call because readNextHeader() returned false");
+ checkHeaderReady();
+ return mKey;
}
@Implementation
public int getDataSize() {
- throw new AssertionError("Can't call because readNextHeader() returned false");
+ checkHeaderReady();
+ return mSize;
}
@Implementation
public int readEntityData(byte[] data, int offset, int size) throws IOException {
- throw new AssertionError("Can't call because readNextHeader() returned false");
+ checkHeaderReady();
+ int result = mInput.read(data, offset, size);
+ if (result < 0) {
+ throw new IOException("result=0x" + Integer.toHexString(result));
+ }
+ return result;
}
@Implementation
public void skipEntityData() throws IOException {
- throw new AssertionError("Can't call because readNextHeader() returned false");
+ checkHeaderReady();
+ mInput.read(new byte[mSize], 0, mSize);
+ }
+
+ private void checkHeaderReady() {
+ if (!mHeaderReady) {
+ throw new IllegalStateException("Entity header not read");
+ }
}
}
diff --git a/services/robotests/src/com/android/server/testing/shadows/ShadowBackupDataOutput.java b/services/robotests/src/com/android/server/testing/shadows/ShadowBackupDataOutput.java
index c7deada..e78a4b3 100644
--- a/services/robotests/src/com/android/server/testing/shadows/ShadowBackupDataOutput.java
+++ b/services/robotests/src/com/android/server/testing/shadows/ShadowBackupDataOutput.java
@@ -21,16 +21,29 @@
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
+import java.io.ByteArrayOutputStream;
import java.io.FileDescriptor;
+import java.io.FileOutputStream;
import java.io.IOException;
+import java.io.ObjectOutputStream;
+/**
+ * Shadow for {@link BackupDataOutput}. Format written does NOT match implementation. To read data
+ * written with this shadow you should also declare shadow {@link ShadowBackupDataInput}.
+ */
@Implements(BackupDataOutput.class)
public class ShadowBackupDataOutput {
private long mQuota;
private int mTransportFlags;
+ private ObjectOutputStream mOutput;
@Implementation
public void __constructor__(FileDescriptor fd, long quota, int transportFlags) {
+ try {
+ mOutput = new ObjectOutputStream(new FileOutputStream(fd));
+ } catch (IOException e) {
+ throw new AssertionError(e);
+ }
mQuota = quota;
mTransportFlags = transportFlags;
}
@@ -47,11 +60,27 @@
@Implementation
public int writeEntityHeader(String key, int dataSize) throws IOException {
- return 0;
+ final int size;
+ try (ByteArrayOutputStream byteStream = new ByteArrayOutputStream()) {
+ writeEntityHeader(new ObjectOutputStream(byteStream), key, dataSize);
+ size = byteStream.size();
+ }
+ writeEntityHeader(mOutput, key, dataSize);
+ return size;
+ }
+
+ private void writeEntityHeader(ObjectOutputStream stream, String key, int dataSize)
+ throws IOException {
+ // Write the int first because readInt() throws EOFException, to know when stream ends
+ stream.writeInt(dataSize);
+ stream.writeUTF(key);
+ stream.flush();
}
@Implementation
public int writeEntityData(byte[] data, int size) throws IOException {
- return 0;
+ mOutput.write(data, 0, size);
+ mOutput.flush();
+ return size;
}
}
diff --git a/services/tests/servicestests/AndroidManifest.xml b/services/tests/servicestests/AndroidManifest.xml
index 5d8aca1..97d6c43 100644
--- a/services/tests/servicestests/AndroidManifest.xml
+++ b/services/tests/servicestests/AndroidManifest.xml
@@ -60,6 +60,7 @@
<uses-permission android:name="android.permission.ACCESS_SURFACE_FLINGER" />
<uses-permission android:name="android.permission.READ_FRAME_BUFFER" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+ <uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
<!-- Uses API introduced in O (26) -->
<uses-sdk android:minSdkVersion="1"
diff --git a/services/tests/servicestests/src/com/android/server/ForceAppStandbyTrackerTest.java b/services/tests/servicestests/src/com/android/server/AppStateTrackerTest.java
similarity index 93%
rename from services/tests/servicestests/src/com/android/server/ForceAppStandbyTrackerTest.java
rename to services/tests/servicestests/src/com/android/server/AppStateTrackerTest.java
index a499472..90db2a3 100644
--- a/services/tests/servicestests/src/com/android/server/ForceAppStandbyTrackerTest.java
+++ b/services/tests/servicestests/src/com/android/server/AppStateTrackerTest.java
@@ -15,7 +15,7 @@
*/
package com.android.server;
-import static com.android.server.ForceAppStandbyTracker.TARGET_OP;
+import static com.android.server.AppStateTracker.TARGET_OP;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -33,6 +33,7 @@
import static org.mockito.Mockito.when;
import android.app.ActivityManager;
+import android.app.ActivityManagerInternal;
import android.app.AppOpsManager;
import android.app.AppOpsManager.OpEntry;
import android.app.AppOpsManager.PackageOps;
@@ -63,7 +64,7 @@
import com.android.internal.app.IAppOpsCallback;
import com.android.internal.app.IAppOpsService;
-import com.android.server.ForceAppStandbyTracker.Listener;
+import com.android.server.AppStateTracker.Listener;
import org.junit.Before;
import org.junit.Test;
@@ -82,17 +83,17 @@
import java.util.function.Consumer;
/**
- * Tests for {@link ForceAppStandbyTracker}
+ * Tests for {@link AppStateTracker}
*
* Run with:
- atest $ANDROID_BUILD_TOP/frameworks/base/services/tests/servicestests/src/com/android/server/ForceAppStandbyTrackerTest.java
+ atest $ANDROID_BUILD_TOP/frameworks/base/services/tests/servicestests/src/com/android/server/AppStateTrackerTest.java
*/
@SmallTest
@RunWith(AndroidJUnit4.class)
-public class ForceAppStandbyTrackerTest {
+public class AppStateTrackerTest {
- private class ForceAppStandbyTrackerTestable extends ForceAppStandbyTracker {
- ForceAppStandbyTrackerTestable() {
+ private class AppStateTrackerTestable extends AppStateTracker {
+ AppStateTrackerTestable() {
super(mMockContext, Looper.getMainLooper());
}
@@ -112,6 +113,11 @@
}
@Override
+ ActivityManagerInternal injectActivityManagerInternal() {
+ return mMockIActivityManagerInternal;
+ }
+
+ @Override
PowerManagerInternal injectPowerManagerInternal() {
return mMockPowerManagerInternal;
}
@@ -152,6 +158,9 @@
private IActivityManager mMockIActivityManager;
@Mock
+ private ActivityManagerInternal mMockIActivityManagerInternal;
+
+ @Mock
private AppOpsManager mMockAppOpsManager;
@Mock
@@ -195,7 +204,7 @@
return new PowerSaveState.Builder().setBatterySaverEnabled(mPowerSaveMode).build();
}
- private ForceAppStandbyTrackerTestable newInstance() throws Exception {
+ private AppStateTrackerTestable newInstance() throws Exception {
MockitoAnnotations.initMocks(this);
when(mMockIAppOpsService.checkOperation(eq(TARGET_OP), anyInt(), anyString()))
@@ -205,12 +214,12 @@
AppOpsManager.MODE_IGNORED : AppOpsManager.MODE_ALLOWED;
});
- final ForceAppStandbyTrackerTestable instance = new ForceAppStandbyTrackerTestable();
+ final AppStateTrackerTestable instance = new AppStateTrackerTestable();
return instance;
}
- private void callStart(ForceAppStandbyTrackerTestable instance) throws RemoteException {
+ private void callStart(AppStateTrackerTestable instance) throws RemoteException {
// Set up functions that start() calls.
when(mMockPowerManagerInternal.getLowPowerState(eq(ServiceType.FORCE_ALL_APPS_STANDBY)))
@@ -223,7 +232,7 @@
when(mMockContext.getContentResolver()).thenReturn(mMockContentResolver);
// Call start.
- instance.start();
+ instance.onSystemServicesReady();
// Capture the listeners.
ArgumentCaptor<IUidObserver> uidObserverArgumentCaptor =
@@ -287,7 +296,7 @@
private static final int JOBS_ONLY = 1 << 1;
private static final int JOBS_AND_ALARMS = ALARMS_ONLY | JOBS_ONLY;
- private void areRestricted(ForceAppStandbyTrackerTestable instance, int uid, String packageName,
+ private void areRestricted(AppStateTrackerTestable instance, int uid, String packageName,
int restrictionTypes, boolean exemptFromBatterySaver) {
assertEquals(((restrictionTypes & JOBS_ONLY) != 0),
instance.areJobsRestricted(uid, packageName, exemptFromBatterySaver));
@@ -295,13 +304,13 @@
instance.areAlarmsRestricted(uid, packageName, exemptFromBatterySaver));
}
- private void areRestricted(ForceAppStandbyTrackerTestable instance, int uid, String packageName,
+ private void areRestricted(AppStateTrackerTestable instance, int uid, String packageName,
int restrictionTypes) {
areRestricted(instance, uid, packageName, restrictionTypes,
/*exemptFromBatterySaver=*/ false);
}
- private void areRestrictedWithExemption(ForceAppStandbyTrackerTestable instance,
+ private void areRestrictedWithExemption(AppStateTrackerTestable instance,
int uid, String packageName, int restrictionTypes) {
areRestricted(instance, uid, packageName, restrictionTypes,
/*exemptFromBatterySaver=*/ true);
@@ -309,7 +318,7 @@
@Test
public void testAll() throws Exception {
- final ForceAppStandbyTrackerTestable instance = newInstance();
+ final AppStateTrackerTestable instance = newInstance();
callStart(instance);
assertFalse(instance.isForceAllAppsStandbyEnabled());
@@ -466,7 +475,7 @@
@Test
public void testUidStateForeground() throws Exception {
- final ForceAppStandbyTrackerTestable instance = newInstance();
+ final AppStateTrackerTestable instance = newInstance();
callStart(instance);
mIUidObserver.onUidActive(UID_1);
@@ -476,6 +485,10 @@
assertFalse(instance.isUidActive(UID_2));
assertTrue(instance.isUidActive(Process.SYSTEM_UID));
+ assertTrue(instance.isUidActiveSynced(UID_1));
+ assertFalse(instance.isUidActiveSynced(UID_2));
+ assertTrue(instance.isUidActiveSynced(Process.SYSTEM_UID));
+
assertFalse(instance.isUidInForeground(UID_1));
assertFalse(instance.isUidInForeground(UID_2));
assertTrue(instance.isUidInForeground(Process.SYSTEM_UID));
@@ -489,6 +502,10 @@
assertFalse(instance.isUidActive(UID_2));
assertTrue(instance.isUidActive(Process.SYSTEM_UID));
+ assertTrue(instance.isUidActiveSynced(UID_1));
+ assertFalse(instance.isUidActiveSynced(UID_2));
+ assertTrue(instance.isUidActiveSynced(Process.SYSTEM_UID));
+
assertFalse(instance.isUidInForeground(UID_1));
assertTrue(instance.isUidInForeground(UID_2));
assertTrue(instance.isUidInForeground(Process.SYSTEM_UID));
@@ -548,14 +565,34 @@
assertFalse(instance.isUidActive(UID_2));
assertTrue(instance.isUidActive(Process.SYSTEM_UID));
+ assertFalse(instance.isUidActiveSynced(UID_1));
+ assertFalse(instance.isUidActiveSynced(UID_2));
+ assertTrue(instance.isUidActiveSynced(Process.SYSTEM_UID));
+
assertFalse(instance.isUidInForeground(UID_1));
assertFalse(instance.isUidInForeground(UID_2));
assertTrue(instance.isUidInForeground(Process.SYSTEM_UID));
+
+ // The result from AMI.isUidActive() only affects isUidActiveSynced().
+ when(mMockIActivityManagerInternal.isUidActive(anyInt())).thenReturn(true);
+
+ assertFalse(instance.isUidActive(UID_1));
+ assertFalse(instance.isUidActive(UID_2));
+ assertTrue(instance.isUidActive(Process.SYSTEM_UID));
+
+ assertTrue(instance.isUidActiveSynced(UID_1));
+ assertTrue(instance.isUidActiveSynced(UID_2));
+ assertTrue(instance.isUidActiveSynced(Process.SYSTEM_UID));
+
+ assertFalse(instance.isUidInForeground(UID_1));
+ assertFalse(instance.isUidInForeground(UID_2));
+ assertTrue(instance.isUidInForeground(Process.SYSTEM_UID));
+
}
@Test
public void testExempt() throws Exception {
- final ForceAppStandbyTrackerTestable instance = newInstance();
+ final AppStateTrackerTestable instance = newInstance();
callStart(instance);
assertFalse(instance.isForceAllAppsStandbyEnabled());
@@ -621,7 +658,7 @@
}
public void loadPersistedAppOps() throws Exception {
- final ForceAppStandbyTrackerTestable instance = newInstance();
+ final AppStateTrackerTestable instance = newInstance();
final List<PackageOps> ops = new ArrayList<>();
@@ -631,7 +668,7 @@
AppOpsManager.OP_ACCESS_NOTIFICATIONS,
AppOpsManager.MODE_IGNORED, 0, 0, 0, 0, null));
entries.add(new AppOpsManager.OpEntry(
- ForceAppStandbyTracker.TARGET_OP,
+ AppStateTracker.TARGET_OP,
AppOpsManager.MODE_IGNORED, 0, 0, 0, 0, null));
ops.add(new PackageOps(PACKAGE_1, UID_1, entries));
@@ -639,7 +676,7 @@
//--------------------------------------------------
entries = new ArrayList<>();
entries.add(new AppOpsManager.OpEntry(
- ForceAppStandbyTracker.TARGET_OP,
+ AppStateTracker.TARGET_OP,
AppOpsManager.MODE_IGNORED, 0, 0, 0, 0, null));
ops.add(new PackageOps(PACKAGE_2, UID_2, entries));
@@ -647,7 +684,7 @@
//--------------------------------------------------
entries = new ArrayList<>();
entries.add(new AppOpsManager.OpEntry(
- ForceAppStandbyTracker.TARGET_OP,
+ AppStateTracker.TARGET_OP,
AppOpsManager.MODE_ALLOWED, 0, 0, 0, 0, null));
ops.add(new PackageOps(PACKAGE_1, UID_10_1, entries));
@@ -655,7 +692,7 @@
//--------------------------------------------------
entries = new ArrayList<>();
entries.add(new AppOpsManager.OpEntry(
- ForceAppStandbyTracker.TARGET_OP,
+ AppStateTracker.TARGET_OP,
AppOpsManager.MODE_IGNORED, 0, 0, 0, 0, null));
entries.add(new AppOpsManager.OpEntry(
AppOpsManager.OP_ACCESS_NOTIFICATIONS,
@@ -688,10 +725,10 @@
@Test
public void testPowerSaveListener() throws Exception {
- final ForceAppStandbyTrackerTestable instance = newInstance();
+ final AppStateTrackerTestable instance = newInstance();
callStart(instance);
- ForceAppStandbyTracker.Listener l = mock(ForceAppStandbyTracker.Listener.class);
+ AppStateTracker.Listener l = mock(AppStateTracker.Listener.class);
instance.addListener(l);
// Power save on.
@@ -731,10 +768,10 @@
@Test
public void testAllListeners() throws Exception {
- final ForceAppStandbyTrackerTestable instance = newInstance();
+ final AppStateTrackerTestable instance = newInstance();
callStart(instance);
- ForceAppStandbyTracker.Listener l = mock(ForceAppStandbyTracker.Listener.class);
+ AppStateTracker.Listener l = mock(AppStateTracker.Listener.class);
instance.addListener(l);
// -------------------------------------------------------------------------
@@ -1042,7 +1079,7 @@
@Test
public void testUserRemoved() throws Exception {
- final ForceAppStandbyTrackerTestable instance = newInstance();
+ final AppStateTrackerTestable instance = newInstance();
callStart(instance);
mIUidObserver.onUidActive(UID_1);
@@ -1077,7 +1114,7 @@
// This is a small battery device
mIsSmallBatteryDevice = true;
- final ForceAppStandbyTrackerTestable instance = newInstance();
+ final AppStateTrackerTestable instance = newInstance();
callStart(instance);
assertFalse(instance.isForceAllAppsStandbyEnabled());
@@ -1103,7 +1140,7 @@
// Not a small battery device, so plugged in status should not affect forced app standby
mIsSmallBatteryDevice = false;
- final ForceAppStandbyTrackerTestable instance = newInstance();
+ final AppStateTrackerTestable instance = newInstance();
callStart(instance);
assertFalse(instance.isForceAllAppsStandbyEnabled());
@@ -1152,7 +1189,7 @@
private void checkAnyAppIdUnwhitelisted(int[] prevArray, int[] newArray, boolean expected) {
assertEquals("Input: " + Arrays.toString(prevArray) + " " + Arrays.toString(newArray),
- expected, ForceAppStandbyTracker.isAnyAppIdUnwhitelisted(prevArray, newArray));
+ expected, AppStateTracker.isAnyAppIdUnwhitelisted(prevArray, newArray));
// Also test isAnyAppIdUnwhitelistedSlow.
assertEquals("Input: " + Arrays.toString(prevArray) + " " + Arrays.toString(newArray),
@@ -1184,7 +1221,7 @@
final int[] array2 = makeRandomArray();
final boolean expected = isAnyAppIdUnwhitelistedSlow(array1, array2);
- final boolean actual = ForceAppStandbyTracker.isAnyAppIdUnwhitelisted(array1, array2);
+ final boolean actual = AppStateTracker.isAnyAppIdUnwhitelisted(array1, array2);
assertEquals("Input: " + Arrays.toString(array1) + " " + Arrays.toString(array2),
expected, actual);
diff --git a/services/tests/servicestests/src/com/android/server/WatchdogDiagnosticsTest.java b/services/tests/servicestests/src/com/android/server/WatchdogDiagnosticsTest.java
new file mode 100644
index 0000000..6e76b67
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/WatchdogDiagnosticsTest.java
@@ -0,0 +1,189 @@
+/*
+ * Copyright (C) 2018 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;
+
+import static org.junit.Assert.assertEquals;
+
+import android.support.test.runner.AndroidJUnit4;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Unit tests for {@link WatchdogDiagnostics}
+ */
+@RunWith(AndroidJUnit4.class)
+public class WatchdogDiagnosticsTest {
+
+ private static class TestThread1 extends Thread {
+ Object lock1;
+ Object lock2;
+ volatile boolean inB = false;
+
+ public TestThread1(Object lock1, Object lock2) {
+ super("TestThread1");
+ this.lock1 = lock1;
+ this.lock2 = lock2;
+ }
+
+ @Override
+ public void run() {
+ a();
+ }
+
+ private void a() {
+ synchronized(lock1) {
+ b();
+ }
+ }
+
+ private void b() {
+ inB = true;
+ synchronized(lock2) {
+ // Nothing.
+ }
+ }
+ }
+
+ private static class TestThread2 extends Thread {
+ Object lock1;
+ Object lock2;
+ volatile boolean inY = false;
+
+ public TestThread2(Object lock1, Object lock2) {
+ super("TestThread2");
+ this.lock1 = lock1;
+ this.lock2 = lock2;
+ }
+
+ @Override
+ public void run() {
+ x();
+ }
+
+ private void x() {
+ synchronized(lock1) {
+ y();
+ }
+ }
+
+ private void y() {
+ synchronized(lock2) {
+ inY = true;
+ try {
+ lock2.wait();
+ } catch (Exception exc) {
+ throw new RuntimeException(exc);
+ }
+ }
+ }
+ }
+
+ @Test
+ public void printAnnotatedStack() throws Exception {
+ // Preparation.
+
+ Object heldLock1 = new Object();
+ Object heldLock2 = 0;
+ Object waitLock = "123";
+
+ TestThread1 thread1 = new TestThread1(heldLock1, heldLock2);
+ TestThread2 thread2 = new TestThread2(heldLock2, waitLock);
+
+ // Start the second thread, ensure it grabs heldLock2.
+ thread2.start();
+ while(!thread2.inY) {
+ Thread.yield();
+ }
+
+ // Start the first thread, ensure it made progress.
+ thread1.start();
+ while(!thread1.inB) {
+ Thread.yield();
+ }
+
+ // Now wait till both are no longer in runnable state.
+ while (thread1.getState() == Thread.State.RUNNABLE) {
+ Thread.yield();
+ }
+ while (thread2.getState() == Thread.State.RUNNABLE) {
+ Thread.yield();
+ }
+
+ // Now do the test.
+ StringWriter stringBuffer = new StringWriter();
+ PrintWriter print = new PrintWriter(stringBuffer, true);
+
+ {
+ WatchdogDiagnostics.printAnnotatedStack(thread1, print);
+
+ String output = stringBuffer.toString();
+ String expected =
+ "TestThread1 annotated stack trace:\n" +
+ " at com.android.server.WatchdogDiagnosticsTest$TestThread1.b(" +
+ "WatchdogDiagnosticsTest.java:59)\n" +
+ " - waiting to lock <HASH> (a java.lang.Integer)\n" +
+ " at com.android.server.WatchdogDiagnosticsTest$TestThread1.a(" +
+ "WatchdogDiagnosticsTest.java:53)\n" +
+ " - locked <HASH> (a java.lang.Object)\n" +
+ " at com.android.server.WatchdogDiagnosticsTest$TestThread1.run(" +
+ "WatchdogDiagnosticsTest.java:48)\n";
+ assertEquals(expected, filterHashes(output));
+ }
+
+ stringBuffer.getBuffer().setLength(0);
+
+ {
+ WatchdogDiagnostics.printAnnotatedStack(thread2, print);
+
+ String output = stringBuffer.toString();
+ String expected =
+ "TestThread2 annotated stack trace:\n" +
+ " at java.lang.Object.wait(Native Method)\n" +
+ " at com.android.server.WatchdogDiagnosticsTest$TestThread2.y(" +
+ "WatchdogDiagnosticsTest.java:91)\n" +
+ " - locked <HASH> (a java.lang.String)\n" +
+ " at com.android.server.WatchdogDiagnosticsTest$TestThread2.x(" +
+ "WatchdogDiagnosticsTest.java:83)\n" +
+ " - locked <HASH> (a java.lang.Integer)\n" +
+ " at com.android.server.WatchdogDiagnosticsTest$TestThread2.run(" +
+ "WatchdogDiagnosticsTest.java:78)\n";
+ assertEquals(expected, filterHashes(output));
+ }
+
+ // Let the threads finish.
+ synchronized (waitLock) {
+ waitLock.notifyAll();
+ }
+
+ thread1.join();
+ thread2.join();
+ }
+
+ /**
+ * A filter function that removes hash codes (which will change between tests and cannot be
+ * controlled.)
+ * <p>
+ * Note: leaves "<HASH>" to indicate that something was replaced.
+ */
+ private static String filterHashes(String t) {
+ return t.replaceAll("<0x[0-9a-f]{8}>", "<HASH>");
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java
index 00e27c9..ab0bfefb 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java
@@ -447,5 +447,8 @@
return new TransferOwnershipMetadataManager(
new TransferOwnershipMetadataManagerTest.MockInjector());
}
+
+ @Override
+ public void runCryptoSelfTest() {}
}
}
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncTaskTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncTaskTest.java
index ce5ee13..e40e3a4 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncTaskTest.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncTaskTest.java
@@ -275,7 +275,7 @@
}
@Test
- public void run_sendsEncryptedKeysIfAvailableToSync() throws Exception {
+ public void run_sendsEncryptedKeysIfAvailableToSync_withRawPublicKey() throws Exception {
mRecoverableKeyStoreDb.setRecoveryServicePublicKey(
TEST_USER_ID, TEST_RECOVERY_AGENT_UID, mKeyPair.getPublic());
@@ -323,6 +323,26 @@
}
@Test
+ public void run_sendsEncryptedKeysIfAvailableToSync_withCertPath() throws Exception {
+ mRecoverableKeyStoreDb.setRecoveryServiceCertPath(
+ TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TestData.CERT_PATH_1);
+ mRecoverableKeyStoreDb.setServerParams(
+ TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_VAULT_HANDLE);
+ when(mSnapshotListenersStorage.hasListener(TEST_RECOVERY_AGENT_UID)).thenReturn(true);
+ addApplicationKey(TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_APP_KEY_ALIAS);
+
+ mKeySyncTask.run();
+
+ KeyChainSnapshot keyChainSnapshot = mRecoverySnapshotStorage.get(TEST_RECOVERY_AGENT_UID);
+ verify(mSnapshotListenersStorage).recoverySnapshotAvailable(TEST_RECOVERY_AGENT_UID);
+ List<WrappedApplicationKey> applicationKeys = keyChainSnapshot.getWrappedApplicationKeys();
+ assertThat(applicationKeys).hasSize(1);
+ assertThat(keyChainSnapshot.getTrustedHardwarePublicKey())
+ .isEqualTo(SecureBox.encodePublicKey(
+ TestData.CERT_PATH_1.getCertificates().get(0).getPublicKey()));
+ }
+
+ @Test
public void run_setsCorrectSnapshotVersion() throws Exception {
mRecoverableKeyStoreDb.setRecoveryServicePublicKey(
TEST_USER_ID, TEST_RECOVERY_AGENT_UID, mKeyPair.getPublic());
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java
index 2343dee..a523b86 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java
@@ -238,15 +238,81 @@
}
@Test
- public void initRecoveryService_updatesShouldCreateSnapshot() throws Exception {
+ public void initRecoveryService_succeeds() throws Exception {
int uid = Binder.getCallingUid();
int userId = UserHandle.getCallingUserId();
- // Sync is not needed.
+ long certSerial = 1000L;
+ mRecoverableKeyStoreDb.setShouldCreateSnapshot(userId, uid, false);
+
+ mRecoverableKeyStoreManager.initRecoveryService(ROOT_CERTIFICATE_ALIAS,
+ TestData.getCertXmlWithSerial(certSerial));
+
+ assertThat(mRecoverableKeyStoreDb.getShouldCreateSnapshot(userId, uid)).isTrue();
+ assertThat(mRecoverableKeyStoreDb.getRecoveryServiceCertPath(userId, uid)).isEqualTo(
+ TestData.CERT_PATH_1);
+ assertThat(mRecoverableKeyStoreDb.getRecoveryServiceCertSerial(userId, uid)).isEqualTo(
+ certSerial);
+ assertThat(mRecoverableKeyStoreDb.getRecoveryServicePublicKey(userId, uid)).isNull();
+ }
+
+ @Test
+ public void initRecoveryService_updatesWithLargerSerial() throws Exception {
+ int uid = Binder.getCallingUid();
+ int userId = UserHandle.getCallingUserId();
+ long certSerial = 1000L;
+
+ mRecoverableKeyStoreManager.initRecoveryService(ROOT_CERTIFICATE_ALIAS,
+ TestData.getCertXmlWithSerial(certSerial));
+ mRecoverableKeyStoreManager.initRecoveryService(ROOT_CERTIFICATE_ALIAS,
+ TestData.getCertXmlWithSerial(certSerial + 1));
+
+ assertThat(mRecoverableKeyStoreDb.getRecoveryServiceCertSerial(userId, uid))
+ .isEqualTo(certSerial + 1);
+ }
+
+ @Test
+ public void initRecoveryService_ignoresSmallerSerial() throws Exception {
+ int uid = Binder.getCallingUid();
+ int userId = UserHandle.getCallingUserId();
+ long certSerial = 1000L;
+
+ mRecoverableKeyStoreManager.initRecoveryService(ROOT_CERTIFICATE_ALIAS,
+ TestData.getCertXmlWithSerial(certSerial));
+ mRecoverableKeyStoreManager.initRecoveryService(ROOT_CERTIFICATE_ALIAS,
+ TestData.getCertXmlWithSerial(certSerial - 1));
+
+ assertThat(mRecoverableKeyStoreDb.getRecoveryServiceCertSerial(userId, uid))
+ .isEqualTo(certSerial);
+ }
+
+ @Test
+ public void initRecoveryService_ignoresTheSameSerial() throws Exception {
+ int uid = Binder.getCallingUid();
+ int userId = UserHandle.getCallingUserId();
+ long certSerial = 1000L;
+
+ mRecoverableKeyStoreManager.initRecoveryService(ROOT_CERTIFICATE_ALIAS,
+ TestData.getCertXmlWithSerial(certSerial));
+ mRecoverableKeyStoreDb.setShouldCreateSnapshot(userId, uid, false);
+ mRecoverableKeyStoreManager.initRecoveryService(ROOT_CERTIFICATE_ALIAS,
+ TestData.getCertXmlWithSerial(certSerial));
+
+ // If the second update succeeds, getShouldCreateSnapshot() will return true.
+ assertThat(mRecoverableKeyStoreDb.getShouldCreateSnapshot(userId, uid)).isFalse();
+ }
+
+ @Test
+ public void initRecoveryService_succeedsWithRawPublicKey() throws Exception {
+ int uid = Binder.getCallingUid();
+ int userId = UserHandle.getCallingUserId();
mRecoverableKeyStoreDb.setShouldCreateSnapshot(userId, uid, false);
mRecoverableKeyStoreManager.initRecoveryService(ROOT_CERTIFICATE_ALIAS, TEST_PUBLIC_KEY);
assertThat(mRecoverableKeyStoreDb.getShouldCreateSnapshot(userId, uid)).isTrue();
+ assertThat(mRecoverableKeyStoreDb.getRecoveryServiceCertPath(userId, uid)).isNull();
+ assertThat(mRecoverableKeyStoreDb.getRecoveryServiceCertSerial(userId, uid)).isNull();
+ assertThat(mRecoverableKeyStoreDb.getRecoveryServicePublicKey(userId, uid)).isNotNull();
}
@Test
@@ -344,26 +410,6 @@
}
@Test
- public void startRecoverySession_throwsIfBadKey() throws Exception {
- try {
- mRecoverableKeyStoreManager.startRecoverySession(
- TEST_SESSION_ID,
- getUtf8Bytes("0"),
- TEST_VAULT_PARAMS,
- TEST_VAULT_CHALLENGE,
- ImmutableList.of(
- new KeyChainProtectionParams(
- TYPE_LOCKSCREEN,
- UI_FORMAT_PASSWORD,
- KeyDerivationParams.createSha256Params(TEST_SALT),
- TEST_SECRET)));
- fail("should have thrown");
- } catch (ServiceSpecificException e) {
- assertEquals("Not a valid X509 key", e.getMessage());
- }
- }
-
- @Test
public void startRecoverySession_throwsIfPublicKeysMismatch() throws Exception {
byte[] vaultParams = TEST_VAULT_PARAMS.clone();
vaultParams[1] ^= (byte) 1; // Flip 1 bit
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/TestData.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/TestData.java
new file mode 100644
index 0000000..0e4f91b
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/TestData.java
@@ -0,0 +1,202 @@
+package com.android.server.locksettings.recoverablekeystore;
+
+import com.android.server.locksettings.recoverablekeystore.certificate.CertUtils;
+
+import java.io.ByteArrayInputStream;
+import java.nio.charset.StandardCharsets;
+import java.security.cert.CertPath;
+import java.security.cert.CertificateFactory;
+
+public final class TestData {
+
+ private static final String CERT_PATH_ENCODING = "PkiPath";
+
+ private static final String CERT_PATH_1_BASE64 = ""
+ + "MIIIPzCCBS8wggMXoAMCAQICAhAAMA0GCSqGSIb3DQEBCwUAMCAxHjAcBgNVBAMM"
+ + "FUdvb2dsZSBDcnlwdEF1dGhWYXVsdDAeFw0xODAyMDMwMDQyMDNaFw0yODAyMDEw"
+ + "MDQyMDNaMC0xKzApBgNVBAMMIkdvb2dsZSBDcnlwdEF1dGhWYXVsdCBJbnRlcm1l"
+ + "ZGlhdGUwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDckHib0X6rQyDq"
+ + "k4519b5du0OrCPk30XXKwz+Hz5y4cGZaWKGcHOHWS2X9YApRzO00/EbvFkWVUTVG"
+ + "27wJ54V+C3HHSOAUWHhEgfFWvvHwfn9HTDx1BEk79aQqJ7DuJ06Sn/WOiMtKVAT5"
+ + "6Mi8mekBxpMOrdZqwlcLrUVsZxEHsw5/ceZu4cSWzc7SzlnbNK1cCgyRDGqWf6Gp"
+ + "3hGE86kUOtM1i95RgUIpw+w/z0wxpF6kIyQTjK+KjiYH/RBOJIEcm6sSWZlMotKL"
+ + "Sn2lhf+XL8yUxExIHTosfeb077QWW4w2BB2NZM4wPAO3w4aw33FNigDQc2SQYmnU"
+ + "EYmIcD8kx77+JWCgCxBJc2zTHXtBxWuXAQ+iegt8RO+QD97pd6XKM9xPsAOkcWLp"
+ + "79o+AJol4P5fwvgYM69mM4lwH12v86RI4aptPQOag0KDIHXyKbjaQyAgv30l4KkD"
+ + "pf2uWODhOOTwNbVPYUm3sYUlhBcbyhTk8YqN9sPU4QAao5sKTAYZgB/mlheQypTU"
+ + "wyvqz6bRzGehVB3ltP9gCyKdI04VXEUuUBWk3STyV2REQen5/LKAns6v11Cz22Zr"
+ + "EdCvNLgetnyV7CJsOa/wD/GiUWL2Ta7pzshi9ahJqrrcNPRbAzOLcNKZkFexhzPp"
+ + "onuo/pNrcaRda1frepXxVkmbsgOULwIDAQABo2YwZDAdBgNVHQ4EFgQUd6md2hCP"
+ + "lmf3VkEX5FfDxKBLbaAwHwYDVR0jBBgwFoAUm2X66jmB+eBCaZHSjGYzHM/x6fgw"
+ + "EgYDVR0TAQH/BAgwBgEB/wIBATAOBgNVHQ8BAf8EBAMCAYYwDQYJKoZIhvcNAQEL"
+ + "BQADggIBAFgShhuW+WVTowN080PLf0TWPlHACHHUPghf7rFGxgUjJypCloE84Beg"
+ + "3ROpP5l19CDqZ9OyPzA1z6VAzeGXyFhZvby7G2tZDBRP/v0u8pnSAdC5F8l8Vh2Y"
+ + "GdgE3sZD25vpdBi7P0Ef6LYQetOJXn86PqgmgW1F6lzxDjKCsi9kpeU0AWwDdOVg"
+ + "748wku50o8UEzsVwxzFd9toGlge/nn3FH5J7EuGzAlFwToHqpwTVEegaAd0l9mr5"
+ + "+rS7Urd3X80BHDqCBcXE7Uqbtzw5Y+lmowMCnW0kFN02dC9dLt2c9IxC+9sPIA5e"
+ + "TkrZBkrkTVRGLj2r29j7nC9m5VaKcBqcLZDWy8pRna8yaZprgNdE8d/WTY9nVsic"
+ + "09N8zNF5Q0bhhWa3QonlB9XW5ZqDguiclvn+5TtREzSAtSOyxM+gfG3l0wjOywIk"
+ + "1aFa52RaqAWPL67KOM6G3vKNpMnW5hrmHrijuKxiarGIoZfkZMR5ijK0uFgv3/p6"
+ + "NHL/YQBaHJJhkKet5ThiPxwW9+1k/ZcXVeY26Xh+22Gp/8to7ZW8guPPiN1hfpD+"
+ + "7f1IdSmHDrsZQQ7bfzV0bppsyNNB7e2Ecyw+GQny27nytBLJDGdRBurbwQvzppQO"
+ + "6Qmlk0rfCszh7bGCoCQNxXmuDsQ5BC+pQUqJplTqds1smyi29xs3MIIDCDCB8aAD"
+ + "AgECAgYBYVkuU0cwDQYJKoZIhvcNAQELBQAwLTErMCkGA1UEAwwiR29vZ2xlIENy"
+ + "eXB0QXV0aFZhdWx0IEludGVybWVkaWF0ZTAeFw0xODAyMDIwMTAxMDNaFw0yMDAy"
+ + "MDMwMTAxMDNaMCkxJzAlBgNVBAMTHkdvb2dsZSBDcnlwdEF1dGhWYXVsdCBJbnN0"
+ + "YW5jZTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABLgAERiYHfButJT+htocB40B"
+ + "tDr2jdxh0EZJlQ8QhpMkZuA/0t/zeSAdkVWw5b16izJ9JVOi/KVl4b0hRH54Uvow"
+ + "DQYJKoZIhvcNAQELBQADggIBAJ3PM4GNTNYzMr8E/IGsWZkLx9ARAALqBXz7As59"
+ + "F8y5UcLMqkXD/ewOfBZgF5VzjlAePyE/wSw0wc3xzvrDVVDiZaMBW1DVtSlbn25q"
+ + "00m00mmcUeyyMc7vuRkPoDshIMQTc8+U3yyYsVScSV+B4TvSx6wPZ9FpwnSPjVPD"
+ + "2GkqeMTWszuxNVEWq0wmm0K5lMaX0hfiak+4/IZxOPPGIg2py1KLA/H2gdyeqyJR"
+ + "cAsyEkfwLlushR5T9abSiPsIRcYoX8Ck8Lt+gQ7RCMefnm8CoOBKIfcjuV4PGOoe"
+ + "Xrq57VR5SsOeT07bL+D7B+mohYFI1v2G3WClAE8XgM3q8NoFFvaYmoi0+UcTduil"
+ + "47qvozjdNmjRAgu5j6vMKXEdG5Rqsja8hy0LG1hwfnR0gNiwcZ5Le3GyFnwH1Igq"
+ + "vsGOUM0ohnDUAU0zJY7nG0QYrDYe5/QPRNhWDpYkwHDiqcG28wIQCOTPAZHU2EoS"
+ + "KjSqEG2l0S5JPcor2BEde9ikSkcmK8foxlOHIdFn+n7RNF3bSEfKn1IOuXoqPidm"
+ + "eBQLevqG8KTy/C9CHqlaCNlpbIA9h+WVfsjm2s6JXBu0YbcfoIbJAmSuZVeqB/+Z"
+ + "Vvpfiad/jQWzY49fRnsSmV7VveTFPGtJxC89EadbMAinMZo+72u59319RqN5wsP2"
+ + "Zus8";
+ private static String CERT_PATH_2_BASE64 = ""
+ + "MIIFMzCCBS8wggMXoAMCAQICAhAAMA0GCSqGSIb3DQEBCwUAMCAxHjAcBgNVBAMM"
+ + "FUdvb2dsZSBDcnlwdEF1dGhWYXVsdDAeFw0xODAyMDMwMDQyMDNaFw0yODAyMDEw"
+ + "MDQyMDNaMC0xKzApBgNVBAMMIkdvb2dsZSBDcnlwdEF1dGhWYXVsdCBJbnRlcm1l"
+ + "ZGlhdGUwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDckHib0X6rQyDq"
+ + "k4519b5du0OrCPk30XXKwz+Hz5y4cGZaWKGcHOHWS2X9YApRzO00/EbvFkWVUTVG"
+ + "27wJ54V+C3HHSOAUWHhEgfFWvvHwfn9HTDx1BEk79aQqJ7DuJ06Sn/WOiMtKVAT5"
+ + "6Mi8mekBxpMOrdZqwlcLrUVsZxEHsw5/ceZu4cSWzc7SzlnbNK1cCgyRDGqWf6Gp"
+ + "3hGE86kUOtM1i95RgUIpw+w/z0wxpF6kIyQTjK+KjiYH/RBOJIEcm6sSWZlMotKL"
+ + "Sn2lhf+XL8yUxExIHTosfeb077QWW4w2BB2NZM4wPAO3w4aw33FNigDQc2SQYmnU"
+ + "EYmIcD8kx77+JWCgCxBJc2zTHXtBxWuXAQ+iegt8RO+QD97pd6XKM9xPsAOkcWLp"
+ + "79o+AJol4P5fwvgYM69mM4lwH12v86RI4aptPQOag0KDIHXyKbjaQyAgv30l4KkD"
+ + "pf2uWODhOOTwNbVPYUm3sYUlhBcbyhTk8YqN9sPU4QAao5sKTAYZgB/mlheQypTU"
+ + "wyvqz6bRzGehVB3ltP9gCyKdI04VXEUuUBWk3STyV2REQen5/LKAns6v11Cz22Zr"
+ + "EdCvNLgetnyV7CJsOa/wD/GiUWL2Ta7pzshi9ahJqrrcNPRbAzOLcNKZkFexhzPp"
+ + "onuo/pNrcaRda1frepXxVkmbsgOULwIDAQABo2YwZDAdBgNVHQ4EFgQUd6md2hCP"
+ + "lmf3VkEX5FfDxKBLbaAwHwYDVR0jBBgwFoAUm2X66jmB+eBCaZHSjGYzHM/x6fgw"
+ + "EgYDVR0TAQH/BAgwBgEB/wIBATAOBgNVHQ8BAf8EBAMCAYYwDQYJKoZIhvcNAQEL"
+ + "BQADggIBAFgShhuW+WVTowN080PLf0TWPlHACHHUPghf7rFGxgUjJypCloE84Beg"
+ + "3ROpP5l19CDqZ9OyPzA1z6VAzeGXyFhZvby7G2tZDBRP/v0u8pnSAdC5F8l8Vh2Y"
+ + "GdgE3sZD25vpdBi7P0Ef6LYQetOJXn86PqgmgW1F6lzxDjKCsi9kpeU0AWwDdOVg"
+ + "748wku50o8UEzsVwxzFd9toGlge/nn3FH5J7EuGzAlFwToHqpwTVEegaAd0l9mr5"
+ + "+rS7Urd3X80BHDqCBcXE7Uqbtzw5Y+lmowMCnW0kFN02dC9dLt2c9IxC+9sPIA5e"
+ + "TkrZBkrkTVRGLj2r29j7nC9m5VaKcBqcLZDWy8pRna8yaZprgNdE8d/WTY9nVsic"
+ + "09N8zNF5Q0bhhWa3QonlB9XW5ZqDguiclvn+5TtREzSAtSOyxM+gfG3l0wjOywIk"
+ + "1aFa52RaqAWPL67KOM6G3vKNpMnW5hrmHrijuKxiarGIoZfkZMR5ijK0uFgv3/p6"
+ + "NHL/YQBaHJJhkKet5ThiPxwW9+1k/ZcXVeY26Xh+22Gp/8to7ZW8guPPiN1hfpD+"
+ + "7f1IdSmHDrsZQQ7bfzV0bppsyNNB7e2Ecyw+GQny27nytBLJDGdRBurbwQvzppQO"
+ + "6Qmlk0rfCszh7bGCoCQNxXmuDsQ5BC+pQUqJplTqds1smyi29xs3";
+
+ private static final String THM_CERT_XML_BEFORE_SERIAL = ""
+ + "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
+ + "<certificates>\n"
+ + " <metadata>\n"
+ + " <serial>\n";
+ private static final String THM_CERT_XML_AFTER_SERIAL = ""
+ + " </serial>\n"
+ + " <creation-time>\n"
+ + " 1515697631\n"
+ + " </creation-time>\n"
+ + " <refresh-interval>\n"
+ + " 2592000\n"
+ + " </refresh-interval>\n"
+ + " <previous>\n"
+ + " <serial>\n"
+ + " 0\n"
+ + " </serial>\n"
+ + " <hash>\n"
+ + " 47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU=\n"
+ + " </hash>\n"
+ + " </previous>\n"
+ + " </metadata>\n"
+ + " <intermediates>\n"
+ + " <cert>\n"
+ + " MIIFLzCCAxegAwIBAgICEAAwDQYJKoZIhvcNAQELBQAwIDEeMBwGA1UEAwwVR29v\n"
+ + " Z2xlIENyeXB0QXV0aFZhdWx0MB4XDTE4MDIwMzAwNDIwM1oXDTI4MDIwMTAwNDIw\n"
+ + " M1owLTErMCkGA1UEAwwiR29vZ2xlIENyeXB0QXV0aFZhdWx0IEludGVybWVkaWF0\n"
+ + " ZTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANyQeJvRfqtDIOqTjnX1\n"
+ + " vl27Q6sI+TfRdcrDP4fPnLhwZlpYoZwc4dZLZf1gClHM7TT8Ru8WRZVRNUbbvAnn\n"
+ + " hX4LccdI4BRYeESB8Va+8fB+f0dMPHUESTv1pConsO4nTpKf9Y6Iy0pUBPnoyLyZ\n"
+ + " 6QHGkw6t1mrCVwutRWxnEQezDn9x5m7hxJbNztLOWds0rVwKDJEMapZ/oaneEYTz\n"
+ + " qRQ60zWL3lGBQinD7D/PTDGkXqQjJBOMr4qOJgf9EE4kgRybqxJZmUyi0otKfaWF\n"
+ + " /5cvzJTETEgdOix95vTvtBZbjDYEHY1kzjA8A7fDhrDfcU2KANBzZJBiadQRiYhw\n"
+ + " PyTHvv4lYKALEElzbNMde0HFa5cBD6J6C3xE75AP3ul3pcoz3E+wA6RxYunv2j4A\n"
+ + " miXg/l/C+Bgzr2YziXAfXa/zpEjhqm09A5qDQoMgdfIpuNpDICC/fSXgqQOl/a5Y\n"
+ + " 4OE45PA1tU9hSbexhSWEFxvKFOTxio32w9ThABqjmwpMBhmAH+aWF5DKlNTDK+rP\n"
+ + " ptHMZ6FUHeW0/2ALIp0jThVcRS5QFaTdJPJXZERB6fn8soCezq/XULPbZmsR0K80\n"
+ + " uB62fJXsImw5r/AP8aJRYvZNrunOyGL1qEmqutw09FsDM4tw0pmQV7GHM+mie6j+\n"
+ + " k2txpF1rV+t6lfFWSZuyA5QvAgMBAAGjZjBkMB0GA1UdDgQWBBR3qZ3aEI+WZ/dW\n"
+ + " QRfkV8PEoEttoDAfBgNVHSMEGDAWgBSbZfrqOYH54EJpkdKMZjMcz/Hp+DASBgNV\n"
+ + " HRMBAf8ECDAGAQH/AgEBMA4GA1UdDwEB/wQEAwIBhjANBgkqhkiG9w0BAQsFAAOC\n"
+ + " AgEAWBKGG5b5ZVOjA3TzQ8t/RNY+UcAIcdQ+CF/usUbGBSMnKkKWgTzgF6DdE6k/\n"
+ + " mXX0IOpn07I/MDXPpUDN4ZfIWFm9vLsba1kMFE/+/S7ymdIB0LkXyXxWHZgZ2ATe\n"
+ + " xkPbm+l0GLs/QR/othB604lefzo+qCaBbUXqXPEOMoKyL2Sl5TQBbAN05WDvjzCS\n"
+ + " 7nSjxQTOxXDHMV322gaWB7+efcUfknsS4bMCUXBOgeqnBNUR6BoB3SX2avn6tLtS\n"
+ + " t3dfzQEcOoIFxcTtSpu3PDlj6WajAwKdbSQU3TZ0L10u3Zz0jEL72w8gDl5OStkG\n"
+ + " SuRNVEYuPavb2PucL2blVopwGpwtkNbLylGdrzJpmmuA10Tx39ZNj2dWyJzT03zM\n"
+ + " 0XlDRuGFZrdCieUH1dblmoOC6JyW+f7lO1ETNIC1I7LEz6B8beXTCM7LAiTVoVrn\n"
+ + " ZFqoBY8vrso4zobe8o2kydbmGuYeuKO4rGJqsYihl+RkxHmKMrS4WC/f+no0cv9h\n"
+ + " AFockmGQp63lOGI/HBb37WT9lxdV5jbpeH7bYan/y2jtlbyC48+I3WF+kP7t/Uh1\n"
+ + " KYcOuxlBDtt/NXRummzI00Ht7YRzLD4ZCfLbufK0EskMZ1EG6tvBC/OmlA7pCaWT\n"
+ + " St8KzOHtsYKgJA3Fea4OxDkEL6lBSommVOp2zWybKLb3Gzc=\n"
+ + " </cert>\n"
+ + " </intermediates>\n"
+ + " <endpoints>\n"
+ + " <cert>\n"
+ + " MIIDCDCB8aADAgECAgYBYVkuU0cwDQYJKoZIhvcNAQELBQAwLTErMCkGA1UEAwwi\n"
+ + " R29vZ2xlIENyeXB0QXV0aFZhdWx0IEludGVybWVkaWF0ZTAeFw0xODAyMDIwMTAx\n"
+ + " MDNaFw0yMDAyMDMwMTAxMDNaMCkxJzAlBgNVBAMTHkdvb2dsZSBDcnlwdEF1dGhW\n"
+ + " YXVsdCBJbnN0YW5jZTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABLgAERiYHfBu\n"
+ + " tJT+htocB40BtDr2jdxh0EZJlQ8QhpMkZuA/0t/zeSAdkVWw5b16izJ9JVOi/KVl\n"
+ + " 4b0hRH54UvowDQYJKoZIhvcNAQELBQADggIBAJ3PM4GNTNYzMr8E/IGsWZkLx9AR\n"
+ + " AALqBXz7As59F8y5UcLMqkXD/ewOfBZgF5VzjlAePyE/wSw0wc3xzvrDVVDiZaMB\n"
+ + " W1DVtSlbn25q00m00mmcUeyyMc7vuRkPoDshIMQTc8+U3yyYsVScSV+B4TvSx6wP\n"
+ + " Z9FpwnSPjVPD2GkqeMTWszuxNVEWq0wmm0K5lMaX0hfiak+4/IZxOPPGIg2py1KL\n"
+ + " A/H2gdyeqyJRcAsyEkfwLlushR5T9abSiPsIRcYoX8Ck8Lt+gQ7RCMefnm8CoOBK\n"
+ + " IfcjuV4PGOoeXrq57VR5SsOeT07bL+D7B+mohYFI1v2G3WClAE8XgM3q8NoFFvaY\n"
+ + " moi0+UcTduil47qvozjdNmjRAgu5j6vMKXEdG5Rqsja8hy0LG1hwfnR0gNiwcZ5L\n"
+ + " e3GyFnwH1IgqvsGOUM0ohnDUAU0zJY7nG0QYrDYe5/QPRNhWDpYkwHDiqcG28wIQ\n"
+ + " COTPAZHU2EoSKjSqEG2l0S5JPcor2BEde9ikSkcmK8foxlOHIdFn+n7RNF3bSEfK\n"
+ + " n1IOuXoqPidmeBQLevqG8KTy/C9CHqlaCNlpbIA9h+WVfsjm2s6JXBu0YbcfoIbJ\n"
+ + " AmSuZVeqB/+ZVvpfiad/jQWzY49fRnsSmV7VveTFPGtJxC89EadbMAinMZo+72u5\n"
+ + " 9319RqN5wsP2Zus8\n"
+ + " </cert>\n"
+ + " </endpoints>\n"
+ + "</certificates>\n";
+
+ public static byte[] getCertPath1Bytes() {
+ try {
+ return CertUtils.decodeBase64(CERT_PATH_1_BASE64);
+ } catch (Exception e){
+ throw new RuntimeException(e);
+ }
+ }
+
+ public static byte[] getCertPath2Bytes() {
+ try {
+ return CertUtils.decodeBase64(CERT_PATH_2_BASE64);
+ } catch (Exception e){
+ throw new RuntimeException(e);
+ }
+ }
+
+ public static final CertPath CERT_PATH_1;
+ public static final CertPath CERT_PATH_2;
+
+ static {
+ try {
+ CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
+ CERT_PATH_1 = certFactory.generateCertPath(
+ new ByteArrayInputStream(getCertPath1Bytes()), CERT_PATH_ENCODING);
+ CERT_PATH_2 = certFactory.generateCertPath(
+ new ByteArrayInputStream(getCertPath2Bytes()), CERT_PATH_ENCODING);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public static byte[] getCertXmlWithSerial(long serial) {
+ String xml = THM_CERT_XML_BEFORE_SERIAL + serial + THM_CERT_XML_AFTER_SERIAL;
+ return xml.getBytes(StandardCharsets.UTF_8);
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbHelperTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbHelperTest.java
new file mode 100644
index 0000000..37482a3
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbHelperTest.java
@@ -0,0 +1,179 @@
+/*
+ * 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.locksettings.recoverablekeystore.storage;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import android.content.ContentValues;
+import android.content.Context;
+import android.database.sqlite.SQLiteDatabase;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import com.android.server.locksettings.recoverablekeystore.storage.RecoverableKeyStoreDbContract.KeysEntry;
+import com.android.server.locksettings.recoverablekeystore.storage.RecoverableKeyStoreDbContract.RecoveryServiceMetadataEntry;
+import com.android.server.locksettings.recoverablekeystore.storage.RecoverableKeyStoreDbContract.UserMetadataEntry;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class RecoverableKeyStoreDbHelperTest {
+
+ private static final long TEST_USER_ID = 10L;
+ private static final long TEST_UID = 60001L;
+ private static final String TEST_ALIAS = "test-alias";
+ private static final byte[] TEST_NONCE = "test-nonce".getBytes(UTF_8);
+ private static final byte[] TEST_WRAPPED_KEY = "test-wrapped-key".getBytes(UTF_8);
+ private static final long TEST_GENERATION_ID = 13L;
+ private static final long TEST_LAST_SYNCED_AT = 1517990732000L;
+ private static final int TEST_RECOVERY_STATUS = 3;
+ private static final int TEST_PLATFORM_KEY_GENERATION_ID = 11;
+ private static final int TEST_SNAPSHOT_VERSION = 31;
+ private static final int TEST_SHOULD_CREATE_SNAPSHOT = 1;
+ private static final byte[] TEST_PUBLIC_KEY = "test-public-key".getBytes(UTF_8);
+ private static final String TEST_SECRET_TYPES = "test-secret-types";
+ private static final long TEST_COUNTER_ID = -3981205205038476415L;
+ private static final byte[] TEST_SERVER_PARAMS = "test-server-params".getBytes(UTF_8);
+ private static final byte[] TEST_CERT_PATH = "test-cert-path".getBytes(UTF_8);
+ private static final long TEST_CERT_SERIAL = 1000L;
+
+ private static final String SQL_CREATE_V2_TABLE_KEYS =
+ "CREATE TABLE " + KeysEntry.TABLE_NAME + "( "
+ + KeysEntry._ID + " INTEGER PRIMARY KEY,"
+ + KeysEntry.COLUMN_NAME_USER_ID + " INTEGER,"
+ + KeysEntry.COLUMN_NAME_UID + " INTEGER,"
+ + KeysEntry.COLUMN_NAME_ALIAS + " TEXT,"
+ + KeysEntry.COLUMN_NAME_NONCE + " BLOB,"
+ + KeysEntry.COLUMN_NAME_WRAPPED_KEY + " BLOB,"
+ + KeysEntry.COLUMN_NAME_GENERATION_ID + " INTEGER,"
+ + KeysEntry.COLUMN_NAME_LAST_SYNCED_AT + " INTEGER,"
+ + KeysEntry.COLUMN_NAME_RECOVERY_STATUS + " INTEGER,"
+ + "UNIQUE(" + KeysEntry.COLUMN_NAME_UID + ","
+ + KeysEntry.COLUMN_NAME_ALIAS + "))";
+
+ private static final String SQL_CREATE_V2_TABLE_USER_METADATA =
+ "CREATE TABLE " + UserMetadataEntry.TABLE_NAME + "( "
+ + UserMetadataEntry._ID + " INTEGER PRIMARY KEY,"
+ + UserMetadataEntry.COLUMN_NAME_USER_ID + " INTEGER UNIQUE,"
+ + UserMetadataEntry.COLUMN_NAME_PLATFORM_KEY_GENERATION_ID + " INTEGER)";
+
+ private static final String SQL_CREATE_V2_TABLE_RECOVERY_SERVICE_METADATA =
+ "CREATE TABLE " + RecoveryServiceMetadataEntry.TABLE_NAME + " ("
+ + RecoveryServiceMetadataEntry._ID + " INTEGER PRIMARY KEY,"
+ + RecoveryServiceMetadataEntry.COLUMN_NAME_USER_ID + " INTEGER,"
+ + RecoveryServiceMetadataEntry.COLUMN_NAME_UID + " INTEGER,"
+ + RecoveryServiceMetadataEntry.COLUMN_NAME_SNAPSHOT_VERSION + " INTEGER,"
+ + RecoveryServiceMetadataEntry.COLUMN_NAME_SHOULD_CREATE_SNAPSHOT + " INTEGER,"
+ + RecoveryServiceMetadataEntry.COLUMN_NAME_PUBLIC_KEY + " BLOB,"
+ + RecoveryServiceMetadataEntry.COLUMN_NAME_SECRET_TYPES + " TEXT,"
+ + RecoveryServiceMetadataEntry.COLUMN_NAME_COUNTER_ID + " INTEGER,"
+ + RecoveryServiceMetadataEntry.COLUMN_NAME_SERVER_PARAMS + " BLOB,"
+ + "UNIQUE("
+ + RecoveryServiceMetadataEntry.COLUMN_NAME_USER_ID + ","
+ + RecoveryServiceMetadataEntry.COLUMN_NAME_UID + "))";
+
+ private SQLiteDatabase mDatabase;
+ private RecoverableKeyStoreDbHelper mDatabaseHelper;
+
+ @Before
+ public void setUp() throws Exception {
+ Context context = InstrumentationRegistry.getTargetContext();
+ mDatabaseHelper = new RecoverableKeyStoreDbHelper(context);
+ mDatabase = SQLiteDatabase.create(null);
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ mDatabase.close();
+ }
+
+ private void createV2Tables() throws Exception {
+ mDatabase.execSQL(SQL_CREATE_V2_TABLE_KEYS);
+ mDatabase.execSQL(SQL_CREATE_V2_TABLE_USER_METADATA);
+ mDatabase.execSQL(SQL_CREATE_V2_TABLE_RECOVERY_SERVICE_METADATA);
+ }
+
+ @Test
+ public void onCreate() throws Exception {
+ mDatabaseHelper.onCreate(mDatabase);
+ checkAllColumns();
+ }
+
+ @Test
+ public void onUpgrade_beforeV2() throws Exception {
+ mDatabaseHelper.onUpgrade(mDatabase, /*oldVersion=*/ 1,
+ RecoverableKeyStoreDbHelper.DATABASE_VERSION);
+ checkAllColumns();
+ }
+
+ @Test
+ public void onUpgrade_fromV2() throws Exception {
+ createV2Tables();
+ mDatabaseHelper.onUpgrade(mDatabase, /*oldVersion=*/ 2,
+ RecoverableKeyStoreDbHelper.DATABASE_VERSION);
+ checkAllColumns();
+ }
+
+ private void checkAllColumns() throws Exception {
+ // Check the table containing encrypted application keys
+ ContentValues values = new ContentValues();
+ values.put(KeysEntry.COLUMN_NAME_USER_ID, TEST_USER_ID);
+ values.put(KeysEntry.COLUMN_NAME_UID, TEST_UID);
+ values.put(KeysEntry.COLUMN_NAME_ALIAS, TEST_ALIAS);
+ values.put(KeysEntry.COLUMN_NAME_NONCE, TEST_NONCE);
+ values.put(KeysEntry.COLUMN_NAME_WRAPPED_KEY, TEST_WRAPPED_KEY);
+ values.put(KeysEntry.COLUMN_NAME_GENERATION_ID, TEST_GENERATION_ID);
+ values.put(KeysEntry.COLUMN_NAME_LAST_SYNCED_AT, TEST_LAST_SYNCED_AT);
+ values.put(KeysEntry.COLUMN_NAME_RECOVERY_STATUS, TEST_RECOVERY_STATUS);
+ assertThat(mDatabase.insert(KeysEntry.TABLE_NAME, /*nullColumnHack=*/ null, values))
+ .isGreaterThan(-1L);
+
+ // Check the table about user metadata
+ values = new ContentValues();
+ values.put(UserMetadataEntry.COLUMN_NAME_USER_ID, TEST_USER_ID);
+ values.put(UserMetadataEntry.COLUMN_NAME_PLATFORM_KEY_GENERATION_ID,
+ TEST_PLATFORM_KEY_GENERATION_ID);
+ assertThat(mDatabase.insert(UserMetadataEntry.TABLE_NAME, /*nullColumnHack=*/ null, values))
+ .isGreaterThan(-1L);
+
+ // Check the table about recovery service metadata
+ values = new ContentValues();
+ values.put(RecoveryServiceMetadataEntry.COLUMN_NAME_USER_ID, TEST_USER_ID);
+ values.put(RecoveryServiceMetadataEntry.COLUMN_NAME_UID, TEST_UID);
+ values.put(RecoveryServiceMetadataEntry.COLUMN_NAME_SNAPSHOT_VERSION,
+ TEST_SNAPSHOT_VERSION);
+ values.put(RecoveryServiceMetadataEntry.COLUMN_NAME_SHOULD_CREATE_SNAPSHOT,
+ TEST_SHOULD_CREATE_SNAPSHOT);
+ values.put(RecoveryServiceMetadataEntry.COLUMN_NAME_PUBLIC_KEY, TEST_PUBLIC_KEY);
+ values.put(RecoveryServiceMetadataEntry.COLUMN_NAME_SECRET_TYPES, TEST_SECRET_TYPES);
+ values.put(RecoveryServiceMetadataEntry.COLUMN_NAME_COUNTER_ID, TEST_COUNTER_ID);
+ values.put(RecoveryServiceMetadataEntry.COLUMN_NAME_SERVER_PARAMS, TEST_SERVER_PARAMS);
+ values.put(RecoveryServiceMetadataEntry.COLUMN_NAME_CERT_PATH, TEST_CERT_PATH);
+ values.put(RecoveryServiceMetadataEntry.COLUMN_NAME_CERT_SERIAL, TEST_CERT_SERIAL);
+ assertThat(
+ mDatabase.insert(RecoveryServiceMetadataEntry.TABLE_NAME, /*nullColumnHack=*/ null,
+ values))
+ .isGreaterThan(-1L);
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbTest.java
index 097d214..1c5bcd4 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbTest.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbTest.java
@@ -32,6 +32,8 @@
import android.support.test.InstrumentationRegistry;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
+
+import com.android.server.locksettings.recoverablekeystore.TestData;
import com.android.server.locksettings.recoverablekeystore.WrappedKey;
import java.io.File;
@@ -370,6 +372,57 @@
pubkey);
}
+ public void setRecoveryServiceCertPath_replaceOldValue() throws Exception {
+ int userId = 12;
+ int uid = 10009;
+ mRecoverableKeyStoreDb.setRecoveryServiceCertPath(userId, uid, TestData.CERT_PATH_1);
+ mRecoverableKeyStoreDb.setRecoveryServiceCertPath(userId, uid, TestData.CERT_PATH_2);
+ assertThat(mRecoverableKeyStoreDb.getRecoveryServiceCertPath(userId, uid)).isEqualTo(
+ TestData.CERT_PATH_2);
+ }
+
+ @Test
+ public void getRecoveryServiceCertPath_returnsNullIfNoValue() throws Exception {
+ int userId = 12;
+ int uid = 10009;
+ assertThat(mRecoverableKeyStoreDb.getRecoveryServiceCertPath(userId, uid)).isNull();
+ }
+
+ @Test
+ public void getRecoveryServiceCertPath_returnsInsertedValue() throws Exception {
+ int userId = 12;
+ int uid = 10009;
+ mRecoverableKeyStoreDb.setRecoveryServiceCertPath(userId, uid, TestData.CERT_PATH_1);
+ assertThat(mRecoverableKeyStoreDb.getRecoveryServiceCertPath(userId, uid)).isEqualTo(
+ TestData.CERT_PATH_1);
+ }
+
+ @Test
+ public void setRecoveryServiceCertSerial_replaceOldValue() throws Exception {
+ int userId = 12;
+ int uid = 10009;
+
+ mRecoverableKeyStoreDb.setRecoveryServiceCertSerial(userId, uid, 1L);
+ mRecoverableKeyStoreDb.setRecoveryServiceCertSerial(userId, uid, 3L);
+ assertThat(mRecoverableKeyStoreDb.getRecoveryServiceCertSerial(userId, uid)).isEqualTo(3L);
+ }
+
+ @Test
+ public void getRecoveryServiceCertSerial_returnsNullIfNoValue() throws Exception {
+ int userId = 12;
+ int uid = 10009;
+ assertThat(mRecoverableKeyStoreDb.getRecoveryServiceCertSerial(userId, uid)).isNull();
+ }
+
+ @Test
+ public void getRecoveryServiceCertSerial_returnsInsertedValue() throws Exception {
+ int userId = 12;
+ int uid = 10009;
+ mRecoverableKeyStoreDb.setRecoveryServiceCertSerial(userId, uid, 1234L);
+ assertThat(mRecoverableKeyStoreDb.getRecoveryServiceCertSerial(userId, uid)).isEqualTo(
+ 1234L);
+ }
+
@Test
public void getRecoveryAgents_returnsUidIfSet() throws Exception {
int userId = 12;
@@ -493,17 +546,6 @@
}
@Test
- public void getRecoveryServicePublicKey_returnsFirstKey() throws Exception {
- int userId = 68;
- int uid = 12904;
- PublicKey publicKey = genRandomPublicKey();
-
- mRecoverableKeyStoreDb.setRecoveryServicePublicKey(userId, uid, publicKey);
-
- assertThat(mRecoverableKeyStoreDb.getRecoveryServicePublicKey(userId)).isEqualTo(publicKey);
- }
-
- @Test
public void setServerParams_replaceOldValue() throws Exception {
int userId = 12;
int uid = 10009;
diff --git a/services/tests/servicestests/src/com/android/server/net/watchlist/WatchlistLoggingHandlerTests.java b/services/tests/servicestests/src/com/android/server/net/watchlist/WatchlistLoggingHandlerTests.java
index 070de5b..a38b353 100644
--- a/services/tests/servicestests/src/com/android/server/net/watchlist/WatchlistLoggingHandlerTests.java
+++ b/services/tests/servicestests/src/com/android/server/net/watchlist/WatchlistLoggingHandlerTests.java
@@ -16,18 +16,43 @@
package com.android.server.net.watchlist;
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.doAnswer;
+
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
+import android.content.Context;
+import android.content.ContextWrapper;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.UserInfo;
+import android.os.FileUtils;
+import android.os.Looper;
+import android.os.UserManager;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
+import android.support.test.InstrumentationRegistry;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.mockito.invocation.InvocationOnMock;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
+
+import java.io.File;
+import java.util.ArrayList;
import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
/**
* runtest frameworks-services -c com.android.server.net.watchlist.WatchlistLoggingHandlerTests
@@ -36,18 +61,126 @@
@SmallTest
public class WatchlistLoggingHandlerTests {
+ private static final String APK_A = "A.apk";
+ private static final String APK_B = "B.apk";
+ private static final String APK_A_CONTENT = "AAA";
+ private static final String APK_B_CONTENT = "BBB";
+ // Sha256 of "AAA"
+ private static final String APK_A_CONTENT_HASH =
+ "CB1AD2119D8FAFB69566510EE712661F9F14B83385006EF92AEC47F523A38358";
+ // Sha256 of "BBB"
+ private static final String APK_B_CONTENT_HASH =
+ "DCDB704109A454784B81229D2B05F368692E758BFA33CB61D04C1B93791B0273";
+
+ private Context mServiceContext;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+
+ final Context context = InstrumentationRegistry.getContext();
+ final UserManager mockUserManager = mock(UserManager.class);
+ final PackageManager mockPackageManager = mock(PackageManager.class);
+
+ // Context that will be used by WatchlistLoggingHandler
+ mServiceContext = new ContextWrapper(context) {
+ @Override
+ public PackageManager getPackageManager() {
+ return mockPackageManager;
+ }
+
+ @Override
+ public Object getSystemService(String name) {
+ switch (name) {
+ case Context.USER_SERVICE:
+ return mockUserManager;
+ default:
+ return super.getSystemService(name);
+ }
+ }
+ };
+
+ // Returns 2 users, user 0 and user 10
+ doAnswer((InvocationOnMock invocation) -> {
+ final ArrayList<UserInfo> info = new ArrayList<>();
+ info.add(new UserInfo(0, "user1", 0));
+ info.add(new UserInfo(10, "user2", 0));
+ return info;
+ }).when(mockUserManager).getUsers();
+
+ // Returns 2 apps, with uid 1 and uid 2
+ doAnswer((InvocationOnMock invocation) -> {
+ final List<ApplicationInfo> result = new ArrayList<>();
+ ApplicationInfo info1 = new ApplicationInfo();
+ info1.uid = 1;
+ result.add(info1);
+ ApplicationInfo info2 = new ApplicationInfo();
+ info2.uid = 2;
+ result.add(info2);
+ return result;
+ }).when(mockPackageManager).getInstalledApplications(anyInt());
+
+ // Uid 1 app with is installed in primary user and package name is "A"
+ // Uid 2 app is installed in secondary user and package name is "B"
+ doAnswer((InvocationOnMock invocation) -> {
+ int uid = (int) invocation.getArguments()[0];
+ if (uid == 1) {
+ return new String[]{"A"};
+ } else if (uid == 1000001) {
+ return null;
+ } else if (uid == 2) {
+ return null;
+ } else if (uid == 1000002) {
+ return new String[]{"B"};
+ }
+ return null;
+ }).when(mockPackageManager).getPackagesForUid(anyInt());
+
+ String fileDir = InstrumentationRegistry.getContext().getFilesDir().getAbsolutePath();
+ // Simulate app's apk file path
+ doAnswer((InvocationOnMock invocation) -> {
+ String pkg = (String) invocation.getArguments()[0];
+ PackageInfo result = new PackageInfo();
+ result.applicationInfo = new ApplicationInfo();
+ result.applicationInfo.publicSourceDir = fileDir + "/" + pkg + ".apk";
+ return result;
+ }).when(mockPackageManager).getPackageInfoAsUser(anyString(), anyInt(), anyInt());
+
+ FileUtils.bytesToFile(fileDir + "/" + APK_A, APK_A_CONTENT.getBytes());
+ FileUtils.bytesToFile(fileDir + "/" + APK_B, APK_B_CONTENT.getBytes());
+ }
+
+ @After
+ public void tearDown() {
+ String fileDir = InstrumentationRegistry.getContext().getFilesDir().getAbsolutePath();
+ new File(fileDir, APK_A).delete();
+ new File(fileDir, APK_B).delete();
+ }
+
+ @Test
+ public void testWatchlistLoggingHandler_getAllDigestsForReportWithMultiUsers()
+ throws Exception {
+ List<String> result = new WatchlistLoggingHandler(mServiceContext,
+ Looper.getMainLooper()).getAllDigestsForReport(
+ new WatchlistReportDbHelper.AggregatedResult(new HashSet<String>(), null,
+ new HashMap<String, String>()));
+ assertEquals(2, result.size());
+ assertEquals(APK_A_CONTENT_HASH, result.get(0));
+ assertEquals(APK_B_CONTENT_HASH, result.get(1));
+ }
+
@Test
public void testWatchlistLoggingHandler_getAllSubDomains() throws Exception {
String[] subDomains = WatchlistLoggingHandler.getAllSubDomains("abc.def.gh.i.jkl.mm");
- assertTrue(Arrays.equals(subDomains, new String[] {"abc.def.gh.i.jkl.mm",
+ assertTrue(Arrays.equals(subDomains, new String[]{"abc.def.gh.i.jkl.mm",
"def.gh.i.jkl.mm", "gh.i.jkl.mm", "i.jkl.mm", "jkl.mm", "mm"}));
subDomains = WatchlistLoggingHandler.getAllSubDomains(null);
assertNull(subDomains);
subDomains = WatchlistLoggingHandler.getAllSubDomains("jkl.mm");
- assertTrue(Arrays.equals(subDomains, new String[] {"jkl.mm", "mm"}));
+ assertTrue(Arrays.equals(subDomains, new String[]{"jkl.mm", "mm"}));
subDomains = WatchlistLoggingHandler.getAllSubDomains("abc");
- assertTrue(Arrays.equals(subDomains, new String[] {"abc"}));
+ assertTrue(Arrays.equals(subDomains, new String[]{"abc"}));
subDomains = WatchlistLoggingHandler.getAllSubDomains("jkl.mm.");
- assertTrue(Arrays.equals(subDomains, new String[] {"jkl.mm.", "mm."}));
+ assertTrue(Arrays.equals(subDomains, new String[]{"jkl.mm.", "mm."}));
}
}
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
index 0abb48f..98483a8 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
@@ -294,7 +294,7 @@
null /*disabledPkg*/,
null /*sharedUser*/,
UPDATED_CODE_PATH /*codePath*/,
- null /*resourcePath*/,
+ UPDATED_CODE_PATH /*resourcePath*/,
null /*legacyNativeLibraryPath*/,
"arm64-v8a" /*primaryCpuAbi*/,
"armeabi" /*secondaryCpuAbi*/,
@@ -328,7 +328,7 @@
null /*disabledPkg*/,
null /*sharedUser*/,
UPDATED_CODE_PATH /*codePath*/,
- null /*resourcePath*/,
+ UPDATED_CODE_PATH /*resourcePath*/,
null /*legacyNativeLibraryPath*/,
"arm64-v8a" /*primaryCpuAbi*/,
"armeabi" /*secondaryCpuAbi*/,
@@ -561,34 +561,6 @@
false /*notLaunched*/, false /*stopped*/, true /*installed*/);
}
- @Test
- public void testInsertPackageSetting() {
- final PackageSetting ps = createPackageSetting(0 /*sharedUserId*/, 0 /*pkgFlags*/);
- final PackageParser.Package pkg = new PackageParser.Package(PACKAGE_NAME);
- pkg.applicationInfo.setCodePath(ps.codePathString);
- pkg.applicationInfo.setResourcePath(ps.resourcePathString);
- final Context context = InstrumentationRegistry.getContext();
- final Object lock = new Object();
- PermissionManagerInternal pmInt = PermissionManagerService.create(context, null, lock);
- final Settings settings =
- new Settings(context.getFilesDir(), pmInt.getPermissionSettings(), lock);
- pkg.usesStaticLibraries = new ArrayList<>(
- Arrays.asList("foo.bar1", "foo.bar2", "foo.bar3"));
- pkg.usesStaticLibrariesVersions = new long[] {2, 4, 6};
- settings.insertPackageSettingLPw(ps, pkg);
- assertEquals(pkg, ps.pkg);
- assertArrayEquals(pkg.usesStaticLibraries.toArray(new String[0]), ps.usesStaticLibraries);
- assertArrayEquals(pkg.usesStaticLibrariesVersions, ps.usesStaticLibrariesVersions);
-
- pkg.usesStaticLibraries = null;
- pkg.usesStaticLibrariesVersions = null;
- settings.insertPackageSettingLPw(ps, pkg);
- assertEquals(pkg, ps.pkg);
- assertNull("Actual: " + Arrays.toString(ps.usesStaticLibraries), ps.usesStaticLibraries);
- assertNull("Actual: " + Arrays.toString(ps.usesStaticLibrariesVersions),
- ps.usesStaticLibrariesVersions);
- }
-
private <T> void assertArrayEquals(T[] a, T[] b) {
assertTrue("Expected: " + Arrays.toString(a) + ", actual: " + Arrays.toString(b),
Arrays.equals(a, b));
diff --git a/services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java
index 2284bbb..63ac4af 100644
--- a/services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java
@@ -28,6 +28,7 @@
import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR;
import static android.view.WindowManager.LayoutParams.TYPE_VOICE_INTERACTION;
import static com.android.server.wm.WindowContainer.POSITION_TOP;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
@@ -38,7 +39,6 @@
import android.annotation.SuppressLint;
import android.content.res.Configuration;
import android.graphics.Path;
-import android.graphics.Point;
import android.graphics.Rect;
import android.os.SystemClock;
import android.platform.test.annotations.Presubmit;
@@ -166,6 +166,7 @@
assertTrue(appWin.canBeImeTarget());
WindowState imeTarget = mDisplayContent.computeImeTarget(false /* updateImeTarget */);
assertEquals(appWin, imeTarget);
+ appWin.mHidden = false;
// Verify that an child window can be an ime target.
final WindowState childWin = createWindow(appWin,
diff --git a/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java b/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java
index 000cf38..7a55904 100644
--- a/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java
+++ b/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java
@@ -16,6 +16,7 @@
package com.android.server.wm;
+import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR;
@@ -107,6 +108,13 @@
sWm = WindowManagerService.main(context, ims, true, false,
false, new TestWindowManagerPolicy());
+
+ sWm.onInitReady();
+
+ // Display creation is driven by the ActivityManagerService via ActivityStackSupervisor.
+ // We emulate those steps here.
+ sWm.mRoot.createDisplayContent(sWm.mDisplayManager.getDisplay(DEFAULT_DISPLAY),
+ mock(DisplayWindowController.class));
}
return sWm;
}
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowStateTests.java b/services/tests/servicestests/src/com/android/server/wm/WindowStateTests.java
index 6a4710b..74c72bf 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowStateTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowStateTests.java
@@ -36,10 +36,15 @@
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA_OVERLAY;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_SUB_PANEL;
import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.verify;
/**
* Tests for the {@link WindowState} class.
@@ -57,15 +62,17 @@
final WindowState child1 = createWindow(parentWindow, FIRST_SUB_WINDOW, "child1");
final WindowState child2 = createWindow(parentWindow, FIRST_SUB_WINDOW, "child2");
- assertFalse(parentWindow.mHidden);
+ // parentWindow is initially set to hidden.
+ assertTrue(parentWindow.mHidden);
+ assertFalse(parentWindow.isParentWindowHidden());
+ assertTrue(child1.isParentWindowHidden());
+ assertTrue(child2.isParentWindowHidden());
+
+ parentWindow.mHidden = false;
assertFalse(parentWindow.isParentWindowHidden());
assertFalse(child1.isParentWindowHidden());
assertFalse(child2.isParentWindowHidden());
- parentWindow.mHidden = true;
- assertFalse(parentWindow.isParentWindowHidden());
- assertTrue(child1.isParentWindowHidden());
- assertTrue(child2.isParentWindowHidden());
}
@Test
@@ -237,11 +244,11 @@
}
private void testPrepareWindowToDisplayDuringRelayout(boolean wasVisible) {
+ reset(mPowerManagerWrapper);
final WindowState root = createWindow(null, TYPE_APPLICATION, "root");
root.mAttrs.flags |= WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON;
- root.mTurnOnScreen = false;
root.prepareWindowToDisplayDuringRelayout(wasVisible /*wasVisible*/);
- assertTrue(root.mTurnOnScreen);
+ verify(mPowerManagerWrapper).wakeUp(anyLong(), anyString());
}
}
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java
index 69b1378..91d5ea4 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java
@@ -29,6 +29,7 @@
import org.junit.Assert;
import org.junit.After;
import org.junit.Before;
+import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import android.content.Context;
@@ -84,6 +85,9 @@
WindowState mChildAppWindowBelow;
HashSet<WindowState> mCommonWindows;
+ @Mock
+ static WindowState.PowerManagerWrapper mPowerManagerWrapper;
+
@Before
public void setUp() throws Exception {
if (!sOneTimeSetupDone) {
@@ -245,7 +249,7 @@
attrs.setTitle(name);
final WindowState w = new WindowState(sWm, sMockSession, sIWindow, token, parent, OP_NONE,
- 0, attrs, VISIBLE, ownerId, ownerCanAddInternalSystemWindow);
+ 0, attrs, VISIBLE, ownerId, ownerCanAddInternalSystemWindow, mPowerManagerWrapper);
// TODO: Probably better to make this call in the WindowState ctor to avoid errors with
// adding it to the token...
token.addWindow(w);
@@ -284,7 +288,8 @@
final int displayId = sNextDisplayId++;
final Display display = new Display(DisplayManagerGlobal.getInstance(), displayId,
mDisplayInfo, DEFAULT_DISPLAY_ADJUSTMENTS);
- return new DisplayContent(display, sWm, new WallpaperController(sWm));
+ return new DisplayContent(display, sWm, new WallpaperController(sWm),
+ mock(DisplayWindowController.class));
}
/** Creates a {@link com.android.server.wm.WindowTestUtils.TestWindowState} */
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index d6f61cd..7b2c040 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -188,6 +188,11 @@
protected void reportSeen(NotificationRecord r) {
return;
}
+
+ @Override
+ protected void reportUserInteraction(NotificationRecord r) {
+ return;
+ }
}
@Before
diff --git a/services/usage/java/com/android/server/usage/AppStandbyController.java b/services/usage/java/com/android/server/usage/AppStandbyController.java
index cc21199..32db752 100644
--- a/services/usage/java/com/android/server/usage/AppStandbyController.java
+++ b/services/usage/java/com/android/server/usage/AppStandbyController.java
@@ -177,6 +177,12 @@
long mAppIdleParoleDurationMillis;
long[] mAppStandbyScreenThresholds = SCREEN_TIME_THRESHOLDS;
long[] mAppStandbyElapsedThresholds = ELAPSED_TIME_THRESHOLDS;
+ /** Minimum time a strong usage event should keep the bucket elevated. */
+ long mStrongUsageTimeoutMillis;
+ /** Minimum time a notification seen event should keep the bucket elevated. */
+ long mNotificationSeenTimeoutMillis;
+ /** Minimum time a system update event should keep the buckets elevated. */
+ long mSystemUpdateUsageTimeoutMillis;
volatile boolean mAppIdleEnabled;
boolean mAppIdleTempParoled;
@@ -330,7 +336,7 @@
synchronized (mAppIdleLock) {
AppUsageHistory appUsage = mAppIdleHistory.reportUsage(packageName, userId,
STANDBY_BUCKET_ACTIVE, elapsedRealtime,
- elapsedRealtime + 2 * ONE_HOUR);
+ elapsedRealtime + mStrongUsageTimeoutMillis);
maybeInformListeners(packageName, userId, elapsedRealtime,
appUsage.currentBucket, false);
}
@@ -539,6 +545,7 @@
}
}
+ @GuardedBy("mAppIdleLock")
@StandbyBuckets int getBucketForLocked(String packageName, int userId,
long elapsedRealtime) {
int bucketIndex = mAppIdleHistory.getThresholdIndex(packageName, userId,
@@ -627,11 +634,11 @@
if (event.mEventType == UsageEvents.Event.NOTIFICATION_SEEN) {
mAppIdleHistory.reportUsage(appHistory, event.mPackage,
STANDBY_BUCKET_WORKING_SET,
- elapsedRealtime, elapsedRealtime + 2 * ONE_HOUR);
+ elapsedRealtime, elapsedRealtime + mNotificationSeenTimeoutMillis);
} else {
mAppIdleHistory.reportUsage(event.mPackage, userId,
STANDBY_BUCKET_ACTIVE,
- elapsedRealtime, elapsedRealtime + 2 * ONE_HOUR);
+ elapsedRealtime, elapsedRealtime + mStrongUsageTimeoutMillis);
}
final boolean userStartedInteracting =
@@ -1113,10 +1120,10 @@
final PackageInfo pi = packages.get(i);
String packageName = pi.packageName;
if (pi.applicationInfo != null && pi.applicationInfo.isSystemApp()) {
- // Mark app as used for 4 hours. After that it can timeout to whatever the
+ // Mark app as used for 2 hours. After that it can timeout to whatever the
// past usage pattern was.
mAppIdleHistory.reportUsage(packageName, userId, STANDBY_BUCKET_ACTIVE, 0,
- elapsedRealtime + 4 * ONE_HOUR);
+ elapsedRealtime + mSystemUpdateUsageTimeoutMillis);
}
}
}
@@ -1395,6 +1402,12 @@
private static final String KEY_PAROLE_DURATION = "parole_duration";
private static final String KEY_SCREEN_TIME_THRESHOLDS = "screen_thresholds";
private static final String KEY_ELAPSED_TIME_THRESHOLDS = "elapsed_thresholds";
+ private static final String KEY_STRONG_USAGE_HOLD_DURATION = "strong_usage_duration";
+ private static final String KEY_NOTIFICATION_SEEN_HOLD_DURATION =
+ "notification_seen_duration";
+ private static final String KEY_SYSTEM_UPDATE_HOLD_DURATION =
+ "system_update_usage_duration";
+
private final KeyValueListParser mParser = new KeyValueListParser(',');
@@ -1455,7 +1468,15 @@
ELAPSED_TIME_THRESHOLDS);
mCheckIdleIntervalMillis = Math.min(mAppStandbyElapsedThresholds[1] / 4,
COMPRESS_TIME ? ONE_MINUTE : 4 * 60 * ONE_MINUTE); // 4 hours
-
+ mStrongUsageTimeoutMillis = mParser.getDurationMillis
+ (KEY_STRONG_USAGE_HOLD_DURATION,
+ COMPRESS_TIME ? ONE_MINUTE : 1 * ONE_HOUR);
+ mNotificationSeenTimeoutMillis = mParser.getDurationMillis
+ (KEY_NOTIFICATION_SEEN_HOLD_DURATION,
+ COMPRESS_TIME ? 12 * ONE_MINUTE : 12 * ONE_HOUR);
+ mSystemUpdateUsageTimeoutMillis = mParser.getDurationMillis
+ (KEY_SYSTEM_UPDATE_HOLD_DURATION,
+ COMPRESS_TIME ? 2 * ONE_MINUTE : 2 * ONE_HOUR);
}
}
diff --git a/services/usb/java/com/android/server/usb/UsbProfileGroupSettingsManager.java b/services/usb/java/com/android/server/usb/UsbProfileGroupSettingsManager.java
index 4b2d9b9..43f189b 100644
--- a/services/usb/java/com/android/server/usb/UsbProfileGroupSettingsManager.java
+++ b/services/usb/java/com/android/server/usb/UsbProfileGroupSettingsManager.java
@@ -314,6 +314,7 @@
* Upgrade any single-user settings from {@link #sSingleUserSettingsFile}.
* Should only by called by owner.
*/
+ @GuardedBy("mLock")
private void upgradeSingleUserLocked() {
if (sSingleUserSettingsFile.exists()) {
mDevicePreferenceMap.clear();
@@ -347,6 +348,7 @@
}
}
+ @GuardedBy("mLock")
private void readSettingsLocked() {
if (DEBUG) Slog.v(TAG, "readSettingsLocked()");
@@ -386,6 +388,7 @@
* <p>In the uncommon case that the system crashes in between the scheduling and the write the
* update is lost.</p>
*/
+ @GuardedBy("mLock")
private void scheduleWriteSettingsLocked() {
if (mIsWriteSettingsScheduled) {
return;
@@ -869,6 +872,7 @@
return null;
}
+ @GuardedBy("mLock")
private boolean clearCompatibleMatchesLocked(@NonNull UserPackage userPackage,
@NonNull DeviceFilter filter) {
ArrayList<DeviceFilter> keysToRemove = new ArrayList<>();
@@ -892,6 +896,7 @@
return !keysToRemove.isEmpty();
}
+ @GuardedBy("mLock")
private boolean clearCompatibleMatchesLocked(@NonNull UserPackage userPackage,
@NonNull AccessoryFilter filter) {
ArrayList<AccessoryFilter> keysToRemove = new ArrayList<>();
@@ -915,6 +920,7 @@
return !keysToRemove.isEmpty();
}
+ @GuardedBy("mLock")
private boolean handlePackageAddedLocked(UserPackage userPackage, ActivityInfo aInfo,
String metaDataName) {
XmlResourceParser parser = null;
diff --git a/services/usb/java/com/android/server/usb/UsbService.java b/services/usb/java/com/android/server/usb/UsbService.java
index 1edc469..c1a7591 100644
--- a/services/usb/java/com/android/server/usb/UsbService.java
+++ b/services/usb/java/com/android/server/usb/UsbService.java
@@ -236,6 +236,7 @@
*
* @return Iff the caller is in the current user's profile group
*/
+ @GuardedBy("mLock")
private boolean isCallerInCurrentUserProfileGroupLocked() {
int userIdInt = UserHandle.getCallingUserId();
diff --git a/telecomm/java/android/telecom/Call.java b/telecomm/java/android/telecom/Call.java
index 6799417..8c18518 100644
--- a/telecomm/java/android/telecom/Call.java
+++ b/telecomm/java/android/telecom/Call.java
@@ -352,8 +352,11 @@
*/
public static final int CAPABILITY_CAN_PULL_CALL = 0x00800000;
+ /** Call supports the deflect feature. */
+ public static final int CAPABILITY_SUPPORT_DEFLECT = 0x01000000;
+
//******************************************************************************************
- // Next CAPABILITY value: 0x01000000
+ // Next CAPABILITY value: 0x02000000
//******************************************************************************************
/**
@@ -528,6 +531,9 @@
if (can(capabilities, CAPABILITY_CAN_PULL_CALL)) {
builder.append(" CAPABILITY_CAN_PULL_CALL");
}
+ if (can(capabilities, CAPABILITY_SUPPORT_DEFLECT)) {
+ builder.append(" CAPABILITY_SUPPORT_DEFLECT");
+ }
builder.append("]");
return builder.toString();
}
@@ -1235,6 +1241,15 @@
}
/**
+ * Instructs this {@link #STATE_RINGING} {@code Call} to deflect.
+ *
+ * @param address The address to which the call will be deflected.
+ */
+ public void deflect(Uri address) {
+ mInCallAdapter.deflectCall(mTelecomCallId, address);
+ }
+
+ /**
* Instructs this {@link #STATE_RINGING} {@code Call} to reject.
*
* @param rejectWithMessage Whether to reject with a text message.
diff --git a/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java
index e0b0bbf..24184e0 100644
--- a/telecomm/java/android/telecom/Connection.java
+++ b/telecomm/java/android/telecom/Connection.java
@@ -328,8 +328,11 @@
*/
public static final int CAPABILITY_CAN_PULL_CALL = 0x01000000;
+ /** Call supports the deflect feature. */
+ public static final int CAPABILITY_SUPPORT_DEFLECT = 0x02000000;
+
//**********************************************************************************************
- // Next CAPABILITY value: 0x02000000
+ // Next CAPABILITY value: 0x04000000
//**********************************************************************************************
/**
@@ -726,6 +729,9 @@
if (can(capabilities, CAPABILITY_CAN_PULL_CALL)) {
builder.append(isLong ? " CAPABILITY_CAN_PULL_CALL" : " pull");
}
+ if (can(capabilities, CAPABILITY_SUPPORT_DEFLECT)) {
+ builder.append(isLong ? " CAPABILITY_SUPPORT_DEFLECT" : " sup_def");
+ }
builder.append("]");
return builder.toString();
@@ -2752,6 +2758,12 @@
/**
* Notifies this Connection, which is in {@link #STATE_RINGING}, of
+ * a request to deflect.
+ */
+ public void onDeflect(Uri address) {}
+
+ /**
+ * Notifies this Connection, which is in {@link #STATE_RINGING}, of
* a request to reject.
*/
public void onReject() {}
diff --git a/telecomm/java/android/telecom/ConnectionService.java b/telecomm/java/android/telecom/ConnectionService.java
index c1040ad..211699e 100644
--- a/telecomm/java/android/telecom/ConnectionService.java
+++ b/telecomm/java/android/telecom/ConnectionService.java
@@ -124,6 +124,7 @@
private static final String SESSION_ABORT = "CS.ab";
private static final String SESSION_ANSWER = "CS.an";
private static final String SESSION_ANSWER_VIDEO = "CS.anV";
+ private static final String SESSION_DEFLECT = "CS.def";
private static final String SESSION_REJECT = "CS.r";
private static final String SESSION_REJECT_MESSAGE = "CS.rWM";
private static final String SESSION_SILENCE = "CS.s";
@@ -181,6 +182,7 @@
private static final int MSG_CONNECTION_SERVICE_FOCUS_GAINED = 31;
private static final int MSG_HANDOVER_FAILED = 32;
private static final int MSG_HANDOVER_COMPLETE = 33;
+ private static final int MSG_DEFLECT = 34;
private static Connection sNullConnection;
@@ -353,6 +355,20 @@
}
@Override
+ public void deflect(String callId, Uri address, Session.Info sessionInfo) {
+ Log.startSession(sessionInfo, SESSION_DEFLECT);
+ try {
+ SomeArgs args = SomeArgs.obtain();
+ args.arg1 = callId;
+ args.arg2 = address;
+ args.arg3 = Log.createSubsession();
+ mHandler.obtainMessage(MSG_DEFLECT, args).sendToTarget();
+ } finally {
+ Log.endSession();
+ }
+ }
+
+ @Override
public void reject(String callId, Session.Info sessionInfo) {
Log.startSession(sessionInfo, SESSION_REJECT);
try {
@@ -847,6 +863,17 @@
}
break;
}
+ case MSG_DEFLECT: {
+ SomeArgs args = (SomeArgs) msg.obj;
+ Log.continueSession((Session) args.arg3, SESSION_HANDLER + SESSION_DEFLECT);
+ try {
+ deflect((String) args.arg1, (Uri) args.arg2);
+ } finally {
+ args.recycle();
+ Log.endSession();
+ }
+ break;
+ }
case MSG_REJECT: {
SomeArgs args = (SomeArgs) msg.obj;
Log.continueSession((Session) args.arg2, SESSION_HANDLER + SESSION_REJECT);
@@ -1605,6 +1632,11 @@
findConnectionForAction(callId, "answer").onAnswer();
}
+ private void deflect(String callId, Uri address) {
+ Log.d(this, "deflect %s", callId);
+ findConnectionForAction(callId, "deflect").onDeflect(address);
+ }
+
private void reject(String callId) {
Log.d(this, "reject %s", callId);
findConnectionForAction(callId, "reject").onReject();
diff --git a/telecomm/java/android/telecom/DisconnectCause.java b/telecomm/java/android/telecom/DisconnectCause.java
index dcf5c27..1de67a5 100644
--- a/telecomm/java/android/telecom/DisconnectCause.java
+++ b/telecomm/java/android/telecom/DisconnectCause.java
@@ -33,47 +33,48 @@
public final class DisconnectCause implements Parcelable {
/** Disconnected because of an unknown or unspecified reason. */
- public static final int UNKNOWN = 0;
+ public static final int UNKNOWN = TelecomProtoEnums.UNKNOWN; // = 0
/** Disconnected because there was an error, such as a problem with the network. */
- public static final int ERROR = 1;
+ public static final int ERROR = TelecomProtoEnums.ERROR; // = 1
/** Disconnected because of a local user-initiated action, such as hanging up. */
- public static final int LOCAL = 2;
+ public static final int LOCAL = TelecomProtoEnums.LOCAL; // = 2
/**
* Disconnected because of a remote user-initiated action, such as the other party hanging up
* up.
*/
- public static final int REMOTE = 3;
+ public static final int REMOTE = TelecomProtoEnums.REMOTE; // = 3
/** Disconnected because it has been canceled. */
- public static final int CANCELED = 4;
+ public static final int CANCELED = TelecomProtoEnums.CANCELED; // = 4
/** Disconnected because there was no response to an incoming call. */
- public static final int MISSED = 5;
+ public static final int MISSED = TelecomProtoEnums.MISSED; // = 5
/** Disconnected because the user rejected an incoming call. */
- public static final int REJECTED = 6;
+ public static final int REJECTED = TelecomProtoEnums.REJECTED; // = 6
/** Disconnected because the other party was busy. */
- public static final int BUSY = 7;
+ public static final int BUSY = TelecomProtoEnums.BUSY; // = 7
/**
* Disconnected because of a restriction on placing the call, such as dialing in airplane
* mode.
*/
- public static final int RESTRICTED = 8;
+ public static final int RESTRICTED = TelecomProtoEnums.RESTRICTED; // = 8
/** Disconnected for reason not described by other disconnect codes. */
- public static final int OTHER = 9;
+ public static final int OTHER = TelecomProtoEnums.OTHER; // = 9
/**
* Disconnected because the connection manager did not support the call. The call will be tried
* again without a connection manager. See {@link PhoneAccount#CAPABILITY_CONNECTION_MANAGER}.
*/
- public static final int CONNECTION_MANAGER_NOT_SUPPORTED = 10;
+ public static final int CONNECTION_MANAGER_NOT_SUPPORTED =
+ TelecomProtoEnums.CONNECTION_MANAGER_NOT_SUPPORTED; // = 10
/**
* Disconnected because the user did not locally answer the incoming call, but it was answered
* on another device where the call was ringing.
*/
- public static final int ANSWERED_ELSEWHERE = 11;
+ public static final int ANSWERED_ELSEWHERE = TelecomProtoEnums.ANSWERED_ELSEWHERE; // = 11
/**
* Disconnected because the call was pulled from the current device to another device.
*/
- public static final int CALL_PULLED = 12;
+ public static final int CALL_PULLED = TelecomProtoEnums.CALL_PULLED; // = 12
/**
* Reason code (returned via {@link #getReason()}) which indicates that a call could not be
diff --git a/telecomm/java/android/telecom/InCallAdapter.java b/telecomm/java/android/telecom/InCallAdapter.java
index 658685f..8678e33 100644
--- a/telecomm/java/android/telecom/InCallAdapter.java
+++ b/telecomm/java/android/telecom/InCallAdapter.java
@@ -16,6 +16,7 @@
package android.telecom;
+import android.net.Uri;
import android.bluetooth.BluetoothDevice;
import android.os.Bundle;
import android.os.RemoteException;
@@ -61,6 +62,19 @@
}
/**
+ * Instructs Telecom to deflect the specified call.
+ *
+ * @param callId The identifier of the call to deflect.
+ * @param address The address to deflect.
+ */
+ public void deflectCall(String callId, Uri address) {
+ try {
+ mAdapter.deflectCall(callId, address);
+ } catch (RemoteException e) {
+ }
+ }
+
+ /**
* Instructs Telecom to reject the specified call.
*
* @param callId The identifier of the call to reject.
diff --git a/telecomm/java/android/telecom/InCallService.java b/telecomm/java/android/telecom/InCallService.java
index fcf04c9..af65c65 100644
--- a/telecomm/java/android/telecom/InCallService.java
+++ b/telecomm/java/android/telecom/InCallService.java
@@ -60,6 +60,26 @@
* </service>
* }
* </pre>
+ * <p>
+ * In addition to implementing the {@link InCallService} API, you must also declare an activity in
+ * your manifest which handles the {@link Intent#ACTION_DIAL} intent. The example below illustrates
+ * how this is done:
+ * <pre>
+ * {@code
+ * <activity android:name="your.package.YourDialerActivity"
+ * android:label="@string/yourDialerActivityLabel">
+ * <intent-filter>
+ * <action android:name="android.intent.action.DIAL" />
+ * <category android:name="android.intent.category.DEFAULT" />
+ * </intent-filter>
+ * </activity>
+ * }
+ * </pre>
+ * <p>
+ * When a user installs your application and runs it for the first time, you should prompt the user
+ * to see if they would like your application to be the new default phone app. See the
+ * {@link TelecomManager#ACTION_CHANGE_DEFAULT_DIALER} intent documentation for more information on
+ * how to do this.
*/
public abstract class InCallService extends Service {
diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java
index 1fe5db5..a180da6 100644
--- a/telecomm/java/android/telecom/TelecomManager.java
+++ b/telecomm/java/android/telecom/TelecomManager.java
@@ -1320,7 +1320,7 @@
public boolean endCall() {
try {
if (isServiceConnected()) {
- return getTelecomService().endCall();
+ return getTelecomService().endCall(mContext.getPackageName());
}
} catch (RemoteException e) {
Log.e(TAG, "Error calling ITelecomService#endCall", e);
diff --git a/telecomm/java/com/android/internal/telecom/IConnectionService.aidl b/telecomm/java/com/android/internal/telecom/IConnectionService.aidl
index 3a84f00..e35093c 100644
--- a/telecomm/java/com/android/internal/telecom/IConnectionService.aidl
+++ b/telecomm/java/com/android/internal/telecom/IConnectionService.aidl
@@ -16,6 +16,7 @@
package com.android.internal.telecom;
+import android.net.Uri;
import android.os.Bundle;
import android.os.ParcelFileDescriptor;
import android.telecom.CallAudioState;
@@ -58,6 +59,8 @@
void answer(String callId, in Session.Info sessionInfo);
+ void deflect(String callId, in Uri address, in Session.Info sessionInfo);
+
void reject(String callId, in Session.Info sessionInfo);
void rejectWithMessage(String callId, String message, in Session.Info sessionInfo);
diff --git a/telecomm/java/com/android/internal/telecom/IInCallAdapter.aidl b/telecomm/java/com/android/internal/telecom/IInCallAdapter.aidl
index 87ccd3e..57df5c1 100644
--- a/telecomm/java/com/android/internal/telecom/IInCallAdapter.aidl
+++ b/telecomm/java/com/android/internal/telecom/IInCallAdapter.aidl
@@ -16,6 +16,7 @@
package com.android.internal.telecom;
+import android.net.Uri;
import android.os.Bundle;
import android.telecom.PhoneAccountHandle;
@@ -29,6 +30,8 @@
oneway interface IInCallAdapter {
void answerCall(String callId, int videoState);
+ void deflectCall(String callId, in Uri address);
+
void rejectCall(String callId, boolean rejectWithMessage, String textMessage);
void disconnectCall(String callId);
diff --git a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
index 3460754..b4e7d56 100644
--- a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
+++ b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
@@ -187,7 +187,7 @@
/**
* @see TelecomServiceImpl#endCall
*/
- boolean endCall();
+ boolean endCall(String callingPackage);
/**
* @see TelecomServiceImpl#acceptRingingCall
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 7eb691b..c8c898a 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -1376,6 +1376,12 @@
*/
public static final String KEY_ALLOW_HOLD_IN_IMS_CALL_BOOL = "allow_hold_in_ims_call";
+ /**
+ * Flag indicating whether the carrier supports call deflection for an incoming IMS call.
+ * @hide
+ */
+ public static final String KEY_CARRIER_ALLOW_DEFLECT_IMS_CALL_BOOL =
+ "carrier_allow_deflect_ims_call_bool";
/**
* Flag indicating whether the carrier always wants to play an "on-hold" tone when a call has
@@ -1844,6 +1850,7 @@
static {
sDefaults = new PersistableBundle();
sDefaults.putBoolean(KEY_ALLOW_HOLD_IN_IMS_CALL_BOOL, true);
+ sDefaults.putBoolean(KEY_CARRIER_ALLOW_DEFLECT_IMS_CALL_BOOL, false);
sDefaults.putBoolean(KEY_ALWAYS_PLAY_REMOTE_HOLD_TONE_BOOL, false);
sDefaults.putBoolean(KEY_ADDITIONAL_CALL_SETTING_BOOL, true);
sDefaults.putBoolean(KEY_ALLOW_EMERGENCY_NUMBERS_IN_CALL_LOG_BOOL, false);
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 03a8f33..03d8b5c 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -904,6 +904,61 @@
public static final String EVENT_CALL_FORWARDED =
"android.telephony.event.EVENT_CALL_FORWARDED";
+ /**
+ * {@link android.telecom.Connection} event used to indicate that a supplementary service
+ * notification has been received.
+ * <p>
+ * Sent via {@link android.telecom.Connection#sendConnectionEvent(String, Bundle)}.
+ * The {@link Bundle} parameter is expected to include the following extras:
+ * <ul>
+ * <li>{@link #EXTRA_NOTIFICATION_TYPE} - the notification type.</li>
+ * <li>{@link #EXTRA_NOTIFICATION_CODE} - the notification code.</li>
+ * <li>{@link #EXTRA_NOTIFICATION_MESSAGE} - human-readable message associated with the
+ * supplementary service notification.</li>
+ * </ul>
+ * @hide
+ */
+ public static final String EVENT_SUPPLEMENTARY_SERVICE_NOTIFICATION =
+ "android.telephony.event.EVENT_SUPPLEMENTARY_SERVICE_NOTIFICATION";
+
+ /**
+ * Integer extra key used with {@link #EVENT_SUPPLEMENTARY_SERVICE_NOTIFICATION} which indicates
+ * the type of supplementary service notification which occurred.
+ * Will be either
+ * {@link com.android.internal.telephony.gsm.SuppServiceNotification#NOTIFICATION_TYPE_CODE_1}
+ * or
+ * {@link com.android.internal.telephony.gsm.SuppServiceNotification#NOTIFICATION_TYPE_CODE_2}
+ * <p>
+ * Set in the extras for the {@link #EVENT_SUPPLEMENTARY_SERVICE_NOTIFICATION} connection event.
+ * @hide
+ */
+ public static final String EXTRA_NOTIFICATION_TYPE =
+ "android.telephony.extra.NOTIFICATION_TYPE";
+
+ /**
+ * Integer extra key used with {@link #EVENT_SUPPLEMENTARY_SERVICE_NOTIFICATION} which indicates
+ * the supplementary service notification which occurred.
+ * <p>
+ * Depending on the {@link #EXTRA_NOTIFICATION_TYPE}, the code will be one of the {@code CODE_*}
+ * codes defined in {@link com.android.internal.telephony.gsm.SuppServiceNotification}.
+ * <p>
+ * Set in the extras for the {@link #EVENT_SUPPLEMENTARY_SERVICE_NOTIFICATION} connection event.
+ * @hide
+ */
+ public static final String EXTRA_NOTIFICATION_CODE =
+ "android.telephony.extra.NOTIFICATION_CODE";
+
+ /**
+ * {@link CharSequence} extra key used with {@link #EVENT_SUPPLEMENTARY_SERVICE_NOTIFICATION}
+ * which contains a human-readable message which can be displayed to the user for the
+ * supplementary service notification.
+ * <p>
+ * Set in the extras for the {@link #EVENT_SUPPLEMENTARY_SERVICE_NOTIFICATION} connection event.
+ * @hide
+ */
+ public static final String EXTRA_NOTIFICATION_MESSAGE =
+ "android.telephony.extra.NOTIFICATION_MESSAGE";
+
/* Visual voicemail protocols */
/**
diff --git a/telephony/java/android/telephony/UiccSlotInfo.java b/telephony/java/android/telephony/UiccSlotInfo.java
index 0b3cbad..0c17147 100644
--- a/telephony/java/android/telephony/UiccSlotInfo.java
+++ b/telephony/java/android/telephony/UiccSlotInfo.java
@@ -55,10 +55,11 @@
/** Card state restricted. */
public static final int CARD_STATE_INFO_RESTRICTED = 4;
- public final boolean isActive;
- public final boolean isEuicc;
- public final String cardId;
- public final @CardStateInfo int cardStateInfo;
+ private final boolean mIsActive;
+ private final boolean mIsEuicc;
+ private final String mCardId;
+ private final @CardStateInfo int mCardStateInfo;
+ private final int mLogicalSlotIdx;
public static final Creator<UiccSlotInfo> CREATOR = new Creator<UiccSlotInfo>() {
@Override
@@ -73,18 +74,20 @@
};
private UiccSlotInfo(Parcel in) {
- isActive = in.readByte() != 0;
- isEuicc = in.readByte() != 0;
- cardId = in.readString();
- cardStateInfo = in.readInt();
+ mIsActive = in.readByte() != 0;
+ mIsEuicc = in.readByte() != 0;
+ mCardId = in.readString();
+ mCardStateInfo = in.readInt();
+ mLogicalSlotIdx = in.readInt();
}
@Override
public void writeToParcel(Parcel dest, int flags) {
- dest.writeByte((byte) (isActive ? 1 : 0));
- dest.writeByte((byte) (isEuicc ? 1 : 0));
- dest.writeString(cardId);
- dest.writeInt(cardStateInfo);
+ dest.writeByte((byte) (mIsActive ? 1 : 0));
+ dest.writeByte((byte) (mIsEuicc ? 1 : 0));
+ dest.writeString(mCardId);
+ dest.writeInt(mCardStateInfo);
+ dest.writeInt(mLogicalSlotIdx);
}
@Override
@@ -93,28 +96,33 @@
}
public UiccSlotInfo(boolean isActive, boolean isEuicc, String cardId,
- @CardStateInfo int cardStateInfo) {
- this.isActive = isActive;
- this.isEuicc = isEuicc;
- this.cardId = cardId;
- this.cardStateInfo = cardStateInfo;
+ @CardStateInfo int cardStateInfo, int logicalSlotIdx) {
+ this.mIsActive = isActive;
+ this.mIsEuicc = isEuicc;
+ this.mCardId = cardId;
+ this.mCardStateInfo = cardStateInfo;
+ this.mLogicalSlotIdx = logicalSlotIdx;
}
public boolean getIsActive() {
- return isActive;
+ return mIsActive;
}
public boolean getIsEuicc() {
- return isEuicc;
+ return mIsEuicc;
}
public String getCardId() {
- return cardId;
+ return mCardId;
}
@CardStateInfo
public int getCardStateInfo() {
- return cardStateInfo;
+ return mCardStateInfo;
+ }
+
+ public int getLogicalSlotIdx() {
+ return mLogicalSlotIdx;
}
@Override
@@ -127,32 +135,36 @@
}
UiccSlotInfo that = (UiccSlotInfo) obj;
- return (isActive == that.isActive)
- && (isEuicc == that.isEuicc)
- && (cardId == that.cardId)
- && (cardStateInfo == that.cardStateInfo);
+ return (mIsActive == that.mIsActive)
+ && (mIsEuicc == that.mIsEuicc)
+ && (mCardId == that.mCardId)
+ && (mCardStateInfo == that.mCardStateInfo)
+ && (mLogicalSlotIdx == that.mLogicalSlotIdx);
}
@Override
public int hashCode() {
int result = 1;
- result = 31 * result + (isActive ? 1 : 0);
- result = 31 * result + (isEuicc ? 1 : 0);
- result = 31 * result + Objects.hashCode(cardId);
- result = 31 * result + cardStateInfo;
+ result = 31 * result + (mIsActive ? 1 : 0);
+ result = 31 * result + (mIsEuicc ? 1 : 0);
+ result = 31 * result + Objects.hashCode(mCardId);
+ result = 31 * result + mCardStateInfo;
+ result = 31 * result + mLogicalSlotIdx;
return result;
}
@Override
public String toString() {
- return "UiccSlotInfo (isActive="
- + isActive
- + ", isEuicc="
- + isEuicc
- + ", cardId="
- + cardId
+ return "UiccSlotInfo (mIsActive="
+ + mIsActive
+ + ", mIsEuicc="
+ + mIsEuicc
+ + ", mCardId="
+ + mCardId
+ ", cardState="
- + cardStateInfo
+ + mCardStateInfo
+ + ", phoneId="
+ + mLogicalSlotIdx
+ ")";
}
}
diff --git a/telephony/java/android/telephony/ims/ImsCallSession.java b/telephony/java/android/telephony/ims/ImsCallSession.java
index c3d103f..a20d4f5 100644
--- a/telephony/java/android/telephony/ims/ImsCallSession.java
+++ b/telephony/java/android/telephony/ims/ImsCallSession.java
@@ -754,6 +754,22 @@
}
/**
+ * Deflects an incoming call.
+ *
+ * @param number number to be deflected to
+ */
+ public void deflect(String number) {
+ if (mClosed) {
+ return;
+ }
+
+ try {
+ miSession.deflect(number);
+ } catch (RemoteException e) {
+ }
+ }
+
+ /**
* Rejects an incoming call or session update.
*
* @param reason reason code to reject an incoming call
diff --git a/telephony/java/android/telephony/ims/compat/stub/ImsCallSessionImplBase.java b/telephony/java/android/telephony/ims/compat/stub/ImsCallSessionImplBase.java
index 00cb1e2..e5ed825 100644
--- a/telephony/java/android/telephony/ims/compat/stub/ImsCallSessionImplBase.java
+++ b/telephony/java/android/telephony/ims/compat/stub/ImsCallSessionImplBase.java
@@ -182,6 +182,15 @@
}
/**
+ * Deflects an incoming call.
+ *
+ * @param deflectNumber number to deflect the call
+ */
+ @Override
+ public void deflect(String deflectNumber) {
+ }
+
+ /**
* Rejects an incoming call or session update.
*
* @param reason reason code to reject an incoming call, defined in {@link ImsReasonInfo}.
diff --git a/telephony/java/android/telephony/ims/stub/ImsCallSessionImplBase.java b/telephony/java/android/telephony/ims/stub/ImsCallSessionImplBase.java
index c6ca6fd..7b9fe2b 100644
--- a/telephony/java/android/telephony/ims/stub/ImsCallSessionImplBase.java
+++ b/telephony/java/android/telephony/ims/stub/ImsCallSessionImplBase.java
@@ -174,6 +174,11 @@
}
@Override
+ public void deflect(String deflectNumber) {
+ ImsCallSessionImplBase.this.deflect(deflectNumber);
+ }
+
+ @Override
public void reject(int reason) {
ImsCallSessionImplBase.this.reject(reason);
}
@@ -395,6 +400,14 @@
}
/**
+ * Deflects an incoming call.
+ *
+ * @param deflectNumber number to deflect the call
+ */
+ public void deflect(String deflectNumber) {
+ }
+
+ /**
* Rejects an incoming call or session update.
*
* @param reason reason code to reject an incoming call, defined in {@link ImsReasonInfo}.
diff --git a/telephony/java/com/android/ims/internal/IImsCallSession.aidl b/telephony/java/com/android/ims/internal/IImsCallSession.aidl
index 203e6cf..15234e5 100644
--- a/telephony/java/com/android/ims/internal/IImsCallSession.aidl
+++ b/telephony/java/com/android/ims/internal/IImsCallSession.aidl
@@ -136,6 +136,13 @@
void accept(int callType, in ImsStreamMediaProfile profile);
/**
+ * Deflects an incoming call.
+ *
+ * @param deflectNumber number to deflect the call
+ */
+ void deflect(String deflectNumber);
+
+ /**
* Rejects an incoming call or session update.
*
* @param reason reason code to reject an incoming call
diff --git a/tests/ActivityManagerPerfTests/test-app/AndroidManifest.xml b/tests/ActivityManagerPerfTests/test-app/AndroidManifest.xml
index 021e386..ec5a9c6 100644
--- a/tests/ActivityManagerPerfTests/test-app/AndroidManifest.xml
+++ b/tests/ActivityManagerPerfTests/test-app/AndroidManifest.xml
@@ -15,6 +15,9 @@
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.frameworks.perftests.amteststestapp">
+ <uses-sdk
+ android:minSdkVersion="21"
+ android:targetSdkVersion="27" />
<application android:name=".TestApplication">
<activity android:name=".TestActivity" android:exported="true"/>
<provider
diff --git a/tests/ActivityManagerPerfTests/tests/AndroidManifest.xml b/tests/ActivityManagerPerfTests/tests/AndroidManifest.xml
index 4e194c6..a1ab33a 100644
--- a/tests/ActivityManagerPerfTests/tests/AndroidManifest.xml
+++ b/tests/ActivityManagerPerfTests/tests/AndroidManifest.xml
@@ -15,6 +15,9 @@
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.frameworks.perftests.amtests">
+ <uses-sdk
+ android:minSdkVersion="21"
+ android:targetSdkVersion="27" />
<uses-permission android:name="android.permission.DUMP" />
<uses-permission android:name="android.permission.FORCE_STOP_PACKAGES" />
diff --git a/tests/FrameworkPerf/AndroidManifest.xml b/tests/FrameworkPerf/AndroidManifest.xml
index 2591aaf..d62ef9e 100644
--- a/tests/FrameworkPerf/AndroidManifest.xml
+++ b/tests/FrameworkPerf/AndroidManifest.xml
@@ -1,5 +1,6 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.frameworkperf">
+ <uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-sdk android:minSdkVersion="5" />
diff --git a/tests/JankBench/Android.mk b/tests/JankBench/Android.mk
index 12568a0..291ba78 100644
--- a/tests/JankBench/Android.mk
+++ b/tests/JankBench/Android.mk
@@ -19,7 +19,7 @@
LOCAL_STATIC_ANDROID_LIBRARIES := \
- android-support-design \
+ $(ANDROID_SUPPORT_DESIGN_TARGETS) \
android-support-v4 \
android-support-v7-appcompat \
android-support-v7-cardview \
diff --git a/tests/OneMedia/AndroidManifest.xml b/tests/OneMedia/AndroidManifest.xml
index c6824ec..8697f1b 100644
--- a/tests/OneMedia/AndroidManifest.xml
+++ b/tests/OneMedia/AndroidManifest.xml
@@ -5,6 +5,7 @@
android:versionName="1.0" >
<uses-sdk android:minSdkVersion="19"/>
+ <uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
diff --git a/tests/UiBench/Android.mk b/tests/UiBench/Android.mk
index 60327e5..c8e6c20 100644
--- a/tests/UiBench/Android.mk
+++ b/tests/UiBench/Android.mk
@@ -15,7 +15,7 @@
LOCAL_USE_AAPT2 := true
LOCAL_STATIC_ANDROID_LIBRARIES := \
- android-support-design \
+ $(ANDROID_SUPPORT_DESIGN_TARGETS) \
android-support-v4 \
android-support-v7-appcompat \
android-support-v7-cardview \
diff --git a/tools/aapt/ResourceTable.cpp b/tools/aapt/ResourceTable.cpp
index 669afe1..734a5ab 100644
--- a/tools/aapt/ResourceTable.cpp
+++ b/tools/aapt/ResourceTable.cpp
@@ -4848,6 +4848,7 @@
const String16 pathInterpolator16("pathInterpolator");
const String16 objectAnimator16("objectAnimator");
const String16 gradient16("gradient");
+ const String16 animatedSelector16("animated-selector");
const int minSdk = getMinSdkVersion(bundle);
if (minSdk >= SDK_LOLLIPOP_MR1) {
@@ -4876,7 +4877,8 @@
node->getElementName() == animatedVector16 ||
node->getElementName() == objectAnimator16 ||
node->getElementName() == pathInterpolator16 ||
- node->getElementName() == gradient16)) {
+ node->getElementName() == gradient16 ||
+ node->getElementName() == animatedSelector16)) {
// We were told not to version vector tags, so skip the children here.
continue;
}
diff --git a/tools/aapt2/cmd/Link.cpp b/tools/aapt2/cmd/Link.cpp
index c9e272c..15c5eae 100644
--- a/tools/aapt2/cmd/Link.cpp
+++ b/tools/aapt2/cmd/Link.cpp
@@ -450,7 +450,7 @@
static bool IsVectorElement(const std::string& name) {
return name == "vector" || name == "animated-vector" || name == "pathInterpolator" ||
- name == "objectAnimator" || name == "gradient";
+ name == "objectAnimator" || name == "gradient" || name == "animated-selector";
}
template <typename T>
@@ -1446,6 +1446,13 @@
ContainerReaderEntry* entry;
ContainerReader reader(input_stream.get());
+
+ if (reader.HadError()) {
+ context_->GetDiagnostics()->Error(DiagMessage(src)
+ << "failed to read file: " << reader.GetError());
+ return false;
+ }
+
while ((entry = reader.Next()) != nullptr) {
if (entry->Type() == ContainerEntryType::kResTable) {
pb::ResourceTable pb_table;
diff --git a/wifi/java/android/net/wifi/IRttManager.aidl b/wifi/java/android/net/wifi/IRttManager.aidl
deleted file mode 100644
index 3831809..0000000
--- a/wifi/java/android/net/wifi/IRttManager.aidl
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * Copyright (C) 2008 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.net.wifi;
-import android.os.Messenger;
-import android.net.wifi.RttManager;
-
-/**
- * {@hide}
- */
-interface IRttManager
-{
- Messenger getMessenger(in IBinder binder, out int[] key);
- RttManager.RttCapabilities getRttCapabilities();
-}
diff --git a/wifi/java/android/net/wifi/RttManager.java b/wifi/java/android/net/wifi/RttManager.java
index fe63aa1..bdbc149 100644
--- a/wifi/java/android/net/wifi/RttManager.java
+++ b/wifi/java/android/net/wifi/RttManager.java
@@ -12,7 +12,7 @@
import android.net.wifi.rtt.RangingResult;
import android.net.wifi.rtt.RangingResultCallback;
import android.net.wifi.rtt.WifiRttManager;
-import android.os.Looper;
+import android.os.Handler;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.Log;
@@ -24,6 +24,7 @@
/** @hide */
@SystemApi
+@Deprecated
@SystemService(Context.WIFI_RTT_SERVICE)
public class RttManager {
@@ -181,6 +182,7 @@
/**
* This class describe the RTT capability of the Hardware
*/
+ @Deprecated
public static class RttCapabilities implements Parcelable {
/** @deprecated It is not supported*/
@Deprecated
@@ -314,12 +316,16 @@
};
}
+ /**
+ * This method is deprecated. Please use the {@link WifiRttManager} API.
+ */
@RequiresPermission(Manifest.permission.LOCATION_HARDWARE)
public RttCapabilities getRttCapabilities() {
return mRttCapabilities;
}
/** specifies parameters for RTT request */
+ @Deprecated
public static class RttParams {
/**
* type of destination device being ranged
@@ -502,6 +508,7 @@
}
/** pseudo-private class used to parcel arguments */
+ @Deprecated
public static class ParcelableRttParams implements Parcelable {
@NonNull
@@ -589,12 +596,14 @@
};
}
+ @Deprecated
public static class WifiInformationElement {
/** Information Element ID 0xFF means element is invalid. */
public byte id;
public byte[] data;
}
/** specifies RTT results */
+ @Deprecated
public static class RttResult {
/** mac address of the device being ranged. */
public String bssid;
@@ -746,6 +755,7 @@
/** pseudo-private class used to parcel results. */
+ @Deprecated
public static class ParcelableRttResults implements Parcelable {
public RttResult mResults[];
@@ -838,8 +848,8 @@
}
dest.writeByte(result.LCR.id);
if (result.LCR.id != (byte) 0xFF) {
- dest.writeInt((byte) result.LCR.data.length);
- dest.writeByte(result.LCR.id);
+ dest.writeByte((byte) result.LCR.data.length);
+ dest.writeByteArray(result.LCR.data);
}
dest.writeByte(result.secure ? (byte) 1 : 0);
}
@@ -911,7 +921,7 @@
};
}
-
+ @Deprecated
public static interface RttListener {
public void onSuccess(RttResult[] results);
public void onFailure(int reason, String description);
@@ -919,52 +929,10 @@
}
/**
- * A parcelable that contains rtt client information.
- *
- * @hide
- */
- public static class RttClient implements Parcelable {
- // Package name of RttClient.
- private final String mPackageName;
-
- public RttClient(String packageName) {
- mPackageName = packageName;
- }
-
- protected RttClient(Parcel in) {
- mPackageName = in.readString();
- }
-
- public static final Creator<RttManager.RttClient> CREATOR =
- new Creator<RttManager.RttClient>() {
- @Override
- public RttManager.RttClient createFromParcel(Parcel in) {
- return new RttManager.RttClient(in);
- }
-
- @Override
- public RttManager.RttClient[] newArray(int size) {
- return new RttManager.RttClient[size];
- }
- };
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void writeToParcel(Parcel parcel, int i) {
- parcel.writeString(mPackageName);
- }
-
- public String getPackageName() {
- return mPackageName;
- }
- }
-
- /**
* Request to start an RTT ranging
+ * <p>
+ * This method is deprecated. Please use the
+ * {@link WifiRttManager#startRanging(RangingRequest, RangingResultCallback, Handler)} API.
*
* @param params -- RTT request Parameters
* @param listener -- Call back to inform RTT result
@@ -1036,6 +1004,10 @@
}
}
+ /**
+ * This method is deprecated and performs no function. Please use the {@link WifiRttManager}
+ * API.
+ */
@RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE)
public void stopRanging(RttListener listener) {
Log.e(TAG, "stopRanging: unsupported operation - nop");
@@ -1050,6 +1022,7 @@
* The client can freely destroy or reuse the callback after {@link RttManager#disableResponder}
* is called.
*/
+ @Deprecated
public abstract static class ResponderCallback {
/** Callback when responder is enabled. */
public abstract void onResponderEnabled(ResponderConfig config);
@@ -1065,6 +1038,10 @@
* Note calling this method with the same callback when the responder is already enabled won't
* change the responder state, a cached {@link ResponderConfig} from the last enabling will be
* returned through the callback.
+ * <p>
+ * This method is deprecated and will throw an {@link UnsupportedOperationException}
+ * exception. Please use the {@link WifiRttManager} API to perform a Wi-Fi Aware peer-to-peer
+ * ranging.
*
* @param callback Callback for responder enabling/disabling result.
* @throws IllegalArgumentException If {@code callback} is null.
@@ -1081,6 +1058,10 @@
* <p>
* Calling this method when responder isn't enabled won't have any effect. The callback can be
* reused for enabling responder after this method is called.
+ * <p>
+ * This method is deprecated and will throw an {@link UnsupportedOperationException}
+ * exception. Please use the {@link WifiRttManager} API to perform a Wi-Fi Aware peer-to-peer
+ * ranging.
*
* @param callback The same callback used for enabling responder.
* @throws IllegalArgumentException If {@code callback} is null.
@@ -1097,6 +1078,7 @@
*
* @see ScanResult
*/
+ @Deprecated
public static class ResponderConfig implements Parcelable {
// TODO: make all fields final once we can get mac address from responder HAL APIs.
@@ -1202,7 +1184,6 @@
/** @hide */
public static final int CMD_OP_REG_BINDER = BASE + 9;
- private final Context mContext;
private final WifiRttManager mNewService;
private RttCapabilities mRttCapabilities;
@@ -1211,17 +1192,14 @@
* Applications will almost always want to use
* {@link android.content.Context#getSystemService Context.getSystemService()} to retrieve
* the standard {@link android.content.Context#WIFI_RTT_SERVICE Context.WIFI_RTT_SERVICE}.
- * @param context the application context
- * @param service the Binder interface
- * @param looper Looper for running the callbacks.
+ * @param service the new WifiRttManager service
*
* @hide
*/
- public RttManager(Context context, IRttManager service, Looper looper) {
- mContext = context;
- mNewService = (WifiRttManager) mContext.getSystemService(Context.WIFI_RTT_RANGING_SERVICE);
+ public RttManager(Context context, WifiRttManager service) {
+ mNewService = service;
- boolean rttSupported = mContext.getPackageManager().hasSystemFeature(
+ boolean rttSupported = context.getPackageManager().hasSystemFeature(
PackageManager.FEATURE_WIFI_RTT);
mRttCapabilities = new RttCapabilities();
diff --git a/wifi/java/android/net/wifi/ScanResult.java b/wifi/java/android/net/wifi/ScanResult.java
index c46789c..8024bf0 100644
--- a/wifi/java/android/net/wifi/ScanResult.java
+++ b/wifi/java/android/net/wifi/ScanResult.java
@@ -273,27 +273,6 @@
public RadioChainInfo[] radioChainInfos;
/**
- * @hide
- * Update RSSI of the scan result
- * @param previousRssi
- * @param previousSeen
- * @param maxAge
- */
- public void averageRssi(int previousRssi, long previousSeen, int maxAge) {
-
- if (seen == 0) {
- seen = System.currentTimeMillis();
- }
- long age = seen - previousSeen;
-
- if (previousSeen > 0 && age > 0 && age < maxAge/2) {
- // Average the RSSI with previously seen instances of this scan result
- double alpha = 0.5 - (double) age / (double) maxAge;
- level = (int) ((double) level * (1 - alpha) + (double) previousRssi * alpha);
- }
- }
-
- /**
* Status indicating the scan result does not correspond to a user's saved configuration
* @hide
* @removed
diff --git a/wifi/java/android/net/wifi/WifiConfiguration.java b/wifi/java/android/net/wifi/WifiConfiguration.java
index 7da2656..ddcf327 100644
--- a/wifi/java/android/net/wifi/WifiConfiguration.java
+++ b/wifi/java/android/net/wifi/WifiConfiguration.java
@@ -917,6 +917,9 @@
* Does not guarantee that the returned address is valid for use.
*/
public MacAddress getRandomizedMacAddress() {
+ if (mRandomizedMacAddress == null) {
+ mRandomizedMacAddress = MacAddress.ALL_ZEROS_ADDRESS;
+ }
return mRandomizedMacAddress;
}
@@ -1617,6 +1620,7 @@
creatorUid = -1;
shared = true;
dtimInterval = 0;
+ mRandomizedMacAddress = MacAddress.ALL_ZEROS_ADDRESS;
}
/**
diff --git a/wifi/java/android/net/wifi/rtt/RangingRequest.java b/wifi/java/android/net/wifi/rtt/RangingRequest.java
index 32f21b9..52b3d86 100644
--- a/wifi/java/android/net/wifi/rtt/RangingRequest.java
+++ b/wifi/java/android/net/wifi/rtt/RangingRequest.java
@@ -156,14 +156,18 @@
/**
* Add the device specified by the {@code peerMacAddress} to the list of devices with
* which to measure range.
- *
+ * <p>
* The MAC address may be obtained out-of-band from a peer Wi-Fi Aware device. A Wi-Fi
* Aware device may obtain its MAC address using the {@link IdentityChangedListener}
* provided to
* {@link WifiAwareManager#attach(AttachCallback, IdentityChangedListener, Handler)}.
- *
- * * Note: in order to use this API the device must support Wi-Fi Aware
- * {@link android.net.wifi.aware}.
+ * <p>
+ * Note: in order to use this API the device must support Wi-Fi Aware
+ * {@link android.net.wifi.aware}. The peer device which is being ranged to must be
+ * configured to publish a service (with any name) with:
+ * <li>Type {@link android.net.wifi.aware.PublishConfig#PUBLISH_TYPE_UNSOLICITED}.
+ * <li>Ranging enabled
+ * {@link android.net.wifi.aware.PublishConfig.Builder#setRangingEnabled(boolean)}.
*
* @param peerMacAddress The MAC address of the Wi-Fi Aware peer.
* @return The builder, to facilitate chaining {@code builder.setXXX(..).setXXX(..)}.
@@ -179,12 +183,16 @@
/**
* Add a device specified by a {@link PeerHandle} to the list of devices with which to
* measure range.
- *
+ * <p>
* The {@link PeerHandle} may be obtained as part of the Wi-Fi Aware discovery process. E.g.
* using {@link DiscoverySessionCallback#onServiceDiscovered(PeerHandle, byte[], List)}.
- *
+ * <p>
* Note: in order to use this API the device must support Wi-Fi Aware
- * {@link android.net.wifi.aware}.
+ * {@link android.net.wifi.aware}. The peer device which is being ranged to must be
+ * configured to publish a service (with any name) with:
+ * <li>Type {@link android.net.wifi.aware.PublishConfig#PUBLISH_TYPE_UNSOLICITED}.
+ * <li>Ranging enabled
+ * {@link android.net.wifi.aware.PublishConfig.Builder#setRangingEnabled(boolean)}.
*
* @param peerHandle The peer handler of the peer Wi-Fi Aware device.
* @return The builder, to facilitate chaining {@code builder.setXXX(..).setXXX(..)}.
diff --git a/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java b/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java
index e7377c1..8a3a7f5 100644
--- a/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java
@@ -176,6 +176,8 @@
@Test
public void testGetOrCreateRandomizedMacAddress_SavesAndReturnsSameAddress() {
WifiConfiguration config = new WifiConfiguration();
+ assertEquals(MacAddress.ALL_ZEROS_ADDRESS, config.getRandomizedMacAddress());
+
MacAddress firstMacAddress = config.getOrCreateRandomizedMacAddress();
MacAddress secondMacAddress = config.getOrCreateRandomizedMacAddress();
@@ -185,6 +187,8 @@
@Test
public void testSetRandomizedMacAddress_ChangesSavedAddress() {
WifiConfiguration config = new WifiConfiguration();
+ assertEquals(MacAddress.ALL_ZEROS_ADDRESS, config.getRandomizedMacAddress());
+
MacAddress macToChangeInto = MacAddress.createRandomUnicastAddress();
config.setRandomizedMacAddress(macToChangeInto);
MacAddress macAfterChange = config.getOrCreateRandomizedMacAddress();