Merge "Import some plugin documentation into the tree"
diff --git a/api/current.txt b/api/current.txt
index c0223b7..1c97c66 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -6595,7 +6595,7 @@
method public void uninstallCaCert(android.content.ComponentName, byte[]);
method public boolean updateOverrideApn(android.content.ComponentName, int, android.telephony.data.ApnSetting);
method public void wipeData(int);
- method public void wipeDataWithReason(int, java.lang.CharSequence);
+ method public void wipeData(int, java.lang.CharSequence);
field public static final java.lang.String ACTION_ADD_DEVICE_ADMIN = "android.app.action.ADD_DEVICE_ADMIN";
field public static final java.lang.String ACTION_APPLICATION_DELEGATION_SCOPES_CHANGED = "android.app.action.APPLICATION_DELEGATION_SCOPES_CHANGED";
field public static final java.lang.String ACTION_DEVICE_ADMIN_SERVICE = "android.app.action.DEVICE_ADMIN_SERVICE";
@@ -14510,7 +14510,9 @@
ctor public AnimatedImageDrawable();
method public void clearAnimationCallbacks();
method public void draw(android.graphics.Canvas);
+ method public int getLoopCount();
method public int getOpacity();
+ method public final boolean isAutoMirrored();
method public boolean isRunning();
method public void registerAnimationCallback(android.graphics.drawable.Animatable2.AnimationCallback);
method public void setAlpha(int);
@@ -24580,13 +24582,19 @@
field public static final int DIRECTIONALITY_OMNI = 1; // 0x1
field public static final int DIRECTIONALITY_SUPER_CARDIOID = 5; // 0x5
field public static final int DIRECTIONALITY_UNKNOWN = 0; // 0x0
+ field public static final int GROUP_UNKNOWN = -1; // 0xffffffff
+ field public static final int INDEX_IN_THE_GROUP_UNKNOWN = -1; // 0xffffffff
field public static final int LOCATION_MAINBODY = 1; // 0x1
field public static final int LOCATION_MAINBODY_MOVABLE = 2; // 0x2
field public static final int LOCATION_PERIPHERAL = 3; // 0x3
field public static final int LOCATION_UNKNOWN = 0; // 0x0
+ field public static final android.media.MicrophoneInfo.Coordinate3F ORIENTATION_UNKNOWN;
+ field public static final android.media.MicrophoneInfo.Coordinate3F POSITION_UNKNOWN;
+ field public static final float SENSITIVITY_UNKNOWN = -3.4028235E38f;
+ field public static final float SPL_UNKNOWN = -3.4028235E38f;
}
- public class MicrophoneInfo.Coordinate3F {
+ public static final class MicrophoneInfo.Coordinate3F {
field public final float x;
field public final float y;
field public final float z;
@@ -32987,7 +32995,7 @@
method public android.os.StrictMode.ThreadPolicy.Builder penaltyDialog();
method public android.os.StrictMode.ThreadPolicy.Builder penaltyDropBox();
method public android.os.StrictMode.ThreadPolicy.Builder penaltyFlashScreen();
- method public android.os.StrictMode.ThreadPolicy.Builder penaltyListener(android.os.StrictMode.OnThreadViolationListener, java.util.concurrent.Executor);
+ method public android.os.StrictMode.ThreadPolicy.Builder penaltyListener(java.util.concurrent.Executor, android.os.StrictMode.OnThreadViolationListener);
method public android.os.StrictMode.ThreadPolicy.Builder penaltyLog();
method public android.os.StrictMode.ThreadPolicy.Builder permitAll();
method public android.os.StrictMode.ThreadPolicy.Builder permitCustomSlowCalls();
@@ -33019,7 +33027,7 @@
method public android.os.StrictMode.VmPolicy.Builder penaltyDeathOnCleartextNetwork();
method public android.os.StrictMode.VmPolicy.Builder penaltyDeathOnFileUriExposure();
method public android.os.StrictMode.VmPolicy.Builder penaltyDropBox();
- method public android.os.StrictMode.VmPolicy.Builder penaltyListener(android.os.StrictMode.OnVmViolationListener, java.util.concurrent.Executor);
+ method public android.os.StrictMode.VmPolicy.Builder penaltyListener(java.util.concurrent.Executor, android.os.StrictMode.OnVmViolationListener);
method public android.os.StrictMode.VmPolicy.Builder penaltyLog();
method public android.os.StrictMode.VmPolicy.Builder setClassInstanceLimit(java.lang.Class, int);
}
@@ -38253,17 +38261,6 @@
method public byte[] transmit(byte[]) throws java.io.IOException;
}
- public abstract interface ISecureElementListener implements android.os.IInterface {
- method public abstract void serviceConnected() throws android.os.RemoteException;
- }
-
- public static abstract class ISecureElementListener.Stub extends android.os.Binder implements android.se.omapi.ISecureElementListener {
- ctor public ISecureElementListener.Stub();
- method public android.os.IBinder asBinder();
- method public static android.se.omapi.ISecureElementListener asInterface(android.os.IBinder);
- method public boolean onTransact(int, android.os.Parcel, android.os.Parcel, int) throws android.os.RemoteException;
- }
-
public class Reader {
method public void closeSessions();
method public java.lang.String getName();
@@ -38273,13 +38270,19 @@
}
public class SEService {
- ctor public SEService(android.content.Context, android.se.omapi.ISecureElementListener);
+ ctor public SEService(android.content.Context, android.se.omapi.SEService.SecureElementListener);
method public android.se.omapi.Reader[] getReaders();
method public java.lang.String getVersion();
method public boolean isConnected();
method public void shutdown();
}
+ public static abstract class SEService.SecureElementListener extends android.os.Binder {
+ ctor public SEService.SecureElementListener();
+ method public android.os.IBinder asBinder();
+ method public void serviceConnected();
+ }
+
public class Session {
method public void close();
method public void closeChannels();
diff --git a/api/removed.txt b/api/removed.txt
index 55022f3..79c54fd 100644
--- a/api/removed.txt
+++ b/api/removed.txt
@@ -257,6 +257,14 @@
ctor public RecoverySystem();
}
+ public static final class StrictMode.ThreadPolicy.Builder {
+ method public android.os.StrictMode.ThreadPolicy.Builder penaltyListener(android.os.StrictMode.OnThreadViolationListener, java.util.concurrent.Executor);
+ }
+
+ public static final class StrictMode.VmPolicy.Builder {
+ method public android.os.StrictMode.VmPolicy.Builder penaltyListener(android.os.StrictMode.OnVmViolationListener, java.util.concurrent.Executor);
+ }
+
public final class SystemClock {
method public static java.time.Clock elapsedRealtimeClock();
method public static java.time.Clock uptimeClock();
diff --git a/api/system-current.txt b/api/system-current.txt
index d24a313..0932223 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -204,6 +204,7 @@
field public static final int isVrOnly = 16844152; // 0x1010578
field public static final int requiredSystemPropertyName = 16844133; // 0x1010565
field public static final int requiredSystemPropertyValue = 16844134; // 0x1010566
+ field public static final int userRestriction = 16844165; // 0x1010585
}
public static final class R.raw {
@@ -4176,6 +4177,8 @@
method public static boolean putString(android.content.ContentResolver, java.lang.String, java.lang.String, java.lang.String, boolean);
method public static void resetToDefaults(android.content.ContentResolver, java.lang.String);
field public static final java.lang.String AUTOFILL_COMPAT_ALLOWED_PACKAGES = "autofill_compat_allowed_packages";
+ field public static final java.lang.String CARRIER_APP_NAMES = "carrier_app_names";
+ field public static final java.lang.String CARRIER_APP_WHITELIST = "carrier_app_whitelist";
field public static final java.lang.String DEFAULT_SM_DP_PLUS = "default_sm_dp_plus";
field public static final java.lang.String INSTALL_CARRIER_APP_NOTIFICATION_PERSISTENT = "install_carrier_app_notification_persistent";
field public static final java.lang.String INSTALL_CARRIER_APP_NOTIFICATION_SLEEP_MILLIS = "install_carrier_app_notification_sleep_millis";
@@ -4271,7 +4274,7 @@
method public int getMaxAttempts();
method public byte[] getServerParams();
method public int getSnapshotVersion();
- method public byte[] getTrustedHardwarePublicKey();
+ method public java.security.cert.CertPath getTrustedHardwareCertPath();
method public java.util.List<android.security.keystore.recovery.WrappedApplicationKey> getWrappedApplicationKeys();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.security.keystore.recovery.KeyChainSnapshot> CREATOR;
@@ -4294,12 +4297,11 @@
public class RecoveryController {
method public android.security.keystore.recovery.RecoverySession createRecoverySession();
method public byte[] generateAndStoreKey(java.lang.String, byte[]) throws android.security.keystore.recovery.InternalRecoveryServiceException, android.security.keystore.recovery.LockScreenRequiredException;
- method public java.util.List<java.lang.String> getAliases(java.lang.String) throws android.security.keystore.recovery.InternalRecoveryServiceException;
+ method public java.util.List<java.lang.String> getAliases() throws android.security.keystore.recovery.InternalRecoveryServiceException;
method public static android.security.keystore.recovery.RecoveryController getInstance(android.content.Context);
method public int[] getPendingRecoverySecretTypes() throws android.security.keystore.recovery.InternalRecoveryServiceException;
- method public android.security.keystore.recovery.KeyChainSnapshot getRecoveryData() throws android.security.keystore.recovery.InternalRecoveryServiceException;
method public int[] getRecoverySecretTypes() throws android.security.keystore.recovery.InternalRecoveryServiceException;
- method public int getRecoveryStatus(java.lang.String, java.lang.String) throws android.security.keystore.recovery.InternalRecoveryServiceException;
+ method public int getRecoveryStatus(java.lang.String) throws android.security.keystore.recovery.InternalRecoveryServiceException;
method public void initRecoveryService(java.lang.String, byte[]) throws java.security.cert.CertificateException, android.security.keystore.recovery.InternalRecoveryServiceException;
method public void recoverySecretAvailable(android.security.keystore.recovery.KeyChainProtectionParams) throws android.security.keystore.recovery.InternalRecoveryServiceException;
method public void removeKey(java.lang.String) throws android.security.keystore.recovery.InternalRecoveryServiceException;
@@ -4315,7 +4317,7 @@
public class RecoverySession implements java.lang.AutoCloseable {
method public void close();
method public java.util.Map<java.lang.String, byte[]> recoverKeys(byte[], java.util.List<android.security.keystore.recovery.WrappedApplicationKey>) throws android.security.keystore.recovery.DecryptionFailedException, android.security.keystore.recovery.InternalRecoveryServiceException, android.security.keystore.recovery.SessionExpiredException;
- method public byte[] start(byte[], byte[], byte[], java.util.List<android.security.keystore.recovery.KeyChainProtectionParams>) throws java.security.cert.CertificateException, android.security.keystore.recovery.InternalRecoveryServiceException;
+ method public byte[] start(java.security.cert.CertPath, byte[], byte[], java.util.List<android.security.keystore.recovery.KeyChainProtectionParams>) throws java.security.cert.CertificateException, android.security.keystore.recovery.InternalRecoveryServiceException;
}
public class SessionExpiredException extends java.security.GeneralSecurityException {
@@ -5220,6 +5222,7 @@
method public int describeContents();
method public int getCarrierPrivilegeStatus(android.content.pm.PackageInfo);
method public int getCarrierPrivilegeStatus(android.content.pm.Signature, java.lang.String);
+ method public java.lang.String getCertificateHexString();
method public java.lang.String getPackageName();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.telephony.UiccAccessRule> CREATOR;
diff --git a/api/system-removed.txt b/api/system-removed.txt
index ac40052..cd56c46 100644
--- a/api/system-removed.txt
+++ b/api/system-removed.txt
@@ -93,10 +93,21 @@
package android.security.keystore.recovery {
+ public final class KeyChainSnapshot implements android.os.Parcelable {
+ method public deprecated byte[] getTrustedHardwarePublicKey();
+ }
+
public class RecoveryController {
+ method public deprecated java.util.List<java.lang.String> getAliases(java.lang.String) throws android.security.keystore.recovery.InternalRecoveryServiceException;
+ method public deprecated android.security.keystore.recovery.KeyChainSnapshot getRecoveryData() throws android.security.keystore.recovery.InternalRecoveryServiceException;
+ method public deprecated int getRecoveryStatus(java.lang.String, java.lang.String) throws android.security.keystore.recovery.InternalRecoveryServiceException;
method public deprecated void setRecoveryStatus(java.lang.String, java.lang.String, int) throws android.security.keystore.recovery.InternalRecoveryServiceException, android.content.pm.PackageManager.NameNotFoundException;
}
+ public class RecoverySession implements java.lang.AutoCloseable {
+ method public deprecated byte[] start(byte[], byte[], byte[], java.util.List<android.security.keystore.recovery.KeyChainProtectionParams>) throws java.security.cert.CertificateException, android.security.keystore.recovery.InternalRecoveryServiceException;
+ }
+
}
package android.service.notification {
diff --git a/api/test-current.txt b/api/test-current.txt
index 55e7926..d5b4311 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -732,6 +732,10 @@
field public static final java.lang.String MBMS_STREAMING_SERVICE_OVERRIDE_METADATA = "mbms-streaming-service-override";
}
+ public class ServiceState implements android.os.Parcelable {
+ method public void setSystemAndNetworkId(int, int);
+ }
+
}
package android.telephony.mbms {
diff --git a/cmds/statsd/Android.mk b/cmds/statsd/Android.mk
index 244fbce..87825f1 100644
--- a/cmds/statsd/Android.mk
+++ b/cmds/statsd/Android.mk
@@ -249,7 +249,8 @@
benchmark/main.cpp \
benchmark/hello_world_benchmark.cpp \
benchmark/log_event_benchmark.cpp \
- benchmark/stats_write_benchmark.cpp
+ benchmark/stats_write_benchmark.cpp \
+ benchmark/filter_value_benchmark.cpp
LOCAL_C_INCLUDES := $(statsd_common_c_includes)
diff --git a/cmds/statsd/benchmark/filter_value_benchmark.cpp b/cmds/statsd/benchmark/filter_value_benchmark.cpp
new file mode 100644
index 0000000..b9ddf36
--- /dev/null
+++ b/cmds/statsd/benchmark/filter_value_benchmark.cpp
@@ -0,0 +1,53 @@
+/*
+ * 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 <vector>
+#include "benchmark/benchmark.h"
+#include "FieldValue.h"
+#include "HashableDimensionKey.h"
+#include "logd/LogEvent.h"
+
+namespace android {
+namespace os {
+namespace statsd {
+
+using std::vector;
+
+static void BM_FilterValue(benchmark::State& state) {
+ LogEvent event(1, 100000);
+ event.write(3.2f);
+ event.write("LOCATION");
+ event.write((int64_t)990);
+ event.init();
+
+ FieldMatcher field_matcher;
+ field_matcher.set_field(1);
+ field_matcher.add_child()->set_field(2);
+ field_matcher.add_child()->set_field(3);
+
+ std::vector<Matcher> matchers;
+ translateFieldMatcher(field_matcher, &matchers);
+
+ while (state.KeepRunning()) {
+ vector<HashableDimensionKey> output;
+ filterValues(matchers, event.getValues(), &output);
+ }
+}
+
+BENCHMARK(BM_FilterValue);
+
+} // namespace statsd
+} // namespace os
+} // namespace android
diff --git a/cmds/statsd/src/FieldValue.cpp b/cmds/statsd/src/FieldValue.cpp
index 6894bcf..b541612 100644
--- a/cmds/statsd/src/FieldValue.cpp
+++ b/cmds/statsd/src/FieldValue.cpp
@@ -135,6 +135,8 @@
case STRING:
str_value = from.str_value;
break;
+ default:
+ break;
}
}
@@ -148,6 +150,8 @@
return std::to_string(float_value) + "[F]";
case STRING:
return str_value + "[S]";
+ default:
+ return "[UNKNOWN]";
}
}
@@ -163,6 +167,8 @@
return float_value == that.float_value;
case STRING:
return str_value == that.str_value;
+ default:
+ return false;
}
}
@@ -177,6 +183,8 @@
return float_value != that.float_value;
case STRING:
return str_value != that.str_value;
+ default:
+ return false;
}
}
diff --git a/cmds/statsd/src/FieldValue.h b/cmds/statsd/src/FieldValue.h
index d17dded..21f30e2 100644
--- a/cmds/statsd/src/FieldValue.h
+++ b/cmds/statsd/src/FieldValue.h
@@ -31,7 +31,7 @@
const int32_t kLastBitMask = 0x80;
const int32_t kClearLastBitDeco = 0x7f;
-enum Type { INT, LONG, FLOAT, STRING };
+enum Type { UNKNOWN, INT, LONG, FLOAT, STRING };
int32_t getEncodedField(int32_t pos[], int32_t depth, bool includeDepth);
@@ -82,6 +82,8 @@
int32_t mField;
public:
+ Field() {}
+
Field(int32_t tag, int32_t pos[], int32_t depth) : mTag(tag) {
mField = getEncodedField(pos, depth, true);
}
@@ -229,6 +231,8 @@
*
*/
struct Value {
+ Value() : type(UNKNOWN) {}
+
Value(int32_t v) {
int_value = v;
type = INT;
@@ -280,15 +284,13 @@
bool operator!=(const Value& that) const;
bool operator<(const Value& that) const;
-
-private:
- Value(){};
};
/**
* Represents a log item, or a dimension item (They are essentially the same).
*/
struct FieldValue {
+ FieldValue() {}
FieldValue(const Field& field, const Value& value) : mField(field), mValue(value) {
}
bool operator==(const FieldValue& that) const {
diff --git a/cmds/statsd/src/HashableDimensionKey.cpp b/cmds/statsd/src/HashableDimensionKey.cpp
index 3c69f9e..1502a00 100644
--- a/cmds/statsd/src/HashableDimensionKey.cpp
+++ b/cmds/statsd/src/HashableDimensionKey.cpp
@@ -16,6 +16,8 @@
#define DEBUG false // STOPSHIP if true
#include "Log.h"
+#include <mutex>
+
#include "HashableDimensionKey.h"
#include "FieldValue.h"
@@ -46,10 +48,12 @@
fieldValue.mValue.str_value)));
break;
case FLOAT: {
- float floatVal = fieldValue.mValue.float_value;
- hash = android::JenkinsHashMixBytes(hash, (uint8_t*)&floatVal, sizeof(float));
+ hash = android::JenkinsHashMix(hash,
+ android::hash_type(fieldValue.mValue.float_value));
break;
}
+ default:
+ break;
}
}
return JenkinsHashWhiten(hash);
@@ -64,26 +68,32 @@
int prevAnyMatcherPrefix = 0;
size_t prevPrevFanout = 0;
size_t prevFanout = 0;
+
// For each matcher get matched results.
+ vector<FieldValue> matchedResults(2);
for (const auto& matcher : matcherFields) {
- vector<FieldValue> matchedResults;
+ size_t num_matches = 0;
for (const auto& value : values) {
// TODO: potential optimization here to break early because all fields are naturally
// sorted.
if (value.mField.matches(matcher)) {
- matchedResults.push_back(FieldValue(
- Field(value.mField.getTag(), (value.mField.getField() & matcher.mMask)),
- value.mValue));
+ if (num_matches >= matchedResults.size()) {
+ matchedResults.resize(num_matches * 2);
+ }
+ matchedResults[num_matches].mField.setTag(value.mField.getTag());
+ matchedResults[num_matches].mField.setField(value.mField.getField() & matcher.mMask);
+ matchedResults[num_matches].mValue = value.mValue;
+ num_matches++;
}
}
- if (matchedResults.size() == 0) {
+ if (num_matches == 0) {
VLOG("We can't find a dimension value for matcher (%d)%#x.", matcher.mMatcher.getTag(),
matcher.mMatcher.getField());
continue;
}
- if (matchedResults.size() == 1) {
+ if (num_matches == 1) {
for (auto& dimension : *output) {
dimension.addValue(matchedResults[0]);
}
@@ -119,23 +129,23 @@
// First create fanout (fanout size is matchedResults.Size which could be one,
// which means we do nothing here)
oldSize = output->size();
- for (size_t i = 1; i < matchedResults.size(); i++) {
+ for (size_t i = 1; i < num_matches; i++) {
output->insert(output->end(), output->begin(), output->begin() + oldSize);
}
prevPrevFanout = oldSize;
- prevFanout = matchedResults.size();
+ prevFanout = num_matches;
} else {
// If we should not create fanout, e.g., uid tag from same position should be remain
// together.
oldSize = prevPrevFanout;
- if (prevFanout != matchedResults.size()) {
+ if (prevFanout != num_matches) {
// sanity check.
ALOGE("2 Any matcher result in different output");
return false;
}
}
// now add the matched field value to output
- for (size_t i = 0; i < matchedResults.size(); i++) {
+ for (size_t i = 0; i < num_matches; i++) {
for (int j = 0; j < oldSize; j++) {
(*output)[i * oldSize + j].addValue(matchedResults[i]);
}
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index 2b02025..04ebfcd 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -940,6 +940,11 @@
// Empty if not set.
optional string launch_token = 13;
+ // The compiler filter used when when the package was optimized.
+ optional string package_optimization_compilation_filter = 14;
+
+ // The reason why the package was optimized.
+ optional string package_optimization_compilation_reason = 15;
}
message AppStartCancelChanged {
diff --git a/cmds/statsd/src/stats_log_util.cpp b/cmds/statsd/src/stats_log_util.cpp
index 2c7b919..78ebe33 100644
--- a/cmds/statsd/src/stats_log_util.cpp
+++ b/cmds/statsd/src/stats_log_util.cpp
@@ -183,6 +183,8 @@
case STRING:
protoOutput->write(FIELD_TYPE_STRING | fieldNum, dim.mValue.str_value);
break;
+ default:
+ break;
}
(*index)++;
} else if (valueDepth > depth && valuePrefix == prefix) {
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 3ebe89f..3d04e2c 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -4161,9 +4161,10 @@
* <p>You can override this function to force global search, e.g. in response to a dedicated
* search key, or to block search entirely (by simply returning false).
*
- * <p>Note: when running in a {@link Configuration#UI_MODE_TYPE_TELEVISION}, the default
- * implementation changes to simply return false and you must supply your own custom
- * implementation if you want to support search.</p>
+ * <p>Note: when running in a {@link Configuration#UI_MODE_TYPE_TELEVISION} or
+ * {@link Configuration#UI_MODE_TYPE_WATCH}, the default implementation changes to simply
+ * return false and you must supply your own custom implementation if you want to support
+ * search.
*
* @param searchEvent The {@link SearchEvent} that signaled this search.
* @return Returns {@code true} if search launched, and {@code false} if the activity does
@@ -4183,8 +4184,10 @@
* @see #onSearchRequested(SearchEvent)
*/
public boolean onSearchRequested() {
- if ((getResources().getConfiguration().uiMode&Configuration.UI_MODE_TYPE_MASK)
- != Configuration.UI_MODE_TYPE_TELEVISION) {
+ final int uiMode = getResources().getConfiguration().uiMode
+ & Configuration.UI_MODE_TYPE_MASK;
+ if (uiMode != Configuration.UI_MODE_TYPE_TELEVISION
+ && uiMode != Configuration.UI_MODE_TYPE_WATCH) {
startSearch(null, false, null, false);
return true;
} else {
@@ -4213,6 +4216,9 @@
* is to inject specific data such as context data, it is preferred to <i>override</i>
* onSearchRequested(), so that any callers to it will benefit from the override.
*
+ * <p>Note: when running in a {@link Configuration#UI_MODE_TYPE_WATCH}, use of this API is
+ * not supported.
+ *
* @param initialQuery Any non-null non-empty string will be inserted as
* pre-entered text in the search query box.
* @param selectInitialQuery If true, the initial query will be preselected, which means that
diff --git a/core/java/android/app/Dialog.java b/core/java/android/app/Dialog.java
index eb26026..4a168fe 100644
--- a/core/java/android/app/Dialog.java
+++ b/core/java/android/app/Dialog.java
@@ -291,7 +291,10 @@
if (mWindow.hasFeature(Window.FEATURE_ACTION_BAR)) {
mWindow.invalidatePanelMenu(Window.FEATURE_ACTION_BAR);
}
- mDecor.setVisibility(View.VISIBLE);
+ if (mDecor.getVisibility() != View.VISIBLE) {
+ mDecor.setVisibility(View.VISIBLE);
+ sendShowMessage();
+ }
}
return;
}
diff --git a/core/java/android/app/SearchManager.java b/core/java/android/app/SearchManager.java
index ea990ad..4e2cb64 100644
--- a/core/java/android/app/SearchManager.java
+++ b/core/java/android/app/SearchManager.java
@@ -48,6 +48,9 @@
* and the {@link android.content.Intent#ACTION_SEARCH ACTION_SEARCH}
* {@link android.content.Intent Intent}.
*
+ * <p>
+ * {@link Configuration#UI_MODE_TYPE_WATCH} does not support this system service.
+ *
* <div class="special reference">
* <h3>Developer Guides</h3>
* <p>For more information about using the search dialog and adding search
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 19f6a49..16e36bc 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -3495,18 +3495,18 @@
* that uses {@link DeviceAdminInfo#USES_POLICY_WIPE_DATA}
* @throws IllegalArgumentException if the input reason string is null or empty.
*/
- public void wipeDataWithReason(int flags, @NonNull CharSequence reason) {
- throwIfParentInstance("wipeDataWithReason");
+ public void wipeData(int flags, @NonNull CharSequence reason) {
+ throwIfParentInstance("wipeData");
Preconditions.checkNotNull(reason, "CharSequence is null");
wipeDataInternal(flags, reason.toString());
}
/**
* Internal function for both {@link #wipeData(int)} and
- * {@link #wipeDataWithReason(int, CharSequence)} to call.
+ * {@link #wipeData(int, CharSequence)} to call.
*
* @see #wipeData(int)
- * @see #wipeDataWithReason(int, CharSequence)
+ * @see #wipeData(int, CharSequence)
* @hide
*/
private void wipeDataInternal(int flags, @NonNull String wipeReasonForUser) {
diff --git a/core/java/android/app/slice/SliceManager.java b/core/java/android/app/slice/SliceManager.java
index e2c8662..ae1d8d7 100644
--- a/core/java/android/app/slice/SliceManager.java
+++ b/core/java/android/app/slice/SliceManager.java
@@ -24,6 +24,7 @@
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
+import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.net.Uri;
import android.os.Binder;
@@ -295,12 +296,10 @@
}
/**
- * Turns a slice intent into a slice uri. Expects an explicit intent. If there is no
- * {@link android.content.ContentProvider} associated with the given intent this will throw
- * {@link IllegalArgumentException}.
+ * Turns a slice intent into a slice uri. Expects an explicit intent.
*
* @param intent The intent associated with a slice.
- * @return The Slice Uri provided by the app or null if none is given.
+ * @return The Slice Uri provided by the app or null if none exists.
* @see Slice
* @see SliceProvider#onMapIntentToUri(Intent)
* @see Intent
@@ -320,7 +319,16 @@
List<ResolveInfo> providers =
mContext.getPackageManager().queryIntentContentProviders(intent, 0);
if (providers == null || providers.isEmpty()) {
- throw new IllegalArgumentException("Unable to resolve intent " + intent);
+ // There are no providers, see if this activity has a direct link.
+ ResolveInfo resolve = mContext.getPackageManager().resolveActivity(intent,
+ PackageManager.GET_META_DATA);
+ if (resolve != null && resolve.activityInfo != null
+ && resolve.activityInfo.metaData != null
+ && resolve.activityInfo.metaData.containsKey(SLICE_METADATA_KEY)) {
+ return Uri.parse(
+ resolve.activityInfo.metaData.getString(SLICE_METADATA_KEY));
+ }
+ return null;
}
String authority = providers.get(0).providerInfo.authority;
Uri uri = new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT)
@@ -371,7 +379,16 @@
List<ResolveInfo> providers =
mContext.getPackageManager().queryIntentContentProviders(intent, 0);
if (providers == null || providers.isEmpty()) {
- throw new IllegalArgumentException("Unable to resolve intent " + intent);
+ // There are no providers, see if this activity has a direct link.
+ ResolveInfo resolve = mContext.getPackageManager().resolveActivity(intent,
+ PackageManager.GET_META_DATA);
+ if (resolve != null && resolve.activityInfo != null
+ && resolve.activityInfo.metaData != null
+ && resolve.activityInfo.metaData.containsKey(SLICE_METADATA_KEY)) {
+ return bindSlice(Uri.parse(resolve.activityInfo.metaData
+ .getString(SLICE_METADATA_KEY)), supportedSpecs);
+ }
+ return null;
}
String authority = providers.get(0).providerInfo.authority;
Uri uri = new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT)
diff --git a/core/java/android/app/usage/UsageEvents.java b/core/java/android/app/usage/UsageEvents.java
index 6b573e9..521ab4e 100644
--- a/core/java/android/app/usage/UsageEvents.java
+++ b/core/java/android/app/usage/UsageEvents.java
@@ -109,7 +109,8 @@
public static final int NOTIFICATION_SEEN = 10;
/**
- * An event type denoting a change in App Standby Bucket.
+ * An event type denoting a change in App Standby Bucket. Additional bucket information
+ * is contained in mBucketAndReason.
* @hide
*/
@SystemApi
@@ -180,11 +181,12 @@
public String[] mContentAnnotations;
/**
- * The app standby bucket assigned.
+ * The app standby bucket assigned and reason. Bucket is the high order 16 bits, reason
+ * is the low order 16 bits.
* Only present for {@link #STANDBY_BUCKET_CHANGED} event types
* {@hide}
*/
- public int mBucket;
+ public int mBucketAndReason;
/** @hide */
@EventFlags
@@ -205,7 +207,7 @@
mContentType = orig.mContentType;
mContentAnnotations = orig.mContentAnnotations;
mFlags = orig.mFlags;
- mBucket = orig.mBucket;
+ mBucketAndReason = orig.mBucketAndReason;
}
/**
@@ -268,7 +270,19 @@
*/
@SystemApi
public int getStandbyBucket() {
- return mBucket;
+ return (mBucketAndReason & 0xFFFF0000) >>> 16;
+ }
+
+ /**
+ * Returns the reason for the bucketing, if the event is of type
+ * {@link #STANDBY_BUCKET_CHANGED}, otherwise returns 0. Reason values include
+ * the main reason which is one of REASON_MAIN_*, OR'ed with REASON_SUB_*, if there
+ * are sub-reasons for the main reason, such as REASON_SUB_USAGE_* when the main reason
+ * is REASON_MAIN_USAGE.
+ * @hide
+ */
+ public int getStandbyReason() {
+ return mBucketAndReason & 0x0000FFFF;
}
/** @hide */
@@ -428,7 +442,7 @@
p.writeStringArray(event.mContentAnnotations);
break;
case Event.STANDBY_BUCKET_CHANGED:
- p.writeInt(event.mBucket);
+ p.writeInt(event.mBucketAndReason);
break;
}
}
@@ -474,7 +488,7 @@
eventOut.mContentAnnotations = p.createStringArray();
break;
case Event.STANDBY_BUCKET_CHANGED:
- eventOut.mBucket = p.readInt();
+ eventOut.mBucketAndReason = p.readInt();
break;
}
}
diff --git a/core/java/android/app/usage/UsageStatsManager.java b/core/java/android/app/usage/UsageStatsManager.java
index 9a0bb1d..5a57b06 100644
--- a/core/java/android/app/usage/UsageStatsManager.java
+++ b/core/java/android/app/usage/UsageStatsManager.java
@@ -132,24 +132,37 @@
@SystemApi
public static final int STANDBY_BUCKET_NEVER = 50;
- /** {@hide} Reason for bucketing -- default initial state */
- public static final String REASON_DEFAULT = "default";
+ /** @hide */
+ public static final int REASON_MAIN_MASK = 0xFF00;
+ /** @hide */
+ public static final int REASON_MAIN_DEFAULT = 0x0100;
+ /** @hide */
+ public static final int REASON_MAIN_TIMEOUT = 0x0200;
+ /** @hide */
+ public static final int REASON_MAIN_USAGE = 0x0300;
+ /** @hide */
+ public static final int REASON_MAIN_FORCED = 0x0400;
+ /** @hide */
+ public static final int REASON_MAIN_PREDICTED = 0x0500;
- /** {@hide} Reason for bucketing -- timeout */
- public static final String REASON_TIMEOUT = "timeout";
-
- /** {@hide} Reason for bucketing -- usage */
- public static final String REASON_USAGE = "usage";
-
- /** {@hide} Reason for bucketing -- forced by user / shell command */
- public static final String REASON_FORCED = "forced";
-
- /**
- * {@hide}
- * Reason for bucketing -- predicted. This is a prefix and the UID of the bucketeer will
- * be appended.
- */
- public static final String REASON_PREDICTED = "predicted";
+ /** @hide */
+ public static final int REASON_SUB_MASK = 0x00FF;
+ /** @hide */
+ public static final int REASON_SUB_USAGE_SYSTEM_INTERACTION = 0x0001;
+ /** @hide */
+ public static final int REASON_SUB_USAGE_NOTIFICATION_SEEN = 0x0002;
+ /** @hide */
+ public static final int REASON_SUB_USAGE_USER_INTERACTION = 0x0003;
+ /** @hide */
+ public static final int REASON_SUB_USAGE_MOVE_TO_FOREGROUND = 0x0004;
+ /** @hide */
+ public static final int REASON_SUB_USAGE_MOVE_TO_BACKGROUND = 0x0005;
+ /** @hide */
+ public static final int REASON_SUB_USAGE_SYSTEM_UPDATE = 0x0006;
+ /** @hide */
+ public static final int REASON_SUB_USAGE_ACTIVE_TIMEOUT = 0x0007;
+ /** @hide */
+ public static final int REASON_SUB_USAGE_SYNC_ADAPTER = 0x0008;
/** @hide */
@IntDef(flag = false, prefix = { "STANDBY_BUCKET_" }, value = {
@@ -427,6 +440,55 @@
}
}
+ /** @hide */
+ public static String reasonToString(int standbyReason) {
+ StringBuilder sb = new StringBuilder();
+ switch (standbyReason & REASON_MAIN_MASK) {
+ case REASON_MAIN_DEFAULT:
+ sb.append("d");
+ break;
+ case REASON_MAIN_FORCED:
+ sb.append("f");
+ break;
+ case REASON_MAIN_PREDICTED:
+ sb.append("p");
+ break;
+ case REASON_MAIN_TIMEOUT:
+ sb.append("t");
+ break;
+ case REASON_MAIN_USAGE:
+ sb.append("u-");
+ switch (standbyReason & REASON_SUB_MASK) {
+ case REASON_SUB_USAGE_SYSTEM_INTERACTION:
+ sb.append("si");
+ break;
+ case REASON_SUB_USAGE_NOTIFICATION_SEEN:
+ sb.append("ns");
+ break;
+ case REASON_SUB_USAGE_USER_INTERACTION:
+ sb.append("ui");
+ break;
+ case REASON_SUB_USAGE_MOVE_TO_FOREGROUND:
+ sb.append("mf");
+ break;
+ case REASON_SUB_USAGE_MOVE_TO_BACKGROUND:
+ sb.append("mb");
+ break;
+ case REASON_SUB_USAGE_SYSTEM_UPDATE:
+ sb.append("su");
+ break;
+ case REASON_SUB_USAGE_ACTIVE_TIMEOUT:
+ sb.append("at");
+ break;
+ case REASON_SUB_USAGE_SYNC_ADAPTER:
+ sb.append("sa");
+ break;
+ }
+ break;
+ }
+ return sb.toString();
+ }
+
/**
* {@hide}
* Temporarily whitelist the specified app for a short duration. This is to allow an app
diff --git a/core/java/android/app/usage/UsageStatsManagerInternal.java b/core/java/android/app/usage/UsageStatsManagerInternal.java
index 5d6a989..b62b1ee 100644
--- a/core/java/android/app/usage/UsageStatsManagerInternal.java
+++ b/core/java/android/app/usage/UsageStatsManagerInternal.java
@@ -139,7 +139,7 @@
/** Callback to inform listeners that the idle state has changed to a new bucket. */
public abstract void onAppIdleStateChanged(String packageName, @UserIdInt int userId,
- boolean idle, int bucket);
+ boolean idle, int bucket, int reason);
/**
* Callback to inform listeners that the parole state has changed. This means apps are
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index f184380..e1a00b1 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -3370,7 +3370,11 @@
* Use with {@link #getSystemService(String)} to retrieve a {@link
* android.app.SearchManager} for handling searches.
*
- * @see #getSystemService(String)
+ * <p>
+ * {@link Configuration#UI_MODE_TYPE_WATCH} does not support
+ * {@link android.app.SearchManager}.
+ *
+ * @see #getSystemService
* @see android.app.SearchManager
*/
public static final String SEARCH_SERVICE = "search";
diff --git a/core/java/android/content/pm/dex/ArtManagerInternal.java b/core/java/android/content/pm/dex/ArtManagerInternal.java
new file mode 100644
index 0000000..62ab9e0
--- /dev/null
+++ b/core/java/android/content/pm/dex/ArtManagerInternal.java
@@ -0,0 +1,34 @@
+/**
+ * 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.content.pm.dex;
+
+import android.content.pm.ApplicationInfo;
+
+/**
+ * Art manager local system service interface.
+ *
+ * @hide Only for use within the system server.
+ */
+public abstract class ArtManagerInternal {
+
+ /**
+ * Return optimization information about the application {@code info} when
+ * in executes using the specified {@code abi}.
+ */
+ public abstract PackageOptimizationInfo getPackageOptimizationInfo(
+ ApplicationInfo info, String abi);
+}
diff --git a/core/java/android/content/pm/dex/PackageOptimizationInfo.java b/core/java/android/content/pm/dex/PackageOptimizationInfo.java
new file mode 100644
index 0000000..b650457
--- /dev/null
+++ b/core/java/android/content/pm/dex/PackageOptimizationInfo.java
@@ -0,0 +1,47 @@
+/**
+ * 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.content.pm.dex;
+
+/**
+ * Encapsulates information about the optimizations performed on a package.
+ *
+ * @hide
+ */
+public class PackageOptimizationInfo {
+ private final String mCompilationFilter;
+ private final String mCompilationReason;
+
+ public PackageOptimizationInfo(String compilerFilter, String compilationReason) {
+ this.mCompilationReason = compilationReason;
+ this.mCompilationFilter = compilerFilter;
+ }
+
+ public String getCompilationReason() {
+ return mCompilationReason;
+ }
+
+ public String getCompilationFilter() {
+ return mCompilationFilter;
+ }
+
+ /**
+ * Create a default optimization info object for the case when we have no information.
+ */
+ public static PackageOptimizationInfo createWithNoInfo() {
+ return new PackageOptimizationInfo("no-info", "no-info");
+ }
+}
diff --git a/core/java/android/content/res/Configuration.java b/core/java/android/content/res/Configuration.java
index eb30979..93690bf 100644
--- a/core/java/android/content/res/Configuration.java
+++ b/core/java/android/content/res/Configuration.java
@@ -18,13 +18,27 @@
import static android.content.ConfigurationProto.DENSITY_DPI;
import static android.content.ConfigurationProto.FONT_SCALE;
+import static android.content.ConfigurationProto.HARD_KEYBOARD_HIDDEN;
+import static android.content.ConfigurationProto.HDR_COLOR_MODE;
+import static android.content.ConfigurationProto.KEYBOARD_HIDDEN;
+import static android.content.ConfigurationProto.LOCALES;
+import static android.content.ConfigurationProto.MCC;
+import static android.content.ConfigurationProto.MNC;
+import static android.content.ConfigurationProto.NAVIGATION;
+import static android.content.ConfigurationProto.NAVIGATION_HIDDEN;
import static android.content.ConfigurationProto.ORIENTATION;
import static android.content.ConfigurationProto.SCREEN_HEIGHT_DP;
import static android.content.ConfigurationProto.SCREEN_LAYOUT;
import static android.content.ConfigurationProto.SCREEN_WIDTH_DP;
import static android.content.ConfigurationProto.SMALLEST_SCREEN_WIDTH_DP;
+import static android.content.ConfigurationProto.TOUCHSCREEN;
import static android.content.ConfigurationProto.UI_MODE;
+import static android.content.ConfigurationProto.WIDE_COLOR_GAMUT;
import static android.content.ConfigurationProto.WINDOW_CONFIGURATION;
+import static android.content.ResourcesConfigurationProto.CONFIGURATION;
+import static android.content.ResourcesConfigurationProto.SCREEN_HEIGHT_PX;
+import static android.content.ResourcesConfigurationProto.SCREEN_WIDTH_PX;
+import static android.content.ResourcesConfigurationProto.SDK_VERSION;
import android.annotation.IntDef;
import android.annotation.NonNull;
@@ -38,6 +52,7 @@
import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;
+import android.util.DisplayMetrics;
import android.util.proto.ProtoOutputStream;
import android.view.View;
@@ -1076,7 +1091,19 @@
public void writeToProto(ProtoOutputStream protoOutputStream, long fieldId) {
final long token = protoOutputStream.start(fieldId);
protoOutputStream.write(FONT_SCALE, fontScale);
+ protoOutputStream.write(MCC, mcc);
+ protoOutputStream.write(MNC, mnc);
+ mLocaleList.writeToProto(protoOutputStream, LOCALES);
protoOutputStream.write(SCREEN_LAYOUT, screenLayout);
+ protoOutputStream.write(HDR_COLOR_MODE,
+ (colorMode & Configuration.COLOR_MODE_HDR_MASK) >> COLOR_MODE_HDR_SHIFT);
+ protoOutputStream.write(WIDE_COLOR_GAMUT,
+ colorMode & Configuration.COLOR_MODE_WIDE_COLOR_GAMUT_MASK);
+ protoOutputStream.write(TOUCHSCREEN, touchscreen);
+ protoOutputStream.write(KEYBOARD_HIDDEN, keyboardHidden);
+ protoOutputStream.write(HARD_KEYBOARD_HIDDEN, hardKeyboardHidden);
+ protoOutputStream.write(NAVIGATION, navigation);
+ protoOutputStream.write(NAVIGATION_HIDDEN, navigationHidden);
protoOutputStream.write(ORIENTATION, orientation);
protoOutputStream.write(UI_MODE, uiMode);
protoOutputStream.write(SCREEN_WIDTH_DP, screenWidthDp);
@@ -1088,6 +1115,36 @@
}
/**
+ * Write full {@link android.content.ResourcesConfigurationProto} to protocol buffer output
+ * stream.
+ *
+ * @param protoOutputStream Stream to write the Configuration object to.
+ * @param fieldId Field Id of the Configuration as defined in the parent message
+ * @param metrics Current display information
+ * @hide
+ */
+ public void writeResConfigToProto(ProtoOutputStream protoOutputStream, long fieldId,
+ DisplayMetrics metrics) {
+ final int width, height;
+ if (metrics.widthPixels >= metrics.heightPixels) {
+ width = metrics.widthPixels;
+ height = metrics.heightPixels;
+ } else {
+ //noinspection SuspiciousNameCombination
+ width = metrics.heightPixels;
+ //noinspection SuspiciousNameCombination
+ height = metrics.widthPixels;
+ }
+
+ final long token = protoOutputStream.start(fieldId);
+ writeToProto(protoOutputStream, CONFIGURATION);
+ protoOutputStream.write(SDK_VERSION, Build.VERSION.RESOURCES_SDK_INT);
+ protoOutputStream.write(SCREEN_WIDTH_PX, width);
+ protoOutputStream.write(SCREEN_HEIGHT_PX, height);
+ protoOutputStream.end(token);
+ }
+
+ /**
* Convert the UI mode to a human readable format.
* @hide
*/
@@ -1925,11 +1982,21 @@
/**
* Returns a string representation of the configuration that can be parsed
- * by build tools (like AAPT).
+ * by build tools (like AAPT), without display metrics included
*
* @hide
*/
public static String resourceQualifierString(Configuration config) {
+ return resourceQualifierString(config, null);
+ }
+
+ /**
+ * Returns a string representation of the configuration that can be parsed
+ * by build tools (like AAPT).
+ *
+ * @hide
+ */
+ public static String resourceQualifierString(Configuration config, DisplayMetrics metrics) {
ArrayList<String> parts = new ArrayList<String>();
if (config.mcc != 0) {
@@ -2177,6 +2244,20 @@
break;
}
+ if (metrics != null) {
+ final int width, height;
+ if (metrics.widthPixels >= metrics.heightPixels) {
+ width = metrics.widthPixels;
+ height = metrics.heightPixels;
+ } else {
+ //noinspection SuspiciousNameCombination
+ width = metrics.heightPixels;
+ //noinspection SuspiciousNameCombination
+ height = metrics.widthPixels;
+ }
+ parts.add(width + "x" + height);
+ }
+
parts.add("v" + Build.VERSION.RESOURCES_SDK_INT);
return TextUtils.join("-", parts);
}
diff --git a/core/java/android/net/metrics/NetworkMetrics.java b/core/java/android/net/metrics/NetworkMetrics.java
index 2425bba..66d92c4 100644
--- a/core/java/android/net/metrics/NetworkMetrics.java
+++ b/core/java/android/net/metrics/NetworkMetrics.java
@@ -98,6 +98,9 @@
/** Accumulate a single netd sock_diag poll result reported by netd. */
public void addTcpStatsResult(int sent, int lost, int rttUs, int sentAckDiffMs) {
+ if (pendingSummary == null) {
+ pendingSummary = new Summary(netId, transports);
+ }
pendingSummary.tcpLossRate.count(lost, sent);
pendingSummary.roundTripTimeUs.count(rttUs);
pendingSummary.sentAckTimeDiffenceMs.count(sentAckDiffMs);
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index dc46b73..5e9d39c 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -447,6 +447,11 @@
*/
public abstract LongCounter getScanTimeCounter();
+ /**
+ * @return a non-null {@link LongCounter} representing time spent (milliseconds) in the
+ * sleep state.
+ */
+ public abstract LongCounter getSleepTimeCounter();
/**
* @return a non-null {@link LongCounter} representing time spent (milliseconds) in the
@@ -3408,8 +3413,6 @@
for (LongCounter txState : counter.getTxTimeCounters()) {
totalTxTimeMs += txState.getCountLocked(which);
}
- final long sleepTimeMs
- = totalControllerActivityTimeMs - (idleTimeMs + rxTimeMs + totalTxTimeMs);
if (controllerName.equals(WIFI_CONTROLLER_NAME)) {
final long scanTimeMs = counter.getScanTimeCounter().getCountLocked(which);
@@ -3423,18 +3426,34 @@
sb.append(formatRatioLocked(scanTimeMs, totalControllerActivityTimeMs));
sb.append(")");
pw.println(sb.toString());
+
+ final long sleepTimeMs
+ = totalControllerActivityTimeMs - (idleTimeMs + rxTimeMs + totalTxTimeMs);
+ sb.setLength(0);
+ sb.append(prefix);
+ sb.append(" ");
+ sb.append(controllerName);
+ sb.append(" Sleep time: ");
+ formatTimeMs(sb, sleepTimeMs);
+ sb.append("(");
+ sb.append(formatRatioLocked(sleepTimeMs, totalControllerActivityTimeMs));
+ sb.append(")");
+ pw.println(sb.toString());
}
- sb.setLength(0);
- sb.append(prefix);
- sb.append(" ");
- sb.append(controllerName);
- sb.append(" Sleep time: ");
- formatTimeMs(sb, sleepTimeMs);
- sb.append("(");
- sb.append(formatRatioLocked(sleepTimeMs, totalControllerActivityTimeMs));
- sb.append(")");
- pw.println(sb.toString());
+ if (controllerName.equals(CELLULAR_CONTROLLER_NAME)) {
+ final long sleepTimeMs = counter.getSleepTimeCounter().getCountLocked(which);
+ sb.setLength(0);
+ sb.append(prefix);
+ sb.append(" ");
+ sb.append(controllerName);
+ sb.append(" Sleep time: ");
+ formatTimeMs(sb, sleepTimeMs);
+ sb.append("(");
+ sb.append(formatRatioLocked(sleepTimeMs, totalControllerActivityTimeMs));
+ sb.append(")");
+ pw.println(sb.toString());
+ }
sb.setLength(0);
sb.append(prefix);
diff --git a/core/java/android/os/Handler.java b/core/java/android/os/Handler.java
index fc88e90..0417ded 100644
--- a/core/java/android/os/Handler.java
+++ b/core/java/android/os/Handler.java
@@ -683,6 +683,23 @@
return enqueueMessage(queue, msg, 0);
}
+ /**
+ * Executes the message synchronously if called on the same thread this handler corresponds to,
+ * or {@link #sendMessage pushes it to the queue} otherwise
+ *
+ * @return Returns true if the message was successfully ran or placed in to the
+ * message queue. Returns false on failure, usually because the
+ * looper processing the message queue is exiting.
+ * @hide
+ */
+ public final boolean executeOrSendMessage(Message msg) {
+ if (mLooper == Looper.myLooper()) {
+ dispatchMessage(msg);
+ return true;
+ }
+ return sendMessage(msg);
+ }
+
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
diff --git a/core/java/android/os/LocaleList.java b/core/java/android/os/LocaleList.java
index ca9cbec..87e1b7d 100644
--- a/core/java/android/os/LocaleList.java
+++ b/core/java/android/os/LocaleList.java
@@ -20,7 +20,9 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.Size;
+import android.content.LocaleProto;
import android.icu.util.ULocale;
+import android.util.proto.ProtoOutputStream;
import com.android.internal.annotations.GuardedBy;
@@ -140,6 +142,25 @@
}
/**
+ * Helper to write LocaleList to a protocol buffer output stream. Assumes the parent
+ * protobuf has declared the locale as repeated.
+ *
+ * @param protoOutputStream Stream to write the locale to.
+ * @param fieldId Field Id of the Locale as defined in the parent message.
+ * @hide
+ */
+ public void writeToProto(ProtoOutputStream protoOutputStream, long fieldId) {
+ for (int i = 0; i < mList.length; i++) {
+ final Locale locale = mList[i];
+ final long token = protoOutputStream.start(fieldId);
+ protoOutputStream.write(LocaleProto.LANGUAGE, locale.getLanguage());
+ protoOutputStream.write(LocaleProto.COUNTRY, locale.getCountry());
+ protoOutputStream.write(LocaleProto.VARIANT, locale.getVariant());
+ protoOutputStream.end(token);
+ }
+ }
+
+ /**
* Retrieves a String representation of the language tags in this list.
*/
@NonNull
diff --git a/core/java/android/os/StatsLogEventWrapper.java b/core/java/android/os/StatsLogEventWrapper.java
index 3e8161f..d4d3dc8 100644
--- a/core/java/android/os/StatsLogEventWrapper.java
+++ b/core/java/android/os/StatsLogEventWrapper.java
@@ -46,16 +46,17 @@
* @param tag The integer representing the tag for this event.
* @param fields The number of fields specified in this event.
*/
- public StatsLogEventWrapper(int tag, int fields) {
+ public StatsLogEventWrapper(long elapsedNanos, int tag, int fields) {
// Write four bytes from tag, starting with least-significant bit.
// For pulled data, this tag number is not really used. We use the same tag number as
// pushed ones to be consistent.
write4Bytes(STATS_BUFFER_TAG_ID);
mStorage.write(EVENT_TYPE_LIST); // This is required to start the log entry.
- mStorage.write(fields + 1); // Indicate number of elements in this list. +1 for the tag
- mStorage.write(EVENT_TYPE_INT);
- // The first element is the real atom tag number
- write4Bytes(tag);
+ mStorage.write(fields + 2); // Indicate number of elements in this list. +1 for the tag
+ // The first element is the elapsed realtime.
+ writeLong(elapsedNanos);
+ // The second element is the real atom tag number
+ writeInt(tag);
}
/**
diff --git a/core/java/android/os/StrictMode.java b/core/java/android/os/StrictMode.java
index f90604a..76c13be 100644
--- a/core/java/android/os/StrictMode.java
+++ b/core/java/android/os/StrictMode.java
@@ -636,7 +636,7 @@
* executor every violation.
*/
public Builder penaltyListener(
- @NonNull OnThreadViolationListener listener, @NonNull Executor executor) {
+ @NonNull Executor executor, @NonNull OnThreadViolationListener listener) {
if (executor == null) {
throw new NullPointerException("executor must not be null");
}
@@ -645,6 +645,12 @@
return this;
}
+ /** @removed */
+ public Builder penaltyListener(
+ @NonNull OnThreadViolationListener listener, @NonNull Executor executor) {
+ return penaltyListener(executor, listener);
+ }
+
private Builder enable(int bit) {
mMask |= bit;
return this;
@@ -971,7 +977,7 @@
* Call #{@link OnVmViolationListener#onVmViolation(Violation)} on every violation.
*/
public Builder penaltyListener(
- @NonNull OnVmViolationListener listener, @NonNull Executor executor) {
+ @NonNull Executor executor, @NonNull OnVmViolationListener listener) {
if (executor == null) {
throw new NullPointerException("executor must not be null");
}
@@ -980,6 +986,12 @@
return this;
}
+ /** @removed */
+ public Builder penaltyListener(
+ @NonNull OnVmViolationListener listener, @NonNull Executor executor) {
+ return penaltyListener(executor, listener);
+ }
+
private Builder enable(int bit) {
mMask |= bit;
return this;
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 1ae0bc4..0442c9c 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -9085,14 +9085,28 @@
*/
public static final String TETHER_OFFLOAD_DISABLED = "tether_offload_disabled";
- /**
- * List of carrier apps which are whitelisted to prompt the user for install when
- * a sim card with matching uicc carrier privilege rules is inserted.
- *
- * The value is "package1;package2;..."
- * @hide
- */
- public static final String CARRIER_APP_WHITELIST = "carrier_app_whitelist";
+ /**
+ * List of certificate (hex string representation of the application's certificate - SHA-1
+ * or SHA-256) and carrier app package pairs which are whitelisted to prompt the user for
+ * install when a sim card with matching UICC carrier privilege rules is inserted. The
+ * certificate is used as a key, so the certificate encoding here must be the same as the
+ * certificate encoding used on the SIM.
+ *
+ * The value is "cert1:package1;cert2:package2;..."
+ * @hide
+ */
+ @SystemApi
+ public static final String CARRIER_APP_WHITELIST = "carrier_app_whitelist";
+
+ /**
+ * Map of package name to application names. Package names must be lower cased as they are
+ * used as a key in the map. The application names cannot and will not be localized.
+ *
+ * The value is "packageName1:appName1;packageName2:appName2;..."
+ * @hide
+ */
+ @SystemApi
+ public static final String CARRIER_APP_NAMES = "carrier_app_names";
/**
* USB Mass Storage Enabled
diff --git a/core/java/android/se/omapi/ISecureElementListener.aidl b/core/java/android/se/omapi/ISecureElementListener.aidl
index 3a99d63..e0c6e04 100644
--- a/core/java/android/se/omapi/ISecureElementListener.aidl
+++ b/core/java/android/se/omapi/ISecureElementListener.aidl
@@ -21,6 +21,7 @@
/**
* Interface to receive call-backs when the service is connected.
+ * @hide
*/
interface ISecureElementListener {
/**
diff --git a/core/java/android/se/omapi/SEService.java b/core/java/android/se/omapi/SEService.java
index b8937e6..d59e86a 100644
--- a/core/java/android/se/omapi/SEService.java
+++ b/core/java/android/se/omapi/SEService.java
@@ -59,6 +59,21 @@
*/
public static final int NO_SUCH_ELEMENT_ERROR = 2;
+ /**
+ * Interface to send call-backs to the application when the service is connected.
+ */
+ public abstract static class SecureElementListener extends ISecureElementListener.Stub {
+ @Override
+ public IBinder asBinder() {
+ return this;
+ }
+
+ /**
+ * Called by the framework when the service is connected.
+ */
+ public void serviceConnected() {};
+ }
+
private static final String TAG = "OMAPI.SEService";
private final Object mLock = new Object();
@@ -98,9 +113,9 @@
* the context of the calling application. Cannot be
* <code>null</code>.
* @param listener
- * a ISecureElementListener object. Can be <code>null</code>.
+ * a SecureElementListener object. Can be <code>null</code>.
*/
- public SEService(Context context, ISecureElementListener listener) {
+ public SEService(Context context, SecureElementListener listener) {
if (context == null) {
throw new NullPointerException("context must not be null");
diff --git a/core/java/android/security/keystore/RecoveryController.java b/core/java/android/security/keystore/RecoveryController.java
index 786d454..4a0de5f 100644
--- a/core/java/android/security/keystore/RecoveryController.java
+++ b/core/java/android/security/keystore/RecoveryController.java
@@ -291,7 +291,7 @@
// IPC doesn't support generic Maps.
@SuppressWarnings("unchecked")
Map<String, Integer> result =
- (Map<String, Integer>) mBinder.getRecoveryStatus(/*packageName=*/ null);
+ (Map<String, Integer>) mBinder.getRecoveryStatus();
return result;
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
diff --git a/core/java/android/security/keystore/recovery/KeyChainSnapshot.java b/core/java/android/security/keystore/recovery/KeyChainSnapshot.java
index df535ed..d4ec472 100644
--- a/core/java/android/security/keystore/recovery/KeyChainSnapshot.java
+++ b/core/java/android/security/keystore/recovery/KeyChainSnapshot.java
@@ -23,6 +23,7 @@
import com.android.internal.util.Preconditions;
+import java.security.cert.CertPath;
import java.util.List;
/**
@@ -52,7 +53,8 @@
private int mMaxAttempts = DEFAULT_MAX_ATTEMPTS;
private long mCounterId = DEFAULT_COUNTER_ID;
private byte[] mServerParams;
- private byte[] mPublicKey;
+ private byte[] mPublicKey; // The raw public key bytes used
+ private CertPath mCertPath; // The certificate path including the intermediate certificates
private List<KeyChainProtectionParams> mKeyChainProtectionParams;
private List<WrappedApplicationKey> mEntryRecoveryData;
private byte[] mEncryptedRecoveryKeyBlob;
@@ -111,14 +113,25 @@
/**
* Public key used to encrypt {@code encryptedRecoveryKeyBlob}.
*
- * See implementation for binary key format
+ * See implementation for binary key format.
+ *
+ * @deprecated Use {@link #getTrustedHardwareCertPath} instead.
+ * @removed
*/
- // TODO: document key format.
+ @Deprecated
public @NonNull byte[] getTrustedHardwarePublicKey() {
return mPublicKey;
}
/**
+ * CertPath containing the public key used to encrypt {@code encryptedRecoveryKeyBlob}.
+ */
+ // TODO: Change to @NonNull
+ public CertPath getTrustedHardwareCertPath() {
+ return mCertPath;
+ }
+
+ /**
* UI and key derivation parameters. Note that combination of secrets may be used.
*/
public @NonNull List<KeyChainProtectionParams> getKeyChainProtectionParams() {
@@ -207,13 +220,29 @@
*
* @param publicKey The public key
* @return This builder.
+ * @deprecated Use {@link #setTrustedHardwareCertPath} instead.
+ * @removed
*/
+ @Deprecated
public Builder setTrustedHardwarePublicKey(byte[] publicKey) {
mInstance.mPublicKey = publicKey;
return this;
}
/**
+ * Sets CertPath used to validate the trusted hardware public key. The CertPath should
+ * contain a certificate of the trusted hardware public key and any necessary intermediate
+ * certificates.
+ *
+ * @param certPath The public key
+ * @return This builder.
+ */
+ public Builder setTrustedHardwareCertPath(CertPath certPath) {
+ mInstance.mCertPath = certPath;
+ return this;
+ }
+
+ /**
* Sets UI and key derivation parameters
*
* @param recoveryMetadata The UI and key derivation parameters
diff --git a/core/java/android/security/keystore/recovery/RecoveryCertPath.aidl b/core/java/android/security/keystore/recovery/RecoveryCertPath.aidl
new file mode 100644
index 0000000..c171a92
--- /dev/null
+++ b/core/java/android/security/keystore/recovery/RecoveryCertPath.aidl
@@ -0,0 +1,20 @@
+/*
+ * 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.security.keystore.recovery;
+
+/* @hide */
+parcelable RecoveryCertPath;
diff --git a/core/java/android/security/keystore/recovery/RecoveryCertPath.java b/core/java/android/security/keystore/recovery/RecoveryCertPath.java
new file mode 100644
index 0000000..1950947
--- /dev/null
+++ b/core/java/android/security/keystore/recovery/RecoveryCertPath.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.keystore.recovery;
+
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.util.Preconditions;
+
+import java.io.ByteArrayInputStream;
+import java.security.cert.CertPath;
+import java.security.cert.CertificateEncodingException;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateFactory;
+
+/**
+ * The certificate path of the recovery service.
+ *
+ * @hide
+ */
+public final class RecoveryCertPath implements Parcelable {
+
+ private static final String CERT_PATH_ENCODING = "PkiPath";
+
+ private final byte[] mEncodedCertPath;
+
+ /**
+ * Wraps a {@code CertPath} to create a {@code Parcelable} for Binder calls.
+ *
+ * @param certPath The certificate path to be wrapped.
+ * @throws CertificateException if the given certificate path cannot be encoded properly.
+ */
+ public static RecoveryCertPath createRecoveryCertPath(@NonNull CertPath certPath)
+ throws CertificateException {
+ // Perform the encoding here to avoid throwing exceptions in writeToParcel
+ try {
+ return new RecoveryCertPath(encodeCertPath(certPath));
+ } catch (CertificateEncodingException e) {
+ throw new CertificateException("Failed to encode the given CertPath", e);
+ }
+ }
+
+ /**
+ * Obtains the {@code CertPath} wrapped in the Parcelable.
+ *
+ * @return the wrapped certificate path.
+ * @throws CertificateException if the wrapped certificate path cannot be decoded properly.
+ */
+ public CertPath getCertPath() throws CertificateException {
+ // Perform the decoding here to avoid throwing exceptions in createFromParcel
+ return decodeCertPath(mEncodedCertPath);
+ }
+
+ private RecoveryCertPath(@NonNull byte[] encodedCertPath) {
+ mEncodedCertPath = Preconditions.checkNotNull(encodedCertPath);
+ }
+
+ private RecoveryCertPath(Parcel in) {
+ mEncodedCertPath = in.createByteArray();
+ }
+
+ public static final Parcelable.Creator<RecoveryCertPath> CREATOR =
+ new Parcelable.Creator<RecoveryCertPath>() {
+ public RecoveryCertPath createFromParcel(Parcel in) {
+ return new RecoveryCertPath(in);
+ }
+
+ public RecoveryCertPath[] newArray(int length) {
+ return new RecoveryCertPath[length];
+ }
+ };
+
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeByteArray(mEncodedCertPath);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @NonNull
+ private static byte[] encodeCertPath(@NonNull CertPath certPath)
+ throws CertificateEncodingException {
+ Preconditions.checkNotNull(certPath);
+ return certPath.getEncoded(CERT_PATH_ENCODING);
+ }
+
+ @NonNull
+ private static CertPath decodeCertPath(@NonNull byte[] bytes) throws CertificateException {
+ Preconditions.checkNotNull(bytes);
+ 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/core/java/android/security/keystore/recovery/RecoveryController.java b/core/java/android/security/keystore/recovery/RecoveryController.java
index fcca5af..3389238 100644
--- a/core/java/android/security/keystore/recovery/RecoveryController.java
+++ b/core/java/android/security/keystore/recovery/RecoveryController.java
@@ -175,28 +175,13 @@
}
/**
- * Deprecated - use getKeyChainSnapshot.
- *
- * Returns data necessary to store all recoverable keys. Key material is
- * encrypted with user secret and recovery public key.
- *
- * @return Data necessary to recover keystore.
- * @throws InternalRecoveryServiceException if an unexpected error occurred in the recovery
- * service.
+ * @deprecated Use {@link #getKeyChainSnapshot()}
+ * @removed
*/
+ @Deprecated
@RequiresPermission(android.Manifest.permission.RECOVER_KEYSTORE)
- public @Nullable KeyChainSnapshot getRecoveryData()
- throws InternalRecoveryServiceException {
- try {
- return mBinder.getKeyChainSnapshot();
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- } catch (ServiceSpecificException e) {
- if (e.errorCode == ERROR_NO_SNAPSHOT_PENDING) {
- return null;
- }
- throw wrapUnexpectedServiceSpecificException(e);
- }
+ public @Nullable KeyChainSnapshot getRecoveryData() throws InternalRecoveryServiceException {
+ return getKeyChainSnapshot();
}
/**
@@ -268,17 +253,21 @@
}
/**
- * Gets aliases of recoverable keys for the application.
- *
- * @param packageName which recoverable keys' aliases will be returned.
- *
- * @return {@code List} of all aliases.
+ * @deprecated Use {@link #getAliases()}.
+ * @removed
*/
+ @Deprecated
public List<String> getAliases(@Nullable String packageName)
throws InternalRecoveryServiceException {
+ return getAliases();
+ }
+
+ /**
+ * Returns a list of aliases of keys belonging to the application.
+ */
+ public List<String> getAliases() throws InternalRecoveryServiceException {
try {
- // TODO: update aidl
- Map<String, Integer> allStatuses = mBinder.getRecoveryStatus(packageName);
+ Map<String, Integer> allStatuses = mBinder.getRecoveryStatus();
return new ArrayList<>(allStatuses.keySet());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
@@ -323,28 +312,31 @@
}
/**
- * Returns recovery status for Application's KeyStore key.
- * Negative status values are reserved for recovery agent specific codes. List of common codes:
+ * @deprecated Use {@link #getRecoveryStatus(String)}.
+ * @removed
+ */
+ @Deprecated
+ public int getRecoveryStatus(String packageName, String alias)
+ throws InternalRecoveryServiceException {
+ return getRecoveryStatus(alias);
+ }
+
+ /**
+ * Returns the recovery status for the key with the given {@code alias}.
*
* <ul>
* <li>{@link #RECOVERY_STATUS_SYNCED}
* <li>{@link #RECOVERY_STATUS_SYNC_IN_PROGRESS}
- * <li>{@link #RECOVERY_STATUS_MISSING_ACCOUNT}
* <li>{@link #RECOVERY_STATUS_PERMANENT_FAILURE}
* </ul>
*
- * @param packageName Application whose recoverable key status is returned.
- * @param alias Application-specific key alias.
- * @return Recovery status.
- * @see #setRecoveryStatus
+ * @see #setRecoveryStatus(String, int)
* @throws InternalRecoveryServiceException if an unexpected error occurred in the recovery
* service.
*/
- public int getRecoveryStatus(String packageName, String alias)
- throws InternalRecoveryServiceException {
+ public int getRecoveryStatus(String alias) throws InternalRecoveryServiceException {
try {
- // TODO: update aidl
- Map<String, Integer> allStatuses = mBinder.getRecoveryStatus(packageName);
+ Map<String, Integer> allStatuses = mBinder.getRecoveryStatus();
Integer status = allStatuses.get(alias);
if (status == null) {
return RecoveryController.RECOVERY_STATUS_PERMANENT_FAILURE;
diff --git a/core/java/android/security/keystore/recovery/RecoverySession.java b/core/java/android/security/keystore/recovery/RecoverySession.java
index 4db5d6e..069af91 100644
--- a/core/java/android/security/keystore/recovery/RecoverySession.java
+++ b/core/java/android/security/keystore/recovery/RecoverySession.java
@@ -24,6 +24,7 @@
import android.util.Log;
import java.security.SecureRandom;
+import java.security.cert.CertPath;
import java.security.cert.CertificateException;
import java.util.List;
import java.util.Map;
@@ -91,7 +92,10 @@
* format.
* @throws InternalRecoveryServiceException if an unexpected error occurred in the recovery
* service.
+ * @deprecated Use {@link #start(CertPath, byte[], byte[], List)} instead.
+ * @removed
*/
+ @Deprecated
@RequiresPermission(android.Manifest.permission.RECOVER_KEYSTORE)
@NonNull public byte[] start(
@NonNull byte[] verifierPublicKey,
@@ -119,6 +123,55 @@
}
/**
+ * Starts a recovery session and returns a blob with proof of recovery secret possession.
+ * The method generates a symmetric key for a session, which trusted remote device can use to
+ * return recovery key.
+ *
+ * @param verifierCertPath The certificate path used to create the recovery blob on the source
+ * device. Keystore will verify the certificate path by using the root of trust.
+ * @param vaultParams Must match the parameters in the corresponding field in the recovery blob.
+ * Used to limit number of guesses.
+ * @param vaultChallenge Data passed from server for this recovery session and used to prevent
+ * replay attacks
+ * @param secrets Secrets provided by user, the method only uses type and secret fields.
+ * @return The recovery claim. Claim provides a b binary blob with recovery claim. It is
+ * encrypted with verifierPublicKey and contains a proof of user secrets, session symmetric
+ * key and parameters necessary to identify the counter with the number of failed recovery
+ * attempts.
+ * @throws CertificateException if the {@code verifierCertPath} is invalid.
+ * @throws InternalRecoveryServiceException if an unexpected error occurred in the recovery
+ * service.
+ */
+ @RequiresPermission(android.Manifest.permission.RECOVER_KEYSTORE)
+ @NonNull public byte[] start(
+ @NonNull CertPath verifierCertPath,
+ @NonNull byte[] vaultParams,
+ @NonNull byte[] vaultChallenge,
+ @NonNull List<KeyChainProtectionParams> secrets)
+ throws CertificateException, InternalRecoveryServiceException {
+ // Wrap the CertPath in a Parcelable so it can be passed via Binder calls.
+ RecoveryCertPath recoveryCertPath =
+ RecoveryCertPath.createRecoveryCertPath(verifierCertPath);
+ try {
+ byte[] recoveryClaim =
+ mRecoveryController.getBinder().startRecoverySessionWithCertPath(
+ mSessionId,
+ recoveryCertPath,
+ vaultParams,
+ vaultChallenge,
+ secrets);
+ return recoveryClaim;
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ } catch (ServiceSpecificException e) {
+ if (e.errorCode == RecoveryController.ERROR_BAD_CERTIFICATE_FORMAT) {
+ throw new CertificateException(e.getMessage());
+ }
+ throw mRecoveryController.wrapUnexpectedServiceSpecificException(e);
+ }
+ }
+
+ /**
* Imports keys.
*
* @param recoveryKeyBlob Recovery blob encrypted by symmetric key generated for this session.
diff --git a/core/java/android/service/dreams/DreamService.java b/core/java/android/service/dreams/DreamService.java
index 99e2c62..2b114d5 100644
--- a/core/java/android/service/dreams/DreamService.java
+++ b/core/java/android/service/dreams/DreamService.java
@@ -665,6 +665,11 @@
}
private void updateDoze() {
+ if (mWindowToken == null) {
+ Slog.w(TAG, "Updating doze without a window token.");
+ return;
+ }
+
if (mDozing) {
try {
mSandman.startDozing(mWindowToken, mDozeScreenState, mDozeScreenBrightness);
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 9b4ea33..5966a86 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -137,7 +137,7 @@
private static final int MAGIC = 0xBA757475; // 'BATSTATS'
// Current on-disk Parcel version
- private static final int VERSION = 175 + (USE_OLD_HISTORY ? 1000 : 0);
+ private static final int VERSION = 176 + (USE_OLD_HISTORY ? 1000 : 0);
// Maximum number of items we will record in the history.
private static final int MAX_HISTORY_ITEMS;
@@ -2823,6 +2823,7 @@
implements Parcelable {
private final LongSamplingCounter mIdleTimeMillis;
private final LongSamplingCounter mScanTimeMillis;
+ private final LongSamplingCounter mSleepTimeMillis;
private final LongSamplingCounter mRxTimeMillis;
private final LongSamplingCounter[] mTxTimeMillis;
private final LongSamplingCounter mPowerDrainMaMs;
@@ -2830,6 +2831,7 @@
public ControllerActivityCounterImpl(TimeBase timeBase, int numTxStates) {
mIdleTimeMillis = new LongSamplingCounter(timeBase);
mScanTimeMillis = new LongSamplingCounter(timeBase);
+ mSleepTimeMillis = new LongSamplingCounter(timeBase);
mRxTimeMillis = new LongSamplingCounter(timeBase);
mTxTimeMillis = new LongSamplingCounter[numTxStates];
for (int i = 0; i < numTxStates; i++) {
@@ -2841,6 +2843,7 @@
public ControllerActivityCounterImpl(TimeBase timeBase, int numTxStates, Parcel in) {
mIdleTimeMillis = new LongSamplingCounter(timeBase, in);
mScanTimeMillis = new LongSamplingCounter(timeBase, in);
+ mSleepTimeMillis = new LongSamplingCounter(timeBase, in);
mRxTimeMillis = new LongSamplingCounter(timeBase, in);
final int recordedTxStates = in.readInt();
if (recordedTxStates != numTxStates) {
@@ -2857,6 +2860,7 @@
public void readSummaryFromParcel(Parcel in) {
mIdleTimeMillis.readSummaryFromParcelLocked(in);
mScanTimeMillis.readSummaryFromParcelLocked(in);
+ mSleepTimeMillis.readSummaryFromParcelLocked(in);
mRxTimeMillis.readSummaryFromParcelLocked(in);
final int recordedTxStates = in.readInt();
if (recordedTxStates != mTxTimeMillis.length) {
@@ -2876,6 +2880,7 @@
public void writeSummaryToParcel(Parcel dest) {
mIdleTimeMillis.writeSummaryFromParcelLocked(dest);
mScanTimeMillis.writeSummaryFromParcelLocked(dest);
+ mSleepTimeMillis.writeSummaryFromParcelLocked(dest);
mRxTimeMillis.writeSummaryFromParcelLocked(dest);
dest.writeInt(mTxTimeMillis.length);
for (LongSamplingCounter counter : mTxTimeMillis) {
@@ -2888,6 +2893,7 @@
public void writeToParcel(Parcel dest, int flags) {
mIdleTimeMillis.writeToParcel(dest);
mScanTimeMillis.writeToParcel(dest);
+ mSleepTimeMillis.writeToParcel(dest);
mRxTimeMillis.writeToParcel(dest);
dest.writeInt(mTxTimeMillis.length);
for (LongSamplingCounter counter : mTxTimeMillis) {
@@ -2899,6 +2905,7 @@
public void reset(boolean detachIfReset) {
mIdleTimeMillis.reset(detachIfReset);
mScanTimeMillis.reset(detachIfReset);
+ mSleepTimeMillis.reset(detachIfReset);
mRxTimeMillis.reset(detachIfReset);
for (LongSamplingCounter counter : mTxTimeMillis) {
counter.reset(detachIfReset);
@@ -2909,6 +2916,7 @@
public void detach() {
mIdleTimeMillis.detach();
mScanTimeMillis.detach();
+ mSleepTimeMillis.detach();
mRxTimeMillis.detach();
for (LongSamplingCounter counter : mTxTimeMillis) {
counter.detach();
@@ -2935,6 +2943,15 @@
}
/**
+ * @return a LongSamplingCounter, measuring time spent in the sleep state in
+ * milliseconds.
+ */
+ @Override
+ public LongSamplingCounter getSleepTimeCounter() {
+ return mSleepTimeMillis;
+ }
+
+ /**
* @return a LongSamplingCounter, measuring time spent in the receive state in
* milliseconds.
*/
@@ -11438,6 +11455,8 @@
mHasModemReporting = true;
mModemActivity.getIdleTimeCounter().addCountLocked(
deltaInfo.getIdleTimeMillis());
+ mModemActivity.getSleepTimeCounter().addCountLocked(
+ deltaInfo.getSleepTimeMillis());
mModemActivity.getRxTimeCounter().addCountLocked(deltaInfo.getRxTimeMillis());
for (int lvl = 0; lvl < ModemActivityInfo.TX_POWER_LEVELS; lvl++) {
mModemActivity.getTxTimeCounters()[lvl]
@@ -12960,6 +12979,7 @@
final int which = STATS_SINCE_CHARGED;
final long rawRealTime = SystemClock.elapsedRealtime() * 1000;
final ControllerActivityCounter counter = getModemControllerActivity();
+ final long sleepTimeMs = counter.getSleepTimeCounter().getCountLocked(which);
final long idleTimeMs = counter.getIdleTimeCounter().getCountLocked(which);
final long rxTimeMs = counter.getRxTimeCounter().getCountLocked(which);
final long energyConsumedMaMs = counter.getPowerCounter().getCountLocked(which);
@@ -12979,10 +12999,6 @@
txTimeMs[i] = counter.getTxTimeCounters()[i].getCountLocked(which);
totalTxTimeMs += txTimeMs[i];
}
- final long totalControllerActivityTimeMs
- = computeBatteryRealtime(SystemClock.elapsedRealtime() * 1000, which) / 1000;
- final long sleepTimeMs
- = totalControllerActivityTimeMs - (idleTimeMs + rxTimeMs + totalTxTimeMs);
s.setLoggingDurationMs(computeBatteryRealtime(rawRealTime, which) / 1000);
s.setKernelActiveTimeMs(getMobileRadioActiveTime(rawRealTime, which) / 1000);
s.setNumPacketsTx(getNetworkActivityPackets(NETWORK_MOBILE_TX_DATA, which));
@@ -14602,6 +14618,7 @@
u.mJobsFreshnessTimeMs.writeSummaryFromParcelLocked(out);
for (int i = 0; i < JOB_FRESHNESS_BUCKETS.length; i++) {
if (u.mJobsFreshnessBuckets[i] != null) {
+ out.writeInt(1);
u.mJobsFreshnessBuckets[i].writeSummaryFromParcelLocked(out);
} else {
out.writeInt(0);
diff --git a/core/java/com/android/internal/os/KernelWakelockReader.java b/core/java/com/android/internal/os/KernelWakelockReader.java
index 7178ec7..46667d1 100644
--- a/core/java/com/android/internal/os/KernelWakelockReader.java
+++ b/core/java/com/android/internal/os/KernelWakelockReader.java
@@ -16,6 +16,7 @@
package com.android.internal.os;
import android.os.Process;
+import android.os.StrictMode;
import android.os.SystemClock;
import android.util.Slog;
@@ -69,6 +70,7 @@
boolean wakeup_sources;
final long startTime = SystemClock.uptimeMillis();
+ final int oldMask = StrictMode.allowThreadDiskReadsMask();
try {
FileInputStream is;
try {
@@ -90,6 +92,8 @@
} catch (java.io.IOException e) {
Slog.wtf(TAG, "failed to read kernel wakelocks", e);
return null;
+ } finally {
+ StrictMode.setThreadPolicyMask(oldMask);
}
final long readTime = SystemClock.uptimeMillis() - startTime;
diff --git a/core/java/com/android/internal/policy/PhoneWindow.java b/core/java/com/android/internal/policy/PhoneWindow.java
index 03a7cd2..50c9d6c 100644
--- a/core/java/com/android/internal/policy/PhoneWindow.java
+++ b/core/java/com/android/internal/policy/PhoneWindow.java
@@ -2040,6 +2040,10 @@
if (getKeyguardManager().inKeyguardRestrictedInputMode()) {
break;
}
+ if ((getContext().getResources().getConfiguration().uiMode
+ & Configuration.UI_MODE_TYPE_MASK) == Configuration.UI_MODE_TYPE_WATCH) {
+ break;
+ }
if (event.isTracking() && !event.isCanceled()) {
launchDefaultSearch(event);
}
diff --git a/core/java/com/android/internal/widget/ILockSettings.aidl b/core/java/com/android/internal/widget/ILockSettings.aidl
index 17e498c..1fc2796 100644
--- a/core/java/com/android/internal/widget/ILockSettings.aidl
+++ b/core/java/com/android/internal/widget/ILockSettings.aidl
@@ -22,6 +22,7 @@
import android.security.keystore.recovery.WrappedApplicationKey;
import android.security.keystore.recovery.KeyChainSnapshot;
import android.security.keystore.recovery.KeyChainProtectionParams;
+import android.security.keystore.recovery.RecoveryCertPath;
import com.android.internal.widget.ICheckCredentialProgressCallback;
import com.android.internal.widget.VerifyCredentialResponse;
@@ -73,7 +74,7 @@
Map getRecoverySnapshotVersions();
void setServerParams(in byte[] serverParams);
void setRecoveryStatus(in String alias, int status);
- Map getRecoveryStatus(in String packageName);
+ Map getRecoveryStatus();
void setRecoverySecretTypes(in int[] secretTypes);
int[] getRecoverySecretTypes();
int[] getPendingRecoverySecretTypes();
@@ -81,6 +82,9 @@
byte[] startRecoverySession(in String sessionId,
in byte[] verifierPublicKey, in byte[] vaultParams, in byte[] vaultChallenge,
in List<KeyChainProtectionParams> secrets);
+ byte[] startRecoverySessionWithCertPath(in String sessionId,
+ in RecoveryCertPath verifierCertPath, in byte[] vaultParams, in byte[] vaultChallenge,
+ in List<KeyChainProtectionParams> secrets);
Map/*<String, byte[]>*/ recoverKeys(in String sessionId, in byte[] recoveryKeyBlob,
in List<WrappedApplicationKey> applicationKeys);
void closeSession(in String sessionId);
diff --git a/core/java/com/android/internal/widget/LockPatternView.java b/core/java/com/android/internal/widget/LockPatternView.java
index 7a248f2..51dd929 100644
--- a/core/java/com/android/internal/widget/LockPatternView.java
+++ b/core/java/com/android/internal/widget/LockPatternView.java
@@ -125,6 +125,7 @@
private boolean mInStealthMode = false;
private boolean mEnableHapticFeedback = true;
private boolean mPatternInProgress = false;
+ private boolean mFadePattern = true;
private float mHitFactor = 0.6f;
@@ -376,6 +377,14 @@
}
/**
+ * Set whether the pattern should fade as it's being drawn. If
+ * true, each segment of the pattern fades over time.
+ */
+ public void setFadePattern(boolean fadePattern) {
+ mFadePattern = fadePattern;
+ }
+
+ /**
* Set whether the view will use tactile feedback. If true, there will be
* tactile feedback as the user enters the pattern.
*
@@ -1167,10 +1176,18 @@
currentPath.moveTo(lastX, lastY);
if (state.lineEndX != Float.MIN_VALUE && state.lineEndY != Float.MIN_VALUE) {
currentPath.lineTo(state.lineEndX, state.lineEndY);
- mPathPaint.setAlpha((int) 255 - lineFadeVal );
+ if (mFadePattern) {
+ mPathPaint.setAlpha((int) 255 - lineFadeVal );
+ } else {
+ mPathPaint.setAlpha(255);
+ }
} else {
currentPath.lineTo(centerX, centerY);
- mPathPaint.setAlpha((int) 255 - lineFadeVal );
+ if (mFadePattern) {
+ mPathPaint.setAlpha((int) 255 - lineFadeVal );
+ } else {
+ mPathPaint.setAlpha(255);
+ }
}
canvas.drawPath(currentPath, mPathPaint);
}
diff --git a/core/jni/android/graphics/AnimatedImageDrawable.cpp b/core/jni/android/graphics/AnimatedImageDrawable.cpp
index ba56d59..6fd4abf 100644
--- a/core/jni/android/graphics/AnimatedImageDrawable.cpp
+++ b/core/jni/android/graphics/AnimatedImageDrawable.cpp
@@ -132,6 +132,11 @@
// Java's LOOP_INFINITE relies on this being the same.
static_assert(SkCodec::kRepetitionCountInfinite == -1);
+static jint AnimatedImageDrawable_nGetLoopCount(JNIEnv* env, jobject /*clazz*/, jlong nativePtr) {
+ auto* drawable = reinterpret_cast<AnimatedImageDrawable*>(nativePtr);
+ return drawable->getRepetitionCount();
+}
+
static void AnimatedImageDrawable_nSetLoopCount(JNIEnv* env, jobject /*clazz*/, jlong nativePtr,
jint loopCount) {
auto* drawable = reinterpret_cast<AnimatedImageDrawable*>(nativePtr);
@@ -218,6 +223,7 @@
{ "nIsRunning", "(J)Z", (void*) AnimatedImageDrawable_nIsRunning },
{ "nStart", "(J)Z", (void*) AnimatedImageDrawable_nStart },
{ "nStop", "(J)Z", (void*) AnimatedImageDrawable_nStop },
+ { "nGetLoopCount", "(J)I", (void*) AnimatedImageDrawable_nGetLoopCount },
{ "nSetLoopCount", "(JI)V", (void*) AnimatedImageDrawable_nSetLoopCount },
{ "nSetOnAnimationEndListener", "(JLandroid/graphics/drawable/AnimatedImageDrawable;)V", (void*) AnimatedImageDrawable_nSetOnAnimationEndListener },
{ "nNativeByteSize", "(J)J", (void*) AnimatedImageDrawable_nNativeByteSize },
diff --git a/core/jni/android_media_MicrophoneInfo.cpp b/core/jni/android_media_MicrophoneInfo.cpp
index 9198cbe..5bd808b 100644
--- a/core/jni/android_media_MicrophoneInfo.cpp
+++ b/core/jni/android_media_MicrophoneInfo.cpp
@@ -65,13 +65,11 @@
}
jGeometricLocation = env->NewObject(gMicrophoneInfoCoordinateClass,
gMicrophoneInfoCoordinateCstor,
- NULL,
microphoneInfo->getGeometricLocation()[0],
microphoneInfo->getGeometricLocation()[1],
microphoneInfo->getGeometricLocation()[2]);
jOrientation = env->NewObject(gMicrophoneInfoCoordinateClass,
gMicrophoneInfoCoordinateCstor,
- NULL,
microphoneInfo->getOrientation()[0],
microphoneInfo->getOrientation()[1],
microphoneInfo->getOrientation()[2]);
@@ -177,7 +175,7 @@
env, "android/media/MicrophoneInfo$Coordinate3F");
gMicrophoneInfoCoordinateClass = MakeGlobalRefOrDie(env, microphoneInfoCoordinateClass);
gMicrophoneInfoCoordinateCstor = GetMethodIDOrDie(env, microphoneInfoCoordinateClass, "<init>",
- "(Landroid/media/MicrophoneInfo;FFF)V");
+ "(FFF)V");
jclass pairClass = FindClassOrDie(env, "android/util/Pair");
gPairClass = MakeGlobalRefOrDie(env, pairClass);
diff --git a/core/proto/android/content/configuration.proto b/core/proto/android/content/configuration.proto
index a62d56c..834ecde 100644
--- a/core/proto/android/content/configuration.proto
+++ b/core/proto/android/content/configuration.proto
@@ -25,7 +25,7 @@
import "frameworks/base/libs/incident/proto/android/privacy.proto";
/**
- * An android resource configuration.
+ * An android Configuration object.
*/
message ConfigurationProto {
option (.android.msg_privacy).dest = DEST_AUTOMATIC;
@@ -35,17 +35,65 @@
optional uint32 mnc = 3;
repeated LocaleProto locales = 4;
optional uint32 screen_layout = 5;
- optional uint32 touchscreen = 6;
- optional uint32 keyboard_hidden = 7;
- optional uint32 hard_keyboard_hidden = 8;
- optional uint32 navigation = 9;
- optional uint32 navigation_hidden = 10;
- optional uint32 orientation = 11;
- optional uint32 ui_mode = 12;
- optional uint32 screen_width_dp = 13;
- optional uint32 screen_height_dp = 14;
- optional uint32 smallest_screen_width_dp = 15;
- optional uint32 density_dpi = 16;
- optional .android.app.WindowConfigurationProto window_configuration = 17;
+ optional uint32 hdr_color_mode = 6;
+ optional uint32 wide_color_gamut = 7;
+ optional uint32 touchscreen = 8;
+ optional uint32 keyboard_hidden = 9;
+ optional uint32 hard_keyboard_hidden = 10;
+ optional uint32 navigation = 11;
+ optional uint32 navigation_hidden = 12;
+ optional uint32 orientation = 13;
+ optional uint32 ui_mode = 14;
+ optional uint32 screen_width_dp = 15;
+ optional uint32 screen_height_dp = 16;
+ optional uint32 smallest_screen_width_dp = 17;
+ optional uint32 density_dpi = 18;
+ optional .android.app.WindowConfigurationProto window_configuration = 19;
}
+/**
+ * All current configuration data used to select resources.
+ */
+message ResourcesConfigurationProto {
+ option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
+ required ConfigurationProto configuration = 1;
+
+ optional uint32 sdk_version = 2;
+ optional uint32 screen_width_px = 3;
+ optional uint32 screen_height_px = 4;
+}
+
+/**
+ * Overall device configuration data.
+ */
+message DeviceConfigurationProto {
+ option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
+ optional uint32 stable_screen_width_px = 1;
+ optional uint32 stable_screen_height_px = 2;
+ optional uint32 stable_density_dpi = 3;
+
+ optional uint64 total_ram = 4;
+ optional bool low_ram = 5;
+ optional uint32 max_cores = 6;
+ optional bool has_secure_screen_lock = 7;
+
+ optional uint32 opengl_version = 8;
+ repeated string opengl_extensions = 9;
+
+ repeated string shared_libraries = 10;
+ repeated string features = 11;
+ repeated string cpu_architectures = 12;
+}
+
+/**
+ * All current configuration data device is running with, everything used
+ * to filter and target apps.
+ */
+message GlobalConfigurationProto {
+ option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
+ optional ResourcesConfigurationProto resources = 1;
+ optional DeviceConfigurationProto device = 2;
+}
diff --git a/core/proto/android/providers/settings.proto b/core/proto/android/providers/settings.proto
index c35b2ec..9752d3b 100644
--- a/core/proto/android/providers/settings.proto
+++ b/core/proto/android/providers/settings.proto
@@ -179,9 +179,11 @@
optional SettingProto tether_dun_required = 114 [ (android.privacy).dest = DEST_AUTOMATIC ];
optional SettingProto tether_dun_apn = 115;
optional SettingProto tether_offload_disabled = 301 [ (android.privacy).dest = DEST_AUTOMATIC ];
- // List of carrier apps which are whitelisted to prompt the user for install
- // when a SIM card with marchin UICC carrier privilege rules is inserted.
+ // List of carrier app certificate mapped to carrier app package id which are whitelisted to
+ // prompt the user for install when a SIM card with matching UICC carrier privilege rules is
+ // inserted.
optional SettingProto carrier_app_whitelist = 116 [ (android.privacy).dest = DEST_AUTOMATIC ];
+ optional SettingProto carrier_app_names = 358 [ (android.privacy).dest = DEST_AUTOMATIC ];
optional SettingProto usb_mass_storage_enabled = 117 [ (android.privacy).dest = DEST_AUTOMATIC ];
optional SettingProto use_google_mail = 118 [ (android.privacy).dest = DEST_AUTOMATIC ];
optional SettingProto webview_data_reduction_proxy_key = 119;
@@ -433,7 +435,7 @@
// Please insert fields in the same order as in
// frameworks/base/core/java/android/provider/Settings.java.
- // Next tag = 356;
+ // Next tag = 359;
}
message SecureSettingsProto {
diff --git a/core/res/res/drawable-watch/sym_def_app_icon.xml b/core/res/res/drawable-watch/sym_def_app_icon.xml
new file mode 100644
index 0000000..6945256
--- /dev/null
+++ b/core/res/res/drawable-watch/sym_def_app_icon.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
+ <background android:drawable="@drawable/sym_def_app_icon_background" />
+ <foreground android:drawable="@mipmap/sym_def_app_icon_foreground" />
+</adaptive-icon>
diff --git a/core/res/res/drawable-watch/sym_def_app_icon_background.xml b/core/res/res/drawable-watch/sym_def_app_icon_background.xml
new file mode 100644
index 0000000..6d6352f
--- /dev/null
+++ b/core/res/res/drawable-watch/sym_def_app_icon_background.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
+ <solid android:color="#2374CE"/>
+</shape>
\ No newline at end of file
diff --git a/core/res/res/layout/notification_template_material_ambient.xml b/core/res/res/layout/notification_template_material_ambient.xml
index 525d493..346aad6c 100644
--- a/core/res/res/layout/notification_template_material_ambient.xml
+++ b/core/res/res/layout/notification_template_material_ambient.xml
@@ -43,6 +43,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="top"
+ android:layout_weight="1"
android:paddingStart="@dimen/notification_content_margin_start"
android:paddingEnd="@dimen/notification_content_margin_end"
android:clipToPadding="false"
@@ -75,19 +76,19 @@
android:maxLines="3"
/>
</LinearLayout>
+ <FrameLayout android:id="@+id/actions_container"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="bottom">
+ <com.android.internal.widget.NotificationActionListLayout
+ android:id="@+id/actions"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/notification_action_list_height"
+ android:paddingEnd="4dp"
+ android:orientation="horizontal"
+ android:gravity="center"
+ android:visibility="gone"
+ />
+ </FrameLayout>
</LinearLayout>
- <FrameLayout android:id="@+id/actions_container"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_gravity="bottom">
- <com.android.internal.widget.NotificationActionListLayout
- android:id="@+id/actions"
- android:layout_width="match_parent"
- android:layout_height="@dimen/notification_action_list_height"
- android:paddingEnd="4dp"
- android:orientation="horizontal"
- android:gravity="center"
- android:visibility="gone"
- />
- </FrameLayout>
</FrameLayout>
diff --git a/core/res/res/mipmap-watch-anydpi/sym_def_app_icon_foreground.xml b/core/res/res/mipmap-watch-anydpi/sym_def_app_icon_foreground.xml
new file mode 100644
index 0000000..69c241c
--- /dev/null
+++ b/core/res/res/mipmap-watch-anydpi/sym_def_app_icon_foreground.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<inset
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:insetLeft="24dp"
+ android:insetRight="24dp"
+ android:insetTop="24dp"
+ android:insetBottom="24dp">
+ <vector
+ android:width="60dp"
+ android:height="60dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FFFFFF"
+ android:pathData="M20,12c0,-2.6 -1.2,-4.8 -3.1,-6.3l-0.5,-2.8c-1.4,-0.7 -2.9,-1.1 -4.5,-1.1c-1.6,0 -3.1,0.4 -4.5,1.1L7,5.7C5.1,7.2 3.9,9.4 3.9,12c0,2.6 1.2,4.8 3.1,6.3l0.5,2.8c1.4,0.7 2.9,1.1 4.5,1.1c1.6,0 3.2,-0.4 4.5,-1.1l0.5,-2.8C18.8,16.8 20,14.6 20,12zM12,18c-3.3,0 -6,-2.7 -6,-6c0,-3.3 2.7,-6 6,-6s6,2.7 6,6C18,15.3 15.3,18 12,18z"/>
+ <path
+ android:fillColor="#FFFFFF"
+ android:pathData="M11.2,15.6h1.4v-4.3h-1.4V15.6zM11.2,9.8h1.4V8.4h-1.4V9.8z"/>
+ </vector>
+</inset>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 0b33211..96a83f8 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -8006,6 +8006,8 @@
<attr name="icon"/>
<!-- The activity to launch when the setting is clicked on. -->
<attr name="settingsActivity"/>
+ <!-- The user restriction for this preference. -->
+ <attr name="userRestriction"/>
</declare-styleable>
<!-- =============================== -->
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 82fefef..a5ba4c6 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2870,6 +2870,8 @@
<public name="outlineAmbientShadowColor" />
<public name="maxLongVersionCode" />
<public name="urlBarResourceId" />
+ <!-- @hide @SystemApi -->
+ <public name="userRestriction" />
</public-group>
<public-group type="style" first-id="0x010302e0">
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index cadc3ff..5c9f863 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -3204,9 +3204,10 @@
<string name="sim_restart_button">Restart</string>
<!-- See Carrier_App_Dialog. This is the message of that dialog. -->
<string name="install_carrier_app_notification_title">Activate mobile service</string>
- <string name="install_carrier_app_notification_text">
- Download the carrier app to activate your new SIM
- </string>
+ <!-- Notification message that shows when the user inserts a SIM card that requires a carrier app download, but the app name is unknown -->
+ <string name="install_carrier_app_notification_text">Download the carrier app to activate your new SIM</string>
+ <!-- Notification message that shows when the user inserts a SIM card that requires a carrier app download. App name is known -->
+ <string name="install_carrier_app_notification_text_app_name">Download the <xliff:g id="app_name">%1$s</xliff:g> app to activate your new SIM</string>
<!-- See Carrier_App_Notification. This is the button of that dialog. -->
<string name="install_carrier_app_notification_button">Download app</string>
<!-- See carrier_app_notification. This is the headline. -->
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index a2af57e..a6a3663 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2774,6 +2774,7 @@
<java-symbol type="string" name="install_carrier_app_notification_title" />
<java-symbol type="string" name="install_carrier_app_notification_text" />
+ <java-symbol type="string" name="install_carrier_app_notification_text_app_name" />
<java-symbol type="string" name="install_carrier_app_notification_button" />
<java-symbol type="string" name="carrier_app_notification_title" />
<java-symbol type="string" name="carrier_app_notification_text" />
diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
index bba8c1a..be2c235 100644
--- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
@@ -155,6 +155,7 @@
Settings.Global.CAPTIVE_PORTAL_USER_AGENT,
Settings.Global.CAR_DOCK_SOUND,
Settings.Global.CARRIER_APP_WHITELIST,
+ Settings.Global.CARRIER_APP_NAMES,
Settings.Global.CAR_UNDOCK_SOUND,
Settings.Global.CDMA_CELL_BROADCAST_SMS,
Settings.Global.CDMA_ROAMING_MODE,
diff --git a/graphics/java/android/graphics/drawable/AnimatedImageDrawable.java b/graphics/java/android/graphics/drawable/AnimatedImageDrawable.java
index 796d008..f70d6e1 100644
--- a/graphics/java/android/graphics/drawable/AnimatedImageDrawable.java
+++ b/graphics/java/android/graphics/drawable/AnimatedImageDrawable.java
@@ -16,8 +16,6 @@
package android.graphics.drawable;
-import dalvik.annotation.optimization.FastNative;
-
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -25,8 +23,6 @@
import android.content.res.Resources;
import android.content.res.Resources.Theme;
import android.content.res.TypedArray;
-import android.util.AttributeSet;
-import android.view.InflateException;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.ColorFilter;
@@ -35,20 +31,21 @@
import android.graphics.Rect;
import android.os.Handler;
import android.os.Looper;
+import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.util.TypedValue;
import com.android.internal.R;
+import dalvik.annotation.optimization.FastNative;
+
+import libcore.util.NativeAllocationRegistry;
+
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
-import libcore.io.IoUtils;
-import libcore.util.NativeAllocationRegistry;
-
import java.io.IOException;
import java.io.InputStream;
-import java.lang.Runnable;
import java.util.ArrayList;
/**
@@ -109,6 +106,16 @@
}
/**
+ * Retrieve the number of times the animation will loop.
+ */
+ public int getLoopCount() {
+ if (mState.mNativePtr == 0) {
+ throw new IllegalStateException("called getLoopCount on empty AnimatedImageDrawable");
+ }
+ return nGetLoopCount(mState.mNativePtr);
+ }
+
+ /**
* Create an empty AnimatedImageDrawable.
*/
public AnimatedImageDrawable() {
@@ -501,6 +508,8 @@
@FastNative
private static native boolean nStop(long nativePtr);
@FastNative
+ private static native int nGetLoopCount(long nativePtr);
+ @FastNative
private static native void nSetLoopCount(long nativePtr, int loopCount);
// Pass the drawable down to native so it can call onAnimationEnd.
private static native void nSetOnAnimationEndListener(long nativePtr,
diff --git a/libs/hwui/hwui/AnimatedImageDrawable.h b/libs/hwui/hwui/AnimatedImageDrawable.h
index 07469d2..a5260be 100644
--- a/libs/hwui/hwui/AnimatedImageDrawable.h
+++ b/libs/hwui/hwui/AnimatedImageDrawable.h
@@ -72,6 +72,7 @@
// already stopped)
bool stop();
bool isRunning();
+ int getRepetitionCount() const { return mSkAnimatedImage->getRepetitionCount(); }
void setRepetitionCount(int count) { mSkAnimatedImage->setRepetitionCount(count); }
void setOnAnimationEndListener(std::unique_ptr<OnAnimationEndListener> listener) {
diff --git a/libs/hwui/renderthread/VulkanManager.cpp b/libs/hwui/renderthread/VulkanManager.cpp
index 9d246ff..21c91a2 100644
--- a/libs/hwui/renderthread/VulkanManager.cpp
+++ b/libs/hwui/renderthread/VulkanManager.cpp
@@ -310,7 +310,7 @@
for (uint32_t i = 0; i < surface->mImageCount; ++i) {
GrVkImageInfo info;
info.fImage = surface->mImages[i];
- info.fAlloc = {VK_NULL_HANDLE, 0, 0, 0};
+ info.fAlloc = GrVkAlloc();
info.fImageLayout = VK_IMAGE_LAYOUT_UNDEFINED;
info.fImageTiling = VK_IMAGE_TILING_OPTIMAL;
info.fFormat = format;
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 17a8d5c..5b627ec 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -53,6 +53,7 @@
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.Log;
+import android.util.Pair;
import android.util.Slog;
import android.view.KeyEvent;
@@ -4784,6 +4785,28 @@
}
/**
+ * Convert {@link AudioDeviceInfo} to {@link MicrophoneInfo}.
+ * @hide
+ */
+ public static MicrophoneInfo microphoneInfoFromAudioDeviceInfo(AudioDeviceInfo deviceInfo) {
+ int deviceType = deviceInfo.getType();
+ int micLocation = (deviceType == AudioDeviceInfo.TYPE_BUILTIN_MIC
+ || deviceType == AudioDeviceInfo.TYPE_TELEPHONY) ? MicrophoneInfo.LOCATION_MAINBODY
+ : deviceType == AudioDeviceInfo.TYPE_UNKNOWN ? MicrophoneInfo.LOCATION_UNKNOWN
+ : MicrophoneInfo.LOCATION_PERIPHERAL;
+ MicrophoneInfo microphone = new MicrophoneInfo(
+ deviceInfo.getPort().name() + deviceInfo.getId(),
+ deviceInfo.getPort().type(), deviceInfo.getAddress(), micLocation,
+ MicrophoneInfo.GROUP_UNKNOWN, MicrophoneInfo.INDEX_IN_THE_GROUP_UNKNOWN,
+ MicrophoneInfo.POSITION_UNKNOWN, MicrophoneInfo.ORIENTATION_UNKNOWN,
+ new ArrayList<Pair<Float, Float>>(), new ArrayList<Pair<Integer, Integer>>(),
+ MicrophoneInfo.SENSITIVITY_UNKNOWN, MicrophoneInfo.SPL_UNKNOWN,
+ MicrophoneInfo.SPL_UNKNOWN, MicrophoneInfo.DIRECTIONALITY_UNKNOWN);
+ microphone.setId(deviceInfo.getId());
+ return microphone;
+ }
+
+ /**
* Returns a list of {@link MicrophoneInfo} that corresponds to the characteristics
* of all available microphones. The list is empty when no microphones are available
* on the device. An error during the query will result in an IOException being thrown.
@@ -4800,6 +4823,15 @@
return new ArrayList<MicrophoneInfo>(); // Always return a list.
}
setPortIdForMicrophones(microphones);
+ AudioDeviceInfo[] devices = getDevicesStatic(GET_DEVICES_INPUTS);
+ for (AudioDeviceInfo device : devices) {
+ if (device.getType() == AudioDeviceInfo.TYPE_BUILTIN_MIC ||
+ device.getType() == AudioDeviceInfo.TYPE_TELEPHONY) {
+ continue;
+ }
+ MicrophoneInfo microphone = microphoneInfoFromAudioDeviceInfo(device);
+ microphones.add(microphone);
+ }
return microphones;
}
diff --git a/media/java/android/media/AudioRecord.java b/media/java/android/media/AudioRecord.java
index 3847530..4f0dccb 100644
--- a/media/java/android/media/AudioRecord.java
+++ b/media/java/android/media/AudioRecord.java
@@ -41,6 +41,7 @@
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.Log;
+import android.util.Pair;
import com.android.internal.annotations.GuardedBy;
@@ -1630,6 +1631,20 @@
return new ArrayList<MicrophoneInfo>();
}
AudioManager.setPortIdForMicrophones(activeMicrophones);
+
+ // Use routed device when there is not information returned by hal.
+ if (activeMicrophones.size() == 0) {
+ AudioDeviceInfo device = getRoutedDevice();
+ if (device != null) {
+ MicrophoneInfo microphone = AudioManager.microphoneInfoFromAudioDeviceInfo(device);
+ ArrayList<Pair<Integer, Integer>> channelMapping = new ArrayList<>();
+ for (int i = 0; i < mChannelCount; i++) {
+ channelMapping.add(new Pair(i, MicrophoneInfo.CHANNEL_MAPPING_DIRECT));
+ }
+ microphone.setChannelMapping(channelMapping);
+ activeMicrophones.add(microphone);
+ }
+ }
return activeMicrophones;
}
diff --git a/media/java/android/media/MediaRecorder.java b/media/java/android/media/MediaRecorder.java
index 9ad5cd9..90b6bff 100644
--- a/media/java/android/media/MediaRecorder.java
+++ b/media/java/android/media/MediaRecorder.java
@@ -29,6 +29,7 @@
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.Log;
+import android.util.Pair;
import android.view.Surface;
import java.io.File;
@@ -105,6 +106,8 @@
private OnErrorListener mOnErrorListener;
private OnInfoListener mOnInfoListener;
+ private int mChannelCount;
+
/**
* Default constructor.
*/
@@ -119,6 +122,7 @@
mEventHandler = null;
}
+ mChannelCount = 1;
String packageName = ActivityThread.currentPackageName();
/* Native setup requires a weak reference to our object.
* It's easier to create it here than in C++.
@@ -755,6 +759,7 @@
if (numChannels <= 0) {
throw new IllegalArgumentException("Number of channels is not positive");
}
+ mChannelCount = numChannels;
setParameter("audio-param-number-of-channels=" + numChannels);
}
@@ -1432,6 +1437,20 @@
return new ArrayList<MicrophoneInfo>();
}
AudioManager.setPortIdForMicrophones(activeMicrophones);
+
+ // Use routed device when there is not information returned by hal.
+ if (activeMicrophones.size() == 0) {
+ AudioDeviceInfo device = getRoutedDevice();
+ if (device != null) {
+ MicrophoneInfo microphone = AudioManager.microphoneInfoFromAudioDeviceInfo(device);
+ ArrayList<Pair<Integer, Integer>> channelMapping = new ArrayList<>();
+ for (int i = 0; i < mChannelCount; i++) {
+ channelMapping.add(new Pair(i, MicrophoneInfo.CHANNEL_MAPPING_DIRECT));
+ }
+ microphone.setChannelMapping(channelMapping);
+ activeMicrophones.add(microphone);
+ }
+ }
return activeMicrophones;
}
diff --git a/media/java/android/media/MicrophoneInfo.java b/media/java/android/media/MicrophoneInfo.java
index 131e37b..004efea 100644
--- a/media/java/android/media/MicrophoneInfo.java
+++ b/media/java/android/media/MicrophoneInfo.java
@@ -92,6 +92,38 @@
*/
public static final int CHANNEL_MAPPING_PROCESSED = 2;
+ /**
+ * Value used for when the group of the microphone is unknown.
+ */
+ public static final int GROUP_UNKNOWN = -1;
+
+ /**
+ * Value used for when the index in the group of the microphone is unknown.
+ */
+ public static final int INDEX_IN_THE_GROUP_UNKNOWN = -1;
+
+ /**
+ * Value used for when the position of the microphone is unknown.
+ */
+ public static final Coordinate3F POSITION_UNKNOWN = new Coordinate3F(
+ -Float.MAX_VALUE, -Float.MAX_VALUE, -Float.MAX_VALUE);
+
+ /**
+ * Value used for when the orientation of the microphone is unknown.
+ */
+ public static final Coordinate3F ORIENTATION_UNKNOWN = new Coordinate3F(0.0f, 0.0f, 0.0f);
+
+ /**
+ * Value used for when the sensitivity of the microphone is unknown.
+ */
+ public static final float SENSITIVITY_UNKNOWN = -Float.MAX_VALUE;
+
+ /**
+ * Value used for when the SPL of the microphone is unknown. This value could be used when
+ * maximum SPL or minimum SPL is unknown.
+ */
+ public static final float SPL_UNKNOWN = -Float.MAX_VALUE;
+
/** @hide */
@IntDef(flag = true, prefix = { "LOCATION_" }, value = {
LOCATION_UNKNOWN,
@@ -216,7 +248,7 @@
* Returns A device group id that can be used to group together microphones on the same
* peripheral, attachments or logical groups. Main body is usually group 0.
*
- * @return the group of the microphone
+ * @return the group of the microphone or {@link #GROUP_UNKNOWN} if the group is unknown
*/
public int getGroup() {
return mGroup;
@@ -225,7 +257,8 @@
/**
* Returns unique index for device within its group.
*
- * @return the microphone's index in its group
+ * @return the microphone's index in its group or {@link #INDEX_IN_THE_GROUP_UNKNOWN} if the
+ * index in the group is unknown
*/
public int getIndexInTheGroup() {
return mIndexInTheGroup;
@@ -233,10 +266,11 @@
/**
* Returns A {@link Coordinate3F} object that represents the geometric location of microphone
- * in meters, from botton-left-back corner of appliance. X-axis, Y-axis and Z-axis show
+ * in meters, from bottom-left-back corner of appliance. X-axis, Y-axis and Z-axis show
* as the x, y, z values.
*
- * @return the geometric location of the microphone
+ * @return the geometric location of the microphone or {@link #POSITION_UNKNOWN} if the
+ * geometric location is unknown
*/
public Coordinate3F getPosition() {
return mPosition;
@@ -247,7 +281,8 @@
* X-axis, Y-axis and Z-axis show as the x, y, z value. The orientation will be normalized
* such as sqrt(x^2 + y^2 + z^2) equals 1.
*
- * @return the orientation of the microphone
+ * @return the orientation of the microphone or {@link #ORIENTATION_UNKNOWN} if orientation
+ * is unknown
*/
public Coordinate3F getOrientation() {
return mOrientation;
@@ -283,7 +318,8 @@
/**
* Returns the level in dBFS produced by a 1000Hz tone at 94 dB SPL.
*
- * @return the sensitivity of the microphone
+ * @return the sensitivity of the microphone or {@link #SENSITIVITY_UNKNOWN} if the sensitivity
+ * is unknown
*/
public float getSensitivity() {
return mSensitivity;
@@ -292,7 +328,7 @@
/**
* Returns the level in dB of the maximum SPL supported by the device at 1000Hz.
*
- * @return the maximum level in dB
+ * @return the maximum level in dB or {@link #SPL_UNKNOWN} if maximum SPL is unknown
*/
public float getMaxSpl() {
return mMaxSpl;
@@ -301,7 +337,7 @@
/**
* Returns the level in dB of the minimum SPL that can be registered by the device at 1000Hz.
*
- * @return the minimum level in dB
+ * @return the minimum level in dB or {@link #SPL_UNKNOWN} if minimum SPL is unknown
*/
public float getMinSpl() {
return mMinSpl;
@@ -327,8 +363,16 @@
mPortId = portId;
}
+ /**
+ * Set the channel mapping for the device.
+ * @hide
+ */
+ public void setChannelMapping(List<Pair<Integer, Integer>> channelMapping) {
+ mChannelMapping = channelMapping;
+ }
+
/* A class containing three float value to represent a 3D coordinate */
- public class Coordinate3F {
+ public static final class Coordinate3F {
public final float x;
public final float y;
public final float z;
@@ -338,5 +382,17 @@
this.y = y;
this.z = z;
}
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (!(obj instanceof Coordinate3F)) {
+ return false;
+ }
+ Coordinate3F other = (Coordinate3F) obj;
+ return this.x == other.x && this.y == other.y && this.z == other.z;
+ }
}
}
diff --git a/packages/FusedLocation/res/values-as/strings.xml b/packages/FusedLocation/res/values-as/strings.xml
new file mode 100644
index 0000000..0d2cccc
--- /dev/null
+++ b/packages/FusedLocation/res/values-as/strings.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_label" msgid="5379477904423203699">"Fused Location"</string>
+</resources>
diff --git a/packages/FusedLocation/res/values-or/strings.xml b/packages/FusedLocation/res/values-or/strings.xml
new file mode 100644
index 0000000..b95bc37a
--- /dev/null
+++ b/packages/FusedLocation/res/values-or/strings.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_label" msgid="5379477904423203699">"ଫ୍ୟୁଜ୍ଡ୍ ଲୋକେଶନ୍"</string>
+</resources>
diff --git a/packages/PrintSpooler/src/com/android/printspooler/model/PrintSpoolerService.java b/packages/PrintSpooler/src/com/android/printspooler/model/PrintSpoolerService.java
index 53e8813..eba5edb 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/model/PrintSpoolerService.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/model/PrintSpoolerService.java
@@ -31,6 +31,7 @@
import android.os.AsyncTask;
import android.os.Binder;
import android.os.Bundle;
+import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.ParcelFileDescriptor;
@@ -58,11 +59,11 @@
import android.util.proto.ProtoOutputStream;
import com.android.internal.logging.MetricsLogger;
-import com.android.internal.os.HandlerCaller;
import com.android.internal.util.FastXmlSerializer;
import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.Preconditions;
import com.android.internal.util.dump.DualDumpOutputStream;
+import com.android.internal.util.function.pooled.PooledLambda;
import com.android.printspooler.R;
import com.android.printspooler.util.ApprovedPrintServices;
@@ -116,8 +117,6 @@
private IPrintSpoolerClient mClient;
- private HandlerCaller mHandlerCaller;
-
private PersistenceManager mPersistanceManager;
private NotificationController mNotificationController;
@@ -134,8 +133,6 @@
@Override
public void onCreate() {
super.onCreate();
- mHandlerCaller = new HandlerCaller(this, getMainLooper(),
- new HandlerCallerCallback(), false);
mPersistanceManager = new PersistenceManager();
mNotificationController = new NotificationController(PrintSpoolerService.this);
@@ -230,93 +227,73 @@
}
private void sendOnPrintJobQueued(PrintJobInfo printJob) {
- Message message = mHandlerCaller.obtainMessageO(
- HandlerCallerCallback.MSG_ON_PRINT_JOB_QUEUED, printJob);
- mHandlerCaller.executeOrSendMessage(message);
+ Message message = PooledLambda.obtainMessage(
+ PrintSpoolerService::onPrintJobQueued, this, printJob);
+ Handler.getMain().executeOrSendMessage(message);
}
private void sendOnAllPrintJobsForServiceHandled(ComponentName service) {
- Message message = mHandlerCaller.obtainMessageO(
- HandlerCallerCallback.MSG_ON_ALL_PRINT_JOBS_FOR_SERIVICE_HANDLED, service);
- mHandlerCaller.executeOrSendMessage(message);
+ Message message = PooledLambda.obtainMessage(
+ PrintSpoolerService::onAllPrintJobsForServiceHandled, this, service);
+ Handler.getMain().executeOrSendMessage(message);
}
private void sendOnAllPrintJobsHandled() {
- Message message = mHandlerCaller.obtainMessage(
- HandlerCallerCallback.MSG_ON_ALL_PRINT_JOBS_HANDLED);
- mHandlerCaller.executeOrSendMessage(message);
+ Message message = PooledLambda.obtainMessage(
+ PrintSpoolerService::onAllPrintJobsHandled, this);
+ Handler.getMain().executeOrSendMessage(message);
}
- private final class HandlerCallerCallback implements HandlerCaller.Callback {
- public static final int MSG_SET_CLIENT = 1;
- public static final int MSG_ON_PRINT_JOB_QUEUED = 2;
- public static final int MSG_ON_ALL_PRINT_JOBS_FOR_SERIVICE_HANDLED = 3;
- public static final int MSG_ON_ALL_PRINT_JOBS_HANDLED = 4;
- public static final int MSG_CHECK_ALL_PRINTJOBS_HANDLED = 5;
- public static final int MSG_ON_PRINT_JOB_STATE_CHANGED = 6;
- @Override
- public void executeMessage(Message message) {
- switch (message.what) {
- case MSG_SET_CLIENT: {
- synchronized (mLock) {
- mClient = (IPrintSpoolerClient) message.obj;
- if (mClient != null) {
- Message msg = mHandlerCaller.obtainMessage(
- HandlerCallerCallback.MSG_CHECK_ALL_PRINTJOBS_HANDLED);
- mHandlerCaller.sendMessageDelayed(msg,
- CHECK_ALL_PRINTJOBS_HANDLED_DELAY);
- }
- }
- } break;
+ private void onPrintJobStateChanged(PrintJobInfo printJob) {
+ if (mClient != null) {
+ try {
+ mClient.onPrintJobStateChanged(printJob);
+ } catch (RemoteException re) {
+ Slog.e(LOG_TAG, "Error notify for print job state change.", re);
+ }
+ }
+ }
- case MSG_ON_PRINT_JOB_QUEUED: {
- PrintJobInfo printJob = (PrintJobInfo) message.obj;
- if (mClient != null) {
- try {
- mClient.onPrintJobQueued(printJob);
- } catch (RemoteException re) {
- Slog.e(LOG_TAG, "Error notify for a queued print job.", re);
- }
- }
- } break;
+ private void onAllPrintJobsHandled() {
+ if (mClient != null) {
+ try {
+ mClient.onAllPrintJobsHandled();
+ } catch (RemoteException re) {
+ Slog.e(LOG_TAG, "Error notify for all print job handled.", re);
+ }
+ }
+ }
- case MSG_ON_ALL_PRINT_JOBS_FOR_SERIVICE_HANDLED: {
- ComponentName service = (ComponentName) message.obj;
- if (mClient != null) {
- try {
- mClient.onAllPrintJobsForServiceHandled(service);
- } catch (RemoteException re) {
- Slog.e(LOG_TAG, "Error notify for all print jobs per service"
- + " handled.", re);
- }
- }
- } break;
+ private void onAllPrintJobsForServiceHandled(ComponentName service) {
+ if (mClient != null) {
+ try {
+ mClient.onAllPrintJobsForServiceHandled(service);
+ } catch (RemoteException re) {
+ Slog.e(LOG_TAG, "Error notify for all print jobs per service"
+ + " handled.", re);
+ }
+ }
+ }
- case MSG_ON_ALL_PRINT_JOBS_HANDLED: {
- if (mClient != null) {
- try {
- mClient.onAllPrintJobsHandled();
- } catch (RemoteException re) {
- Slog.e(LOG_TAG, "Error notify for all print job handled.", re);
- }
- }
- } break;
+ private void onPrintJobQueued(PrintJobInfo printJob) {
+ if (mClient != null) {
+ try {
+ mClient.onPrintJobQueued(printJob);
+ } catch (RemoteException re) {
+ Slog.e(LOG_TAG, "Error notify for a queued print job.", re);
+ }
+ }
+ }
- case MSG_CHECK_ALL_PRINTJOBS_HANDLED: {
- checkAllPrintJobsHandled();
- } break;
-
- case MSG_ON_PRINT_JOB_STATE_CHANGED: {
- if (mClient != null) {
- PrintJobInfo printJob = (PrintJobInfo) message.obj;
- try {
- mClient.onPrintJobStateChanged(printJob);
- } catch (RemoteException re) {
- Slog.e(LOG_TAG, "Error notify for print job state change.", re);
- }
- }
- } break;
+ private void setClient(IPrintSpoolerClient client) {
+ synchronized (mLock) {
+ mClient = client;
+ if (mClient != null) {
+ Message msg = PooledLambda.obtainMessage(
+ PrintSpoolerService::checkAllPrintJobsHandled, this);
+ Handler.getMain().sendMessageDelayed(msg,
+ CHECK_ALL_PRINTJOBS_HANDLED_DELAY);
}
}
}
@@ -379,10 +356,9 @@
addPrintJobLocked(printJob);
setPrintJobState(printJob.getId(), PrintJobInfo.STATE_CREATED, null);
- Message message = mHandlerCaller.obtainMessageO(
- HandlerCallerCallback.MSG_ON_PRINT_JOB_STATE_CHANGED,
- printJob);
- mHandlerCaller.executeOrSendMessage(message);
+ Message message = PooledLambda.obtainMessage(
+ PrintSpoolerService::onPrintJobStateChanged, this, printJob);
+ Handler.getMain().executeOrSendMessage(message);
}
}
@@ -546,10 +522,9 @@
* @param printJob The updated print job.
*/
private void notifyPrintJobUpdated(PrintJobInfo printJob) {
- Message message = mHandlerCaller.obtainMessageO(
- HandlerCallerCallback.MSG_ON_PRINT_JOB_STATE_CHANGED,
- printJob);
- mHandlerCaller.executeOrSendMessage(message);
+ Message message = PooledLambda.obtainMessage(
+ PrintSpoolerService::onPrintJobStateChanged, this, printJob);
+ Handler.getMain().executeOrSendMessage(message);
mNotificationController.onUpdateNotifications(mPrintJobs);
}
@@ -742,10 +717,9 @@
}
mNotificationController.onUpdateNotifications(mPrintJobs);
- Message message = mHandlerCaller.obtainMessageO(
- HandlerCallerCallback.MSG_ON_PRINT_JOB_STATE_CHANGED,
- printJob);
- mHandlerCaller.executeOrSendMessage(message);
+ Message message = PooledLambda.obtainMessage(
+ PrintSpoolerService::onPrintJobStateChanged, this, printJob);
+ Handler.getMain().executeOrSendMessage(message);
}
}
}
@@ -1472,9 +1446,9 @@
@Override
public void setClient(IPrintSpoolerClient client) {
- Message message = mHandlerCaller.obtainMessageO(
- HandlerCallerCallback.MSG_SET_CLIENT, client);
- mHandlerCaller.executeOrSendMessage(message);
+ Message message = PooledLambda.obtainMessage(
+ PrintSpoolerService::setClient, PrintSpoolerService.this, client);
+ Handler.getMain().executeOrSendMessage(message);
}
@Override
diff --git a/packages/PrintSpooler/src/com/android/printspooler/model/RemotePrintDocument.java b/packages/PrintSpooler/src/com/android/printspooler/model/RemotePrintDocument.java
index b6a003d..1644546 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/model/RemotePrintDocument.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/model/RemotePrintDocument.java
@@ -16,7 +16,6 @@
package com.android.printspooler.model;
-import android.annotation.NonNull;
import android.content.ContentResolver;
import android.content.Context;
import android.net.Uri;
@@ -39,6 +38,7 @@
import android.print.PrintDocumentInfo;
import android.util.Log;
+import com.android.internal.util.function.pooled.PooledLambda;
import com.android.printspooler.R;
import com.android.printspooler.util.PageRangeUtils;
@@ -549,6 +549,9 @@
}
private static abstract class AsyncCommand implements Runnable {
+ /** Message indicated the desire to {@link #forceCancel} a command */
+ static final int MSG_FORCE_CANCEL = 0;
+
private static final int STATE_PENDING = 0;
private static final int STATE_RUNNING = 1;
private static final int STATE_COMPLETED = 2;
@@ -574,7 +577,7 @@
public AsyncCommand(Looper looper, IPrintDocumentAdapter adapter, RemotePrintDocumentInfo document,
CommandDoneCallback doneCallback) {
- mHandler = new AsyncCommandHandler(looper);
+ mHandler = new Handler(looper);
mAdapter = adapter;
mDocument = document;
mDoneCallback = doneCallback;
@@ -594,12 +597,12 @@
*/
protected void removeForceCancel() {
if (DEBUG) {
- if (mHandler.hasMessages(AsyncCommandHandler.MSG_FORCE_CANCEL)) {
+ if (mHandler.hasMessages(MSG_FORCE_CANCEL)) {
Log.i(LOG_TAG, "[FORCE CANCEL] Removed");
}
}
- mHandler.removeMessages(AsyncCommandHandler.MSG_FORCE_CANCEL);
+ mHandler.removeMessages(MSG_FORCE_CANCEL);
}
/**
@@ -628,7 +631,8 @@
Log.i(LOG_TAG, "[FORCE CANCEL] queued");
}
mHandler.sendMessageDelayed(
- mHandler.obtainMessage(AsyncCommandHandler.MSG_FORCE_CANCEL),
+ PooledLambda.obtainMessage(AsyncCommand::forceCancel, this)
+ .setWhat(MSG_FORCE_CANCEL),
FORCE_CANCEL_TIMEOUT);
}
@@ -698,34 +702,15 @@
return mError;
}
- /**
- * Handler for the async command.
- */
- private class AsyncCommandHandler extends Handler {
- /** Message indicated the desire for to force cancel a command */
- final static int MSG_FORCE_CANCEL = 0;
-
- AsyncCommandHandler(@NonNull Looper looper) {
- super(looper);
- }
-
- @Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case MSG_FORCE_CANCEL:
- if (isCanceling()) {
- if (DEBUG) {
- Log.i(LOG_TAG, "[FORCE CANCEL] executed");
- }
- failed("Command did not respond to cancellation in "
- + FORCE_CANCEL_TIMEOUT + " ms");
-
- mDoneCallback.onDone();
- }
- break;
- default:
- // not reached;
+ private void forceCancel() {
+ if (isCanceling()) {
+ if (DEBUG) {
+ Log.i(LOG_TAG, "[FORCE CANCEL] executed");
}
+ failed("Command did not respond to cancellation in "
+ + FORCE_CANCEL_TIMEOUT + " ms");
+
+ mDoneCallback.onDone();
}
}
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/applications/DefaultAppInfo.java b/packages/SettingsLib/src/com/android/settingslib/applications/DefaultAppInfo.java
new file mode 100644
index 0000000..246ca47
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/applications/DefaultAppInfo.java
@@ -0,0 +1,153 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.applications;
+
+import android.app.AppGlobals;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.ComponentInfo;
+import android.content.pm.PackageItemInfo;
+import android.content.pm.PackageManager;
+import android.graphics.drawable.Drawable;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.util.IconDrawableFactory;
+
+import com.android.settingslib.widget.CandidateInfo;
+import com.android.settingslib.wrapper.PackageManagerWrapper;
+
+/**
+ * Data model representing an app in DefaultAppPicker UI.
+ */
+public class DefaultAppInfo extends CandidateInfo {
+
+ public final int userId;
+ public final ComponentName componentName;
+ public final PackageItemInfo packageItemInfo;
+ public final String summary;
+ protected final PackageManagerWrapper mPm;
+ private final Context mContext;
+
+ public DefaultAppInfo(Context context, PackageManagerWrapper pm, int uid, ComponentName cn) {
+ this(context, pm, uid, cn, null /* summary */, true /* enabled */);
+ }
+
+ public DefaultAppInfo(Context context, PackageManagerWrapper pm, PackageItemInfo info) {
+ this(context, pm, info, null /* summary */, true /* enabled */);
+ }
+
+ public DefaultAppInfo(Context context, PackageManagerWrapper pm, int uid, ComponentName cn,
+ String summary, boolean enabled) {
+ super(enabled);
+ mContext = context;
+ mPm = pm;
+ packageItemInfo = null;
+ userId = uid;
+ componentName = cn;
+ this.summary = summary;
+ }
+
+ public DefaultAppInfo(Context context, PackageManagerWrapper pm, PackageItemInfo info,
+ String summary, boolean enabled) {
+ super(enabled);
+ mContext = context;
+ mPm = pm;
+ userId = UserHandle.myUserId();
+ packageItemInfo = info;
+ componentName = null;
+ this.summary = summary;
+ }
+
+ @Override
+ public CharSequence loadLabel() {
+ if (componentName != null) {
+ try {
+ final ComponentInfo componentInfo = getComponentInfo();
+ if (componentInfo != null) {
+ return componentInfo.loadLabel(mPm.getPackageManager());
+ } else {
+ final ApplicationInfo appInfo = mPm.getApplicationInfoAsUser(
+ componentName.getPackageName(), 0, userId);
+ return appInfo.loadLabel(mPm.getPackageManager());
+ }
+ } catch (PackageManager.NameNotFoundException e) {
+ return null;
+ }
+ } else if (packageItemInfo != null) {
+ return packageItemInfo.loadLabel(mPm.getPackageManager());
+ } else {
+ return null;
+ }
+
+ }
+
+ @Override
+ public Drawable loadIcon() {
+ final IconDrawableFactory factory = IconDrawableFactory.newInstance(mContext);
+ if (componentName != null) {
+ try {
+ final ComponentInfo componentInfo = getComponentInfo();
+ final ApplicationInfo appInfo = mPm.getApplicationInfoAsUser(
+ componentName.getPackageName(), 0, userId);
+ if (componentInfo != null) {
+ return factory.getBadgedIcon(componentInfo, appInfo, userId);
+ } else {
+ return factory.getBadgedIcon(appInfo);
+ }
+ } catch (PackageManager.NameNotFoundException e) {
+ return null;
+ }
+ }
+ if (packageItemInfo != null) {
+ try {
+ final ApplicationInfo appInfo = mPm.getApplicationInfoAsUser(
+ packageItemInfo.packageName, 0, userId);
+ return factory.getBadgedIcon(packageItemInfo, appInfo, userId);
+ } catch (PackageManager.NameNotFoundException e) {
+ return null;
+ }
+ } else {
+ return null;
+ }
+ }
+
+ @Override
+ public String getKey() {
+ if (componentName != null) {
+ return componentName.flattenToString();
+ } else if (packageItemInfo != null) {
+ return packageItemInfo.packageName;
+ } else {
+ return null;
+ }
+ }
+
+ private ComponentInfo getComponentInfo() {
+ try {
+ ComponentInfo componentInfo = AppGlobals.getPackageManager().getActivityInfo(
+ componentName, 0, userId);
+ if (componentInfo == null) {
+ componentInfo = AppGlobals.getPackageManager().getServiceInfo(
+ componentName, 0, userId);
+ }
+ return componentInfo;
+ } catch (RemoteException e) {
+ return null;
+ }
+ }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java
index 3cda9c9..784c714 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java
@@ -444,6 +444,7 @@
private void dispatchActiveDeviceChanged(CachedBluetoothDevice activeDevice,
int bluetoothProfile) {
+ mDeviceManager.onActiveDeviceChanged(activeDevice, bluetoothProfile);
synchronized (mCallbacks) {
for (BluetoothCallback callback : mCallbacks) {
callback.onActiveDeviceChanged(activeDevice, bluetoothProfile);
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
index e1ebbc4..f6ec6a8 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
@@ -27,6 +27,7 @@
import android.text.TextUtils;
import android.util.Log;
import android.bluetooth.BluetoothAdapter;
+import android.support.annotation.VisibleForTesting;
import com.android.settingslib.R;
@@ -461,12 +462,12 @@
}
/**
- * Set the device status as active or non-active per Bluetooth profile.
+ * Update the device status as active or non-active per Bluetooth profile.
*
* @param isActive true if the device is active
* @param bluetoothProfile the Bluetooth profile
*/
- public void setActiveDevice(boolean isActive, int bluetoothProfile) {
+ public void onActiveDeviceChanged(boolean isActive, int bluetoothProfile) {
boolean changed = false;
switch (bluetoothProfile) {
case BluetoothProfile.A2DP:
@@ -478,7 +479,7 @@
mIsActiveDeviceHeadset = isActive;
break;
default:
- Log.w(TAG, "setActiveDevice: unknown profile " + bluetoothProfile +
+ Log.w(TAG, "onActiveDeviceChanged: unknown profile " + bluetoothProfile +
" isActive " + isActive);
break;
}
@@ -487,6 +488,26 @@
}
}
+ /**
+ * Get the device status as active or non-active per Bluetooth profile.
+ *
+ * @param bluetoothProfile the Bluetooth profile
+ * @return true if the device is active
+ */
+ @VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE)
+ public boolean isActiveDevice(int bluetoothProfile) {
+ switch (bluetoothProfile) {
+ case BluetoothProfile.A2DP:
+ return mIsActiveDeviceA2dp;
+ case BluetoothProfile.HEADSET:
+ return mIsActiveDeviceHeadset;
+ default:
+ Log.w(TAG, "getActiveDevice: unknown profile " + bluetoothProfile);
+ break;
+ }
+ return false;
+ }
+
void setRssi(short rssi) {
if (mRssi != rssi) {
mRssi = rssi;
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java
index c3ff617..a8e0039 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java
@@ -24,6 +24,7 @@
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
+import java.util.Objects;
/**
* CachedBluetoothDeviceManager manages the set of remote Bluetooth devices.
@@ -167,6 +168,15 @@
}
}
}
+
+ public synchronized void onActiveDeviceChanged(CachedBluetoothDevice activeDevice,
+ int bluetoothProfile) {
+ for (CachedBluetoothDevice cachedDevice : mCachedDevices) {
+ boolean isActive = Objects.equals(cachedDevice, activeDevice);
+ cachedDevice.onActiveDeviceChanged(isActive, bluetoothProfile);
+ }
+ }
+
private void log(String msg) {
if (DEBUG) {
Log.d(TAG, msg);
diff --git a/packages/SettingsLib/src/com/android/settingslib/widget/CandidateInfo.java b/packages/SettingsLib/src/com/android/settingslib/widget/CandidateInfo.java
new file mode 100644
index 0000000..cdb4ffd
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/widget/CandidateInfo.java
@@ -0,0 +1,37 @@
+/*
+ * 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.widget;
+
+import android.graphics.drawable.Drawable;
+
+/**
+ * Base class for defining a selectable item in UI.
+ */
+public abstract class CandidateInfo {
+
+ public final boolean enabled;
+
+ public CandidateInfo(boolean enabled) {
+ this.enabled = enabled;
+ }
+
+ public abstract CharSequence loadLabel();
+
+ public abstract Drawable loadIcon();
+
+ public abstract String getKey();
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/wrapper/PackageManagerWrapper.java b/packages/SettingsLib/src/com/android/settingslib/wrapper/PackageManagerWrapper.java
index b1f3f3c..235daf2 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wrapper/PackageManagerWrapper.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wrapper/PackageManagerWrapper.java
@@ -130,6 +130,13 @@
}
/**
+ * Calls {@code PackageManager.queryIntentServices}
+ */
+ public List<ResolveInfo> queryIntentServices(Intent intent, int i) {
+ return mPm.queryIntentServices(intent, i);
+ }
+
+ /**
* Calls {@code PackageManager.replacePreferredActivity}
*/
public void replacePreferredActivity(IntentFilter homeFilter, int matchCategoryEmpty,
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/DefaultAppInfoTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/DefaultAppInfoTest.java
new file mode 100644
index 0000000..6a161d0
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/DefaultAppInfoTest.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.applications;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageItemInfo;
+import android.content.pm.PackageManager;
+import android.graphics.drawable.Drawable;
+
+import com.android.settingslib.SettingsLibRobolectricTestRunner;
+import com.android.settingslib.wrapper.PackageManagerWrapper;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+
+@RunWith(SettingsLibRobolectricTestRunner.class)
+public class DefaultAppInfoTest {
+
+ @Mock
+ private PackageItemInfo mPackageItemInfo;
+ @Mock
+ private ComponentName mComponentName;
+ @Mock
+ private PackageManager mPackageManager;
+ @Mock
+ private PackageManagerWrapper mPackageManagerWrapper;
+ @Mock
+ private ApplicationInfo mApplicationInfo;
+ @Mock
+ private Drawable mIcon;
+
+ private Context mContext;
+ private DefaultAppInfo mInfo;
+
+ @Before
+ public void setUp() throws PackageManager.NameNotFoundException {
+ MockitoAnnotations.initMocks(this);
+ mContext = spy(RuntimeEnvironment.application);
+ doReturn(mPackageManager).when(mContext).getPackageManager();
+ when(mPackageManagerWrapper.getPackageManager()).thenReturn(mPackageManager);
+ when(mPackageManagerWrapper.getApplicationInfoAsUser(anyString(), anyInt(),
+ anyInt())).thenReturn(mApplicationInfo);
+ when(mPackageManager.loadUnbadgedItemIcon(mPackageItemInfo, mApplicationInfo)).thenReturn(
+ mIcon);
+ }
+
+ @Test
+ public void initInfoWithActivityInfo_shouldLoadInfo() {
+ mPackageItemInfo.packageName = "test";
+ mInfo = new DefaultAppInfo(mContext, mPackageManagerWrapper, mPackageItemInfo);
+ mInfo.loadLabel();
+ Drawable icon = mInfo.loadIcon();
+
+ assertThat(mInfo.getKey()).isEqualTo(mPackageItemInfo.packageName);
+ assertThat(icon).isNotNull();
+ verify(mPackageItemInfo).loadLabel(mPackageManager);
+ }
+
+ @Test
+ public void initInfoWithComponent_shouldLoadInfo() {
+ when(mComponentName.getPackageName()).thenReturn("com.android.settings");
+
+ mInfo = new DefaultAppInfo(mContext, mPackageManagerWrapper, 0 /* uid */, mComponentName);
+ mInfo.getKey();
+
+ verify(mComponentName).flattenToString();
+ }
+}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManagerTest.java
new file mode 100644
index 0000000..d6b2006
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManagerTest.java
@@ -0,0 +1,283 @@
+/*
+ * 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.bluetooth;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothClass;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothProfile;
+import android.content.Context;
+
+import com.android.settingslib.R;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+
+import java.util.Collection;
+
+@RunWith(RobolectricTestRunner.class)
+public class CachedBluetoothDeviceManagerTest {
+ private final static String DEVICE_NAME_1 = "TestName_1";
+ private final static String DEVICE_NAME_2 = "TestName_2";
+ private final static String DEVICE_ALIAS_1 = "TestAlias_1";
+ private final static String DEVICE_ALIAS_2 = "TestAlias_2";
+ private final static String DEVICE_ADDRESS_1 = "AA:BB:CC:DD:EE:11";
+ private final static String DEVICE_ADDRESS_2 = "AA:BB:CC:DD:EE:22";
+ private final BluetoothClass DEVICE_CLASS_1 =
+ new BluetoothClass(BluetoothClass.Device.AUDIO_VIDEO_HEADPHONES);
+ private final BluetoothClass DEVICE_CLASS_2 =
+ new BluetoothClass(BluetoothClass.Device.AUDIO_VIDEO_HANDSFREE);
+ @Mock
+ private LocalBluetoothAdapter mLocalAdapter;
+ @Mock
+ private LocalBluetoothProfileManager mLocalProfileManager;
+ @Mock
+ private LocalBluetoothManager mLocalBluetoothManager;
+ @Mock
+ private BluetoothEventManager mBluetoothEventManager;
+ @Mock
+ private HeadsetProfile mHfpProfile;
+ @Mock
+ private A2dpProfile mA2dpProfile;
+ @Mock
+ private PanProfile mPanProfile;
+ @Mock
+ private BluetoothDevice mDevice1;
+ @Mock
+ private BluetoothDevice mDevice2;
+ private CachedBluetoothDeviceManager mCachedDeviceManager;
+ private Context mContext;
+ private String[] mActiveDeviceStringsArray;
+ private String mActiveDeviceStringNone;
+ private String mActiveDeviceStringAll;
+ private String mActiveDeviceStringMedia;
+ private String mActiveDeviceStringPhone;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mContext = RuntimeEnvironment.application;
+ when(mDevice1.getAddress()).thenReturn(DEVICE_ADDRESS_1);
+ when(mDevice2.getAddress()).thenReturn(DEVICE_ADDRESS_2);
+ when(mDevice1.getName()).thenReturn(DEVICE_NAME_1);
+ when(mDevice2.getName()).thenReturn(DEVICE_NAME_2);
+ when(mDevice1.getAliasName()).thenReturn(DEVICE_ALIAS_1);
+ when(mDevice2.getAliasName()).thenReturn(DEVICE_ALIAS_2);
+ when(mDevice1.getBluetoothClass()).thenReturn(DEVICE_CLASS_1);
+ when(mDevice2.getBluetoothClass()).thenReturn(DEVICE_CLASS_2);
+
+ when(mLocalBluetoothManager.getEventManager()).thenReturn(mBluetoothEventManager);
+ when(mLocalAdapter.getBluetoothState()).thenReturn(BluetoothAdapter.STATE_ON);
+ when(mHfpProfile.isProfileReady()).thenReturn(true);
+ when(mA2dpProfile.isProfileReady()).thenReturn(true);
+ when(mPanProfile.isProfileReady()).thenReturn(true);
+ mCachedDeviceManager = new CachedBluetoothDeviceManager(mContext, mLocalBluetoothManager);
+ }
+
+ /**
+ * Test to verify addDevice().
+ */
+ @Test
+ public void testAddDevice_validCachedDevices_devicesAdded() {
+ CachedBluetoothDevice cachedDevice1 = mCachedDeviceManager.addDevice(mLocalAdapter,
+ mLocalProfileManager, mDevice1);
+ assertThat(cachedDevice1).isNotNull();
+ CachedBluetoothDevice cachedDevice2 = mCachedDeviceManager.addDevice(mLocalAdapter,
+ mLocalProfileManager, mDevice2);
+ assertThat(cachedDevice2).isNotNull();
+
+ Collection<CachedBluetoothDevice> devices = mCachedDeviceManager.getCachedDevicesCopy();
+ assertThat(devices).contains(cachedDevice1);
+ assertThat(devices).contains(cachedDevice2);
+
+ assertThat(mCachedDeviceManager.findDevice(mDevice1)).isEqualTo(cachedDevice1);
+ assertThat(mCachedDeviceManager.findDevice(mDevice2)).isEqualTo(cachedDevice2);
+ }
+
+ /**
+ * Test to verify getName().
+ */
+ @Test
+ public void testGetName_validCachedDevice_nameFound() {
+ CachedBluetoothDevice cachedDevice1 = mCachedDeviceManager.addDevice(mLocalAdapter,
+ mLocalProfileManager, mDevice1);
+ assertThat(cachedDevice1).isNotNull();
+ assertThat(mCachedDeviceManager.getName(mDevice1)).isEqualTo(DEVICE_ALIAS_1);
+ }
+
+ /**
+ * Test to verify onDeviceNameUpdated().
+ */
+ @Test
+ public void testOnDeviceNameUpdated_validName_nameUpdated() {
+ CachedBluetoothDevice cachedDevice1 = mCachedDeviceManager.addDevice(mLocalAdapter,
+ mLocalProfileManager, mDevice1);
+ assertThat(cachedDevice1).isNotNull();
+ assertThat(cachedDevice1.getName()).isEqualTo(DEVICE_ALIAS_1);
+
+ final String newAliasName = "NewAliasName";
+ when(mDevice1.getAliasName()).thenReturn(newAliasName);
+ mCachedDeviceManager.onDeviceNameUpdated(mDevice1);
+ assertThat(cachedDevice1.getName()).isEqualTo(newAliasName);
+ }
+
+ /**
+ * Test to verify clearNonBondedDevices().
+ */
+ @Test
+ public void testClearNonBondedDevices_bondedAndNonBondedDevices_nonBondedDevicesCleared() {
+ CachedBluetoothDevice cachedDevice1 = mCachedDeviceManager.addDevice(mLocalAdapter,
+ mLocalProfileManager, mDevice1);
+ assertThat(cachedDevice1).isNotNull();
+ CachedBluetoothDevice cachedDevice2 = mCachedDeviceManager.addDevice(mLocalAdapter,
+ mLocalProfileManager, mDevice2);
+ assertThat(cachedDevice2).isNotNull();
+
+ when(mDevice1.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
+ when(mDevice2.getBondState()).thenReturn(BluetoothDevice.BOND_BONDING);
+ mCachedDeviceManager.clearNonBondedDevices();
+ Collection<CachedBluetoothDevice> devices = mCachedDeviceManager.getCachedDevicesCopy();
+ assertThat(devices).contains(cachedDevice1);
+ assertThat(devices).contains(cachedDevice2);
+
+ when(mDevice1.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
+ when(mDevice2.getBondState()).thenReturn(BluetoothDevice.BOND_NONE);
+ mCachedDeviceManager.clearNonBondedDevices();
+ devices = mCachedDeviceManager.getCachedDevicesCopy();
+ assertThat(devices).contains(cachedDevice1);
+ assertThat(devices).doesNotContain(cachedDevice2);
+
+ when(mDevice1.getBondState()).thenReturn(BluetoothDevice.BOND_NONE);
+ when(mDevice2.getBondState()).thenReturn(BluetoothDevice.BOND_NONE);
+ mCachedDeviceManager.clearNonBondedDevices();
+ devices = mCachedDeviceManager.getCachedDevicesCopy();
+ assertThat(devices).doesNotContain(cachedDevice1);
+ assertThat(devices).doesNotContain(cachedDevice2);
+ }
+
+ /**
+ * Test to verify onBtClassChanged().
+ */
+ @Test
+ public void testOnBtClassChanged_validBtClass_classChanged() {
+ CachedBluetoothDevice cachedDevice1 = mCachedDeviceManager.addDevice(mLocalAdapter,
+ mLocalProfileManager, mDevice1);
+ assertThat(cachedDevice1).isNotNull();
+ assertThat(cachedDevice1.getBtClass()).isEqualTo(DEVICE_CLASS_1);
+
+ final BluetoothClass newBluetoothClass = DEVICE_CLASS_2;
+ when(mDevice1.getBluetoothClass()).thenReturn(newBluetoothClass);
+ mCachedDeviceManager.onBtClassChanged(mDevice1);
+ assertThat(cachedDevice1.getBtClass()).isEqualTo(newBluetoothClass);
+ }
+
+ /**
+ * Test to verify onDeviceDisappeared().
+ */
+ @Test
+ public void testOnDeviceDisappeared_deviceBondedUnbonded_unbondedDeviceDisappeared() {
+ CachedBluetoothDevice cachedDevice1 = mCachedDeviceManager.addDevice(mLocalAdapter,
+ mLocalProfileManager, mDevice1);
+ assertThat(cachedDevice1).isNotNull();
+
+ when(mDevice1.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
+ assertThat(mCachedDeviceManager.onDeviceDisappeared(cachedDevice1)).isFalse();
+
+ when(mDevice1.getBondState()).thenReturn(BluetoothDevice.BOND_NONE);
+ assertThat(mCachedDeviceManager.onDeviceDisappeared(cachedDevice1)).isTrue();
+ }
+
+ /**
+ * Test to verify onActiveDeviceChanged().
+ */
+ @Test
+ public void testOnActiveDeviceChanged_connectedDevices_activeDeviceChanged() {
+ CachedBluetoothDevice cachedDevice1 = mCachedDeviceManager.addDevice(mLocalAdapter,
+ mLocalProfileManager, mDevice1);
+ assertThat(cachedDevice1).isNotNull();
+ CachedBluetoothDevice cachedDevice2 = mCachedDeviceManager.addDevice(mLocalAdapter,
+ mLocalProfileManager, mDevice2);
+ assertThat(cachedDevice2).isNotNull();
+
+ when(mDevice1.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
+ when(mDevice2.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
+
+ // Connect both devices for A2DP and HFP
+ cachedDevice1.onProfileStateChanged(mA2dpProfile, BluetoothProfile.STATE_CONNECTED);
+ cachedDevice2.onProfileStateChanged(mA2dpProfile, BluetoothProfile.STATE_CONNECTED);
+ cachedDevice1.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_CONNECTED);
+ cachedDevice2.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_CONNECTED);
+
+ // Verify that both devices are connected and none is Active
+ assertThat(cachedDevice1.isActiveDevice(BluetoothProfile.A2DP)).isFalse();
+ assertThat(cachedDevice1.isActiveDevice(BluetoothProfile.HEADSET)).isFalse();
+ assertThat(cachedDevice2.isActiveDevice(BluetoothProfile.A2DP)).isFalse();
+ assertThat(cachedDevice2.isActiveDevice(BluetoothProfile.HEADSET)).isFalse();
+
+ // The first device is active for A2DP, the second device is active for HFP
+ mCachedDeviceManager.onActiveDeviceChanged(cachedDevice1, BluetoothProfile.A2DP);
+ mCachedDeviceManager.onActiveDeviceChanged(cachedDevice2, BluetoothProfile.HEADSET);
+ assertThat(cachedDevice1.isActiveDevice(BluetoothProfile.A2DP)).isTrue();
+ assertThat(cachedDevice1.isActiveDevice(BluetoothProfile.HEADSET)).isFalse();
+ assertThat(cachedDevice2.isActiveDevice(BluetoothProfile.A2DP)).isFalse();
+ assertThat(cachedDevice2.isActiveDevice(BluetoothProfile.HEADSET)).isTrue();
+
+ // The first device is active for A2DP and HFP
+ mCachedDeviceManager.onActiveDeviceChanged(cachedDevice1, BluetoothProfile.HEADSET);
+ assertThat(cachedDevice1.isActiveDevice(BluetoothProfile.A2DP)).isTrue();
+ assertThat(cachedDevice1.isActiveDevice(BluetoothProfile.HEADSET)).isTrue();
+ assertThat(cachedDevice2.isActiveDevice(BluetoothProfile.A2DP)).isFalse();
+ assertThat(cachedDevice2.isActiveDevice(BluetoothProfile.HEADSET)).isFalse();
+
+ // The second device is active for A2DP and HFP
+ mCachedDeviceManager.onActiveDeviceChanged(cachedDevice2, BluetoothProfile.A2DP);
+ mCachedDeviceManager.onActiveDeviceChanged(cachedDevice2, BluetoothProfile.HEADSET);
+ assertThat(cachedDevice1.isActiveDevice(BluetoothProfile.A2DP)).isFalse();
+ assertThat(cachedDevice1.isActiveDevice(BluetoothProfile.HEADSET)).isFalse();
+ assertThat(cachedDevice2.isActiveDevice(BluetoothProfile.A2DP)).isTrue();
+ assertThat(cachedDevice2.isActiveDevice(BluetoothProfile.HEADSET)).isTrue();
+
+ // No active device for A2DP
+ mCachedDeviceManager.onActiveDeviceChanged(null, BluetoothProfile.A2DP);
+ assertThat(cachedDevice1.isActiveDevice(BluetoothProfile.A2DP)).isFalse();
+ assertThat(cachedDevice1.isActiveDevice(BluetoothProfile.HEADSET)).isFalse();
+ assertThat(cachedDevice2.isActiveDevice(BluetoothProfile.A2DP)).isFalse();
+ assertThat(cachedDevice2.isActiveDevice(BluetoothProfile.HEADSET)).isTrue();
+
+ // No active device for HFP
+ mCachedDeviceManager.onActiveDeviceChanged(null, BluetoothProfile.HEADSET);
+ assertThat(cachedDevice1.isActiveDevice(BluetoothProfile.A2DP)).isFalse();
+ assertThat(cachedDevice1.isActiveDevice(BluetoothProfile.HEADSET)).isFalse();
+ assertThat(cachedDevice2.isActiveDevice(BluetoothProfile.A2DP)).isFalse();
+ assertThat(cachedDevice2.isActiveDevice(BluetoothProfile.HEADSET)).isFalse();
+ }
+}
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 92c68e6..0775727 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
@@ -147,7 +147,7 @@
assertThat(mCachedDevice.getConnectionSummary()).isEqualTo("Connected");
// Set device as Active for A2DP and test connection state summary
- mCachedDevice.setActiveDevice(true, BluetoothProfile.A2DP);
+ mCachedDevice.onActiveDeviceChanged(true, BluetoothProfile.A2DP);
assertThat(mCachedDevice.getConnectionSummary()).isEqualTo("Connected, active(media)");
// Test with battery level
@@ -163,7 +163,7 @@
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);
+ mCachedDevice.onActiveDeviceChanged(true, BluetoothProfile.A2DP);
assertThat(mCachedDevice.getConnectionSummary()).isEqualTo("Connected, active(media)");
// Set A2DP profile to be disconnected and test connection state summary
@@ -179,7 +179,7 @@
assertThat(mCachedDevice.getConnectionSummary()).isEqualTo("Connected");
// Set device as Active for HFP and test connection state summary
- mCachedDevice.setActiveDevice(true, BluetoothProfile.HEADSET);
+ mCachedDevice.onActiveDeviceChanged(true, BluetoothProfile.HEADSET);
assertThat(mCachedDevice.getConnectionSummary()).isEqualTo("Connected, active(phone)");
// Test with battery level
@@ -195,7 +195,7 @@
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);
+ mCachedDevice.onActiveDeviceChanged(true, BluetoothProfile.HEADSET);
assertThat(mCachedDevice.getConnectionSummary()).isEqualTo("Connected, active(phone)");
// Set HFP profile to be disconnected and test connection state summary
@@ -212,8 +212,8 @@
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);
+ mCachedDevice.onActiveDeviceChanged(true, BluetoothProfile.A2DP);
+ mCachedDevice.onActiveDeviceChanged(true, BluetoothProfile.HEADSET);
assertThat(mCachedDevice.getConnectionSummary()).isEqualTo("Connected, active");
// Test with battery level
@@ -222,16 +222,16 @@
"Connected, battery 10%, active");
// Disconnect A2DP only and test connection state summary
- mCachedDevice.setActiveDevice(false, BluetoothProfile.A2DP);
+ mCachedDevice.onActiveDeviceChanged(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.onActiveDeviceChanged(false, BluetoothProfile.HEADSET);
mCachedDevice.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_DISCONNECTED);
mCachedDevice.onProfileStateChanged(mA2dpProfile, BluetoothProfile.STATE_CONNECTED);
- mCachedDevice.setActiveDevice(true, BluetoothProfile.A2DP);
+ mCachedDevice.onActiveDeviceChanged(true, BluetoothProfile.A2DP);
assertThat(mCachedDevice.getConnectionSummary()).isEqualTo(
"Connected (no phone), battery 10%, active(media)");
@@ -240,8 +240,8 @@
// 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);
+ mCachedDevice.onActiveDeviceChanged(true, BluetoothProfile.A2DP);
+ mCachedDevice.onActiveDeviceChanged(true, BluetoothProfile.HEADSET);
assertThat(mCachedDevice.getConnectionSummary()).isEqualTo("Connected, active");
// Set A2DP and HFP profiles to be disconnected and test connection state summary
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
index dd89df1..2b181dc 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
@@ -858,7 +858,10 @@
out.writeInt(NETWORK_POLICIES_BACKUP_VERSION);
out.writeInt(policies.length);
for (NetworkPolicy policy : policies) {
- if (policy != null) {
+ // We purposefully only backup policies that the user has
+ // defined; any inferred policies might include
+ // carrier-protected data that we can't export.
+ if (policy != null && !policy.inferred) {
byte[] marshaledPolicy = policy.getBytesForBackup();
out.writeByte(BackupUtils.NOT_NULL);
out.writeInt(marshaledPolicy.length);
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index 0fee81be..b000d84 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -448,6 +448,9 @@
Settings.Global.CARRIER_APP_WHITELIST,
GlobalSettingsProto.CARRIER_APP_WHITELIST);
dumpSetting(s, p,
+ Settings.Global.CARRIER_APP_NAMES,
+ GlobalSettingsProto.CARRIER_APP_NAMES);
+ dumpSetting(s, p,
Settings.Global.USB_MASS_STORAGE_ENABLED,
GlobalSettingsProto.USB_MASS_STORAGE_ENABLED);
dumpSetting(s, p,
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_status_view.xml b/packages/SystemUI/res-keyguard/layout/keyguard_status_view.xml
index 713c573..fa14d1b 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_status_view.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_status_view.xml
@@ -62,7 +62,7 @@
android:layout_gravity="center_horizontal"
android:layout_centerHorizontal="true"
android:layout_alignParentTop="true"
- android:letterSpacing="0.04"
+ android:letterSpacing="0.03"
android:textColor="?attr/wallpaperTextColor"
android:singleLine="true"
style="@style/widget_big_thin"
diff --git a/packages/SystemUI/res-keyguard/values/dimens.xml b/packages/SystemUI/res-keyguard/values/dimens.xml
index 4934e14..1c1c757 100644
--- a/packages/SystemUI/res-keyguard/values/dimens.xml
+++ b/packages/SystemUI/res-keyguard/values/dimens.xml
@@ -60,8 +60,8 @@
<dimen name="widget_horizontal_padding">8dp</dimen>
<dimen name="widget_icon_size">16dp</dimen>
<dimen name="widget_icon_padding">8dp</dimen>
- <!-- Dash between notification shelf and date/alarm -->
- <dimen name="widget_bottom_separator_padding">29dp</dimen>
+ <!-- Space between notification shelf and dash above it -->
+ <dimen name="widget_bottom_separator_padding">28dp</dimen>
<!-- The y translation to apply at the start in appear animations. -->
<dimen name="appear_y_translation_start">32dp</dimen>
diff --git a/packages/SystemUI/res/values-h560dp-xhdpi/dimens.xml b/packages/SystemUI/res/values-h560dp-xhdpi/dimens.xml
deleted file mode 100644
index f6dbc3d..0000000
--- a/packages/SystemUI/res/values-h560dp-xhdpi/dimens.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ Copyright (C) 2014 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License
- -->
-
-<resources>
- <fraction name="keyguard_clock_y_fraction_max">32.5%</fraction>
- <fraction name="keyguard_clock_y_fraction_min">24%</fraction>
-</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/res/values-h560dp-xxhdpi/config.xml b/packages/SystemUI/res/values-h560dp-xxhdpi/config.xml
deleted file mode 100644
index b2231a6..0000000
--- a/packages/SystemUI/res/values-h560dp-xxhdpi/config.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ Copyright (C) 2014 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License
- -->
-<resources>
- <!-- The maximum count of notifications on Keyguard. The rest will be collapsed in an overflow
- card. -->
- <integer name="keyguard_max_notification_count">4</integer>
-</resources>
-
diff --git a/packages/SystemUI/res/values-h560dp-xxhdpi/dimens.xml b/packages/SystemUI/res/values-h560dp-xxhdpi/dimens.xml
deleted file mode 100644
index 905e9e3..0000000
--- a/packages/SystemUI/res/values-h560dp-xxhdpi/dimens.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ Copyright (C) 2014 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License
- -->
-
-<resources>
- <fraction name="keyguard_clock_y_fraction_max">32.5%</fraction>
- <fraction name="keyguard_clock_y_fraction_min">19.8%</fraction>
-</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/res/values-h650dp/config.xml b/packages/SystemUI/res/values-h650dp/config.xml
deleted file mode 100644
index ee641b4..0000000
--- a/packages/SystemUI/res/values-h650dp/config.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-
-<!--
- ~ Copyright (C) 2014 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License
- -->
-
-<resources>
- <!-- The maximum count of notifications on Keyguard. The rest will be collapsed in an overflow
- card. -->
- <integer name="keyguard_max_notification_count">5</integer>
-</resources>
-
diff --git a/packages/SystemUI/res/values-h650dp/dimens.xml b/packages/SystemUI/res/values-h650dp/dimens.xml
index 3811f67..8a00953 100644
--- a/packages/SystemUI/res/values-h650dp/dimens.xml
+++ b/packages/SystemUI/res/values-h650dp/dimens.xml
@@ -16,7 +16,4 @@
<resources>
<dimen name="keyguard_clock_notifications_margin">32dp</dimen>
-
- <fraction name="keyguard_clock_y_fraction_max">32.5%</fraction>
- <fraction name="keyguard_clock_y_fraction_min">18.5%</fraction>
</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/res/values-sw600dp-land/config.xml b/packages/SystemUI/res/values-sw600dp-land/config.xml
index 6594bd28..c4c4671 100644
--- a/packages/SystemUI/res/values-sw600dp-land/config.xml
+++ b/packages/SystemUI/res/values-sw600dp-land/config.xml
@@ -15,10 +15,6 @@
~ limitations under the License
-->
<resources>
- <!-- The maximum count of notifications on Keyguard. The rest will be collapsed in an overflow
- card. -->
- <integer name="keyguard_max_notification_count">3</integer>
-
<!-- Whether QuickSettings is in a phone landscape -->
<bool name="quick_settings_wide">false</bool>
diff --git a/packages/SystemUI/res/values-sw600dp-land/dimens.xml b/packages/SystemUI/res/values-sw600dp-land/dimens.xml
index 13ca114..f2df4b9 100644
--- a/packages/SystemUI/res/values-sw600dp-land/dimens.xml
+++ b/packages/SystemUI/res/values-sw600dp-land/dimens.xml
@@ -16,9 +16,6 @@
*/
-->
<resources>
- <fraction name="keyguard_clock_y_fraction_max">37%</fraction>
- <fraction name="keyguard_clock_y_fraction_min">20%</fraction>
-
<dimen name="keyguard_clock_notifications_margin">36dp</dimen>
<dimen name="keyguard_indication_margin_bottom">80dp</dimen>
diff --git a/packages/SystemUI/res/values-sw600dp/config.xml b/packages/SystemUI/res/values-sw600dp/config.xml
index 63b9d75..67dabdb 100644
--- a/packages/SystemUI/res/values-sw600dp/config.xml
+++ b/packages/SystemUI/res/values-sw600dp/config.xml
@@ -26,10 +26,6 @@
<!-- The number of columns that the top level tiles span in the QuickSettings -->
<integer name="quick_settings_user_time_settings_tile_span">1</integer>
- <!-- The maximum count of notifications on Keyguard. The rest will be collapsed in an overflow
- card. -->
- <integer name="keyguard_max_notification_count">5</integer>
-
<!-- Set to true to enable the user switcher on the keyguard. -->
<bool name="config_keyguardUserSwitcher">true</bool>
diff --git a/packages/SystemUI/res/values-sw600dp/dimens.xml b/packages/SystemUI/res/values-sw600dp/dimens.xml
index e95d9fe..923edc8 100644
--- a/packages/SystemUI/res/values-sw600dp/dimens.xml
+++ b/packages/SystemUI/res/values-sw600dp/dimens.xml
@@ -38,12 +38,6 @@
<!-- On tablets this is just the close_handle_height -->
<dimen name="peek_height">@dimen/close_handle_height</dimen>
- <!-- The fraction of the screen height where the clock on the Keyguard has its center. The
- max value is used when no notifications are displaying, and the min value is when the
- highest possible number of notifications are showing. -->
- <fraction name="keyguard_clock_y_fraction_max">34%</fraction>
- <fraction name="keyguard_clock_y_fraction_min">24%</fraction>
-
<!-- The margin between the clock and the notifications on Keyguard. See
keyguard_clock_height_fraction_* for the difference between min and max.-->
<dimen name="keyguard_clock_notifications_margin">44dp</dimen>
diff --git a/packages/SystemUI/res/values-sw720dp-land/config.xml b/packages/SystemUI/res/values-sw720dp-land/config.xml
deleted file mode 100644
index 1b50288..0000000
--- a/packages/SystemUI/res/values-sw720dp-land/config.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ Copyright (C) 2014 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License
- -->
-<resources>
- <integer name="keyguard_max_notification_count">4</integer>
-</resources>
-
diff --git a/packages/SystemUI/res/values-sw720dp-land/dimens.xml b/packages/SystemUI/res/values-sw720dp-land/dimens.xml
deleted file mode 100644
index 511d45f..0000000
--- a/packages/SystemUI/res/values-sw720dp-land/dimens.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ Copyright (C) 2014 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License
- -->
-<resources>
- <!-- The fraction of the screen height where the clock on the Keyguard has its center. The
- min value is used when no notifications are displaying, and the max value is when the
- highest possible number of notifications are showing. -->
- <fraction name="keyguard_clock_y_fraction_max">35%</fraction>
- <fraction name="keyguard_clock_y_fraction_min">24%</fraction>
-</resources>
diff --git a/packages/SystemUI/res/values-sw720dp/dimens.xml b/packages/SystemUI/res/values-sw720dp/dimens.xml
index 25e96c8..8cf4adb 100644
--- a/packages/SystemUI/res/values-sw720dp/dimens.xml
+++ b/packages/SystemUI/res/values-sw720dp/dimens.xml
@@ -28,11 +28,5 @@
<dimen name="notification_panel_min_height">770dp</dimen>
<!-- Bottom margin (from display edge) for status bar panels -->
<dimen name="panel_float">56dp</dimen>
-
- <!-- The fraction of the screen height where the clock on the Keyguard has its center. The
- max value is used when no notifications are displaying, and the min value is when the
- highest possible number of notifications are showing. -->
- <fraction name="keyguard_clock_y_fraction_max">35%</fraction>
- <fraction name="keyguard_clock_y_fraction_min">25%</fraction>
</resources>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 1ec59bf..aefcb55 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -439,15 +439,10 @@
<!-- Minimum distance the user has to drag down to go to the full shade. -->
<dimen name="keyguard_drag_down_min_distance">100dp</dimen>
- <!-- The fraction of the screen height where the clock on the Keyguard has its center. The
- max value is used when no notifications are displaying, and the min value is when the
- highest possible number of notifications are showing. -->
- <fraction name="keyguard_clock_y_fraction_max">45%</fraction>
- <fraction name="keyguard_clock_y_fraction_min">19.8%</fraction>
-
- <!-- The margin between the clock and the notifications on Keyguard. See
- keyguard_clock_height_fraction_* for the difference between min and max.-->
+ <!-- The margin between the clock and the notifications on Keyguard.-->
<dimen name="keyguard_clock_notifications_margin">30dp</dimen>
+ <!-- Minimum margin between clock and top of screen or ambient indication -->
+ <dimen name="keyguard_clock_top_margin">26dp</dimen>
<dimen name="heads_up_scrim_height">250dp</dimen>
<item name="scrim_behind_alpha" format="float" type="dimen">0.62</item>
@@ -885,9 +880,6 @@
burn-in on AOD -->
<dimen name="burn_in_prevention_offset_y">50dp</dimen>
- <!-- padding between the notification stack and the keyguard status view when dozing -->
- <dimen name="dozing_stack_padding">30dp</dimen>
-
<dimen name="corner_size">16dp</dimen>
<dimen name="top_padding">0dp</dimen>
<dimen name="bottom_padding">48dp</dimen>
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/BackgroundTaskLoader.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/BackgroundTaskLoader.java
index ddd27b0..a0e7752 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/BackgroundTaskLoader.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/BackgroundTaskLoader.java
@@ -17,15 +17,12 @@
package com.android.systemui.shared.recents.model;
import android.content.Context;
-import android.content.pm.ActivityInfo;
-import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.os.Handler;
import android.os.HandlerThread;
import android.util.Log;
import com.android.systemui.shared.system.ActivityManagerWrapper;
-import com.android.systemui.shared.system.PackageManagerWrapper;
/**
* Background task resource loader
@@ -40,8 +37,7 @@
private final Handler mMainThreadHandler;
private final TaskResourceLoadQueue mLoadQueue;
- private final TaskKeyLruCache<Drawable> mIconCache;
- private final BitmapDrawable mDefaultIcon;
+ private final IconLoader mIconLoader;
private boolean mStarted;
private boolean mCancelled;
@@ -51,11 +47,9 @@
/** Constructor, creates a new loading thread that loads task resources in the background */
public BackgroundTaskLoader(TaskResourceLoadQueue loadQueue,
- TaskKeyLruCache<Drawable> iconCache, BitmapDrawable defaultIcon,
- OnIdleChangedListener onIdleChangedListener) {
+ IconLoader iconLoader, OnIdleChangedListener onIdleChangedListener) {
mLoadQueue = loadQueue;
- mIconCache = iconCache;
- mDefaultIcon = defaultIcon;
+ mIconLoader = iconLoader;
mMainThreadHandler = new Handler();
mOnIdleChangedListener = onIdleChangedListener;
mLoadThread = new HandlerThread("Recents-TaskResourceLoader",
@@ -140,32 +134,7 @@
// Load the next item from the queue
final Task t = mLoadQueue.nextTask();
if (t != null) {
- Drawable cachedIcon = mIconCache.get(t.key);
-
- // Load the icon if it is stale or we haven't cached one yet
- if (cachedIcon == null) {
- cachedIcon = ActivityManagerWrapper.getInstance().getBadgedTaskDescriptionIcon(
- mContext, t.taskDescription, t.key.userId, mContext.getResources());
-
- if (cachedIcon == null) {
- ActivityInfo info = PackageManagerWrapper.getInstance().getActivityInfo(
- t.key.getComponent(), t.key.userId);
- if (info != null) {
- if (DEBUG) Log.d(TAG, "Loading icon: " + t.key);
- cachedIcon = ActivityManagerWrapper.getInstance().getBadgedActivityIcon(
- info, t.key.userId);
- }
- }
-
- if (cachedIcon == null) {
- cachedIcon = mDefaultIcon;
- }
-
- // At this point, even if we can't load the icon, we will set the
- // default icon.
- mIconCache.put(t.key, cachedIcon);
- }
-
+ final Drawable icon = mIconLoader.getIcon(t);
if (DEBUG) Log.d(TAG, "Loading thumbnail: " + t.key);
final ThumbnailData thumbnailData =
ActivityManagerWrapper.getInstance().getTaskThumbnail(t.key.id,
@@ -173,9 +142,8 @@
if (!mCancelled) {
// Notify that the task data has changed
- final Drawable finalIcon = cachedIcon;
mMainThreadHandler.post(
- () -> t.notifyTaskDataLoaded(thumbnailData, finalIcon));
+ () -> t.notifyTaskDataLoaded(thumbnailData, icon));
}
}
}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/IconLoader.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/IconLoader.java
new file mode 100644
index 0000000..3bc1d9a
--- /dev/null
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/IconLoader.java
@@ -0,0 +1,183 @@
+/*
+ * 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.shared.recents.model;
+
+import android.app.ActivityManager;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.pm.ActivityInfo;
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
+import android.os.UserHandle;
+import android.util.IconDrawableFactory;
+import android.util.Log;
+import android.util.LruCache;
+
+import com.android.systemui.shared.system.PackageManagerWrapper;
+
+public abstract class IconLoader {
+
+ private static final String TAG = "IconLoader";
+
+ protected final Context mContext;
+ protected final TaskKeyLruCache<Drawable> mIconCache;
+ protected final LruCache<ComponentName, ActivityInfo> mActivityInfoCache;
+
+ public IconLoader(Context context, TaskKeyLruCache<Drawable> iconCache, LruCache<ComponentName,
+ ActivityInfo> activityInfoCache) {
+ mContext = context;
+ mIconCache = iconCache;
+ mActivityInfoCache = activityInfoCache;
+ }
+
+ /**
+ * Returns the activity info for the given task key, retrieving one from the system if the
+ * task key is expired.
+ *
+ * TODO: Move this to an ActivityInfoCache class
+ */
+ public ActivityInfo getAndUpdateActivityInfo(Task.TaskKey taskKey) {
+ ComponentName cn = taskKey.getComponent();
+ ActivityInfo activityInfo = mActivityInfoCache.get(cn);
+ if (activityInfo == null) {
+ activityInfo = PackageManagerWrapper.getInstance().getActivityInfo(cn, taskKey.userId);
+ if (cn == null || activityInfo == null) {
+ Log.e(TAG, "Unexpected null component name or activity info: " + cn + ", " +
+ activityInfo);
+ return null;
+ }
+ mActivityInfoCache.put(cn, activityInfo);
+ }
+ return activityInfo;
+ }
+
+ public Drawable getIcon(Task t) {
+ Drawable cachedIcon = mIconCache.get(t.key);
+ if (cachedIcon == null) {
+ cachedIcon = createNewIconForTask(t.key, t.taskDescription, true /* returnDefault */);
+ mIconCache.put(t.key, cachedIcon);
+ }
+ return cachedIcon;
+ }
+
+ /**
+ * Returns the cached task icon if the task key is not expired, updating the cache if it is.
+ */
+ public Drawable getAndInvalidateIfModified(Task.TaskKey taskKey,
+ ActivityManager.TaskDescription td, boolean loadIfNotCached) {
+ // Return the cached activity icon if it exists
+ Drawable icon = mIconCache.getAndInvalidateIfModified(taskKey);
+ if (icon != null) {
+ return icon;
+ }
+
+ if (loadIfNotCached) {
+ icon = createNewIconForTask(taskKey, td, false /* returnDefault */);
+ if (icon != null) {
+ mIconCache.put(taskKey, icon);
+ return icon;
+ }
+ }
+
+ // We couldn't load any icon
+ return null;
+ }
+
+ private Drawable createNewIconForTask(Task.TaskKey taskKey,
+ ActivityManager.TaskDescription desc, boolean returnDefault) {
+ int userId = taskKey.userId;
+ Bitmap tdIcon = desc.getInMemoryIcon();
+ if (tdIcon != null) {
+ return createDrawableFromBitmap(tdIcon, userId);
+ }
+ if (desc.getIconResource() != 0) {
+ // TODO: Use task context here
+ try {
+ return createBadgedDrawable(mContext.getDrawable(desc.getIconResource()), userId);
+ } catch (Resources.NotFoundException e) {
+ Log.e(TAG, "Could not find icon drawable from resource", e);
+ }
+ }
+
+ tdIcon = ActivityManager.TaskDescription.loadTaskDescriptionIcon(
+ desc.getIconFilename(), userId);
+ if (tdIcon != null) {
+ return createDrawableFromBitmap(tdIcon, userId);
+ }
+
+ // Load the icon from the activity info and cache it
+ ActivityInfo activityInfo = getAndUpdateActivityInfo(taskKey);
+ if (activityInfo != null) {
+ Drawable icon = getBadgedActivityIcon(activityInfo, userId);
+ if (icon != null) {
+ return icon;
+ }
+ }
+
+ // At this point, even if we can't load the icon, we will set the default icon.
+ return returnDefault ? getDefaultIcon(userId) : null;
+ }
+
+ public abstract Drawable getDefaultIcon(int userId);
+
+ protected Drawable createDrawableFromBitmap(Bitmap icon, int userId) {
+ return createBadgedDrawable(new BitmapDrawable(mContext.getResources(), icon), userId);
+ }
+
+ protected abstract Drawable createBadgedDrawable(Drawable icon, int userId);
+
+ /**
+ * @return the activity icon for the ActivityInfo for a user, badging if necessary.
+ */
+ protected abstract Drawable getBadgedActivityIcon(ActivityInfo info, int userId);
+
+ public static class DefaultIconLoader extends IconLoader {
+
+ private final BitmapDrawable mDefaultIcon;
+ private final IconDrawableFactory mDrawableFactory;
+
+ public DefaultIconLoader(Context context, TaskKeyLruCache<Drawable> iconCache,
+ LruCache<ComponentName, ActivityInfo> activityInfoCache) {
+ super(context, iconCache, activityInfoCache);
+
+ // Create the default assets
+ Bitmap icon = Bitmap.createBitmap(1, 1, Bitmap.Config.ALPHA_8);
+ icon.eraseColor(0);
+ mDefaultIcon = new BitmapDrawable(context.getResources(), icon);
+ mDrawableFactory = IconDrawableFactory.newInstance(context);
+ }
+
+ @Override
+ public Drawable getDefaultIcon(int userId) {
+ return mDefaultIcon;
+ }
+
+ @Override
+ protected Drawable createBadgedDrawable(Drawable icon, int userId) {
+ if (userId != UserHandle.myUserId()) {
+ icon = mContext.getPackageManager().getUserBadgedIcon(icon, new UserHandle(userId));
+ }
+ return icon;
+ }
+
+ @Override
+ protected Drawable getBadgedActivityIcon(ActivityInfo info, int userId) {
+ return mDrawableFactory.getBadgedIcon(info, info.applicationInfo, userId);
+ }
+ }
+}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/RecentsTaskLoadPlan.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/RecentsTaskLoadPlan.java
index 4834bb1..76204df 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/RecentsTaskLoadPlan.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/RecentsTaskLoadPlan.java
@@ -123,7 +123,7 @@
? loader.getAndUpdateContentDescription(taskKey, t.taskDescription)
: "";
Drawable icon = isStackTask
- ? loader.getAndUpdateActivityIcon(taskKey, t.taskDescription, res, false)
+ ? loader.getAndUpdateActivityIcon(taskKey, t.taskDescription, false)
: null;
ThumbnailData thumbnail = loader.getAndUpdateThumbnail(taskKey,
false /* loadIfNotCached */, false /* storeInCache */);
@@ -179,7 +179,7 @@
if (opts.loadIcons && (isRunningTask || isVisibleTask)) {
if (task.icon == null) {
- task.icon = loader.getAndUpdateActivityIcon(taskKey, task.taskDescription, res,
+ task.icon = loader.getAndUpdateActivityIcon(taskKey, task.taskDescription,
true);
}
}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/RecentsTaskLoader.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/RecentsTaskLoader.java
index 0f68026..1309a60 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/RecentsTaskLoader.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/RecentsTaskLoader.java
@@ -21,9 +21,6 @@
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.ActivityInfo;
-import android.content.res.Resources;
-import android.graphics.Bitmap;
-import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.os.Looper;
import android.os.Trace;
@@ -36,7 +33,6 @@
import com.android.systemui.shared.recents.model.Task.TaskKey;
import com.android.systemui.shared.recents.model.TaskKeyLruCache.EvictionCallback;
import com.android.systemui.shared.system.ActivityManagerWrapper;
-import com.android.systemui.shared.system.PackageManagerWrapper;
import java.io.PrintWriter;
import java.util.Map;
@@ -61,8 +57,6 @@
// Disable all thumbnail loading.
public static final int SVELTE_DISABLE_LOADING = 3;
- private final Context mContext;
-
// This activity info LruCache is useful because it can be expensive to retrieve ActivityInfos
// for many tasks, which we use to get the activity labels and icons. Unlike the other caches
// below, this is per-package so we can't invalidate the items in the cache based on the last
@@ -73,6 +67,7 @@
private final TaskKeyLruCache<String> mActivityLabelCache;
private final TaskKeyLruCache<String> mContentDescriptionCache;
private final TaskResourceLoadQueue mLoadQueue;
+ private final IconLoader mIconLoader;
private final BackgroundTaskLoader mLoader;
private final HighResThumbnailLoader mHighResThumbnailLoader;
@GuardedBy("this")
@@ -86,7 +81,6 @@
private int mDefaultTaskBarBackgroundColor;
private int mDefaultTaskViewBackgroundColor;
- private final BitmapDrawable mDefaultIcon;
private EvictionCallback mClearActivityInfoOnEviction = new EvictionCallback() {
@Override
@@ -99,16 +93,10 @@
public RecentsTaskLoader(Context context, int maxThumbnailCacheSize, int maxIconCacheSize,
int svelteLevel) {
- mContext = context;
mMaxThumbnailCacheSize = maxThumbnailCacheSize;
mMaxIconCacheSize = maxIconCacheSize;
mSvelteLevel = svelteLevel;
- // Create the default assets
- Bitmap icon = Bitmap.createBitmap(1, 1, Bitmap.Config.ALPHA_8);
- icon.eraseColor(0);
- mDefaultIcon = new BitmapDrawable(context.getResources(), icon);
-
// Initialize the proxy, cache and loaders
int numRecentTasks = ActivityManager.getMaxRecentTasksStatic();
mHighResThumbnailLoader = new HighResThumbnailLoader(ActivityManagerWrapper.getInstance(),
@@ -119,10 +107,17 @@
mContentDescriptionCache = new TaskKeyLruCache<>(numRecentTasks,
mClearActivityInfoOnEviction);
mActivityInfoCache = new LruCache<>(numRecentTasks);
- mLoader = new BackgroundTaskLoader(mLoadQueue, mIconCache, mDefaultIcon,
+
+ mIconLoader = createNewIconLoader(context, mIconCache, mActivityInfoCache);
+ mLoader = new BackgroundTaskLoader(mLoadQueue, mIconLoader,
mHighResThumbnailLoader::setTaskLoadQueueIdle);
}
+ protected IconLoader createNewIconLoader(Context context,TaskKeyLruCache<Drawable> iconCache,
+ LruCache<ComponentName, ActivityInfo> activityInfoCache) {
+ return new IconLoader.DefaultIconLoader(context, iconCache, activityInfoCache);
+ }
+
/**
* Sets the default task bar/view colors if none are provided by the app.
*/
@@ -187,7 +182,7 @@
*/
public void loadTaskData(Task t) {
Drawable icon = mIconCache.getAndInvalidateIfModified(t.key);
- icon = icon != null ? icon : mDefaultIcon;
+ icon = icon != null ? icon : mIconLoader.getDefaultIcon(t.key.userId);
mLoadQueue.addTask(t);
t.notifyTaskDataLoaded(t.thumbnail, icon);
}
@@ -195,7 +190,7 @@
/** Releases the task resource data back into the pool. */
public void unloadTaskData(Task t) {
mLoadQueue.removeTask(t);
- t.notifyTaskDataUnloaded(mDefaultIcon);
+ t.notifyTaskDataUnloaded(mIconLoader.getDefaultIcon(t.key.userId));
}
/** Completely removes the resource data from the pool. */
@@ -205,7 +200,7 @@
mActivityLabelCache.remove(t.key);
mContentDescriptionCache.remove(t.key);
if (notifyTaskDataUnloaded) {
- t.notifyTaskDataUnloaded(mDefaultIcon);
+ t.notifyTaskDataUnloaded(mIconLoader.getDefaultIcon(t.key.userId));
}
}
@@ -326,35 +321,8 @@
* Returns the cached task icon if the task key is not expired, updating the cache if it is.
*/
Drawable getAndUpdateActivityIcon(TaskKey taskKey, ActivityManager.TaskDescription td,
- Resources res, boolean loadIfNotCached) {
- // Return the cached activity icon if it exists
- Drawable icon = mIconCache.getAndInvalidateIfModified(taskKey);
- if (icon != null) {
- return icon;
- }
-
- if (loadIfNotCached) {
- // Return and cache the task description icon if it exists
- icon = ActivityManagerWrapper.getInstance().getBadgedTaskDescriptionIcon(mContext, td,
- taskKey.userId, res);
- if (icon != null) {
- mIconCache.put(taskKey, icon);
- return icon;
- }
-
- // Load the icon from the activity info and cache it
- ActivityInfo activityInfo = getAndUpdateActivityInfo(taskKey);
- if (activityInfo != null) {
- icon = ActivityManagerWrapper.getInstance().getBadgedActivityIcon(activityInfo,
- taskKey.userId);
- if (icon != null) {
- mIconCache.put(taskKey, icon);
- return icon;
- }
- }
- }
- // We couldn't load any icon
- return null;
+ boolean loadIfNotCached) {
+ return mIconLoader.getAndInvalidateIfModified(taskKey, td, loadIfNotCached);
}
/**
@@ -417,18 +385,7 @@
* task key is expired.
*/
ActivityInfo getAndUpdateActivityInfo(TaskKey taskKey) {
- ComponentName cn = taskKey.getComponent();
- ActivityInfo activityInfo = mActivityInfoCache.get(cn);
- if (activityInfo == null) {
- activityInfo = PackageManagerWrapper.getInstance().getActivityInfo(cn, taskKey.userId);
- if (cn == null || activityInfo == null) {
- Log.e(TAG, "Unexpected null component name or activity info: " + cn + ", " +
- activityInfo);
- return null;
- }
- mActivityInfoCache.put(cn, activityInfo);
- }
- return activityInfo;
+ return mIconLoader.getAndUpdateActivityInfo(taskKey);
}
/**
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 138910c..0103cad 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
@@ -17,7 +17,6 @@
package com.android.systemui.shared.system;
import static android.app.ActivityManager.LOCK_TASK_MODE_NONE;
-import static android.app.ActivityManager.LOCK_TASK_MODE_PINNED;
import static android.app.ActivityManager.RECENT_IGNORE_UNAVAILABLE;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
@@ -40,25 +39,19 @@
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.UserInfo;
-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;
import android.os.Handler;
import android.os.Looper;
import android.os.RemoteException;
import android.os.UserHandle;
import android.provider.Settings;
-import android.util.IconDrawableFactory;
import android.util.Log;
import android.view.IRecentsAnimationController;
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;
@@ -74,14 +67,12 @@
private static final ActivityManagerWrapper sInstance = new ActivityManagerWrapper();
private final PackageManager mPackageManager;
- private final IconDrawableFactory mDrawableFactory;
private final BackgroundExecutor mBackgroundExecutor;
private final TaskStackChangeListeners mTaskStackChangeListeners;
private ActivityManagerWrapper() {
final Context context = AppGlobals.getInitialApplication();
mPackageManager = context.getPackageManager();
- mDrawableFactory = IconDrawableFactory.newInstance(context);
mBackgroundExecutor = BackgroundExecutor.get();
mTaskStackChangeListeners = new TaskStackChangeListeners(Looper.getMainLooper());
}
@@ -156,58 +147,6 @@
}
/**
- * @return the task description icon, loading and badging it if it necessary.
- */
- public Drawable getBadgedTaskDescriptionIcon(Context context,
- ActivityManager.TaskDescription taskDescription, int userId, Resources res) {
- Bitmap tdIcon = taskDescription.getInMemoryIcon();
- Drawable dIcon = null;
- if (tdIcon != null) {
- dIcon = new BitmapDrawable(res, tdIcon);
- } else if (taskDescription.getIconResource() != 0) {
- try {
- dIcon = context.getDrawable(taskDescription.getIconResource());
- } catch (NotFoundException e) {
- Log.e(TAG, "Could not find icon drawable from resource", e);
- }
- } else {
- tdIcon = ActivityManager.TaskDescription.loadTaskDescriptionIcon(
- taskDescription.getIconFilename(), userId);
- if (tdIcon != null) {
- dIcon = new BitmapDrawable(res, tdIcon);
- }
- }
- if (dIcon != null) {
- return getBadgedIcon(dIcon, userId);
- }
- return null;
- }
-
- /**
- * @return the given icon for a user, badging if necessary.
- */
- private Drawable getBadgedIcon(Drawable icon, int userId) {
- if (userId != UserHandle.myUserId()) {
- icon = mPackageManager.getUserBadgedIcon(icon, new UserHandle(userId));
- }
- return icon;
- }
-
- /**
- * @return the activity icon for the ActivityInfo for a user, badging if necessary.
- */
- public Drawable getBadgedActivityIcon(ActivityInfo info, int userId) {
- return mDrawableFactory.getBadgedIcon(info, info.applicationInfo, userId);
- }
-
- /**
- * @return the application icon for the ApplicationInfo for a user, badging if necessary.
- */
- public Drawable getBadgedApplicationIcon(ApplicationInfo appInfo, int userId) {
- return mDrawableFactory.getBadgedIcon(appInfo, userId);
- }
-
- /**
* @return the activity label, badging if necessary.
*/
public String getBadgedActivityLabel(ActivityInfo info, int userId) {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java
index 0272a90..842899b 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java
@@ -22,6 +22,7 @@
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.annotation.Nullable;
+import android.app.AppGlobals;
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.ActivityInfo;
@@ -37,6 +38,7 @@
import android.os.CountDownTimer;
import android.support.v4.graphics.ColorUtils;
import android.util.AttributeSet;
+import android.util.IconDrawableFactory;
import android.view.Gravity;
import android.view.View;
import android.view.ViewAnimationUtils;
@@ -65,6 +67,8 @@
public class TaskViewHeader extends FrameLayout
implements View.OnClickListener, View.OnLongClickListener {
+ private static IconDrawableFactory sDrawableFactory;
+
private static final float HIGHLIGHT_LIGHTNESS_INCREMENT = 0.075f;
private static final float OVERLAY_LIGHTNESS_INCREMENT = -0.0625f;
private static final int OVERLAY_REVEAL_DURATION = 250;
@@ -628,7 +632,7 @@
activityInfo.applicationInfo, userId));
mAppTitleView.setTextColor(mTask.useLightOnPrimaryColor ?
mTaskBarViewLightTextColor : mTaskBarViewDarkTextColor);
- mAppIconView.setImageDrawable(ActivityManagerWrapper.getInstance().getBadgedApplicationIcon(
+ mAppIconView.setImageDrawable(getIconDrawableFactory().getBadgedIcon(
activityInfo.applicationInfo, userId));
mAppInfoView.setImageDrawable(mTask.useLightOnPrimaryColor
? mLightInfoIcon
@@ -671,4 +675,11 @@
revealAnim.start();
}
}
+
+ private static IconDrawableFactory getIconDrawableFactory() {
+ if (sDrawableFactory == null) {
+ sDrawableFactory = IconDrawableFactory.newInstance(AppGlobals.getInitialApplication());
+ }
+ return sDrawableFactory;
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
index 389be1a..0cf26df 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
@@ -19,11 +19,10 @@
import static com.android.systemui.statusbar.notification.NotificationUtils.interpolate;
import android.content.res.Resources;
-import android.graphics.Path;
import android.util.MathUtils;
-import android.view.animation.AccelerateInterpolator;
-import android.view.animation.PathInterpolator;
+import com.android.keyguard.KeyguardStatusView;
+import com.android.systemui.Interpolators;
import com.android.systemui.R;
/**
@@ -31,54 +30,83 @@
*/
public class KeyguardClockPositionAlgorithm {
- private static final float SLOW_DOWN_FACTOR = 0.4f;
-
- private static final float CLOCK_RUBBERBAND_FACTOR_MIN = 0.08f;
- private static final float CLOCK_RUBBERBAND_FACTOR_MAX = 0.8f;
- private static final float CLOCK_SCALE_FADE_START = 0.95f;
- private static final float CLOCK_SCALE_FADE_END = 0.75f;
- private static final float CLOCK_SCALE_FADE_END_NO_NOTIFS = 0.5f;
-
- private static final float CLOCK_ADJ_TOP_PADDING_MULTIPLIER_MIN = 1.4f;
- private static final float CLOCK_ADJ_TOP_PADDING_MULTIPLIER_MAX = 3.2f;
-
private static final long MILLIS_PER_MINUTES = 1000 * 60;
private static final float BURN_IN_PREVENTION_PERIOD_Y = 521;
private static final float BURN_IN_PREVENTION_PERIOD_X = 83;
+ /**
+ * How much the clock height influences the shade position.
+ * 0 means nothing, 1 means move the shade up by the height of the clock
+ * 0.5f means move the shade up by half of the size of the clock.
+ */
+ private static float CLOCK_HEIGHT_WEIGHT = 0.7f;
+
+ /**
+ * Margin between the bottom of the clock and the notification shade.
+ */
private int mClockNotificationsMargin;
- private float mClockYFractionMin;
- private float mClockYFractionMax;
- private int mMaxKeyguardNotifications;
- private int mMaxPanelHeight;
+
+ /**
+ * Current height of {@link NotificationPanelView}, considering how much the
+ * user collapsed it.
+ */
private float mExpandedHeight;
- private int mNotificationCount;
+
+ /**
+ * Height of the parent view - display size in px.
+ */
private int mHeight;
+
+ /**
+ * Height of {@link KeyguardStatusView}.
+ */
private int mKeyguardStatusHeight;
- private float mEmptyDragAmount;
- private float mDensity;
+
+ /**
+ * Height of notification stack: Sum of height of each notification.
+ */
+ private int mNotificationStackHeight;
+
+ /**
+ * Minimum top margin to avoid overlap with status bar.
+ */
+ private int mMinTopMargin;
+
+ /**
+ * Maximum bottom padding to avoid overlap with {@link KeyguardBottomAreaView} or
+ * the ambient indication.
+ */
+ private int mMaxShadeBottom;
+
+ /**
+ * Margin that we should respect within the available space.
+ */
+ private int mContainerPadding;
+
+ /**
+ * Position where clock should be when the panel is collapsed.
+ */
+ private int mClockYTarget;
+
+ /**
+ * @see NotificationPanelView#getMaxPanelHeight()
+ */
+ private float mMaxPanelHeight;
+
+ /**
+ * Burn-in prevention x translation.
+ */
private int mBurnInPreventionOffsetX;
+
+ /**
+ * Burn-in prevention y translation.
+ */
private int mBurnInPreventionOffsetY;
/**
- * The number (fractional) of notifications the "more" card counts when calculating how many
- * notifications are currently visible for the y positioning of the clock.
+ * Doze/AOD transition amount.
*/
- private float mMoreCardNotificationAmount;
-
- private static final PathInterpolator sSlowDownInterpolator;
-
- static {
- Path path = new Path();
- path.moveTo(0, 0);
- path.cubicTo(0.3f, 0.875f, 0.6f, 1f, 1f, 1f);
- sSlowDownInterpolator = new PathInterpolator(path);
- }
-
- private AccelerateInterpolator mAccelerateInterpolator = new AccelerateInterpolator();
- private int mClockBottom;
private float mDarkAmount;
- private int mDozingStackPadding;
/**
* Refreshes the dimension values.
@@ -86,79 +114,95 @@
public void loadDimens(Resources res) {
mClockNotificationsMargin = res.getDimensionPixelSize(
R.dimen.keyguard_clock_notifications_margin);
- mClockYFractionMin = res.getFraction(R.fraction.keyguard_clock_y_fraction_min, 1, 1);
- mClockYFractionMax = res.getFraction(R.fraction.keyguard_clock_y_fraction_max, 1, 1);
- mMoreCardNotificationAmount =
- (float) res.getDimensionPixelSize(R.dimen.notification_shelf_height) /
- res.getDimensionPixelSize(R.dimen.notification_min_height);
- mDensity = res.getDisplayMetrics().density;
+ mContainerPadding = res.getDimensionPixelSize(
+ R.dimen.keyguard_clock_top_margin);
mBurnInPreventionOffsetX = res.getDimensionPixelSize(
R.dimen.burn_in_prevention_offset_x);
mBurnInPreventionOffsetY = res.getDimensionPixelSize(
R.dimen.burn_in_prevention_offset_y);
- mDozingStackPadding = res.getDimensionPixelSize(R.dimen.dozing_stack_padding);
}
- public void setup(int maxKeyguardNotifications, int maxPanelHeight, float expandedHeight,
- int notificationCount, int height, int keyguardStatusHeight, float emptyDragAmount,
- int clockBottom, float dark) {
- mMaxKeyguardNotifications = maxKeyguardNotifications;
- mMaxPanelHeight = maxPanelHeight;
+ public void setup(int minTopMargin, int maxShadeBottom, int notificationStackHeight,
+ float expandedHeight, float maxPanelHeight, int parentHeight, int keyguardStatusHeight,
+ float dark) {
+ mMinTopMargin = minTopMargin;
+ mMaxShadeBottom = maxShadeBottom;
+ mNotificationStackHeight = notificationStackHeight;
mExpandedHeight = expandedHeight;
- mNotificationCount = notificationCount;
- mHeight = height;
+ mMaxPanelHeight = maxPanelHeight;
+ mHeight = parentHeight;
mKeyguardStatusHeight = keyguardStatusHeight;
- mEmptyDragAmount = emptyDragAmount;
- mClockBottom = clockBottom;
mDarkAmount = dark;
- }
- public float getMinStackScrollerPadding(int height, int keyguardStatusHeight) {
- return mClockYFractionMin * height + keyguardStatusHeight / 2
- + mClockNotificationsMargin;
+ // Where the clock should stop when swiping up.
+ // This should be outside of the display when unlocked or
+ // under then status bar when the bouncer will be shown
+ mClockYTarget = -mKeyguardStatusHeight;
+ // TODO: on bouncer animation follow-up CL
+ // mClockYTarget = mMinTopMargin + mContainerPadding;
}
public void run(Result result) {
- int y = getClockY() - mKeyguardStatusHeight / 2;
- float clockAdjustment = getClockYExpansionAdjustment();
- float topPaddingAdjMultiplier = getTopPaddingAdjMultiplier();
- result.stackScrollerPaddingAdjustment = (int) (clockAdjustment*topPaddingAdjMultiplier);
+ final int y = getClockY();
result.clockY = y;
- int clockNotificationsPadding = mClockNotificationsMargin
- + result.stackScrollerPaddingAdjustment;
- int padding = y + clockNotificationsPadding;
- result.clockScale = getClockScale(mKeyguardStatusHeight + padding,
- y, y + mClockNotificationsMargin + mKeyguardStatusHeight);
- result.clockAlpha = getClockAlpha(result.clockScale);
-
- result.stackScrollerPadding = mClockBottom + y + mDozingStackPadding;
+ result.clockAlpha = getClockAlpha(y);
+ result.stackScrollerPadding = y + mKeyguardStatusHeight;
result.clockX = (int) interpolate(0, burnInPreventionOffsetX(), mDarkAmount);
}
- private float getClockScale(int notificationPadding, int clockY, int startPadding) {
- float scaleMultiplier = getNotificationAmountT() == 0 ? 6.0f : 5.0f;
- float scaleEnd = clockY - mKeyguardStatusHeight * scaleMultiplier;
- float distanceToScaleEnd = notificationPadding - scaleEnd;
- float progress = distanceToScaleEnd / (startPadding - scaleEnd);
- progress = Math.max(0.0f, Math.min(progress, 1.0f));
- progress = mAccelerateInterpolator.getInterpolation(progress);
- progress *= Math.pow(1 + mEmptyDragAmount / mDensity / 300, 0.3f);
- return interpolate(progress, 1, mDarkAmount);
+ public float getMinStackScrollerPadding() {
+ return mMinTopMargin + mKeyguardStatusHeight + mClockNotificationsMargin;
}
- private float getClockYFraction() {
- float t = getNotificationAmountT();
- t = Math.min(t, 1.0f);
- return MathUtils.lerp(mClockYFractionMax, mClockYFractionMin, t);
+ private int getMaxClockY() {
+ return mHeight / 2 - mKeyguardStatusHeight - mClockNotificationsMargin;
+ }
+
+ public int getExpandedClockBottom() {
+ return getExpandedClockPosition() + mKeyguardStatusHeight;
+ }
+
+ /**
+ * Vertically align the clock and the shade in the available space considering only
+ * a percentage of the clock height defined by {@code CLOCK_HEIGHT_WEIGHT}.
+ * @return Clock Y in pixels.
+ */
+ private int getExpandedClockPosition() {
+ final int availableHeight = mMaxShadeBottom - mMinTopMargin;
+ final int containerCenter = mMinTopMargin + availableHeight / 2;
+
+ float y = containerCenter - mKeyguardStatusHeight * CLOCK_HEIGHT_WEIGHT
+ - mClockNotificationsMargin - mNotificationStackHeight / 2;
+ if (y < mMinTopMargin + mContainerPadding) {
+ y = mMinTopMargin + mContainerPadding;
+ }
+
+ // Don't allow the clock base to be under half of the screen
+ final float maxClockY = getMaxClockY();
+ if (y > maxClockY) {
+ y = maxClockY;
+ }
+
+ return (int) y;
}
private int getClockY() {
// Dark: Align the bottom edge of the clock at about half of the screen:
- float clockYDark = (mClockYFractionMax * mHeight +
- (float) mKeyguardStatusHeight / 2 - mClockBottom)
- + burnInPreventionOffsetY();
- float clockYRegular = getClockYFraction() * mHeight;
- return (int) interpolate(clockYRegular, clockYDark, mDarkAmount);
+ final float clockYDark = getMaxClockY() + burnInPreventionOffsetY();
+ float clockYRegular = getExpandedClockPosition();
+
+ // Move clock up while collapsing the shade
+ final float shadeExpansion = mExpandedHeight / mMaxPanelHeight;
+ final float clockY = MathUtils.lerp(mClockYTarget, clockYRegular, shadeExpansion);
+
+ return (int) MathUtils.lerp(clockY, clockYDark, mDarkAmount);
+ }
+
+ private float getClockAlpha(int y) {
+ float alphaKeyguard = Math.max(0, Math.min(1, (y - mMinTopMargin)
+ / Math.max(1f, getExpandedClockPosition() - mMinTopMargin)));
+ alphaKeyguard = Interpolators.ACCELERATE.getInterpolation(alphaKeyguard);
+ return MathUtils.lerp(alphaKeyguard, 1f, mDarkAmount);
}
private float burnInPreventionOffsetY() {
@@ -190,63 +234,19 @@
return interpolate(0, amplitude, interpolationAmount);
}
- private float getClockYExpansionAdjustment() {
- float rubberbandFactor = getClockYExpansionRubberbandFactor();
- float value = (rubberbandFactor * (mMaxPanelHeight - mExpandedHeight));
- float t = value / mMaxPanelHeight;
- float slowedDownValue = -sSlowDownInterpolator.getInterpolation(t) * SLOW_DOWN_FACTOR
- * mMaxPanelHeight;
- if (mNotificationCount == 0) {
- return (-2*value + slowedDownValue)/3;
- } else {
- return slowedDownValue;
- }
- }
-
- private float getClockYExpansionRubberbandFactor() {
- float t = getNotificationAmountT();
- t = Math.min(t, 1.0f);
- t = (float) Math.pow(t, 0.3f);
- return (1 - t) * CLOCK_RUBBERBAND_FACTOR_MAX + t * CLOCK_RUBBERBAND_FACTOR_MIN;
- }
-
- private float getTopPaddingAdjMultiplier() {
- float t = getNotificationAmountT();
- t = Math.min(t, 1.0f);
- return (1 - t) * CLOCK_ADJ_TOP_PADDING_MULTIPLIER_MIN
- + t * CLOCK_ADJ_TOP_PADDING_MULTIPLIER_MAX;
- }
-
- private float getClockAlpha(float scale) {
- float fadeEnd = getNotificationAmountT() == 0.0f
- ? CLOCK_SCALE_FADE_END_NO_NOTIFS
- : CLOCK_SCALE_FADE_END;
- float alpha = (scale - fadeEnd)
- / (CLOCK_SCALE_FADE_START - fadeEnd);
- return Math.max(0, Math.min(1, alpha));
- }
-
- /**
- * @return a value from 0 to 1 depending on how many notification there are
- */
- private float getNotificationAmountT() {
- return mNotificationCount
- / (mMaxKeyguardNotifications + mMoreCardNotificationAmount);
- }
-
public static class Result {
/**
+ * The x translation of the clock.
+ */
+ public int clockX;
+
+ /**
* The y translation of the clock.
*/
public int clockY;
/**
- * The scale of the Clock
- */
- public float clockScale;
-
- /**
* The alpha value of the clock.
*/
public float clockAlpha;
@@ -255,14 +255,5 @@
* The top padding of the stack scroller, in pixels.
*/
public int stackScrollerPadding;
-
- /**
- * The top padding adjustment of the stack scroller, in pixels. This value is used to adjust
- * the padding, but not the overall panel size.
- */
- public int stackScrollerPaddingAdjustment;
-
- /** The x translation of the clock. */
- public int clockX;
}
}
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 3b129fc..51d094e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -37,6 +37,7 @@
import android.os.PowerManager;
import android.util.AttributeSet;
import android.util.FloatProperty;
+import android.util.Log;
import android.util.MathUtils;
import android.view.LayoutInflater;
import android.view.MotionEvent;
@@ -172,7 +173,6 @@
private Animator mClockAnimator;
private int mClockAnimationTargetX = Integer.MIN_VALUE;
private int mClockAnimationTargetY = Integer.MIN_VALUE;
- private int mTopPaddingAdjustment;
private KeyguardClockPositionAlgorithm mClockPositionAlgorithm =
new KeyguardClockPositionAlgorithm();
private KeyguardClockPositionAlgorithm.Result mClockPositionResult =
@@ -244,6 +244,7 @@
private int mQsNotificationTopPadding;
private float mExpandOffset;
private boolean mHideIconsDuringNotificationLaunch = true;
+ private int mStackScrollerMeasuringPass;
public NotificationPanelView(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -459,17 +460,17 @@
if (mStatusBarState != StatusBarState.KEYGUARD) {
stackScrollerPadding = (mQs != null ? mQs.getHeader().getHeight() : 0) + mQsPeekHeight
+ mQsNotificationTopPadding;
- mTopPaddingAdjustment = 0;
} else {
+ final int totalHeight = getHeight();
+ final int bottomPadding = Math.max(mIndicationBottomPadding, mAmbientIndicationBottomPadding);
mClockPositionAlgorithm.setup(
- mStatusBar.getMaxNotificationsWhileLocked(),
- getMaxPanelHeight(),
+ mStatusBarMinHeight,
+ totalHeight - bottomPadding,
+ calculatePanelHeightShade() - mNotificationStackScroller.getTopPadding(),
getExpandedHeight(),
- mNotificationStackScroller.getNotGoneChildCount(),
- getHeight(),
+ getMaxPanelHeight(),
+ totalHeight,
mKeyguardStatusView.getHeight(),
- mEmptyDragAmount,
- mKeyguardStatusView.getClockBottom(),
mDarkAmount);
mClockPositionAlgorithm.run(mClockPositionResult);
if (animate || mClockAnimator != null) {
@@ -478,14 +479,16 @@
mKeyguardStatusView.setX(mClockPositionResult.clockX);
mKeyguardStatusView.setY(mClockPositionResult.clockY);
}
- updateClock(mClockPositionResult.clockAlpha, mClockPositionResult.clockScale);
+ updateClock();
stackScrollerPadding = mClockPositionResult.stackScrollerPadding;
- mTopPaddingAdjustment = mClockPositionResult.stackScrollerPaddingAdjustment;
}
mNotificationStackScroller.setIntrinsicPadding(stackScrollerPadding);
mNotificationStackScroller.setAntiBurnInOffsetX(mClockPositionResult.clockX);
mKeyguardBottomArea.setBurnInXOffset(mClockPositionResult.clockX);
+
+ mStackScrollerMeasuringPass++;
requestScrollerTopPaddingUpdate(animate);
+ mStackScrollerMeasuringPass = 0;
}
/**
@@ -493,8 +496,7 @@
* @return the maximum keyguard notifications that can fit on the screen
*/
public int computeMaxKeyguardNotifications(int maximum) {
- float minPadding = mClockPositionAlgorithm.getMinStackScrollerPadding(getHeight(),
- mKeyguardStatusView.getHeight());
+ float minPadding = mClockPositionAlgorithm.getMinStackScrollerPadding();
int notificationPadding = Math.max(1, getResources().getDimensionPixelSize(
R.dimen.notification_divider_height));
NotificationShelf shelf = mNotificationStackScroller.getNotificationShelf();
@@ -579,12 +581,10 @@
});
}
- private void updateClock(float alpha, float scale) {
+ private void updateClock() {
if (!mKeyguardStatusViewAnimating) {
- mKeyguardStatusView.setAlpha(alpha * mQsClockAlphaOverride);
+ mKeyguardStatusView.setAlpha(mClockPositionResult.clockAlpha * mQsClockAlphaOverride);
}
- mKeyguardStatusView.setScaleX(scale);
- mKeyguardStatusView.setScaleY(scale);
}
public void animateToFullShade(long delay) {
@@ -1316,7 +1316,7 @@
newClockAlpha = 1 - MathUtils.constrain(newClockAlpha, 0, 1);
if (newClockAlpha != mQsClockAlphaOverride) {
mQsClockAlphaOverride = Interpolators.ALPHA_OUT.getInterpolation(newClockAlpha);
- updateClock(mClockPositionResult.clockAlpha, mClockPositionResult.clockScale);
+ updateClock();
}
if (mAccessibilityManager.isEnabled()) {
@@ -1356,15 +1356,15 @@
&& (mQsExpandImmediate || mIsExpanding && mQsExpandedWhenExpandingStarted)) {
// Either QS pushes the notifications down when fully expanded, or QS is fully above the
- // notifications (mostly on tablets). maxNotifications denotes the normal top padding
- // on Keyguard, maxQs denotes the top padding from the quick settings panel. We need to
- // take the maximum and linearly interpolate with the panel expansion for a nice motion.
- int maxNotifications = mClockPositionResult.stackScrollerPadding
- - mClockPositionResult.stackScrollerPaddingAdjustment;
- int maxQs = mQsMaxExpansionHeight + mQsNotificationTopPadding;
+ // notifications (mostly on tablets). maxNotificationPadding denotes the normal top
+ // padding on Keyguard, maxQsPadding denotes the top padding from the quick settings
+ // panel. We need to take the maximum and linearly interpolate with the panel expansion
+ // for a nice motion.
+ int maxNotificationPadding = mClockPositionResult.stackScrollerPadding;
+ int maxQsPadding = mQsMaxExpansionHeight + mQsNotificationTopPadding;
int max = mStatusBarState == StatusBarState.KEYGUARD
- ? Math.max(maxNotifications, maxQs)
- : maxQs;
+ ? Math.max(maxNotificationPadding, maxQsPadding)
+ : maxQsPadding;
return (int) interpolate(getExpandedFraction(),
mQsMinExpansionHeight, max);
} else if (mQsSizeChangeAnimator != null) {
@@ -1507,7 +1507,7 @@
if (mQsExpandImmediate || mQsExpanded || mIsExpanding && mQsExpandedWhenExpandingStarted) {
maxHeight = calculatePanelHeightQsExpanded();
} else {
- maxHeight = calculatePanelHeightShade();
+ maxHeight = Math.max(calculatePanelHeightShade(), calculatePanelHeightShadeExpanded());
}
maxHeight = Math.max(maxHeight, min);
return maxHeight;
@@ -1524,7 +1524,15 @@
@Override
protected void onHeightUpdated(float expandedHeight) {
if (!mQsExpanded || mQsExpandImmediate || mIsExpanding && mQsExpandedWhenExpandingStarted) {
- positionClockAndNotifications();
+ // Updating the clock position will set the top padding which might
+ // trigger a new panel height and re-position the clock.
+ // This is a circular dependency and should be avoided, otherwise we'll have
+ // a stack overflow.
+ if (mStackScrollerMeasuringPass > 2) {
+ if (DEBUG) Log.d(TAG, "Unstable notification panel height. Aborting.");
+ } else {
+ positionClockAndNotifications();
+ }
}
if (mQsExpandImmediate || mQsExpanded && !mQsTracking && mQsExpansionAnimator == null
&& !mQsExpansionFromOverscroll) {
@@ -1568,12 +1576,18 @@
private int calculatePanelHeightShade() {
int emptyBottomMargin = mNotificationStackScroller.getEmptyBottomMargin();
- int maxHeight = mNotificationStackScroller.getHeight() - emptyBottomMargin
- - mTopPaddingAdjustment;
+ int maxHeight = mNotificationStackScroller.getHeight() - emptyBottomMargin;
maxHeight += mNotificationStackScroller.getTopPaddingOverflow();
return maxHeight;
}
+ private int calculatePanelHeightShadeExpanded() {
+ return mNotificationStackScroller.getHeight()
+ - mNotificationStackScroller.getEmptyBottomMargin()
+ - mNotificationStackScroller.getTopPadding()
+ + mClockPositionAlgorithm.getExpandedClockBottom();
+ }
+
private int calculatePanelHeightQsExpanded() {
float notificationHeight = mNotificationStackScroller.getHeight()
- mNotificationStackScroller.getEmptyBottomMargin()
@@ -1598,8 +1612,7 @@
}
float totalHeight = Math.max(
maxQsHeight, mStatusBarState == StatusBarState.KEYGUARD
- ? mClockPositionResult.stackScrollerPadding - mTopPaddingAdjustment
- : 0)
+ ? mClockPositionResult.stackScrollerPadding : 0)
+ notificationHeight + mNotificationStackScroller.getTopPaddingOverflow();
if (totalHeight > mNotificationStackScroller.getHeight()) {
float fullyCollapsedHeight = maxQsHeight
@@ -2272,8 +2285,9 @@
}
@Override
- protected void dispatchDraw(Canvas canvas) {
- super.dispatchDraw(canvas);
+ protected void onDraw(Canvas canvas) {
+ super.onDraw(canvas);
+
if (DEBUG) {
Paint p = new Paint();
p.setColor(Color.RED);
@@ -2288,6 +2302,9 @@
p.setColor(Color.YELLOW);
canvas.drawLine(0, calculatePanelHeightShade(), getWidth(),
calculatePanelHeightShade(), p);
+ p.setColor(Color.GRAY);
+ canvas.drawLine(0, calculatePanelHeightShadeExpanded(), getWidth(),
+ calculatePanelHeightShadeExpanded(), p);
p.setColor(Color.MAGENTA);
canvas.drawLine(0, calculateQsTopPadding(), getWidth(),
calculateQsTopPadding(), p);
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 b82b0ee..00aff53 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickScrubController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickScrubController.java
@@ -176,8 +176,10 @@
mTrackAnimator = ObjectAnimator.ofFloat();
mTrackAnimator.addUpdateListener(mTrackAnimatorListener);
+ mTrackAnimator.setFloatValues(0);
mButtonAnimator = ObjectAnimator.ofInt();
mButtonAnimator.addUpdateListener(mButtonTranslationListener);
+ mButtonAnimator.setIntValues(0);
mQuickScrubEndAnimator = new AnimatorSet();
mQuickScrubEndAnimator.playTogether(mTrackAnimator, mButtonAnimator);
mQuickScrubEndAnimator.setDuration(ANIM_DURATION_MS);
@@ -434,6 +436,7 @@
private void animateEnd() {
mButtonAnimator.setIntValues((int) mTranslation, 0);
mTrackAnimator.setFloatValues(mTrackAlpha, 0);
+ mQuickScrubEndAnimator.setCurrentPlayTime(0);
mQuickScrubEndAnimator.start();
}
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 4ffe5fe..933c952 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -4985,7 +4985,7 @@
notificationKey)) {
// Show work challenge, do not run PendingIntent and
// remove notification
- collapsePanel();
+ collapseOnMainThread();
return;
}
}
@@ -5026,11 +5026,7 @@
}
}
if (shouldCollapse()) {
- if (Looper.getMainLooper().isCurrentThread()) {
- collapsePanel();
- } else {
- mStackScroller.post(this::collapsePanel);
- }
+ collapseOnMainThread();
}
try {
@@ -5058,6 +5054,14 @@
}, afterKeyguardGone);
}
+ private void collapseOnMainThread() {
+ if (Looper.getMainLooper().isCurrentThread()) {
+ collapsePanel();
+ } else {
+ mStackScroller.post(this::collapsePanel);
+ }
+ }
+
private boolean shouldCollapse() {
return mState != StatusBarState.SHADE || !mActivityLaunchAnimator.isAnimationPending();
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java
index 944e888..fc043c5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java
@@ -26,7 +26,6 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.content.res.Resources;
-import android.support.test.filters.FlakyTest;
import android.support.test.filters.SmallTest;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
@@ -77,7 +76,6 @@
mSpacing = res.getDimensionPixelSize(R.dimen.smart_reply_button_spacing);
}
- @FlakyTest
@Test
public void testSendSmartReply_intentContainsResultsAndSource() throws InterruptedException {
setRepliesFromRemoteInput(TEST_CHOICES);
@@ -182,7 +180,6 @@
assertEqualLayouts(expectedView.getChildAt(2), mView.getChildAt(2));
}
- @FlakyTest
@Test
public void testMeasure_choiceWithThreeLines() {
final CharSequence[] choices = new CharSequence[]{"Hi", "Hello\nevery\nbody", "Bye"};
diff --git a/proto/src/metrics_constants.proto b/proto/src/metrics_constants.proto
index c0e5960..c56002e 100644
--- a/proto/src/metrics_constants.proto
+++ b/proto/src/metrics_constants.proto
@@ -5278,6 +5278,7 @@
// OS: P
FIELD_QS_MODE = 1311;
+
// OPEN: Settings->Developer Options->Default USB
// CATEGORY: SETTINGS
// OS: P
@@ -5317,6 +5318,16 @@
// OS: P
FIELD_LINKIFY_CALL_ID = 1319;
+ // FIELD: The compiler filter used when when optimizing the package.
+ // Logged together with app transition events.
+ // OS: P
+ PACKAGE_OPTIMIZATION_COMPILATION_FILTER = 1320;
+
+ // FIELD: The reason for optimizing the package.
+ // Logged together with app transition events.
+ // OS: P
+ PACKAGE_OPTIMIZATION_COMPILATION_REASON = 1321;
+
// ---- End P Constants, all P constants go above this line ----
// Add new aosp constants above this line.
// END OF AOSP CONSTANTS
diff --git a/services/accessibility/java/com/android/server/accessibility/TouchExplorer.java b/services/accessibility/java/com/android/server/accessibility/TouchExplorer.java
index 3419b80..84a8d45 100644
--- a/services/accessibility/java/com/android/server/accessibility/TouchExplorer.java
+++ b/services/accessibility/java/com/android/server/accessibility/TouchExplorer.java
@@ -21,7 +21,6 @@
import android.os.Handler;
import android.util.Slog;
import android.view.InputDevice;
-import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.MotionEvent.PointerCoords;
import android.view.MotionEvent.PointerProperties;
@@ -296,7 +295,8 @@
// Already handled.
} break;
default:
- throw new IllegalStateException("Illegal state: " + mCurrentState);
+ Slog.e(LOG_TAG, "Illegal state: " + mCurrentState);
+ clear(event, policyFlags);
}
}
@@ -649,8 +649,10 @@
}
switch (event.getActionMasked()) {
case MotionEvent.ACTION_DOWN: {
- throw new IllegalStateException("Dragging state can be reached only if two "
+ Slog.e(LOG_TAG, "Dragging state can be reached only if two "
+ "pointers are already down");
+ clear(event, policyFlags);
+ return;
}
case MotionEvent.ACTION_POINTER_DOWN: {
// We are in dragging state so we have two pointers and another one
@@ -741,8 +743,10 @@
private void handleMotionEventStateDelegating(MotionEvent event, int policyFlags) {
switch (event.getActionMasked()) {
case MotionEvent.ACTION_DOWN: {
- throw new IllegalStateException("Delegating state can only be reached if "
+ Slog.e(LOG_TAG, "Delegating state can only be reached if "
+ "there is at least one pointer down!");
+ clear(event, policyFlags);
+ return;
}
case MotionEvent.ACTION_UP: {
// Offset the event if we are doing a long press as the
@@ -1093,7 +1097,7 @@
case STATE_GESTURE_DETECTING:
return "STATE_GESTURE_DETECTING";
default:
- throw new IllegalArgumentException("Unknown state: " + state);
+ return "Unknown state: " + state;
}
}
diff --git a/services/core/java/com/android/server/AlarmManagerService.java b/services/core/java/com/android/server/AlarmManagerService.java
index 9cd3621..c93f405 100644
--- a/services/core/java/com/android/server/AlarmManagerService.java
+++ b/services/core/java/com/android/server/AlarmManagerService.java
@@ -3860,7 +3860,7 @@
final class AppStandbyTracker extends UsageStatsManagerInternal.AppIdleStateChangeListener {
@Override
public void onAppIdleStateChanged(final String packageName, final @UserIdInt int userId,
- boolean idle, int bucket) {
+ boolean idle, int bucket, int reason) {
if (DEBUG_STANDBY) {
Slog.d(TAG, "Package " + packageName + " for user " + userId + " now in bucket " +
bucket);
diff --git a/services/core/java/com/android/server/AppStateTracker.java b/services/core/java/com/android/server/AppStateTracker.java
index fc4d463..a6b71b7 100644
--- a/services/core/java/com/android/server/AppStateTracker.java
+++ b/services/core/java/com/android/server/AppStateTracker.java
@@ -678,7 +678,7 @@
final class StandbyTracker extends AppIdleStateChangeListener {
@Override
public void onAppIdleStateChanged(String packageName, int userId, boolean idle,
- int bucket) {
+ int bucket, int reason) {
if (DEBUG) {
Slog.d(TAG,"onAppIdleStateChanged: " + packageName + " u" + userId
+ (idle ? " idle" : " active") + " " + bucket);
diff --git a/services/core/java/com/android/server/am/ActivityManagerConstants.java b/services/core/java/com/android/server/am/ActivityManagerConstants.java
index 0d6d2bd..6550d06 100644
--- a/services/core/java/com/android/server/am/ActivityManagerConstants.java
+++ b/services/core/java/com/android/server/am/ActivityManagerConstants.java
@@ -85,7 +85,7 @@
private static final int DEFAULT_POWER_CHECK_MAX_CPU_3 = 10;
private static final int DEFAULT_POWER_CHECK_MAX_CPU_4 = 2;
private static final long DEFAULT_SERVICE_USAGE_INTERACTION_TIME = 30*60*1000;
- private static final long DEFAULT_USAGE_STATS_INTERACTION_INTERVAL = 24*60*60*1000L;
+ private static final long DEFAULT_USAGE_STATS_INTERACTION_INTERVAL = 2*60*60*1000L;
private static final long DEFAULT_SERVICE_RESTART_DURATION = 1*1000;
private static final long DEFAULT_SERVICE_RESET_RUN_DURATION = 60*1000;
private static final int DEFAULT_SERVICE_RESTART_DURATION_FACTOR = 4;
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 6fcdc3e..d4307d7 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -22355,7 +22355,7 @@
// Update the configuration with WM first and check if any of the stacks need to be resized
// due to the configuration change. If so, resize the stacks now and do any relaunches if
// necessary. This way we don't need to relaunch again afterwards in
- // ensureActivityConfigurationLocked().
+ // ensureActivityConfiguration().
if (mWindowManager != null) {
final int[] resizedStacks =
mWindowManager.setNewDisplayOverrideConfiguration(mTempConfig, displayId);
@@ -22383,7 +22383,7 @@
}
if (starting != null) {
- kept = starting.ensureActivityConfigurationLocked(changes,
+ kept = starting.ensureActivityConfiguration(changes,
false /* preserveWindow */);
// And we need to make sure at this point that all other activities
// are made visible with the correct configuration.
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index 07e27bc..3b2a22d 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -23,6 +23,7 @@
import android.app.IActivityManager;
import android.app.IStopUserCallback;
import android.app.IUidObserver;
+import android.app.KeyguardManager;
import android.app.ProfilerInfo;
import android.app.WaitResult;
import android.app.usage.AppStandbyInfo;
@@ -32,16 +33,24 @@
import android.content.ComponentCallbacks2;
import android.content.ComponentName;
import android.content.Context;
+import android.content.DeviceConfigurationProto;
+import android.content.GlobalConfigurationProto;
import android.content.IIntentReceiver;
import android.content.Intent;
+import android.content.pm.ConfigurationInfo;
+import android.content.pm.FeatureInfo;
import android.content.pm.IPackageManager;
+import android.content.pm.PackageManager;
import android.content.pm.ParceledListSlice;
import android.content.pm.ResolveInfo;
+import android.content.pm.SharedLibraryInfo;
import android.content.pm.UserInfo;
import android.content.res.AssetManager;
import android.content.res.Configuration;
import android.content.res.Resources;
+import android.graphics.Point;
import android.graphics.Rect;
+import android.hardware.display.DisplayManager;
import android.os.Binder;
import android.os.Build;
import android.os.Bundle;
@@ -57,8 +66,11 @@
import android.util.ArrayMap;
import android.util.DebugUtils;
import android.util.DisplayMetrics;
+import android.util.proto.ProtoOutputStream;
+import android.view.Display;
import com.android.internal.util.HexDump;
+import com.android.internal.util.MemInfoReader;
import com.android.internal.util.Preconditions;
import java.io.BufferedReader;
@@ -75,6 +87,11 @@
import java.util.List;
import java.util.Map;
+import javax.microedition.khronos.egl.EGL10;
+import javax.microedition.khronos.egl.EGLContext;
+import javax.microedition.khronos.opengles.GL;
+import javax.microedition.khronos.opengles.GL10;
+
import static android.app.ActivityManager.RESIZE_MODE_SYSTEM;
import static android.app.ActivityManager.RESIZE_MODE_USER;
import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
@@ -1835,17 +1852,112 @@
}
}
- int runGetConfig(PrintWriter pw) throws RemoteException {
- int days = 14;
- String option = getNextOption();
- if (option != null) {
- if (!option.equals("--days")) {
- throw new IllegalArgumentException("unrecognized option " + option);
- }
+ private void writeDeviceConfig(ProtoOutputStream protoOutputStream, long fieldId,
+ PrintWriter pw, Configuration config, DisplayManager dm) {
+ Point stableSize = dm.getStableDisplaySize();
+ long token = -1;
+ if (protoOutputStream != null) {
+ token = protoOutputStream.start(fieldId);
+ protoOutputStream.write(DeviceConfigurationProto.STABLE_SCREEN_WIDTH_PX, stableSize.x);
+ protoOutputStream.write(DeviceConfigurationProto.STABLE_SCREEN_HEIGHT_PX, stableSize.y);
+ protoOutputStream.write(DeviceConfigurationProto.STABLE_DENSITY_DPI,
+ DisplayMetrics.DENSITY_DEVICE_STABLE);
+ }
+ if (pw != null) {
+ pw.print("stable-width-px: "); pw.println(stableSize.x);
+ pw.print("stable-height-px: "); pw.println(stableSize.y);
+ pw.print("stable-density-dpi: "); pw.println(DisplayMetrics.DENSITY_DEVICE_STABLE);
+ }
- days = Integer.parseInt(getNextArgRequired());
- if (days <= 0) {
- throw new IllegalArgumentException("--days must be a positive integer");
+ MemInfoReader memreader = new MemInfoReader();
+ memreader.readMemInfo();
+ KeyguardManager kgm = mInternal.mContext.getSystemService(KeyguardManager.class);
+ if (protoOutputStream != null) {
+ protoOutputStream.write(DeviceConfigurationProto.TOTAL_RAM, memreader.getTotalSize());
+ protoOutputStream.write(DeviceConfigurationProto.LOW_RAM,
+ ActivityManager.isLowRamDeviceStatic());
+ protoOutputStream.write(DeviceConfigurationProto.MAX_CORES,
+ Runtime.getRuntime().availableProcessors());
+ protoOutputStream.write(DeviceConfigurationProto.HAS_SECURE_SCREEN_LOCK,
+ kgm.isDeviceSecure());
+ }
+ if (pw != null) {
+ pw.print("total-ram: "); pw.println(memreader.getTotalSize());
+ pw.print("low-ram: "); pw.println(ActivityManager.isLowRamDeviceStatic());
+ pw.print("max-cores: "); pw.println(Runtime.getRuntime().availableProcessors());
+ pw.print("has-secure-screen-lock: "); pw.println(kgm.isDeviceSecure());
+ }
+
+ ConfigurationInfo configInfo = mInternal.getDeviceConfigurationInfo();
+ if (configInfo.reqGlEsVersion != ConfigurationInfo.GL_ES_VERSION_UNDEFINED) {
+ if (protoOutputStream != null) {
+ protoOutputStream.write(DeviceConfigurationProto.OPENGL_VERSION,
+ configInfo.reqGlEsVersion);
+ }
+ if (pw != null) {
+ pw.print("opengl-version: 0x");
+ pw.println(Integer.toHexString(configInfo.reqGlEsVersion));
+ }
+ }
+
+ /*
+ GL10 gl = ((GL10)((EGL10)EGLContext.getEGL()).eglGetCurrentContext().getGL());
+ protoOutputStream.write(DeviceConfigurationProto.OPENGL_VERSION,
+ gl.glGetString(GL10.GL_VERSION));
+ String glExtensions = gl.glGetString(GL10.GL_EXTENSIONS);
+ for (String ext : glExtensions.split(" ")) {
+ protoOutputStream.write(DeviceConfigurationProto.OPENGL_EXTENSIONS, ext);
+ }
+ */
+
+ PackageManager pm = mInternal.mContext.getPackageManager();
+ List<SharedLibraryInfo> slibs = pm.getSharedLibraries(0);
+ for (int i = 0; i < slibs.size(); i++) {
+ if (protoOutputStream != null) {
+ protoOutputStream.write(DeviceConfigurationProto.SHARED_LIBRARIES,
+ slibs.get(i).getName());
+ }
+ if (pw != null) {
+ pw.print("shared-libraries: "); pw.println(slibs.get(i).getName());
+ }
+ }
+
+ FeatureInfo[] features = pm.getSystemAvailableFeatures();
+ for (int i = 0; i < features.length; i++) {
+ if (features[i].name != null) {
+ if (protoOutputStream != null) {
+ protoOutputStream.write(DeviceConfigurationProto.FEATURES, features[i].name);
+ }
+ if (pw != null) {
+ pw.print("features: "); pw.println(features[i].name);
+ }
+ }
+ }
+
+ if (protoOutputStream != null) {
+ protoOutputStream.end(token);
+ }
+ }
+
+ int runGetConfig(PrintWriter pw) throws RemoteException {
+ int days = -1;
+ boolean asProto = false;
+ boolean inclDevice = false;
+
+ String opt;
+ while ((opt=getNextOption()) != null) {
+ if (opt.equals("--days")) {
+ days = Integer.parseInt(getNextArgRequired());
+ if (days <= 0) {
+ throw new IllegalArgumentException("--days must be a positive integer");
+ }
+ } else if (opt.equals("--proto")) {
+ asProto = true;
+ } else if (opt.equals("--device")) {
+ inclDevice = true;
+ } else {
+ getErrPrintWriter().println("Error: Unknown option: " + opt);
+ return -1;
}
}
@@ -1855,18 +1967,38 @@
return -1;
}
- pw.println("config: " + Configuration.resourceQualifierString(config));
- pw.println("abi: " + TextUtils.join(",", Build.SUPPORTED_ABIS));
+ DisplayManager dm = mInternal.mContext.getSystemService(DisplayManager.class);
+ Display display = dm.getDisplay(Display.DEFAULT_DISPLAY);
+ DisplayMetrics metrics = new DisplayMetrics();
+ display.getMetrics(metrics);
- final List<Configuration> recentConfigs = getRecentConfigurations(days);
- final int recentConfigSize = recentConfigs.size();
- if (recentConfigSize > 0) {
- pw.println("recentConfigs:");
- }
+ if (asProto) {
+ final ProtoOutputStream proto = new ProtoOutputStream(getOutFileDescriptor());
+ config.writeResConfigToProto(proto, GlobalConfigurationProto.RESOURCES, metrics);
+ if (inclDevice) {
+ writeDeviceConfig(proto, GlobalConfigurationProto.DEVICE, null, config, dm);
+ }
+ proto.flush();
- for (int i = 0; i < recentConfigSize; i++) {
- pw.println(" config: " + Configuration.resourceQualifierString(
- recentConfigs.get(i)));
+ } else {
+ pw.println("config: " + Configuration.resourceQualifierString(config, metrics));
+ pw.println("abi: " + TextUtils.join(",", Build.SUPPORTED_ABIS));
+ if (inclDevice) {
+ writeDeviceConfig(null, -1, pw, config, dm);
+ }
+
+ if (days >= 0) {
+ final List<Configuration> recentConfigs = getRecentConfigurations(days);
+ final int recentConfigSize = recentConfigs.size();
+ if (recentConfigSize > 0) {
+ pw.println("recentConfigs:");
+ for (int i = 0; i < recentConfigSize; i++) {
+ pw.println(" config: " + Configuration.resourceQualifierString(
+ recentConfigs.get(i)));
+ }
+ }
+ }
+
}
return 0;
}
@@ -2729,8 +2861,11 @@
pw.println(" Gets the process state of an app given its <UID>.");
pw.println(" attach-agent <PROCESS> <FILE>");
pw.println(" Attach an agent to the specified <PROCESS>, which may be either a process name or a PID.");
- pw.println(" get-config");
- pw.println(" Rtrieve the configuration and any recent configurations of the device.");
+ pw.println(" get-config [--days N] [--device] [--proto]");
+ pw.println(" Retrieve the configuration and any recent configurations of the device.");
+ pw.println(" --days: also return last N days of configurations that have been seen.");
+ pw.println(" --device: also output global device configuration info.");
+ pw.println(" --proto: return result as a proto; does not include --days info.");
pw.println(" supports-multiwindow");
pw.println(" Returns true if the device supports multiwindow.");
pw.println(" supports-split-screen-multi-window");
diff --git a/services/core/java/com/android/server/am/ActivityMetricsLogger.java b/services/core/java/com/android/server/am/ActivityMetricsLogger.java
index db86f1a..978e344 100644
--- a/services/core/java/com/android/server/am/ActivityMetricsLogger.java
+++ b/services/core/java/com/android/server/am/ActivityMetricsLogger.java
@@ -23,6 +23,8 @@
import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.APP_TRANSITION_WINDOWS_DRAWN_DELAY_MS;
import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_CLASS_NAME;
import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_INSTANT_APP_LAUNCH_TOKEN;
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.PACKAGE_OPTIMIZATION_COMPILATION_REASON;
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.PACKAGE_OPTIMIZATION_COMPILATION_FILTER;
import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.TYPE_TRANSITION_COLD_LAUNCH;
import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.TYPE_TRANSITION_HOT_LAUNCH;
import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.TYPE_TRANSITION_REPORTED_DRAWN_NO_BUNDLE;
@@ -35,6 +37,9 @@
import static com.android.server.am.MemoryStatUtil.readMemoryStatFromMemcg;
import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.dex.ArtManagerInternal;
+import android.content.pm.dex.PackageOptimizationInfo;
import android.metrics.LogMaker;
import android.os.Handler;
import android.os.Looper;
@@ -47,6 +52,7 @@
import com.android.internal.logging.MetricsLogger;
import com.android.internal.os.SomeArgs;
+import com.android.server.LocalServices;
import java.util.ArrayList;
@@ -68,7 +74,8 @@
private static final long INVALID_START_TIME = -1;
private static final int MSG_CHECK_VISIBILITY = 0;
- private static final int MSG_LOG_APP_START_MEMORY_STATE_CAPTURE = 1;
+ private static final int MSG_LOG_APP_TRANSITION = 1;
+ private static final int MSG_LOG_APP_START_MEMORY_STATE_CAPTURE = 2;
// Preallocated strings we are sending to tron, so we don't have to allocate a new one every
// time we log.
@@ -93,6 +100,9 @@
private final SparseArray<WindowingModeTransitionInfo> mLastWindowingModeTransitionInfo =
new SparseArray<>();
private final H mHandler;
+
+ private ArtManagerInternal mArtManagerInternal;
+
private final class H extends Handler {
public H(Looper looper) {
@@ -106,12 +116,16 @@
final SomeArgs args = (SomeArgs) msg.obj;
checkVisibility((TaskRecord) args.arg1, (ActivityRecord) args.arg2);
break;
+ case MSG_LOG_APP_TRANSITION:
+ logAppTransition(msg.arg1, msg.arg2,
+ (WindowingModeTransitionInfoSnapshot) msg.obj);
+ break;
case MSG_LOG_APP_START_MEMORY_STATE_CAPTURE:
logAppStartMemoryStateCapture((WindowingModeTransitionInfo) msg.obj);
break;
}
}
- };
+ }
private final class WindowingModeTransitionInfo {
private ActivityRecord launchedActivity;
@@ -125,6 +139,36 @@
private boolean loggedStartingWindowDrawn;
}
+ private final class WindowingModeTransitionInfoSnapshot {
+ final private ApplicationInfo applicationInfo;
+ final private String packageName;
+ final private String launchedActivityName;
+ final private String launchedActivityLaunchedFromPackage;
+ final private String launchedActivityLaunchToken;
+ final private String launchedActivityAppRecordRequiredAbi;
+ final private int reason;
+ final private int startingWindowDelayMs;
+ final private int bindApplicationDelayMs;
+ final private int windowsDrawnDelayMs;
+ final private int type;
+
+ private WindowingModeTransitionInfoSnapshot(WindowingModeTransitionInfo info) {
+ applicationInfo = info.launchedActivity.appInfo;
+ packageName = info.launchedActivity.packageName;
+ launchedActivityName = info.launchedActivity.info.name;
+ launchedActivityLaunchedFromPackage = info.launchedActivity.launchedFromPackage;
+ launchedActivityLaunchToken = info.launchedActivity.info.launchToken;
+ launchedActivityAppRecordRequiredAbi = info.launchedActivity.app == null
+ ? null
+ : info.launchedActivity.app.requiredAbi;
+ reason = info.reason;
+ startingWindowDelayMs = info.startingWindowDelayMs;
+ bindApplicationDelayMs = info.bindApplicationDelayMs;
+ windowsDrawnDelayMs = info.windowsDrawnDelayMs;
+ type = getTransitionType(info);
+ }
+ }
+
ActivityMetricsLogger(ActivityStackSupervisor supervisor, Context context, Looper looper) {
mLastLogTimeSecs = SystemClock.elapsedRealtime() / 1000;
mSupervisor = supervisor;
@@ -456,54 +500,80 @@
if (type == -1) {
return;
}
- final LogMaker builder = new LogMaker(APP_TRANSITION);
- builder.setPackageName(info.launchedActivity.packageName);
- builder.setType(type);
- builder.addTaggedData(FIELD_CLASS_NAME, info.launchedActivity.info.name);
- final boolean isInstantApp = info.launchedActivity.info.applicationInfo.isInstantApp();
- if (info.launchedActivity.launchedFromPackage != null) {
- builder.addTaggedData(APP_TRANSITION_CALLING_PACKAGE_NAME,
- info.launchedActivity.launchedFromPackage);
- }
- String launchToken = info.launchedActivity.info.launchToken;
- if (launchToken != null) {
- builder.addTaggedData(FIELD_INSTANT_APP_LAUNCH_TOKEN, launchToken);
- info.launchedActivity.info.launchToken = null;
- }
- builder.addTaggedData(APP_TRANSITION_IS_EPHEMERAL, isInstantApp ? 1 : 0);
- builder.addTaggedData(APP_TRANSITION_DEVICE_UPTIME_SECONDS,
- mCurrentTransitionDeviceUptime);
- builder.addTaggedData(APP_TRANSITION_DELAY_MS, mCurrentTransitionDelayMs);
- builder.setSubtype(info.reason);
- if (info.startingWindowDelayMs != -1) {
- builder.addTaggedData(APP_TRANSITION_STARTING_WINDOW_DELAY_MS,
- info.startingWindowDelayMs);
- }
- if (info.bindApplicationDelayMs != -1) {
- builder.addTaggedData(APP_TRANSITION_BIND_APPLICATION_DELAY_MS,
- info.bindApplicationDelayMs);
- }
- builder.addTaggedData(APP_TRANSITION_WINDOWS_DRAWN_DELAY_MS, info.windowsDrawnDelayMs);
- mMetricsLogger.write(builder);
- StatsLog.write(
- StatsLog.APP_START_CHANGED,
- info.launchedActivity.appInfo.uid,
- info.launchedActivity.packageName,
- convertAppStartTransitionType(type),
- info.launchedActivity.info.name,
- info.launchedActivity.launchedFromPackage,
- isInstantApp,
- mCurrentTransitionDeviceUptime * 1000,
- info.reason,
- mCurrentTransitionDelayMs,
- info.startingWindowDelayMs,
- info.bindApplicationDelayMs,
- info.windowsDrawnDelayMs,
- launchToken);
+
+ // Take a snapshot of the transition info before sending it to the handler for logging.
+ // This will avoid any races with other operations that modify the ActivityRecord.
+ final WindowingModeTransitionInfoSnapshot infoSnapshot =
+ new WindowingModeTransitionInfoSnapshot(info);
+ mHandler.obtainMessage(MSG_LOG_APP_TRANSITION, mCurrentTransitionDeviceUptime,
+ mCurrentTransitionDelayMs, infoSnapshot).sendToTarget();
+
+ info.launchedActivity.info.launchToken = null;
mHandler.obtainMessage(MSG_LOG_APP_START_MEMORY_STATE_CAPTURE, info).sendToTarget();
}
}
+ // This gets called on the handler without holding the activity manager lock.
+ private void logAppTransition(int currentTransitionDeviceUptime, int currentTransitionDelayMs,
+ WindowingModeTransitionInfoSnapshot info) {
+ final LogMaker builder = new LogMaker(APP_TRANSITION);
+ builder.setPackageName(info.packageName);
+ builder.setType(info.type);
+ builder.addTaggedData(FIELD_CLASS_NAME, info.launchedActivityName);
+ final boolean isInstantApp = info.applicationInfo.isInstantApp();
+ if (info.launchedActivityLaunchedFromPackage != null) {
+ builder.addTaggedData(APP_TRANSITION_CALLING_PACKAGE_NAME,
+ info.launchedActivityLaunchedFromPackage);
+ }
+ String launchToken = info.launchedActivityLaunchToken;
+ if (launchToken != null) {
+ builder.addTaggedData(FIELD_INSTANT_APP_LAUNCH_TOKEN, launchToken);
+ }
+ builder.addTaggedData(APP_TRANSITION_IS_EPHEMERAL, isInstantApp ? 1 : 0);
+ builder.addTaggedData(APP_TRANSITION_DEVICE_UPTIME_SECONDS,
+ currentTransitionDeviceUptime);
+ builder.addTaggedData(APP_TRANSITION_DELAY_MS, currentTransitionDelayMs);
+ builder.setSubtype(info.reason);
+ if (info.startingWindowDelayMs != -1) {
+ builder.addTaggedData(APP_TRANSITION_STARTING_WINDOW_DELAY_MS,
+ info.startingWindowDelayMs);
+ }
+ if (info.bindApplicationDelayMs != -1) {
+ builder.addTaggedData(APP_TRANSITION_BIND_APPLICATION_DELAY_MS,
+ info.bindApplicationDelayMs);
+ }
+ builder.addTaggedData(APP_TRANSITION_WINDOWS_DRAWN_DELAY_MS, info.windowsDrawnDelayMs);
+ final ArtManagerInternal artManagerInternal = getArtManagerInternal();
+ final PackageOptimizationInfo packageOptimizationInfo =
+ (artManagerInternal == null) || (info.launchedActivityAppRecordRequiredAbi == null)
+ ? PackageOptimizationInfo.createWithNoInfo()
+ : artManagerInternal.getPackageOptimizationInfo(
+ info.applicationInfo,
+ info.launchedActivityAppRecordRequiredAbi);
+ builder.addTaggedData(PACKAGE_OPTIMIZATION_COMPILATION_REASON,
+ packageOptimizationInfo.getCompilationReason());
+ builder.addTaggedData(PACKAGE_OPTIMIZATION_COMPILATION_FILTER,
+ packageOptimizationInfo.getCompilationFilter());
+ mMetricsLogger.write(builder);
+ StatsLog.write(
+ StatsLog.APP_START_CHANGED,
+ info.applicationInfo.uid,
+ info.packageName,
+ convertAppStartTransitionType(info.type),
+ info.launchedActivityName,
+ info.launchedActivityLaunchedFromPackage,
+ isInstantApp,
+ currentTransitionDeviceUptime * 1000,
+ info.reason,
+ currentTransitionDelayMs,
+ info.startingWindowDelayMs,
+ info.bindApplicationDelayMs,
+ info.windowsDrawnDelayMs,
+ launchToken,
+ packageOptimizationInfo.getCompilationReason(),
+ packageOptimizationInfo.getCompilationFilter());
+ }
+
private int convertAppStartTransitionType(int tronType) {
if (tronType == TYPE_TRANSITION_COLD_LAUNCH) {
return StatsLog.APP_START_CHANGED__TYPE__COLD;
@@ -592,4 +662,14 @@
launchedActivity.appInfo.uid)
: null;
}
+
+ private ArtManagerInternal getArtManagerInternal() {
+ if (mArtManagerInternal == null) {
+ // Note that this may be null.
+ // ArtManagerInternal is registered during PackageManagerService
+ // initialization which happens after ActivityManagerService.
+ mArtManagerInternal = LocalServices.getService(ArtManagerInternal.class);
+ }
+ return mArtManagerInternal;
+ }
}
diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java
index 06faeb9..8cc9273 100644
--- a/services/core/java/com/android/server/am/ActivityRecord.java
+++ b/services/core/java/com/android/server/am/ActivityRecord.java
@@ -361,7 +361,7 @@
private boolean mTurnScreenOn;
/**
- * Temp configs used in {@link #ensureActivityConfigurationLocked(int, boolean)}
+ * Temp configs used in {@link #ensureActivityConfiguration(int, boolean)}
*/
private final Configuration mTmpConfig = new Configuration();
private final Rect mTmpBounds = new Rect();
@@ -2365,13 +2365,27 @@
outBounds.offsetTo(left, 0 /* top */);
}
+ boolean ensureActivityConfiguration(int globalChanges, boolean preserveWindow) {
+ return ensureActivityConfiguration(globalChanges, preserveWindow,
+ false /* ignoreStopState */);
+ }
+
/**
- * Make sure the given activity matches the current configuration. Returns false if the activity
- * had to be destroyed. Returns true if the configuration is the same, or the activity will
- * remain running as-is for whatever reason. Ensures the HistoryRecord is updated with the
- * correct configuration and all other bookkeeping is handled.
+ * Make sure the given activity matches the current configuration. Ensures the HistoryRecord
+ * is updated with the correct configuration and all other bookkeeping is handled.
+ *
+ * @param globalChanges The changes to the global configuration.
+ * @param preserveWindow If the activity window should be preserved on screen if the activity
+ * is relaunched.
+ * @param ignoreStopState If we should try to relaunch the activity even if it is in the stopped
+ * state. This is useful for the case where we know the activity will be
+ * visible soon and we want to ensure its configuration before we make it
+ * visible.
+ * @return True if the activity was relaunched and false if it wasn't relaunched because we
+ * can't or the app handles the specific configuration that is changing.
*/
- boolean ensureActivityConfigurationLocked(int globalChanges, boolean preserveWindow) {
+ boolean ensureActivityConfiguration(int globalChanges, boolean preserveWindow,
+ boolean ignoreStopState) {
final ActivityStack stack = getStack();
if (stack.mConfigWillChange) {
if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION,
@@ -2387,8 +2401,7 @@
return true;
}
- // Skip updating configuration for activity that are stopping or stopped.
- if (mState == STOPPING || mState == STOPPED) {
+ if (!ignoreStopState && (mState == STOPPING || mState == STOPPED)) {
if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION,
"Skipping config check stopped or stopping: " + this);
return true;
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index 817b699..4987b33 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -123,7 +123,6 @@
import android.graphics.Rect;
import android.net.Uri;
import android.os.Binder;
-import android.os.Bundle;
import android.os.Debug;
import android.os.Handler;
import android.os.IBinder;
@@ -1851,7 +1850,10 @@
// First: if this is not the current activity being started, make
// sure it matches the current configuration.
if (r != starting) {
- r.ensureActivityConfigurationLocked(0 /* globalChanges */, preserveWindows);
+ // Ensure activity configuration ignoring stop state since we are
+ // becoming visible.
+ r.ensureActivityConfiguration(0 /* globalChanges */, preserveWindows,
+ true /* ignoreStopState */);
}
if (r.app == null || r.app.thread == null) {
@@ -4627,7 +4629,7 @@
(start.getTask() == task) ? activities.indexOf(start) : activities.size() - 1;
for (; activityIndex >= 0; --activityIndex) {
final ActivityRecord r = activities.get(activityIndex);
- updatedConfig |= r.ensureActivityConfigurationLocked(0 /* globalChanges */,
+ updatedConfig |= r.ensureActivityConfiguration(0 /* globalChanges */,
preserveWindow);
if (r.fullscreen) {
behindFullscreen = true;
diff --git a/services/core/java/com/android/server/am/CompatModePackages.java b/services/core/java/com/android/server/am/CompatModePackages.java
index d84f487..c6947ab 100644
--- a/services/core/java/com/android/server/am/CompatModePackages.java
+++ b/services/core/java/com/android/server/am/CompatModePackages.java
@@ -369,7 +369,7 @@
}
if (starting != null) {
- starting.ensureActivityConfigurationLocked(0 /* globalChanges */,
+ starting.ensureActivityConfiguration(0 /* globalChanges */,
false /* preserveWindow */);
// And we need to make sure at this point that all other activities
// are made visible with the correct configuration.
diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java
index a07afde..6f6e0d9 100644
--- a/services/core/java/com/android/server/am/TaskRecord.java
+++ b/services/core/java/com/android/server/am/TaskRecord.java
@@ -537,7 +537,7 @@
if (updatedConfig) {
final ActivityRecord r = topRunningActivityLocked();
if (r != null && !deferResume) {
- kept = r.ensureActivityConfigurationLocked(0 /* globalChanges */,
+ kept = r.ensureActivityConfiguration(0 /* globalChanges */,
preserveWindow);
mService.mStackSupervisor.ensureActivitiesVisibleLocked(r, 0,
!PRESERVE_WINDOWS);
diff --git a/services/core/java/com/android/server/job/JobSchedulerService.java b/services/core/java/com/android/server/job/JobSchedulerService.java
index 740866c..017fada 100644
--- a/services/core/java/com/android/server/job/JobSchedulerService.java
+++ b/services/core/java/com/android/server/job/JobSchedulerService.java
@@ -2240,7 +2240,7 @@
@Override
public void onAppIdleStateChanged(final String packageName, final @UserIdInt int userId,
- boolean idle, int bucket) {
+ boolean idle, int bucket, int reason) {
final int uid = mLocalPM.getPackageUid(packageName,
PackageManager.MATCH_UNINSTALLED_PACKAGES, userId);
if (uid < 0) {
diff --git a/services/core/java/com/android/server/job/controllers/AppIdleController.java b/services/core/java/com/android/server/job/controllers/AppIdleController.java
index bd8fe28..ed29a4c 100644
--- a/services/core/java/com/android/server/job/controllers/AppIdleController.java
+++ b/services/core/java/com/android/server/job/controllers/AppIdleController.java
@@ -186,7 +186,8 @@
private final class AppIdleStateChangeListener
extends UsageStatsManagerInternal.AppIdleStateChangeListener {
@Override
- public void onAppIdleStateChanged(String packageName, int userId, boolean idle, int bucket) {
+ public void onAppIdleStateChanged(String packageName, int userId, boolean idle, int bucket,
+ int reason) {
boolean changed = false;
synchronized (mLock) {
if (mAppIdleParoleOn) {
diff --git a/services/core/java/com/android/server/location/GnssLocationProvider.java b/services/core/java/com/android/server/location/GnssLocationProvider.java
index 0dab528..5267f54 100644
--- a/services/core/java/com/android/server/location/GnssLocationProvider.java
+++ b/services/core/java/com/android/server/location/GnssLocationProvider.java
@@ -1704,7 +1704,6 @@
mStarted = false;
mSingleShot = false;
native_stop();
- mTimeToFirstFix = 0;
mLastFixTime = 0;
// reset SV count to zero
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java
index 838aa70..d1c40cc 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsService.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java
@@ -80,6 +80,7 @@
import android.security.keystore.KeyProtection;
import android.security.keystore.UserNotAuthenticatedException;
import android.security.keystore.recovery.KeyChainProtectionParams;
+import android.security.keystore.recovery.RecoveryCertPath;
import android.security.keystore.recovery.WrappedApplicationKey;
import android.security.keystore.recovery.KeyChainSnapshot;
import android.service.gatekeeper.GateKeeperResponse;
@@ -2005,8 +2006,8 @@
mRecoverableKeyStoreManager.setRecoveryStatus(alias, status);
}
- public Map getRecoveryStatus(@Nullable String packageName) throws RemoteException {
- return mRecoverableKeyStoreManager.getRecoveryStatus(packageName);
+ public Map getRecoveryStatus() throws RemoteException {
+ return mRecoverableKeyStoreManager.getRecoveryStatus();
}
@Override
@@ -2041,6 +2042,15 @@
vaultParams, vaultChallenge, secrets);
}
+ @Override
+ public byte[] startRecoverySessionWithCertPath(@NonNull String sessionId,
+ @NonNull RecoveryCertPath verifierCertPath, @NonNull byte[] vaultParams,
+ @NonNull byte[] vaultChallenge, @NonNull List<KeyChainProtectionParams> secrets)
+ throws RemoteException {
+ return mRecoverableKeyStoreManager.startRecoverySessionWithCertPath(
+ sessionId, verifierCertPath, vaultParams, vaultChallenge, secrets);
+ }
+
public void closeSession(@NonNull String sessionId) throws RemoteException {
mRecoverableKeyStoreManager.closeSession(sessionId);
}
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 dee24c7..8efce86 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncTask.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncTask.java
@@ -289,6 +289,7 @@
.setMaxAttempts(TRUSTED_HARDWARE_MAX_ATTEMPTS)
.setCounterId(counterId)
.setTrustedHardwarePublicKey(SecureBox.encodePublicKey(publicKey))
+ .setTrustedHardwareCertPath(certPath)
.setServerParams(vaultHandle)
.setKeyChainProtectionParams(metadataList)
.setWrappedApplicationKeys(createApplicationKeyEntries(encryptedApplicationKeys))
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncUtils.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncUtils.java
index 89e2deb..a7d32ed 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncUtils.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncUtils.java
@@ -273,12 +273,16 @@
*
* @param key The bytes of the key.
* @return The key.
- * @throws NoSuchAlgorithmException if the public key algorithm is unavailable.
* @throws InvalidKeySpecException if the bytes of the key are not a valid key.
*/
- public static PublicKey deserializePublicKey(byte[] key)
- throws NoSuchAlgorithmException, InvalidKeySpecException {
- KeyFactory keyFactory = KeyFactory.getInstance(PUBLIC_KEY_FACTORY_ALGORITHM);
+ public static PublicKey deserializePublicKey(byte[] key) throws InvalidKeySpecException {
+ KeyFactory keyFactory;
+ try {
+ keyFactory = KeyFactory.getInstance(PUBLIC_KEY_FACTORY_ALGORITHM);
+ } catch (NoSuchAlgorithmException e) {
+ // Should not happen
+ throw new RuntimeException(e);
+ }
X509EncodedKeySpec publicKeySpec = new X509EncodedKeySpec(key);
return keyFactory.generatePublic(publicKeySpec);
}
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 35954a5..a462cfc 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java
@@ -34,6 +34,7 @@
import android.os.UserHandle;
import android.security.keystore.recovery.KeyChainProtectionParams;
import android.security.keystore.recovery.KeyChainSnapshot;
+import android.security.keystore.recovery.RecoveryCertPath;
import android.security.keystore.recovery.RecoveryController;
import android.security.keystore.recovery.WrappedApplicationKey;
import android.security.KeyStore;
@@ -57,7 +58,10 @@
import java.security.PublicKey;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertPath;
+import java.security.cert.Certificate;
import java.security.cert.CertificateEncodingException;
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.X509EncodedKeySpec;
import java.util.Arrays;
@@ -285,16 +289,14 @@
}
/**
- * Gets recovery status for caller or other application {@code packageName}.
- * @param packageName which recoverable keys statuses will be returned.
+ * Returns recovery statuses for all keys belonging to the calling uid.
*
- * @return {@code Map} from KeyStore alias to recovery status.
+ * @return {@link Map} from key alias to recovery status. Recovery status is one of
+ * {@link RecoveryController#RECOVERY_STATUS_SYNCED},
+ * {@link RecoveryController#RECOVERY_STATUS_SYNC_IN_PROGRESS} or
+ * {@link RecoveryController#RECOVERY_STATUS_PERMANENT_FAILURE}.
*/
- public @NonNull Map<String, Integer> getRecoveryStatus(@Nullable String packageName)
- throws RemoteException {
- // Any application should be able to check status for its own keys.
- // If caller is a recovery agent it can check statuses for other packages, but
- // only for recoverable keys it manages.
+ public @NonNull Map<String, Integer> getRecoveryStatus() throws RemoteException {
return mDatabase.getStatusForAllKeys(Binder.getCallingUid());
}
@@ -351,7 +353,7 @@
}
/**
- * Initializes recovery session.
+ * Initializes recovery session given the X509-encoded public key of the recovery service.
*
* @param sessionId A unique ID to identify the recovery session.
* @param verifierPublicKey X509-encoded public key.
@@ -359,6 +361,8 @@
* @param vaultChallenge Challenge issued by vault service.
* @param secrets Lock-screen hashes. For now only a single secret is supported.
* @return Encrypted bytes of recovery claim. This can then be issued to the vault service.
+ * @deprecated Use {@link #startRecoverySessionWithCertPath(String, RecoveryCertPath, byte[],
+ * byte[], List)} instead.
*
* @hide
*/
@@ -380,11 +384,9 @@
PublicKey publicKey;
try {
publicKey = KeySyncUtils.deserializePublicKey(verifierPublicKey);
- } catch (NoSuchAlgorithmException e) {
- // Should never happen
- throw new RuntimeException(e);
} catch (InvalidKeySpecException e) {
- throw new ServiceSpecificException(ERROR_BAD_CERTIFICATE_FORMAT, "Not a valid X509 key");
+ throw new ServiceSpecificException(ERROR_BAD_CERTIFICATE_FORMAT,
+ "Not a valid X509 key");
}
// The raw public key bytes contained in vaultParams must match the ones given in
// verifierPublicKey; otherwise, the user secret may be decrypted by a key that is not owned
@@ -417,6 +419,52 @@
}
/**
+ * Initializes recovery session given the certificate path of the recovery service.
+ *
+ * @param sessionId A unique ID to identify the recovery session.
+ * @param verifierCertPath The certificate path of the recovery service.
+ * @param vaultParams Additional params associated with vault.
+ * @param vaultChallenge Challenge issued by vault service.
+ * @param secrets Lock-screen hashes. For now only a single secret is supported.
+ * @return Encrypted bytes of recovery claim. This can then be issued to the vault service.
+ *
+ * @hide
+ */
+ public @NonNull byte[] startRecoverySessionWithCertPath(
+ @NonNull String sessionId,
+ @NonNull RecoveryCertPath verifierCertPath,
+ @NonNull byte[] vaultParams,
+ @NonNull byte[] vaultChallenge,
+ @NonNull List<KeyChainProtectionParams> secrets)
+ throws RemoteException {
+ checkRecoverKeyStorePermission();
+
+ CertPath certPath;
+ try {
+ certPath = verifierCertPath.getCertPath();
+ } catch (CertificateException e) {
+ throw new ServiceSpecificException(ERROR_BAD_CERTIFICATE_FORMAT,
+ "Failed decode the certificate path");
+ }
+
+ // TODO: Validate the cert path according to the root of trust
+
+ if (certPath.getCertificates().isEmpty()) {
+ throw new ServiceSpecificException(ERROR_BAD_CERTIFICATE_FORMAT,
+ "The given CertPath is empty");
+ }
+ byte[] verifierPublicKey = certPath.getCertificates().get(0).getPublicKey().getEncoded();
+ if (verifierPublicKey == null) {
+ Log.e(TAG, "Failed to encode verifierPublicKey");
+ throw new ServiceSpecificException(ERROR_BAD_CERTIFICATE_FORMAT,
+ "Failed to encode verifierPublicKey");
+ }
+
+ return startRecoverySession(
+ sessionId, verifierPublicKey, vaultParams, vaultChallenge, secrets);
+ }
+
+ /**
* Invoked by a recovery agent after a successful recovery claim is sent to the remote vault
* service.
*
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index f29e0bb..ab55553 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -3947,7 +3947,8 @@
extends UsageStatsManagerInternal.AppIdleStateChangeListener {
@Override
- public void onAppIdleStateChanged(String packageName, int userId, boolean idle, int bucket) {
+ public void onAppIdleStateChanged(String packageName, int userId, boolean idle, int bucket,
+ int reason) {
try {
final int uid = mContext.getPackageManager().getPackageUidAsUser(packageName,
PackageManager.MATCH_UNINSTALLED_PACKAGES, userId);
diff --git a/services/core/java/com/android/server/pm/dex/ArtManagerService.java b/services/core/java/com/android/server/pm/dex/ArtManagerService.java
index e290272..2ece2b2 100644
--- a/services/core/java/com/android/server/pm/dex/ArtManagerService.java
+++ b/services/core/java/com/android/server/pm/dex/ArtManagerService.java
@@ -19,18 +19,20 @@
import android.Manifest;
import android.annotation.UserIdInt;
import android.content.pm.ApplicationInfo;
+import android.content.pm.IPackageManager;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageParser;
import android.content.pm.dex.ArtManager;
import android.content.pm.dex.ArtManager.ProfileType;
+import android.content.pm.dex.ArtManagerInternal;
import android.content.pm.dex.DexMetadataHelper;
+import android.content.pm.dex.PackageOptimizationInfo;
import android.os.Binder;
import android.os.Build;
import android.os.Handler;
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
-import android.content.pm.IPackageManager;
import android.content.pm.dex.ISnapshotRuntimeProfileCallback;
import android.os.SystemProperties;
import android.os.UserHandle;
@@ -42,8 +44,13 @@
import com.android.internal.os.BackgroundThread;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.Preconditions;
+import com.android.server.LocalServices;
import com.android.server.pm.Installer;
import com.android.server.pm.Installer.InstallerException;
+
+import dalvik.system.DexFile;
+
+import dalvik.system.VMRuntime;
import java.io.File;
import java.io.FileNotFoundException;
import libcore.io.IoUtils;
@@ -86,6 +93,8 @@
mInstaller = installer;
mInstallLock = installLock;
mHandler = new Handler(BackgroundThread.getHandler().getLooper());
+
+ LocalServices.addService(ArtManagerInternal.class, new ArtManagerInternalImpl());
}
@Override
@@ -397,4 +406,30 @@
}
return result;
}
+
+ private class ArtManagerInternalImpl extends ArtManagerInternal {
+ @Override
+ public PackageOptimizationInfo getPackageOptimizationInfo(
+ ApplicationInfo info, String abi) {
+ String compilationReason;
+ String compilationFilter;
+ try {
+ String isa = VMRuntime.getInstructionSet(abi);
+ String[] stats = DexFile.getDexFileOptimizationStatus(info.getBaseCodePath(), isa);
+ compilationFilter = stats[0];
+ compilationReason = stats[1];
+ } catch (FileNotFoundException e) {
+ Slog.e(TAG, "Could not get optimizations status for " + info.getBaseCodePath(), e);
+ compilationFilter = "error";
+ compilationReason = "error";
+ } catch (IllegalArgumentException e) {
+ Slog.wtf(TAG, "Requested optimization status for " + info.getBaseCodePath()
+ + " due to an invalid abi " + abi, e);
+ compilationFilter = "error";
+ compilationReason = "error";
+ }
+
+ return new PackageOptimizationInfo(compilationFilter, compilationReason);
+ }
+ }
}
diff --git a/services/core/java/com/android/server/stats/StatsCompanionService.java b/services/core/java/com/android/server/stats/StatsCompanionService.java
index d6359b8..95c30d1 100644
--- a/services/core/java/com/android/server/stats/StatsCompanionService.java
+++ b/services/core/java/com/android/server/stats/StatsCompanionService.java
@@ -418,10 +418,11 @@
private void addNetworkStats(
int tag, List<StatsLogEventWrapper> ret, NetworkStats stats, boolean withFGBG) {
int size = stats.size();
+ long elapsedNanos = SystemClock.elapsedRealtimeNanos();
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);
+ StatsLogEventWrapper e = new StatsLogEventWrapper(elapsedNanos, tag, withFGBG ? 6 : 5);
e.writeInt(entry.uid);
if (withFGBG) {
e.writeInt(entry.set);
@@ -500,10 +501,11 @@
private void pullKernelWakelock(int tagId, List<StatsLogEventWrapper> pulledData) {
final KernelWakelockStats wakelockStats =
mKernelWakelockReader.readKernelWakelockStats(mTmpWakelockStats);
+ long elapsedNanos = SystemClock.elapsedRealtimeNanos();
for (Map.Entry<String, KernelWakelockStats.Entry> ent : wakelockStats.entrySet()) {
String name = ent.getKey();
KernelWakelockStats.Entry kws = ent.getValue();
- StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, 4);
+ StatsLogEventWrapper e = new StatsLogEventWrapper(elapsedNanos, tagId, 4);
e.writeString(name);
e.writeInt(kws.mCount);
e.writeInt(kws.mVersion);
@@ -576,8 +578,9 @@
private void pullBluetoothBytesTransfer(int tagId, List<StatsLogEventWrapper> pulledData) {
BluetoothActivityEnergyInfo info = pullBluetoothData();
+ long elapsedNanos = SystemClock.elapsedRealtimeNanos();
for (UidTraffic traffic : info.getUidTraffic()) {
- StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, 3);
+ StatsLogEventWrapper e = new StatsLogEventWrapper(elapsedNanos, tagId, 3);
e.writeInt(traffic.getUid());
e.writeLong(traffic.getRxBytes());
e.writeLong(traffic.getTxBytes());
@@ -605,11 +608,12 @@
}
private void pullCpuTimePerFreq(int tagId, List<StatsLogEventWrapper> pulledData) {
+ long elapsedNanos = SystemClock.elapsedRealtimeNanos();
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);
+ StatsLogEventWrapper e = new StatsLogEventWrapper(elapsedNanos, tagId, 3);
e.writeInt(cluster);
e.writeInt(speed);
e.writeLong(clusterTimeMs[speed]);
@@ -630,7 +634,7 @@
SynchronousResultReceiver wifiReceiver = new SynchronousResultReceiver("wifi");
mWifiManager.requestActivityInfo(wifiReceiver);
final WifiActivityEnergyInfo wifiInfo = awaitControllerInfo(wifiReceiver);
- StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, 6);
+ StatsLogEventWrapper e = new StatsLogEventWrapper(SystemClock.elapsedRealtimeNanos(), tagId, 6);
e.writeLong(wifiInfo.getTimeStamp());
e.writeInt(wifiInfo.getStackState());
e.writeLong(wifiInfo.getControllerTxTimeMillis());
@@ -655,7 +659,7 @@
SynchronousResultReceiver modemReceiver = new SynchronousResultReceiver("telephony");
mTelephony.requestModemActivityInfo(modemReceiver);
final ModemActivityInfo modemInfo = awaitControllerInfo(modemReceiver);
- StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, 6);
+ StatsLogEventWrapper e = new StatsLogEventWrapper(SystemClock.elapsedRealtimeNanos(), tagId, 6);
e.writeLong(modemInfo.getTimestamp());
e.writeLong(modemInfo.getSleepTimeMillis());
e.writeLong(modemInfo.getIdleTimeMillis());
@@ -672,7 +676,7 @@
private void pullBluetoothActivityInfo(int tagId, List<StatsLogEventWrapper> pulledData) {
BluetoothActivityEnergyInfo info = pullBluetoothData();
- StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, 6);
+ StatsLogEventWrapper e = new StatsLogEventWrapper(SystemClock.elapsedRealtimeNanos(), tagId, 6);
e.writeLong(info.getTimeStamp());
e.writeInt(info.getBluetoothStackState());
e.writeLong(info.getControllerTxTimeMillis());
@@ -695,13 +699,13 @@
}
private void pullSystemElapsedRealtime(int tagId, List<StatsLogEventWrapper> pulledData) {
- StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, 1);
+ StatsLogEventWrapper e = new StatsLogEventWrapper(SystemClock.elapsedRealtimeNanos(), tagId, 1);
e.writeLong(SystemClock.elapsedRealtime());
pulledData.add(e);
}
private void pullDiskSpace(int tagId, List<StatsLogEventWrapper> pulledData) {
- StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, 3);
+ StatsLogEventWrapper e = new StatsLogEventWrapper(SystemClock.elapsedRealtimeNanos(), tagId, 3);
e.writeLong(mStatFsData.getAvailableBytes());
e.writeLong(mStatFsSystem.getAvailableBytes());
e.writeLong(mStatFsTemp.getAvailableBytes());
@@ -709,7 +713,7 @@
}
private void pullSystemUpTime(int tagId, List<StatsLogEventWrapper> pulledData) {
- StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, 1);
+ StatsLogEventWrapper e = new StatsLogEventWrapper(SystemClock.elapsedRealtimeNanos(), tagId, 1);
e.writeLong(SystemClock.uptimeMillis());
pulledData.add(e);
}
@@ -718,8 +722,9 @@
List<ProcessMemoryState> processMemoryStates =
LocalServices.getService(ActivityManagerInternal.class)
.getMemoryStateForProcesses();
+ long elapsedNanos = SystemClock.elapsedRealtimeNanos();
for (ProcessMemoryState processMemoryState : processMemoryStates) {
- StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, 8 /* fields */);
+ StatsLogEventWrapper e = new StatsLogEventWrapper(elapsedNanos, tagId, 8 /* fields */);
e.writeInt(processMemoryState.uid);
e.writeString(processMemoryState.processName);
e.writeInt(processMemoryState.oomScore);
diff --git a/services/print/java/com/android/server/print/RemotePrintService.java b/services/print/java/com/android/server/print/RemotePrintService.java
index f72d8ee..d4cbe7b 100644
--- a/services/print/java/com/android/server/print/RemotePrintService.java
+++ b/services/print/java/com/android/server/print/RemotePrintService.java
@@ -18,6 +18,7 @@
import static com.android.internal.print.DumpUtils.writePrinterId;
import static com.android.internal.util.dump.DumpUtils.writeComponentName;
+import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
import android.annotation.FloatRange;
import android.annotation.NonNull;
@@ -33,8 +34,6 @@
import android.os.Handler;
import android.os.IBinder;
import android.os.IBinder.DeathRecipient;
-import android.os.Looper;
-import android.os.Message;
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
import android.os.UserHandle;
@@ -86,8 +85,6 @@
private final RemotePrintServiceClient mPrintServiceClient;
- private final Handler mHandler;
-
private IPrintService mPrintService;
private boolean mBinding;
@@ -128,7 +125,6 @@
mIntent = new Intent().setComponent(mComponentName);
mUserId = userId;
mSpooler = spooler;
- mHandler = new MyHandler(context.getMainLooper());
mPrintServiceClient = new RemotePrintServiceClient(this);
}
@@ -137,7 +133,8 @@
}
public void destroy() {
- mHandler.sendEmptyMessage(MyHandler.MSG_DESTROY);
+ Handler.getMain().sendMessage(obtainMessage(
+ RemotePrintService::handleDestroy, this));
}
private void handleDestroy() {
@@ -163,7 +160,8 @@
@Override
public void binderDied() {
- mHandler.sendEmptyMessage(MyHandler.MSG_BINDER_DIED);
+ Handler.getMain().sendMessage(obtainMessage(
+ RemotePrintService::handleBinderDied, this));
}
private void handleBinderDied() {
@@ -177,7 +175,8 @@
}
public void onAllPrintJobsHandled() {
- mHandler.sendEmptyMessage(MyHandler.MSG_ON_ALL_PRINT_JOBS_HANDLED);
+ Handler.getMain().sendMessage(obtainMessage(
+ RemotePrintService::handleOnAllPrintJobsHandled, this));
}
private void handleOnAllPrintJobsHandled() {
@@ -209,8 +208,8 @@
}
public void onRequestCancelPrintJob(PrintJobInfo printJob) {
- mHandler.obtainMessage(MyHandler.MSG_ON_REQUEST_CANCEL_PRINT_JOB,
- printJob).sendToTarget();
+ Handler.getMain().sendMessage(obtainMessage(
+ RemotePrintService::handleRequestCancelPrintJob, this, printJob));
}
private void handleRequestCancelPrintJob(final PrintJobInfo printJob) {
@@ -235,8 +234,8 @@
}
public void onPrintJobQueued(PrintJobInfo printJob) {
- mHandler.obtainMessage(MyHandler.MSG_ON_PRINT_JOB_QUEUED,
- printJob).sendToTarget();
+ Handler.getMain().sendMessage(obtainMessage(
+ RemotePrintService::handleOnPrintJobQueued, this, printJob));
}
private void handleOnPrintJobQueued(final PrintJobInfo printJob) {
@@ -262,7 +261,8 @@
}
public void createPrinterDiscoverySession() {
- mHandler.sendEmptyMessage(MyHandler.MSG_CREATE_PRINTER_DISCOVERY_SESSION);
+ Handler.getMain().sendMessage(obtainMessage(
+ RemotePrintService::handleCreatePrinterDiscoverySession, this));
}
private void handleCreatePrinterDiscoverySession() {
@@ -288,7 +288,8 @@
}
public void destroyPrinterDiscoverySession() {
- mHandler.sendEmptyMessage(MyHandler.MSG_DESTROY_PRINTER_DISCOVERY_SESSION);
+ Handler.getMain().sendMessage(obtainMessage(
+ RemotePrintService::handleDestroyPrinterDiscoverySession, this));
}
private void handleDestroyPrinterDiscoverySession() {
@@ -325,8 +326,8 @@
}
public void startPrinterDiscovery(List<PrinterId> priorityList) {
- mHandler.obtainMessage(MyHandler.MSG_START_PRINTER_DISCOVERY,
- priorityList).sendToTarget();
+ Handler.getMain().sendMessage(obtainMessage(
+ RemotePrintService::handleStartPrinterDiscovery, this, priorityList));
}
private void handleStartPrinterDiscovery(final List<PrinterId> priorityList) {
@@ -356,7 +357,8 @@
}
public void stopPrinterDiscovery() {
- mHandler.sendEmptyMessage(MyHandler.MSG_STOP_PRINTER_DISCOVERY);
+ Handler.getMain().sendMessage(obtainMessage(
+ RemotePrintService::handleStopPrinterDiscovery, this));
}
private void handleStopPrinterDiscovery() {
@@ -387,8 +389,8 @@
}
public void validatePrinters(List<PrinterId> printerIds) {
- mHandler.obtainMessage(MyHandler.MSG_VALIDATE_PRINTERS,
- printerIds).sendToTarget();
+ Handler.getMain().sendMessage(obtainMessage(
+ RemotePrintService::handleValidatePrinters, this, printerIds));
}
private void handleValidatePrinters(final List<PrinterId> printerIds) {
@@ -413,8 +415,8 @@
}
public void startPrinterStateTracking(@NonNull PrinterId printerId) {
- mHandler.obtainMessage(MyHandler.MSG_START_PRINTER_STATE_TRACKING,
- printerId).sendToTarget();
+ Handler.getMain().sendMessage(obtainMessage(
+ RemotePrintService::handleStartPrinterStateTracking, this, printerId));
}
/**
@@ -424,8 +426,8 @@
* @see android.print.PrinterInfo.Builder#setHasCustomPrinterIcon
*/
public void requestCustomPrinterIcon(@NonNull PrinterId printerId) {
- mHandler.obtainMessage(MyHandler.MSG_REQUEST_CUSTOM_PRINTER_ICON,
- printerId).sendToTarget();
+ Handler.getMain().sendMessage(obtainMessage(
+ RemotePrintService::handleRequestCustomPrinterIcon, this, printerId));
}
/**
@@ -481,8 +483,8 @@
}
public void stopPrinterStateTracking(PrinterId printerId) {
- mHandler.obtainMessage(MyHandler.MSG_STOP_PRINTER_STATE_TRACKING,
- printerId).sendToTarget();
+ Handler.getMain().sendMessage(obtainMessage(
+ RemotePrintService::handleStopPrinterStateTracking, this, printerId));
}
private void handleStopPrinterStateTracking(final PrinterId printerId) {
@@ -672,96 +674,6 @@
}
}
- private final class MyHandler extends Handler {
- public static final int MSG_CREATE_PRINTER_DISCOVERY_SESSION = 1;
- public static final int MSG_DESTROY_PRINTER_DISCOVERY_SESSION = 2;
- public static final int MSG_START_PRINTER_DISCOVERY = 3;
- public static final int MSG_STOP_PRINTER_DISCOVERY = 4;
- public static final int MSG_VALIDATE_PRINTERS = 5;
- public static final int MSG_START_PRINTER_STATE_TRACKING = 6;
- public static final int MSG_STOP_PRINTER_STATE_TRACKING = 7;
- public static final int MSG_ON_ALL_PRINT_JOBS_HANDLED = 8;
- public static final int MSG_ON_REQUEST_CANCEL_PRINT_JOB = 9;
- public static final int MSG_ON_PRINT_JOB_QUEUED = 10;
- public static final int MSG_DESTROY = 11;
- public static final int MSG_BINDER_DIED = 12;
- public static final int MSG_REQUEST_CUSTOM_PRINTER_ICON = 13;
-
- public MyHandler(Looper looper) {
- super(looper, null, false);
- }
-
- @Override
- @SuppressWarnings("unchecked")
- public void handleMessage(Message message) {
- if (mDestroyed) {
- Slog.w(LOG_TAG, "Not handling " + message + " as service for " + mComponentName
- + " is already destroyed");
- return;
- }
- switch (message.what) {
- case MSG_CREATE_PRINTER_DISCOVERY_SESSION: {
- handleCreatePrinterDiscoverySession();
- } break;
-
- case MSG_DESTROY_PRINTER_DISCOVERY_SESSION: {
- handleDestroyPrinterDiscoverySession();
- } break;
-
- case MSG_START_PRINTER_DISCOVERY: {
- List<PrinterId> priorityList = (ArrayList<PrinterId>) message.obj;
- handleStartPrinterDiscovery(priorityList);
- } break;
-
- case MSG_STOP_PRINTER_DISCOVERY: {
- handleStopPrinterDiscovery();
- } break;
-
- case MSG_VALIDATE_PRINTERS: {
- List<PrinterId> printerIds = (List<PrinterId>) message.obj;
- handleValidatePrinters(printerIds);
- } break;
-
- case MSG_START_PRINTER_STATE_TRACKING: {
- PrinterId printerId = (PrinterId) message.obj;
- handleStartPrinterStateTracking(printerId);
- } break;
-
- case MSG_STOP_PRINTER_STATE_TRACKING: {
- PrinterId printerId = (PrinterId) message.obj;
- handleStopPrinterStateTracking(printerId);
- } break;
-
- case MSG_ON_ALL_PRINT_JOBS_HANDLED: {
- handleOnAllPrintJobsHandled();
- } break;
-
- case MSG_ON_REQUEST_CANCEL_PRINT_JOB: {
- PrintJobInfo printJob = (PrintJobInfo) message.obj;
- handleRequestCancelPrintJob(printJob);
- } break;
-
- case MSG_ON_PRINT_JOB_QUEUED: {
- PrintJobInfo printJob = (PrintJobInfo) message.obj;
- handleOnPrintJobQueued(printJob);
- } break;
-
- case MSG_DESTROY: {
- handleDestroy();
- } break;
-
- case MSG_BINDER_DIED: {
- handleBinderDied();
- } break;
-
- case MSG_REQUEST_CUSTOM_PRINTER_ICON: {
- PrinterId printerId = (PrinterId) message.obj;
- handleRequestCustomPrinterIcon(printerId);
- } break;
- }
- }
- }
-
private static final class RemotePrintServiceClient extends IPrintServiceClient.Stub {
private final WeakReference<RemotePrintService> mWeakService;
diff --git a/services/print/java/com/android/server/print/UserState.java b/services/print/java/com/android/server/print/UserState.java
index 3c09cb1..62185d7 100644
--- a/services/print/java/com/android/server/print/UserState.java
+++ b/services/print/java/com/android/server/print/UserState.java
@@ -45,7 +45,6 @@
import android.os.IBinder.DeathRecipient;
import android.os.IInterface;
import android.os.Looper;
-import android.os.Message;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.os.UserHandle;
@@ -81,6 +80,7 @@
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.os.BackgroundThread;
import com.android.internal.util.dump.DualDumpOutputStream;
+import com.android.internal.util.function.pooled.PooledLambda;
import com.android.server.print.RemotePrintService.PrintServiceCallbacks;
import com.android.server.print.RemotePrintServiceRecommendationService
.RemotePrintServiceRecommendationServiceCallbacks;
@@ -93,6 +93,7 @@
import java.util.List;
import java.util.Map;
import java.util.Set;
+import java.util.function.IntSupplier;
/**
* Represents the print state for a user.
@@ -134,8 +135,6 @@
private final RemotePrintSpooler mSpooler;
- private final Handler mHandler;
-
private PrinterDiscoverySessionMediator mPrinterDiscoverySession;
private List<PrintJobStateChangeListenerRecord> mPrintJobStateChangeListenerRecords;
@@ -161,7 +160,6 @@
mUserId = userId;
mLock = lock;
mSpooler = new RemotePrintSpooler(context, userId, lowPriority, this);
- mHandler = new UserStateHandler(context.getMainLooper());
synchronized (mLock) {
readInstalledPrintServicesLocked();
@@ -172,9 +170,7 @@
// Some print services might have gotten installed before the User State came up
prunePrintServices();
- synchronized (mLock) {
- onConfigurationChangedLocked();
- }
+ onConfigurationChanged();
}
public void increasePriority() {
@@ -695,18 +691,22 @@
@Override
public void onPrintJobStateChanged(PrintJobInfo printJob) {
mPrintJobForAppCache.onPrintJobStateChanged(printJob);
- mHandler.obtainMessage(UserStateHandler.MSG_DISPATCH_PRINT_JOB_STATE_CHANGED,
- printJob.getAppId(), 0, printJob.getId()).sendToTarget();
+ Handler.getMain().sendMessage(obtainMessage(
+ UserState::handleDispatchPrintJobStateChanged,
+ this, printJob.getId(),
+ PooledLambda.obtainSupplier(printJob.getAppId()).recycleOnUse()));
}
public void onPrintServicesChanged() {
- mHandler.obtainMessage(UserStateHandler.MSG_DISPATCH_PRINT_SERVICES_CHANGED).sendToTarget();
+ Handler.getMain().sendMessage(obtainMessage(
+ UserState::handleDispatchPrintServicesChanged, this));
}
@Override
public void onPrintServiceRecommendationsUpdated(List<RecommendationInfo> recommendations) {
- mHandler.obtainMessage(UserStateHandler.MSG_DISPATCH_PRINT_SERVICES_RECOMMENDATIONS_UPDATED,
- 0, 0, recommendations).sendToTarget();
+ Handler.getMain().sendMessage(obtainMessage(
+ UserState::handleDispatchPrintServiceRecommendationsUpdated,
+ this, recommendations));
}
@Override
@@ -771,8 +771,8 @@
mActiveServices.remove(service.getComponentName());
// The service might need to be restarted if it died because of an update
- mHandler.sendMessageDelayed(
- mHandler.obtainMessage(UserStateHandler.MSG_CHECK_CONFIG_CHANGED),
+ Handler.getMain().sendMessageDelayed(obtainMessage(
+ UserState::onConfigurationChanged, this),
SERVICE_RESTART_DELAY_MILLIS);
// No session - nothing to do.
@@ -1112,24 +1112,26 @@
}
}
- private void handleDispatchPrintJobStateChanged(PrintJobId printJobId, int appId) {
+ private void handleDispatchPrintJobStateChanged(
+ PrintJobId printJobId, IntSupplier appIdSupplier) {
+ int appId = appIdSupplier.getAsInt();
final List<PrintJobStateChangeListenerRecord> records;
synchronized (mLock) {
if (mPrintJobStateChangeListenerRecords == null) {
return;
}
- records = new ArrayList<PrintJobStateChangeListenerRecord>(
- mPrintJobStateChangeListenerRecords);
+ records = new ArrayList<>(mPrintJobStateChangeListenerRecords);
}
final int recordCount = records.size();
for (int i = 0; i < recordCount; i++) {
PrintJobStateChangeListenerRecord record = records.get(i);
if (record.appId == PrintManager.APP_ID_ANY
- || record.appId == appId)
- try {
- record.listener.onPrintJobStateChanged(printJobId);
- } catch (RemoteException re) {
- Log.e(LOG_TAG, "Error notifying for print job state change", re);
+ || record.appId == appId) {
+ try {
+ record.listener.onPrintJobStateChanged(printJobId);
+ } catch (RemoteException re) {
+ Log.e(LOG_TAG, "Error notifying for print job state change", re);
+ }
}
}
}
@@ -1177,38 +1179,9 @@
}
}
- private final class UserStateHandler extends Handler {
- public static final int MSG_DISPATCH_PRINT_JOB_STATE_CHANGED = 1;
- public static final int MSG_DISPATCH_PRINT_SERVICES_CHANGED = 2;
- public static final int MSG_DISPATCH_PRINT_SERVICES_RECOMMENDATIONS_UPDATED = 3;
- public static final int MSG_CHECK_CONFIG_CHANGED = 4;
-
- public UserStateHandler(Looper looper) {
- super(looper, null, false);
- }
-
- @Override
- public void handleMessage(Message message) {
- switch (message.what) {
- case MSG_DISPATCH_PRINT_JOB_STATE_CHANGED:
- PrintJobId printJobId = (PrintJobId) message.obj;
- final int appId = message.arg1;
- handleDispatchPrintJobStateChanged(printJobId, appId);
- break;
- case MSG_DISPATCH_PRINT_SERVICES_CHANGED:
- handleDispatchPrintServicesChanged();
- break;
- case MSG_DISPATCH_PRINT_SERVICES_RECOMMENDATIONS_UPDATED:
- handleDispatchPrintServiceRecommendationsUpdated(
- (List<RecommendationInfo>) message.obj);
- break;
- case MSG_CHECK_CONFIG_CHANGED:
- synchronized (mLock) {
- onConfigurationChangedLocked();
- }
- default:
- // not reached
- }
+ private void onConfigurationChanged() {
+ synchronized (mLock) {
+ onConfigurationChangedLocked();
}
}
diff --git a/services/tests/servicestests/src/com/android/server/AppStateTrackerTest.java b/services/tests/servicestests/src/com/android/server/AppStateTrackerTest.java
index 90db2a3..796d364 100644
--- a/services/tests/servicestests/src/com/android/server/AppStateTrackerTest.java
+++ b/services/tests/servicestests/src/com/android/server/AppStateTrackerTest.java
@@ -15,6 +15,9 @@
*/
package com.android.server;
+import static android.app.usage.UsageStatsManager.REASON_MAIN_DEFAULT;
+import static android.app.usage.UsageStatsManager.REASON_MAIN_USAGE;
+
import static com.android.server.AppStateTracker.TARGET_OP;
import static org.junit.Assert.assertEquals;
@@ -612,7 +615,7 @@
// Exempt package 2 on user-10.
mAppIdleStateChangeListener.onAppIdleStateChanged(PACKAGE_2, /*user=*/ 10, false,
- UsageStatsManager.STANDBY_BUCKET_EXEMPTED);
+ UsageStatsManager.STANDBY_BUCKET_EXEMPTED, REASON_MAIN_DEFAULT);
areRestricted(instance, UID_1, PACKAGE_1, JOBS_AND_ALARMS);
areRestricted(instance, UID_2, PACKAGE_2, JOBS_AND_ALARMS);
@@ -624,7 +627,7 @@
// Exempt package 1 on user-0.
mAppIdleStateChangeListener.onAppIdleStateChanged(PACKAGE_1, /*user=*/ 0, false,
- UsageStatsManager.STANDBY_BUCKET_EXEMPTED);
+ UsageStatsManager.STANDBY_BUCKET_EXEMPTED, REASON_MAIN_DEFAULT);
areRestricted(instance, UID_1, PACKAGE_1, NONE);
areRestricted(instance, UID_2, PACKAGE_2, JOBS_AND_ALARMS);
@@ -632,7 +635,7 @@
// Unexempt package 2 on user-10.
mAppIdleStateChangeListener.onAppIdleStateChanged(PACKAGE_2, /*user=*/ 10, false,
- UsageStatsManager.STANDBY_BUCKET_ACTIVE);
+ UsageStatsManager.STANDBY_BUCKET_ACTIVE, REASON_MAIN_USAGE);
areRestricted(instance, UID_1, PACKAGE_1, NONE);
areRestricted(instance, UID_2, PACKAGE_2, JOBS_AND_ALARMS);
@@ -644,9 +647,9 @@
mPowerSaveObserver.accept(getPowerSaveState());
mAppIdleStateChangeListener.onAppIdleStateChanged(PACKAGE_1, /*user=*/ 0, false,
- UsageStatsManager.STANDBY_BUCKET_EXEMPTED);
+ UsageStatsManager.STANDBY_BUCKET_EXEMPTED, REASON_MAIN_DEFAULT);
mAppIdleStateChangeListener.onAppIdleStateChanged(PACKAGE_2, /*user=*/ 0, false,
- UsageStatsManager.STANDBY_BUCKET_EXEMPTED);
+ UsageStatsManager.STANDBY_BUCKET_EXEMPTED, REASON_MAIN_DEFAULT);
setAppOps(UID_1, PACKAGE_1, true);
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityRecordTests.java b/services/tests/servicestests/src/com/android/server/am/ActivityRecordTests.java
index 5161114..8b21059 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityRecordTests.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityRecordTests.java
@@ -161,7 +161,7 @@
when(mService.mWindowManager.getNavBarPosition()).thenReturn(navBarPosition);
mTask.getConfiguration().windowConfiguration.setAppBounds(taskBounds);
mActivity.info.maxAspectRatio = aspectRatio;
- mActivity.ensureActivityConfigurationLocked(
+ mActivity.ensureActivityConfiguration(
0 /* globalChanges */, false /* preserveWindow */);
assertEquals(expectedActivityBounds, mActivity.getBounds());
}
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 3c35c5b..b67659d 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
@@ -45,6 +45,7 @@
import android.security.keystore.KeyProperties;
import android.security.keystore.recovery.KeyDerivationParams;
import android.security.keystore.recovery.KeyChainProtectionParams;
+import android.security.keystore.recovery.RecoveryCertPath;
import android.security.keystore.recovery.WrappedApplicationKey;
import android.support.test.filters.SmallTest;
import android.support.test.InstrumentationRegistry;
@@ -67,6 +68,10 @@
import java.io.File;
import java.nio.charset.StandardCharsets;
+import java.security.cert.CertPath;
+import java.security.cert.CertificateFactory;
+import java.security.cert.X509Certificate;
+import java.util.ArrayList;
import java.util.concurrent.Executors;
import java.util.Map;
import java.util.Random;
@@ -335,6 +340,46 @@
}
@Test
+ public void startRecoverySessionWithCertPath_storesTheSessionInfo() throws Exception {
+ mRecoverableKeyStoreManager.startRecoverySessionWithCertPath(
+ TEST_SESSION_ID,
+ RecoveryCertPath.createRecoveryCertPath(TestData.CERT_PATH_1),
+ TEST_VAULT_PARAMS,
+ TEST_VAULT_CHALLENGE,
+ ImmutableList.of(
+ new KeyChainProtectionParams(
+ TYPE_LOCKSCREEN,
+ UI_FORMAT_PASSWORD,
+ KeyDerivationParams.createSha256Params(TEST_SALT),
+ TEST_SECRET)));
+
+ assertEquals(1, mRecoverySessionStorage.size());
+ RecoverySessionStorage.Entry entry =
+ mRecoverySessionStorage.get(Binder.getCallingUid(), TEST_SESSION_ID);
+ assertArrayEquals(TEST_SECRET, entry.getLskfHash());
+ assertEquals(KEY_CLAIMANT_LENGTH_BYTES, entry.getKeyClaimant().length);
+ }
+
+ @Test
+ public void startRecoverySessionWithCertPath_checksPermissionFirst() throws Exception {
+ mRecoverableKeyStoreManager.startRecoverySessionWithCertPath(
+ TEST_SESSION_ID,
+ RecoveryCertPath.createRecoveryCertPath(TestData.CERT_PATH_1),
+ TEST_VAULT_PARAMS,
+ TEST_VAULT_CHALLENGE,
+ ImmutableList.of(
+ new KeyChainProtectionParams(
+ TYPE_LOCKSCREEN,
+ UI_FORMAT_PASSWORD,
+ KeyDerivationParams.createSha256Params(TEST_SALT),
+ TEST_SECRET)));
+
+ verify(mMockContext, times(2))
+ .enforceCallingOrSelfPermission(
+ eq(Manifest.permission.RECOVER_KEYSTORE), any());
+ }
+
+ @Test
public void startRecoverySession_storesTheSessionInfo() throws Exception {
mRecoverableKeyStoreManager.startRecoverySession(
TEST_SESSION_ID,
@@ -432,6 +477,66 @@
}
@Test
+ public void startRecoverySessionWithCertPath_throwsIfBadNumberOfSecrets() throws Exception {
+ try {
+ mRecoverableKeyStoreManager.startRecoverySessionWithCertPath(
+ TEST_SESSION_ID,
+ RecoveryCertPath.createRecoveryCertPath(TestData.CERT_PATH_1),
+ TEST_VAULT_PARAMS,
+ TEST_VAULT_CHALLENGE,
+ ImmutableList.of());
+ fail("should have thrown");
+ } catch (UnsupportedOperationException e) {
+ assertThat(e.getMessage()).startsWith(
+ "Only a single KeyChainProtectionParams is supported");
+ }
+ }
+
+ @Test
+ public void startRecoverySessionWithCertPath_throwsIfPublicKeysMismatch() throws Exception {
+ byte[] vaultParams = TEST_VAULT_PARAMS.clone();
+ vaultParams[1] ^= (byte) 1; // Flip 1 bit
+ try {
+ mRecoverableKeyStoreManager.startRecoverySessionWithCertPath(
+ TEST_SESSION_ID,
+ RecoveryCertPath.createRecoveryCertPath(TestData.CERT_PATH_1),
+ vaultParams,
+ 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) {
+ assertThat(e.getMessage()).contains("do not match");
+ }
+ }
+
+ @Test
+ public void startRecoverySessionWithCertPath_throwsIfEmptyCertPath() throws Exception {
+ CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
+ CertPath emptyCertPath = certFactory.generateCertPath(new ArrayList<X509Certificate>());
+ try {
+ mRecoverableKeyStoreManager.startRecoverySessionWithCertPath(
+ TEST_SESSION_ID,
+ RecoveryCertPath.createRecoveryCertPath(emptyCertPath),
+ 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) {
+ assertThat(e.getMessage()).contains("CertPath is empty");
+ }
+ }
+
+ @Test
public void recoverKeys_throwsIfNoSessionIsPresent() throws Exception {
try {
mRecoverableKeyStoreManager.recoverKeys(
@@ -648,12 +753,12 @@
WrappedKey wrappedKey = new WrappedKey(NONCE, KEY_MATERIAL, GENERATION_ID, status);
mRecoverableKeyStoreDb.insertKey(userId, uid, alias, wrappedKey);
Map<String, Integer> statuses =
- mRecoverableKeyStoreManager.getRecoveryStatus(/*packageName=*/ null);
+ mRecoverableKeyStoreManager.getRecoveryStatus();
assertThat(statuses).hasSize(1);
assertThat(statuses).containsEntry(alias, status);
mRecoverableKeyStoreManager.setRecoveryStatus(alias, status2);
- statuses = mRecoverableKeyStoreManager.getRecoveryStatus(/*packageName=*/ null);
+ statuses = mRecoverableKeyStoreManager.getRecoveryStatus();
assertThat(statuses).hasSize(1);
assertThat(statuses).containsEntry(alias, status2); // updated
}
diff --git a/services/tests/servicestests/src/com/android/server/usage/AppIdleHistoryTests.java b/services/tests/servicestests/src/com/android/server/usage/AppIdleHistoryTests.java
index 7b06648..36504ac 100644
--- a/services/tests/servicestests/src/com/android/server/usage/AppIdleHistoryTests.java
+++ b/services/tests/servicestests/src/com/android/server/usage/AppIdleHistoryTests.java
@@ -16,10 +16,13 @@
package com.android.server.usage;
-import static android.app.usage.UsageStatsManager.REASON_TIMEOUT;
+import static android.app.usage.UsageStatsManager.REASON_MAIN_TIMEOUT;
+import static android.app.usage.UsageStatsManager.REASON_MAIN_USAGE;
+import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_MOVE_TO_FOREGROUND;
import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_ACTIVE;
import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_FREQUENT;
import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_RARE;
+import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_WORKING_SET;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -92,18 +95,18 @@
AppIdleHistory aih = new AppIdleHistory(mStorageDir, 1000);
aih.setAppStandbyBucket(PACKAGE_1, USER_ID, 1000, STANDBY_BUCKET_ACTIVE,
- UsageStatsManager.REASON_USAGE);
+ REASON_MAIN_USAGE);
// ACTIVE means not idle
assertFalse(aih.isIdle(PACKAGE_1, USER_ID, 2000));
aih.setAppStandbyBucket(PACKAGE_2, USER_ID, 2000, STANDBY_BUCKET_ACTIVE,
- UsageStatsManager.REASON_USAGE);
+ REASON_MAIN_USAGE);
aih.setAppStandbyBucket(PACKAGE_1, USER_ID, 3000, STANDBY_BUCKET_RARE,
- REASON_TIMEOUT);
+ REASON_MAIN_TIMEOUT);
assertEquals(aih.getAppStandbyBucket(PACKAGE_1, USER_ID, 3000), STANDBY_BUCKET_RARE);
assertEquals(aih.getAppStandbyBucket(PACKAGE_2, USER_ID, 3000), STANDBY_BUCKET_ACTIVE);
- assertEquals(aih.getAppStandbyReason(PACKAGE_1, USER_ID, 3000), REASON_TIMEOUT);
+ assertEquals(aih.getAppStandbyReason(PACKAGE_1, USER_ID, 3000), REASON_MAIN_TIMEOUT);
// RARE is considered idle
assertTrue(aih.isIdle(PACKAGE_1, USER_ID, 3000));
@@ -115,7 +118,7 @@
aih = new AppIdleHistory(mStorageDir, 4000);
assertEquals(aih.getAppStandbyBucket(PACKAGE_1, USER_ID, 5000), STANDBY_BUCKET_RARE);
assertEquals(aih.getAppStandbyBucket(PACKAGE_2, USER_ID, 5000), STANDBY_BUCKET_ACTIVE);
- assertEquals(aih.getAppStandbyReason(PACKAGE_1, USER_ID, 5000), REASON_TIMEOUT);
+ assertEquals(aih.getAppStandbyReason(PACKAGE_1, USER_ID, 5000), REASON_MAIN_TIMEOUT);
assertTrue(aih.shouldInformListeners(PACKAGE_1, USER_ID, 5000, STANDBY_BUCKET_RARE));
assertFalse(aih.shouldInformListeners(PACKAGE_1, USER_ID, 5000, STANDBY_BUCKET_RARE));
@@ -133,4 +136,18 @@
assertEquals(1000, aih.getTimeSinceLastJobRun(PACKAGE_2, USER_ID, 7000));
assertEquals(5000, aih.getTimeSinceLastJobRun(PACKAGE_1, USER_ID, 7000));
}
+
+ public void testReason() throws Exception {
+ AppIdleHistory aih = new AppIdleHistory(mStorageDir, 1000);
+ aih.reportUsage(PACKAGE_1, USER_ID, STANDBY_BUCKET_ACTIVE,
+ REASON_SUB_USAGE_MOVE_TO_FOREGROUND, 2000, 0);
+ assertEquals(REASON_MAIN_USAGE | REASON_SUB_USAGE_MOVE_TO_FOREGROUND,
+ aih.getAppStandbyReason(PACKAGE_1, USER_ID, 3000));
+ aih.setAppStandbyBucket(PACKAGE_1, USER_ID, 4000, STANDBY_BUCKET_WORKING_SET,
+ REASON_MAIN_TIMEOUT);
+ aih.writeAppIdleTimes(USER_ID);
+
+ aih = new AppIdleHistory(mStorageDir, 5000);
+ assertEquals(REASON_MAIN_TIMEOUT, aih.getAppStandbyReason(PACKAGE_1, USER_ID, 5000));
+ }
}
\ No newline at end of file
diff --git a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
index cbbdca6..edf1f74 100644
--- a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
+++ b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
@@ -18,11 +18,11 @@
import static android.app.usage.UsageEvents.Event.NOTIFICATION_SEEN;
import static android.app.usage.UsageEvents.Event.USER_INTERACTION;
-import static android.app.usage.UsageStatsManager.REASON_DEFAULT;
-import static android.app.usage.UsageStatsManager.REASON_FORCED;
-import static android.app.usage.UsageStatsManager.REASON_PREDICTED;
-import static android.app.usage.UsageStatsManager.REASON_TIMEOUT;
-import static android.app.usage.UsageStatsManager.REASON_USAGE;
+import static android.app.usage.UsageStatsManager.REASON_MAIN_DEFAULT;
+import static android.app.usage.UsageStatsManager.REASON_MAIN_FORCED;
+import static android.app.usage.UsageStatsManager.REASON_MAIN_PREDICTED;
+import static android.app.usage.UsageStatsManager.REASON_MAIN_TIMEOUT;
+import static android.app.usage.UsageStatsManager.REASON_MAIN_USAGE;
import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_ACTIVE;
import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_EXEMPTED;
import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_FREQUENT;
@@ -410,11 +410,11 @@
setChargingState(mController, false);
// Set it to timeout or usage, so that prediction can override it
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RARE,
- REASON_TIMEOUT, 1 * HOUR_MS);
+ REASON_MAIN_TIMEOUT, 1 * HOUR_MS);
assertEquals(STANDBY_BUCKET_RARE, getStandbyBucket(mController));
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_ACTIVE,
- REASON_PREDICTED + ":CTS", 1 * HOUR_MS);
+ REASON_MAIN_PREDICTED, 1 * HOUR_MS);
assertEquals(STANDBY_BUCKET_ACTIVE, getStandbyBucket(mController));
// Fast forward 12 hours
@@ -440,28 +440,28 @@
setChargingState(mController, false);
// Can force to NEVER
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_NEVER,
- REASON_FORCED, 1 * HOUR_MS);
+ REASON_MAIN_FORCED, 1 * HOUR_MS);
assertEquals(STANDBY_BUCKET_NEVER, getStandbyBucket(mController));
// Prediction can't override FORCED reason
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_FREQUENT,
- REASON_FORCED, 1 * HOUR_MS);
+ REASON_MAIN_FORCED, 1 * HOUR_MS);
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_WORKING_SET,
- REASON_PREDICTED, 1 * HOUR_MS);
+ REASON_MAIN_PREDICTED, 1 * HOUR_MS);
assertEquals(STANDBY_BUCKET_FREQUENT, getStandbyBucket(mController));
// Prediction can't override NEVER
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_NEVER,
- REASON_DEFAULT, 2 * HOUR_MS);
+ REASON_MAIN_DEFAULT, 2 * HOUR_MS);
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_ACTIVE,
- REASON_PREDICTED, 2 * HOUR_MS);
+ REASON_MAIN_PREDICTED, 2 * HOUR_MS);
assertEquals(STANDBY_BUCKET_NEVER, getStandbyBucket(mController));
// Prediction can't set to NEVER
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_ACTIVE,
- REASON_USAGE, 2 * HOUR_MS);
+ REASON_MAIN_USAGE, 2 * HOUR_MS);
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_NEVER,
- REASON_PREDICTED, 2 * HOUR_MS);
+ REASON_MAIN_PREDICTED, 2 * HOUR_MS);
assertEquals(STANDBY_BUCKET_ACTIVE, getStandbyBucket(mController));
}
@@ -474,7 +474,7 @@
mInjector.mElapsedRealtime = 2000;
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_FREQUENT,
- REASON_PREDICTED, mInjector.mElapsedRealtime);
+ REASON_MAIN_PREDICTED, mInjector.mElapsedRealtime);
assertBucket(STANDBY_BUCKET_ACTIVE);
// bucketing works after timeout
@@ -483,7 +483,7 @@
assertBucket(STANDBY_BUCKET_WORKING_SET);
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_FREQUENT,
- REASON_PREDICTED, mInjector.mElapsedRealtime);
+ REASON_MAIN_PREDICTED, mInjector.mElapsedRealtime);
assertBucket(STANDBY_BUCKET_FREQUENT);
}
@@ -498,15 +498,15 @@
assertBucket(STANDBY_BUCKET_ACTIVE);
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_WORKING_SET,
- REASON_PREDICTED, 1000);
+ REASON_MAIN_PREDICTED, 1000);
assertBucket(STANDBY_BUCKET_ACTIVE);
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_FREQUENT,
- REASON_PREDICTED, 2000 + mController.mStrongUsageTimeoutMillis);
+ REASON_MAIN_PREDICTED, 2000 + mController.mStrongUsageTimeoutMillis);
assertBucket(STANDBY_BUCKET_WORKING_SET);
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_FREQUENT,
- REASON_PREDICTED, 2000 + mController.mNotificationSeenTimeoutMillis);
+ REASON_MAIN_PREDICTED, 2000 + mController.mNotificationSeenTimeoutMillis);
assertBucket(STANDBY_BUCKET_FREQUENT);
}
@@ -527,18 +527,18 @@
// Still in ACTIVE after first USER_INTERACTION times out
mInjector.mElapsedRealtime = mController.mStrongUsageTimeoutMillis + 1000;
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_FREQUENT,
- REASON_PREDICTED, mInjector.mElapsedRealtime);
+ REASON_MAIN_PREDICTED, mInjector.mElapsedRealtime);
assertBucket(STANDBY_BUCKET_ACTIVE);
// Both timed out, so NOTIFICATION_SEEN timeout should be effective
mInjector.mElapsedRealtime = mController.mStrongUsageTimeoutMillis * 2 + 2000;
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_FREQUENT,
- REASON_PREDICTED, mInjector.mElapsedRealtime);
+ REASON_MAIN_PREDICTED, mInjector.mElapsedRealtime);
assertBucket(STANDBY_BUCKET_WORKING_SET);
mInjector.mElapsedRealtime = mController.mNotificationSeenTimeoutMillis + 2000;
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RARE,
- REASON_PREDICTED, mInjector.mElapsedRealtime);
+ REASON_MAIN_PREDICTED, mInjector.mElapsedRealtime);
assertBucket(STANDBY_BUCKET_RARE);
}
@@ -561,7 +561,7 @@
// Predict to ACTIVE
mInjector.mElapsedRealtime += 1000;
mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_ACTIVE,
- REASON_PREDICTED, mInjector.mElapsedRealtime);
+ REASON_MAIN_PREDICTED, mInjector.mElapsedRealtime);
assertBucket(STANDBY_BUCKET_ACTIVE);
// CheckIdleStates should not change the prediction
diff --git a/services/usage/java/com/android/server/usage/AppIdleHistory.java b/services/usage/java/com/android/server/usage/AppIdleHistory.java
index 8e5a418..fd28b65 100644
--- a/services/usage/java/com/android/server/usage/AppIdleHistory.java
+++ b/services/usage/java/com/android/server/usage/AppIdleHistory.java
@@ -16,10 +16,12 @@
package com.android.server.usage;
-import static android.app.usage.UsageStatsManager.REASON_DEFAULT;
-import static android.app.usage.UsageStatsManager.REASON_FORCED;
-import static android.app.usage.UsageStatsManager.REASON_PREDICTED;
-import static android.app.usage.UsageStatsManager.REASON_USAGE;
+import static android.app.usage.UsageStatsManager.REASON_MAIN_DEFAULT;
+import static android.app.usage.UsageStatsManager.REASON_MAIN_FORCED;
+import static android.app.usage.UsageStatsManager.REASON_MAIN_MASK;
+import static android.app.usage.UsageStatsManager.REASON_MAIN_PREDICTED;
+import static android.app.usage.UsageStatsManager.REASON_MAIN_USAGE;
+import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_USER_INTERACTION;
import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_ACTIVE;
import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_NEVER;
import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_RARE;
@@ -112,8 +114,10 @@
// Standby bucket
@UsageStatsManager.StandbyBuckets
int currentBucket;
- // Reason for setting the standby bucket. TODO: Switch to int.
- String bucketingReason;
+ // Reason for setting the standby bucket. The value here is a combination of
+ // one of UsageStatsManager.REASON_MAIN_* and one (or none) of
+ // UsageStatsManager.REASON_SUB_*. Also see REASON_MAIN_MASK and REASON_SUB_MASK.
+ int bucketingReason;
// In-memory only, last bucket for which the listeners were informed
int lastInformedBucket;
// The last time a job was run for this app, using elapsed timebase
@@ -212,13 +216,14 @@
* @param appUsageHistory the usage record for the app being updated
* @param packageName name of the app being updated, for logging purposes
* @param newBucket the bucket to set the app to
+ * @param usageReason the sub-reason for usage, one of REASON_SUB_USAGE_*
* @param elapsedRealtime mark as used time if non-zero
* @param timeout set the timeout of the specified bucket, if non-zero. Can only be used
* with bucket values of ACTIVE and WORKING_SET.
* @return
*/
public AppUsageHistory reportUsage(AppUsageHistory appUsageHistory, String packageName,
- int newBucket, long elapsedRealtime, long timeout) {
+ int newBucket, int usageReason, long elapsedRealtime, long timeout) {
// Set the timeout if applicable
if (timeout > elapsedRealtime) {
// Convert to elapsed timebase
@@ -246,10 +251,10 @@
if (DEBUG) {
Slog.d(TAG, "Moved " + packageName + " to bucket=" + appUsageHistory
.currentBucket
- + ", reason=" + appUsageHistory.bucketingReason);
+ + ", reason=0x0" + Integer.toHexString(appUsageHistory.bucketingReason));
}
}
- appUsageHistory.bucketingReason = REASON_USAGE;
+ appUsageHistory.bucketingReason = REASON_MAIN_USAGE | usageReason;
return appUsageHistory;
}
@@ -262,16 +267,17 @@
* @param packageName
* @param userId
* @param newBucket the bucket to set the app to
- * @param elapsedRealtime mark as used time if non-zero
+ * @param usageReason sub reason for usage
+ * @param nowElapsed mark as used time if non-zero
* @param timeout set the timeout of the specified bucket, if non-zero. Can only be used
* with bucket values of ACTIVE and WORKING_SET.
* @return
*/
public AppUsageHistory reportUsage(String packageName, int userId, int newBucket,
- long nowElapsed, long timeout) {
+ int usageReason, long nowElapsed, long timeout) {
ArrayMap<String, AppUsageHistory> userHistory = getUserHistory(userId);
AppUsageHistory history = getPackageHistory(userHistory, packageName, nowElapsed, true);
- return reportUsage(history, packageName, newBucket, nowElapsed, timeout);
+ return reportUsage(history, packageName, newBucket, usageReason, nowElapsed, timeout);
}
private ArrayMap<String, AppUsageHistory> getUserHistory(int userId) {
@@ -293,7 +299,7 @@
appUsageHistory.lastUsedScreenTime = getScreenOnTime(elapsedRealtime);
appUsageHistory.lastPredictedTime = getElapsedTime(0);
appUsageHistory.currentBucket = STANDBY_BUCKET_NEVER;
- appUsageHistory.bucketingReason = REASON_DEFAULT;
+ appUsageHistory.bucketingReason = REASON_MAIN_DEFAULT;
appUsageHistory.lastInformedBucket = -1;
appUsageHistory.lastJobRunTime = Long.MIN_VALUE; // long long time ago
userHistory.put(packageName, appUsageHistory);
@@ -328,18 +334,18 @@
}
public void setAppStandbyBucket(String packageName, int userId, long elapsedRealtime,
- int bucket, String reason) {
+ int bucket, int reason) {
ArrayMap<String, AppUsageHistory> userHistory = getUserHistory(userId);
AppUsageHistory appUsageHistory =
getPackageHistory(userHistory, packageName, elapsedRealtime, true);
appUsageHistory.currentBucket = bucket;
appUsageHistory.bucketingReason = reason;
- if (reason.startsWith(REASON_PREDICTED)) {
+ if ((reason & REASON_MAIN_MASK) == REASON_MAIN_PREDICTED) {
appUsageHistory.lastPredictedTime = getElapsedTime(elapsedRealtime);
}
if (DEBUG) {
Slog.d(TAG, "Moved " + packageName + " to bucket=" + appUsageHistory.currentBucket
- + ", reason=" + appUsageHistory.bucketingReason);
+ + ", reason=0x0" + Integer.toHexString(appUsageHistory.bucketingReason));
}
}
@@ -393,11 +399,11 @@
return buckets;
}
- public String getAppStandbyReason(String packageName, int userId, long elapsedRealtime) {
+ public int getAppStandbyReason(String packageName, int userId, long elapsedRealtime) {
ArrayMap<String, AppUsageHistory> userHistory = getUserHistory(userId);
AppUsageHistory appUsageHistory =
getPackageHistory(userHistory, packageName, elapsedRealtime, false);
- return appUsageHistory != null ? appUsageHistory.bucketingReason : null;
+ return appUsageHistory != null ? appUsageHistory.bucketingReason : 0;
}
public long getElapsedTime(long elapsedRealtime) {
@@ -411,11 +417,11 @@
elapsedRealtime, true);
if (idle) {
appUsageHistory.currentBucket = STANDBY_BUCKET_RARE;
- appUsageHistory.bucketingReason = REASON_FORCED;
+ appUsageHistory.bucketingReason = REASON_MAIN_FORCED;
} else {
appUsageHistory.currentBucket = STANDBY_BUCKET_ACTIVE;
// This is to pretend that the app was just used, don't freeze the state anymore.
- appUsageHistory.bucketingReason = REASON_USAGE;
+ appUsageHistory.bucketingReason = REASON_MAIN_USAGE | REASON_SUB_USAGE_USER_INTERACTION;
}
return appUsageHistory.currentBucket;
}
@@ -516,7 +522,7 @@
appUsageHistory.currentBucket = currentBucketString == null
? STANDBY_BUCKET_ACTIVE
: Integer.parseInt(currentBucketString);
- appUsageHistory.bucketingReason =
+ String bucketingReason =
parser.getAttributeValue(null, ATTR_BUCKETING_REASON);
appUsageHistory.lastJobRunTime = getLongValue(parser,
ATTR_LAST_RUN_JOB_TIME, Long.MIN_VALUE);
@@ -524,8 +530,13 @@
ATTR_BUCKET_ACTIVE_TIMEOUT_TIME, 0L);
appUsageHistory.bucketWorkingSetTimeoutTime = getLongValue(parser,
ATTR_BUCKET_WORKING_SET_TIMEOUT_TIME, 0L);
- if (appUsageHistory.bucketingReason == null) {
- appUsageHistory.bucketingReason = REASON_DEFAULT;
+ appUsageHistory.bucketingReason = REASON_MAIN_DEFAULT;
+ if (bucketingReason != null) {
+ try {
+ appUsageHistory.bucketingReason =
+ Integer.parseInt(bucketingReason, 16);
+ } catch (NumberFormatException nfe) {
+ }
}
appUsageHistory.lastInformedBucket = -1;
userHistory.put(packageName, appUsageHistory);
@@ -574,7 +585,8 @@
Long.toString(history.lastPredictedTime));
xml.attribute(null, ATTR_CURRENT_BUCKET,
Integer.toString(history.currentBucket));
- xml.attribute(null, ATTR_BUCKETING_REASON, history.bucketingReason);
+ xml.attribute(null, ATTR_BUCKETING_REASON,
+ Integer.toHexString(history.bucketingReason));
if (history.bucketActiveTimeoutTime > 0) {
xml.attribute(null, ATTR_BUCKET_ACTIVE_TIMEOUT_TIME, Long.toString(history
.bucketActiveTimeoutTime));
@@ -600,7 +612,7 @@
}
public void dump(IndentingPrintWriter idpw, int userId, String pkg) {
- idpw.println("Package idle stats:");
+ idpw.println("App Standby States:");
idpw.increaseIndent();
ArrayMap<String, AppUsageHistory> userHistory = mIdleHistory.get(userId);
final long elapsedRealtime = SystemClock.elapsedRealtime();
@@ -615,24 +627,25 @@
continue;
}
idpw.print("package=" + packageName);
- idpw.print(" userId=" + userId);
- idpw.print(" lastUsedElapsed=");
+ idpw.print(" u=" + userId);
+ idpw.print(" bucket=" + appUsageHistory.currentBucket
+ + " reason="
+ + UsageStatsManager.reasonToString(appUsageHistory.bucketingReason));
+ idpw.print(" used=");
TimeUtils.formatDuration(totalElapsedTime - appUsageHistory.lastUsedElapsedTime, idpw);
- idpw.print(" lastUsedScreenOn=");
+ idpw.print(" usedScr=");
TimeUtils.formatDuration(screenOnTime - appUsageHistory.lastUsedScreenTime, idpw);
- idpw.print(" lastPredictedTime=");
+ idpw.print(" lastPred=");
TimeUtils.formatDuration(totalElapsedTime - appUsageHistory.lastPredictedTime, idpw);
- idpw.print(" bucketActiveTimeoutTime=");
- TimeUtils.formatDuration(totalElapsedTime - appUsageHistory.bucketActiveTimeoutTime,
+ idpw.print(" activeLeft=");
+ TimeUtils.formatDuration(appUsageHistory.bucketActiveTimeoutTime - totalElapsedTime,
idpw);
- idpw.print(" bucketWorkingSetTimeoutTime=");
- TimeUtils.formatDuration(totalElapsedTime - appUsageHistory.bucketWorkingSetTimeoutTime,
+ idpw.print(" wsLeft=");
+ TimeUtils.formatDuration(appUsageHistory.bucketWorkingSetTimeoutTime - totalElapsedTime,
idpw);
- idpw.print(" lastJobRunTime=");
+ idpw.print(" lastJob=");
TimeUtils.formatDuration(totalElapsedTime - appUsageHistory.lastJobRunTime, idpw);
idpw.print(" idle=" + (isIdle(packageName, userId, elapsedRealtime) ? "y" : "n"));
- idpw.print(" bucket=" + appUsageHistory.currentBucket
- + " reason=" + appUsageHistory.bucketingReason);
idpw.println();
}
idpw.println();
diff --git a/services/usage/java/com/android/server/usage/AppStandbyController.java b/services/usage/java/com/android/server/usage/AppStandbyController.java
index f40aa5b..e836677 100644
--- a/services/usage/java/com/android/server/usage/AppStandbyController.java
+++ b/services/usage/java/com/android/server/usage/AppStandbyController.java
@@ -16,11 +16,20 @@
package com.android.server.usage;
-import static android.app.usage.UsageStatsManager.REASON_DEFAULT;
-import static android.app.usage.UsageStatsManager.REASON_FORCED;
-import static android.app.usage.UsageStatsManager.REASON_PREDICTED;
-import static android.app.usage.UsageStatsManager.REASON_TIMEOUT;
-import static android.app.usage.UsageStatsManager.REASON_USAGE;
+import static android.app.usage.UsageStatsManager.REASON_MAIN_DEFAULT;
+import static android.app.usage.UsageStatsManager.REASON_MAIN_FORCED;
+import static android.app.usage.UsageStatsManager.REASON_MAIN_MASK;
+import static android.app.usage.UsageStatsManager.REASON_MAIN_PREDICTED;
+import static android.app.usage.UsageStatsManager.REASON_MAIN_TIMEOUT;
+import static android.app.usage.UsageStatsManager.REASON_MAIN_USAGE;
+import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_ACTIVE_TIMEOUT;
+import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_MOVE_TO_BACKGROUND;
+import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_MOVE_TO_FOREGROUND;
+import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_NOTIFICATION_SEEN;
+import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_SYNC_ADAPTER;
+import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_SYSTEM_INTERACTION;
+import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_SYSTEM_UPDATE;
+import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_USER_INTERACTION;
import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_ACTIVE;
import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_EXEMPTED;
import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_FREQUENT;
@@ -129,8 +138,8 @@
STANDBY_BUCKET_RARE
};
- // Expiration time for predicted bucket
- private static final long PREDICTION_TIMEOUT = 12 * ONE_HOUR;
+ /** Default expiration time for bucket prediction. After this, use thresholds to downgrade. */
+ private static final long DEFAULT_PREDICTION_TIMEOUT = 12 * ONE_HOUR;
/**
* Indicates the maximum wait time for admin data to be available;
@@ -187,6 +196,8 @@
long mNotificationSeenTimeoutMillis;
/** Minimum time a system update event should keep the buckets elevated. */
long mSystemUpdateUsageTimeoutMillis;
+ /** Maximum time to wait for a prediction before using simple timeouts to downgrade buckets. */
+ long mPredictionTimeoutMillis;
volatile boolean mAppIdleEnabled;
boolean mAppIdleTempParoled;
@@ -223,24 +234,30 @@
// Whether the bucket change is because the user has started interacting with the app
boolean isUserInteraction;
- StandbyUpdateRecord(String pkgName, int userId, int bucket, boolean isInteraction) {
+ // Reason for bucket change
+ int reason;
+
+ StandbyUpdateRecord(String pkgName, int userId, int bucket, int reason,
+ boolean isInteraction) {
this.packageName = pkgName;
this.userId = userId;
this.bucket = bucket;
+ this.reason = reason;
this.isUserInteraction = isInteraction;
}
public static StandbyUpdateRecord obtain(String pkgName, int userId,
- int bucket, boolean isInteraction) {
+ int bucket, int reason, boolean isInteraction) {
synchronized (sStandbyUpdatePool) {
final int size = sStandbyUpdatePool.size();
if (size < 1) {
- return new StandbyUpdateRecord(pkgName, userId, bucket, isInteraction);
+ return new StandbyUpdateRecord(pkgName, userId, bucket, reason, isInteraction);
}
StandbyUpdateRecord r = sStandbyUpdatePool.remove(size - 1);
r.packageName = pkgName;
r.userId = userId;
r.bucket = bucket;
+ r.reason = reason;
r.isUserInteraction = isInteraction;
return r;
}
@@ -339,10 +356,11 @@
if (!packageName.equals(providerPkgName)) {
synchronized (mAppIdleLock) {
AppUsageHistory appUsage = mAppIdleHistory.reportUsage(packageName, userId,
- STANDBY_BUCKET_ACTIVE, elapsedRealtime,
+ STANDBY_BUCKET_ACTIVE, REASON_SUB_USAGE_SYNC_ADAPTER,
+ elapsedRealtime,
elapsedRealtime + mStrongUsageTimeoutMillis);
maybeInformListeners(packageName, userId, elapsedRealtime,
- appUsage.currentBucket, false);
+ appUsage.currentBucket, appUsage.bucketingReason, false);
}
}
} catch (PackageManager.NameNotFoundException e) {
@@ -497,50 +515,54 @@
if (isSpecial) {
synchronized (mAppIdleLock) {
mAppIdleHistory.setAppStandbyBucket(packageName, userId, elapsedRealtime,
- STANDBY_BUCKET_EXEMPTED, REASON_DEFAULT);
+ STANDBY_BUCKET_EXEMPTED, REASON_MAIN_DEFAULT);
}
maybeInformListeners(packageName, userId, elapsedRealtime,
- STANDBY_BUCKET_EXEMPTED, false);
+ STANDBY_BUCKET_EXEMPTED, REASON_MAIN_DEFAULT, false);
} else {
synchronized (mAppIdleLock) {
final AppIdleHistory.AppUsageHistory app =
mAppIdleHistory.getAppUsageHistory(packageName,
userId, elapsedRealtime);
- String reason = app.bucketingReason;
+ int reason = app.bucketingReason;
+ final int oldMainReason = reason & REASON_MAIN_MASK;
// If the bucket was forced by the user/developer, leave it alone.
// A usage event will be the only way to bring it out of this forced state
- if (REASON_FORCED.equals(app.bucketingReason)) {
+ if (oldMainReason == REASON_MAIN_FORCED) {
return;
}
final int oldBucket = app.currentBucket;
int newBucket = Math.max(oldBucket, STANDBY_BUCKET_ACTIVE); // Undo EXEMPTED
boolean predictionLate = false;
// Compute age-based bucket
- if (REASON_DEFAULT.equals(app.bucketingReason)
- || REASON_USAGE.equals(app.bucketingReason)
- || REASON_TIMEOUT.equals(app.bucketingReason)
+ if (oldMainReason == REASON_MAIN_DEFAULT
+ || oldMainReason == REASON_MAIN_USAGE
+ || oldMainReason == REASON_MAIN_TIMEOUT
|| (predictionLate = predictionTimedOut(app, elapsedRealtime))) {
newBucket = getBucketForLocked(packageName, userId,
elapsedRealtime);
if (DEBUG) {
Slog.d(TAG, "Evaluated AOSP newBucket = " + newBucket);
}
- reason = REASON_TIMEOUT;
+ reason = REASON_MAIN_TIMEOUT;
}
// Check if the app is within one of the timeouts for forced bucket elevation
final long elapsedTimeAdjusted = mAppIdleHistory.getElapsedTime(elapsedRealtime);
if (newBucket >= STANDBY_BUCKET_ACTIVE
&& app.bucketActiveTimeoutTime > elapsedTimeAdjusted) {
newBucket = STANDBY_BUCKET_ACTIVE;
- reason = REASON_USAGE;
+ reason = app.bucketingReason;
if (DEBUG) {
Slog.d(TAG, " Keeping at ACTIVE due to min timeout");
}
} else if (newBucket >= STANDBY_BUCKET_WORKING_SET
&& app.bucketWorkingSetTimeoutTime > elapsedTimeAdjusted) {
newBucket = STANDBY_BUCKET_WORKING_SET;
- reason = REASON_USAGE;
+ // If it was already there, keep the reason, else assume timeout to WS
+ reason = (newBucket == oldBucket)
+ ? app.bucketingReason
+ : REASON_MAIN_USAGE | REASON_SUB_USAGE_ACTIVE_TIMEOUT;
if (DEBUG) {
Slog.d(TAG, " Keeping at WORKING_SET due to min timeout");
}
@@ -553,43 +575,41 @@
mAppIdleHistory.setAppStandbyBucket(packageName, userId,
elapsedRealtime, newBucket, reason);
maybeInformListeners(packageName, userId, elapsedRealtime,
- newBucket, false);
+ newBucket, reason, false);
}
}
}
}
+ /** Returns true if there hasn't been a prediction for the app in a while. */
private boolean predictionTimedOut(AppIdleHistory.AppUsageHistory app, long elapsedRealtime) {
- return app.bucketingReason != null
- && app.bucketingReason.startsWith(REASON_PREDICTED)
+ return (app.bucketingReason & REASON_MAIN_MASK) == REASON_MAIN_PREDICTED
&& app.lastPredictedTime > 0
&& mAppIdleHistory.getElapsedTime(elapsedRealtime)
- - app.lastPredictedTime > PREDICTION_TIMEOUT;
+ - app.lastPredictedTime > mPredictionTimeoutMillis;
}
- private boolean hasBucketTimeoutPassed(AppIdleHistory.AppUsageHistory app,
- long elapsedRealtime) {
- final long elapsedTimeAdjusted = mAppIdleHistory.getElapsedTime(elapsedRealtime);
- return app.bucketActiveTimeoutTime < elapsedTimeAdjusted
- && app.bucketWorkingSetTimeoutTime < elapsedTimeAdjusted;
- }
-
+ /** Inform listeners if the bucket has changed since it was last reported to listeners */
private void maybeInformListeners(String packageName, int userId,
- long elapsedRealtime, int bucket, boolean userStartedInteracting) {
+ long elapsedRealtime, int bucket, int reason, boolean userStartedInteracting) {
synchronized (mAppIdleLock) {
- // TODO: fold these into one call + lookup for efficiency if needed
if (mAppIdleHistory.shouldInformListeners(packageName, userId,
elapsedRealtime, bucket)) {
- StandbyUpdateRecord r = StandbyUpdateRecord.obtain(packageName, userId,
- bucket, userStartedInteracting);
+ final StandbyUpdateRecord r = StandbyUpdateRecord.obtain(packageName, userId,
+ bucket, reason, userStartedInteracting);
if (DEBUG) Slog.d(TAG, "Standby bucket for " + packageName + "=" + bucket);
- mHandler.sendMessage(mHandler.obtainMessage(MSG_INFORM_LISTENERS,
- StandbyUpdateRecord.obtain(packageName, userId,
- bucket, userStartedInteracting)));
+ mHandler.sendMessage(mHandler.obtainMessage(MSG_INFORM_LISTENERS, r));
}
}
}
+ /**
+ * Evaluates next bucket based on time since last used and the bucketing thresholds.
+ * @param packageName the app
+ * @param userId the user
+ * @param elapsedRealtime as the name suggests, current elapsed time
+ * @return the bucket for the app, based on time since last used
+ */
@GuardedBy("mAppIdleLock")
@StandbyBuckets int getBucketForLocked(String packageName, int userId,
long elapsedRealtime) {
@@ -675,17 +695,20 @@
final AppUsageHistory appHistory = mAppIdleHistory.getAppUsageHistory(
event.mPackage, userId, elapsedRealtime);
final int prevBucket = appHistory.currentBucket;
- final String prevBucketReason = appHistory.bucketingReason;
+ final int prevBucketReason = appHistory.bucketingReason;
final long nextCheckTime;
+ final int subReason = usageEventToSubReason(event.mEventType);
+ final int reason = REASON_MAIN_USAGE | subReason;
if (event.mEventType == UsageEvents.Event.NOTIFICATION_SEEN) {
// Mild usage elevates to WORKING_SET but doesn't change usage time.
mAppIdleHistory.reportUsage(appHistory, event.mPackage,
- STANDBY_BUCKET_WORKING_SET,
+ STANDBY_BUCKET_WORKING_SET, subReason,
0, elapsedRealtime + mNotificationSeenTimeoutMillis);
nextCheckTime = mNotificationSeenTimeoutMillis;
+
} else {
mAppIdleHistory.reportUsage(appHistory, event.mPackage,
- STANDBY_BUCKET_ACTIVE,
+ STANDBY_BUCKET_ACTIVE, subReason,
elapsedRealtime, elapsedRealtime + mStrongUsageTimeoutMillis);
nextCheckTime = mStrongUsageTimeoutMillis;
}
@@ -695,9 +718,9 @@
final boolean userStartedInteracting =
appHistory.currentBucket == STANDBY_BUCKET_ACTIVE &&
prevBucket != appHistory.currentBucket &&
- prevBucketReason != REASON_USAGE;
+ (prevBucketReason & REASON_MAIN_MASK) != REASON_MAIN_USAGE;
maybeInformListeners(event.mPackage, userId, elapsedRealtime,
- appHistory.currentBucket, userStartedInteracting);
+ appHistory.currentBucket, reason, userStartedInteracting);
if (previouslyIdle) {
notifyBatteryStats(event.mPackage, userId, false);
@@ -706,6 +729,17 @@
}
}
+ private int usageEventToSubReason(int eventType) {
+ switch (eventType) {
+ case UsageEvents.Event.MOVE_TO_FOREGROUND: return REASON_SUB_USAGE_MOVE_TO_FOREGROUND;
+ case UsageEvents.Event.MOVE_TO_BACKGROUND: return REASON_SUB_USAGE_MOVE_TO_BACKGROUND;
+ case UsageEvents.Event.SYSTEM_INTERACTION: return REASON_SUB_USAGE_SYSTEM_INTERACTION;
+ case UsageEvents.Event.USER_INTERACTION: return REASON_SUB_USAGE_USER_INTERACTION;
+ case UsageEvents.Event.NOTIFICATION_SEEN: return REASON_SUB_USAGE_NOTIFICATION_SEEN;
+ default: return 0;
+ }
+ }
+
/**
* Forces the app's beginIdleTime and lastUsedTime to reflect idle or active. If idle,
* then it rolls back the beginIdleTime and lastUsedTime to a point in time that's behind
@@ -731,7 +765,8 @@
userId, elapsedRealtime);
// Inform listeners if necessary
if (previouslyIdle != stillIdle) {
- maybeInformListeners(packageName, userId, elapsedRealtime, standbyBucket, false);
+ maybeInformListeners(packageName, userId, elapsedRealtime, standbyBucket,
+ REASON_MAIN_FORCED, false);
if (!stillIdle) {
notifyBatteryStats(packageName, userId, idle);
}
@@ -962,11 +997,11 @@
}
void setAppStandbyBucket(String packageName, int userId, @StandbyBuckets int newBucket,
- String reason, long elapsedRealtime) {
+ int reason, long elapsedRealtime) {
synchronized (mAppIdleLock) {
AppIdleHistory.AppUsageHistory app = mAppIdleHistory.getAppUsageHistory(packageName,
userId, elapsedRealtime);
- boolean predicted = reason != null && reason.startsWith(REASON_PREDICTED);
+ boolean predicted = (reason & REASON_MAIN_MASK) == REASON_MAIN_PREDICTED;
// Don't allow changing bucket if higher than ACTIVE
if (app.currentBucket < STANDBY_BUCKET_ACTIVE) return;
@@ -979,7 +1014,7 @@
}
// If the bucket was forced, don't allow prediction to override
- if (app.bucketingReason.equals(REASON_FORCED) && predicted) return;
+ if ((app.bucketingReason & REASON_MAIN_MASK) == REASON_MAIN_FORCED && predicted) return;
// If the bucket is required to stay in a higher state for a specified duration, don't
// override unless the duration has passed
@@ -989,14 +1024,18 @@
if (newBucket > STANDBY_BUCKET_ACTIVE
&& app.bucketActiveTimeoutTime > elapsedTimeAdjusted) {
newBucket = STANDBY_BUCKET_ACTIVE;
- reason = REASON_USAGE;
+ reason = app.bucketingReason;
if (DEBUG) {
Slog.d(TAG, " Keeping at ACTIVE due to min timeout");
}
} else if (newBucket > STANDBY_BUCKET_WORKING_SET
&& app.bucketWorkingSetTimeoutTime > elapsedTimeAdjusted) {
newBucket = STANDBY_BUCKET_WORKING_SET;
- reason = REASON_USAGE;
+ if (app.currentBucket != newBucket) {
+ reason = REASON_MAIN_USAGE | REASON_SUB_USAGE_ACTIVE_TIMEOUT;
+ } else {
+ reason = app.bucketingReason;
+ }
if (DEBUG) {
Slog.d(TAG, " Keeping at WORKING_SET due to min timeout");
}
@@ -1006,7 +1045,7 @@
mAppIdleHistory.setAppStandbyBucket(packageName, userId, elapsedRealtime, newBucket,
reason);
}
- maybeInformListeners(packageName, userId, elapsedRealtime, newBucket, false);
+ maybeInformListeners(packageName, userId, elapsedRealtime, newBucket, reason, false);
}
@VisibleForTesting
@@ -1106,11 +1145,12 @@
return packageName != null && packageName.equals(activeScorer);
}
- void informListeners(String packageName, int userId, int bucket, boolean userInteraction) {
+ void informListeners(String packageName, int userId, int bucket, int reason,
+ boolean userInteraction) {
final boolean idle = bucket >= STANDBY_BUCKET_RARE;
synchronized (mPackageAccessListeners) {
for (AppIdleStateChangeListener listener : mPackageAccessListeners) {
- listener.onAppIdleStateChanged(packageName, userId, idle, bucket);
+ listener.onAppIdleStateChanged(packageName, userId, idle, bucket, reason);
if (userInteraction) {
listener.onUserInteractionStarted(packageName, userId);
}
@@ -1188,7 +1228,8 @@
if (pi.applicationInfo != null && pi.applicationInfo.isSystemApp()) {
// 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,
+ mAppIdleHistory.reportUsage(packageName, userId, STANDBY_BUCKET_ACTIVE,
+ REASON_SUB_USAGE_SYSTEM_UPDATE, 0,
elapsedRealtime + mSystemUpdateUsageTimeoutMillis);
}
}
@@ -1369,7 +1410,8 @@
switch (msg.what) {
case MSG_INFORM_LISTENERS:
StandbyUpdateRecord r = (StandbyUpdateRecord) msg.obj;
- informListeners(r.packageName, r.userId, r.bucket, r.isUserInteraction);
+ informListeners(r.packageName, r.userId, r.bucket, r.reason,
+ r.isUserInteraction);
r.recycle();
break;
@@ -1477,7 +1519,7 @@
"notification_seen_duration";
private static final String KEY_SYSTEM_UPDATE_HOLD_DURATION =
"system_update_usage_duration";
-
+ private static final String KEY_PREDICTION_TIMEOUT = "prediction_timeout";
private final KeyValueListParser mParser = new KeyValueListParser(',');
@@ -1547,6 +1589,9 @@
mSystemUpdateUsageTimeoutMillis = mParser.getDurationMillis
(KEY_SYSTEM_UPDATE_HOLD_DURATION,
COMPRESS_TIME ? 2 * ONE_MINUTE : 2 * ONE_HOUR);
+ mPredictionTimeoutMillis = mParser.getDurationMillis
+ (KEY_PREDICTION_TIMEOUT,
+ COMPRESS_TIME ? 10 * ONE_MINUTE : DEFAULT_PREDICTION_TIMEOUT);
}
}
diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java
index dedf967..43ac58a 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsService.java
@@ -120,10 +120,10 @@
new UsageStatsManagerInternal.AppIdleStateChangeListener() {
@Override
public void onAppIdleStateChanged(String packageName, int userId, boolean idle,
- int bucket) {
+ int bucket, int reason) {
Event event = new Event();
event.mEventType = Event.STANDBY_BUCKET_CHANGED;
- event.mBucket = bucket;
+ event.mBucketAndReason = (bucket << 16) | (reason & 0xFFFF);
event.mPackage = packageName;
// This will later be converted to system time.
event.mTimeStamp = SystemClock.elapsedRealtime();
@@ -741,9 +741,9 @@
throw re.rethrowFromSystemServer();
}
final boolean shellCaller = callingUid == 0 || callingUid == Process.SHELL_UID;
- final String reason = shellCaller
- ? UsageStatsManager.REASON_FORCED
- : UsageStatsManager.REASON_PREDICTED + ":" + callingUid;
+ final int reason = shellCaller
+ ? UsageStatsManager.REASON_MAIN_FORCED
+ : UsageStatsManager.REASON_MAIN_PREDICTED;
final long token = Binder.clearCallingIdentity();
try {
// Caller cannot set their own standby state
@@ -798,9 +798,9 @@
throw re.rethrowFromSystemServer();
}
final boolean shellCaller = callingUid == 0 || callingUid == Process.SHELL_UID;
- final String reason = shellCaller
- ? UsageStatsManager.REASON_FORCED
- : UsageStatsManager.REASON_PREDICTED + ":" + callingUid;
+ final int reason = shellCaller
+ ? UsageStatsManager.REASON_MAIN_FORCED
+ : UsageStatsManager.REASON_MAIN_PREDICTED;
final long token = Binder.clearCallingIdentity();
try {
final long elapsedRealtime = SystemClock.elapsedRealtime();
diff --git a/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java b/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java
index d1ed599..5e7e80d 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java
@@ -26,10 +26,7 @@
import android.app.usage.UsageEvents;
import android.app.usage.UsageStats;
import android.content.res.Configuration;
-import android.text.TextUtils;
import android.util.ArrayMap;
-import android.util.Log;
-import android.util.LogWriter;
import java.io.IOException;
import java.net.ProtocolException;
@@ -175,7 +172,7 @@
event.mShortcutId = (id != null) ? id.intern() : null;
break;
case UsageEvents.Event.STANDBY_BUCKET_CHANGED:
- event.mBucket = XmlUtils.readIntAttribute(parser, STANDBY_BUCKET_ATTR, 0);
+ event.mBucketAndReason = XmlUtils.readIntAttribute(parser, STANDBY_BUCKET_ATTR, 0);
break;
}
@@ -281,8 +278,8 @@
}
break;
case UsageEvents.Event.STANDBY_BUCKET_CHANGED:
- if (event.mBucket != 0) {
- XmlUtils.writeIntAttribute(xml, STANDBY_BUCKET_ATTR, event.mBucket);
+ if (event.mBucketAndReason != 0) {
+ XmlUtils.writeIntAttribute(xml, STANDBY_BUCKET_ATTR, event.mBucketAndReason);
}
}
diff --git a/services/usage/java/com/android/server/usage/UserUsageStatsService.java b/services/usage/java/com/android/server/usage/UserUsageStatsService.java
index 8afc511..3fbcd81 100644
--- a/services/usage/java/com/android/server/usage/UserUsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UserUsageStatsService.java
@@ -520,7 +520,8 @@
pw.printPair("shortcutId", event.mShortcutId);
}
if (event.mEventType == UsageEvents.Event.STANDBY_BUCKET_CHANGED) {
- pw.printPair("standbyBucket", event.mBucket);
+ pw.printPair("standbyBucket", event.getStandbyBucket());
+ pw.printPair("reason", UsageStatsManager.reasonToString(event.getStandbyReason()));
}
pw.printHexPair("flags", event.mFlags);
pw.println();
diff --git a/telephony/java/android/telephony/ServiceState.java b/telephony/java/android/telephony/ServiceState.java
index b6be3c5..82a7450 100644
--- a/telephony/java/android/telephony/ServiceState.java
+++ b/telephony/java/android/telephony/ServiceState.java
@@ -19,6 +19,7 @@
import android.annotation.IntDef;
import android.annotation.Nullable;
import android.annotation.SystemApi;
+import android.annotation.TestApi;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
@@ -1215,6 +1216,7 @@
}
/** @hide */
+ @TestApi
public void setSystemAndNetworkId(int systemId, int networkId) {
this.mSystemId = systemId;
this.mNetworkId = networkId;
diff --git a/telephony/java/android/telephony/UiccAccessRule.java b/telephony/java/android/telephony/UiccAccessRule.java
index c3f8a19..526733d 100644
--- a/telephony/java/android/telephony/UiccAccessRule.java
+++ b/telephony/java/android/telephony/UiccAccessRule.java
@@ -157,6 +157,13 @@
}
/**
+ * Returns the hex string of the certificate hash.
+ */
+ public String getCertificateHexString() {
+ return IccUtils.bytesToHexString(mCertificateHash);
+ }
+
+ /**
* Returns the carrier privilege status associated with the given package.
*
* @param packageInfo package info fetched from
diff --git a/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java b/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java
index 2a13093..494ee65 100644
--- a/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java
+++ b/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java
@@ -83,6 +83,7 @@
private static final String KEY_TRACE_BUFFERSIZE = "trace_bufferSize";
private static final String KEY_TRACE_DUMPINTERVAL = "tracedump_interval";
private static final String KEY_COMPILER_FILTERS = "compiler_filters";
+ private static final String KEY_FORCE_STOP_APP = "force_stop_app";
private static final String SIMPLEPERF_APP_CMD =
"simpleperf --log fatal stat --csv -e cpu-cycles,major-faults --app %s & %s";
@@ -103,6 +104,7 @@
private static final String DROP_CACHE_SCRIPT = "/data/local/tmp/dropCache.sh";
private static final String APP_LAUNCH_CMD = "am start -W -n";
private static final String SUCCESS_MESSAGE = "Status: ok";
+ private static final String WARNING_MESSAGE = "Warning:";
private static final String COMPILE_SUCCESS = "Success";
private static final String THIS_TIME = "ThisTime:";
private static final String LAUNCH_ITERATION = "LAUNCH_ITERATION - %d";
@@ -125,6 +127,7 @@
private String mLaunchOrder = null;
private boolean mDropCache = false;
private int mLaunchIterations = 10;
+ private boolean mForceStopApp = true;
private int mTraceLaunchCount = 0;
private String mTraceDirectoryStr = null;
private Bundle mResult = new Bundle();
@@ -246,7 +249,7 @@
// We only need to run a trial for the speed-profile filter, but we always
// run one for "applaunch.txt" consistency.
AppLaunchResult launchResult =
- startApp(launch.getApp(), true, launch.getLaunchReason());
+ startApp(launch.getApp(), launch.getLaunchReason());
if (launchResult.mLaunchTime < 0) {
addLaunchResult(launch, new AppLaunchResult());
// simply pass the app if launch isn't successful
@@ -274,7 +277,7 @@
}
// In the "applaunch.txt" file app launches are referenced using
// "LAUNCH_ITERATION - ITERATION NUM"
- launchResults = startApp(launch.getApp(), true, launch.getLaunchReason());
+ launchResults = startApp(launch.getApp(), launch.getLaunchReason());
if (launchResults.mLaunchTime < 0) {
addLaunchResult(launch, new AppLaunchResult());
// if it fails once, skip the rest of the launches
@@ -295,14 +298,18 @@
atraceLogger.atraceStart(traceCategoriesSet, traceBufferSize,
traceDumpInterval, rootTraceSubDir,
String.format("%s-%s", launch.getApp(), launch.getLaunchReason()));
- startApp(launch.getApp(), true, launch.getLaunchReason());
+ startApp(launch.getApp(), launch.getLaunchReason());
sleep(POST_LAUNCH_IDLE_TIMEOUT);
} finally {
// Stop the trace
atraceLogger.atraceStop();
}
}
- closeApp(launch.getApp());
+ if(mForceStopApp) {
+ closeApp(launch.getApp());
+ } else {
+ startHomeIntent();
+ }
sleep(BETWEEN_LAUNCH_SLEEP_TIMEOUT);
}
} finally {
@@ -425,6 +432,10 @@
if (launchIterations != null) {
mLaunchIterations = Integer.parseInt(launchIterations);
}
+ String forceStopApp = args.getString(KEY_FORCE_STOP_APP);
+ if (forceStopApp != null) {
+ mForceStopApp = Boolean.parseBoolean(forceStopApp);
+ }
String appList = args.getString(KEY_APPS);
if (appList == null)
return;
@@ -522,8 +533,8 @@
}
}
- private AppLaunchResult startApp(String appName, boolean forceStopBeforeLaunch,
- String launchReason) throws NameNotFoundException, RemoteException {
+ private AppLaunchResult startApp(String appName, String launchReason)
+ throws NameNotFoundException, RemoteException {
Log.i(TAG, "Starting " + appName);
Intent startIntent = mNameToIntent.get(appName);
@@ -532,8 +543,7 @@
mResult.putString(mNameToResultKey.get(appName), "App does not exist");
return new AppLaunchResult();
}
- AppLaunchRunnable runnable = new AppLaunchRunnable(startIntent, forceStopBeforeLaunch,
- launchReason);
+ AppLaunchRunnable runnable = new AppLaunchRunnable(startIntent, launchReason);
Thread t = new Thread(runnable);
t.start();
try {
@@ -682,13 +692,10 @@
private class AppLaunchRunnable implements Runnable {
private Intent mLaunchIntent;
private AppLaunchResult mLaunchResult;
- private boolean mForceStopBeforeLaunch;
private String mLaunchReason;
- public AppLaunchRunnable(Intent intent, boolean forceStopBeforeLaunch,
- String launchReason) {
+ public AppLaunchRunnable(Intent intent, String launchReason) {
mLaunchIntent = intent;
- mForceStopBeforeLaunch = forceStopBeforeLaunch;
mLaunchReason = launchReason;
mLaunchResult = new AppLaunchResult();
}
@@ -702,7 +709,7 @@
try {
String packageName = mLaunchIntent.getComponent().getPackageName();
String componentName = mLaunchIntent.getComponent().flattenToShortString();
- if (mForceStopBeforeLaunch) {
+ if (mForceStopApp) {
mAm.forceStopPackage(packageName, UserHandle.USER_CURRENT);
}
String launchCmd = String.format("%s %s", APP_LAUNCH_CMD, componentName);
@@ -752,10 +759,11 @@
String launchTime = "-1";
String cpuCycles = "-1";
String majorFaults = "-1";
- boolean launchSuccess = false;
+ boolean coldLaunchSuccess = false;
+ boolean hotLaunchSuccess = false;
try {
InputStream inputStream = new FileInputStream(parcelDesc.getFileDescriptor());
- /* SAMPLE OUTPUT :
+ /* SAMPLE OUTPUT : Cold launch
Starting: Intent { cmp=com.google.android.calculator/com.android.calculator2.Calculator }
Status: ok
Activity: com.google.android.calculator/com.android.calculator2.Calculator
@@ -763,6 +771,15 @@
TotalTime: 357
WaitTime: 377
Complete*/
+ /* SAMPLE OUTPUT : Hot launch
+ Starting: Intent { cmp=com.google.android.calculator/com.android.calculator2.Calculator }
+ Warning: Activity not started, its current task has been brought to the front
+ Status: ok
+ Activity: com.google.android.calculator/com.android.calculator2.CalculatorGoogle
+ ThisTime: 60
+ TotalTime: 60
+ WaitTime: 67
+ Complete*/
/* WITH SIMPLEPERF :
Performance counter statistics,
6595722690,cpu-cycles,4.511040,GHz,(100%),
@@ -776,24 +793,33 @@
mBufferedWriter.write(headerInfo);
mBufferedWriter.newLine();
while ((line = bufferedReader.readLine()) != null) {
- if (lineCount == 2 && line.contains(SUCCESS_MESSAGE)) {
- launchSuccess = true;
+ if (lineCount == 2 && line.startsWith(SUCCESS_MESSAGE)) {
+ coldLaunchSuccess = true;
+ }
+ if (lineCount == 2 && line.startsWith(WARNING_MESSAGE)) {
+ hotLaunchSuccess = true;
}
// Parse TotalTime which is the launch time
- if (launchSuccess && lineCount == 5) {
+ if (coldLaunchSuccess && lineCount == 5) {
+ String launchSplit[] = line.split(":");
+ launchTime = launchSplit[1].trim();
+ }
+ if (hotLaunchSuccess && lineCount == 6) {
String launchSplit[] = line.split(":");
launchTime = launchSplit[1].trim();
}
if (mSimplePerfAppOnly) {
// Parse simpleperf output.
- if (lineCount == 9) {
+ if ((lineCount == 9 && coldLaunchSuccess)
+ || (lineCount == 10 && hotLaunchSuccess)) {
if (!line.contains("cpu-cycles")) {
Log.e(TAG, "Error in simpleperf output");
} else {
cpuCycles = line.split(",")[0].trim();
}
- } else if (lineCount == 10) {
+ } else if ((lineCount == 10 && coldLaunchSuccess)
+ || (lineCount == 11 && hotLaunchSuccess)) {
if (!line.contains("major-faults")) {
Log.e(TAG, "Error in simpleperf output");
} else {
diff --git a/tests/SurfaceComposition/src/android/surfacecomposition/SurfaceCompositionTest.java b/tests/SurfaceComposition/src/android/surfacecomposition/SurfaceCompositionTest.java
index 388f91a..261ea2e 100644
--- a/tests/SurfaceComposition/src/android/surfacecomposition/SurfaceCompositionTest.java
+++ b/tests/SurfaceComposition/src/android/surfacecomposition/SurfaceCompositionTest.java
@@ -66,9 +66,6 @@
super(SurfaceCompositionMeasuringActivity.class);
}
- private void testRestoreContexts() {
- }
-
@SmallTest
public void testSurfaceCompositionPerformance() {
Bundle status = new Bundle();