Merge "Add RoleManagerService as a boot phase"
diff --git a/Android.bp b/Android.bp
index 46fa6e2..abeeb43 100644
--- a/Android.bp
+++ b/Android.bp
@@ -393,6 +393,9 @@
"core/java/com/android/internal/backup/IObbBackupService.aidl",
"core/java/com/android/internal/inputmethod/IInputContentUriToken.aidl",
"core/java/com/android/internal/inputmethod/IInputMethodPrivilegedOperations.aidl",
+ "core/java/com/android/internal/inputmethod/IMultiClientInputMethod.aidl",
+ "core/java/com/android/internal/inputmethod/IMultiClientInputMethodPrivilegedOperations.aidl",
+ "core/java/com/android/internal/inputmethod/IMultiClientInputMethodSession.aidl",
"core/java/com/android/internal/net/INetworkWatchlistManager.aidl",
"core/java/com/android/internal/policy/IKeyguardDrawnCallback.aidl",
"core/java/com/android/internal/policy/IKeyguardDismissCallback.aidl",
@@ -567,7 +570,6 @@
"telephony/java/com/android/internal/telephony/IOnSubscriptionsChangedListener.aidl",
"telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl",
"telephony/java/com/android/internal/telephony/IPhoneSubInfo.aidl",
- "telephony/java/com/android/internal/telephony/IRcs.aidl",
"telephony/java/com/android/internal/telephony/ISms.aidl",
"telephony/java/com/android/internal/telephony/ISub.aidl",
"telephony/java/com/android/internal/telephony/IAns.aidl",
@@ -598,6 +600,7 @@
"telephony/java/com/android/internal/telephony/euicc/ISetDefaultSmdpAddressCallback.aidl",
"telephony/java/com/android/internal/telephony/euicc/ISetNicknameCallback.aidl",
"telephony/java/com/android/internal/telephony/euicc/ISwitchToProfileCallback.aidl",
+ "telephony/java/com/android/internal/telephony/rcs/IRcs.aidl",
"wifi/java/android/net/wifi/INetworkRequestMatchCallback.aidl",
"wifi/java/android/net/wifi/INetworkRequestUserSelectionCallback.aidl",
"wifi/java/android/net/wifi/ISoftApCallback.aidl",
diff --git a/api/current.txt b/api/current.txt
index 533c70f..28863b8 100755
--- a/api/current.txt
+++ b/api/current.txt
@@ -740,6 +740,7 @@
field public static final int immersive = 16843456; // 0x10102c0
field public static final int importantForAccessibility = 16843690; // 0x10103aa
field public static final int importantForAutofill = 16844120; // 0x1010558
+ field public static final int importantForContentCapture = 16844182; // 0x1010596
field public static final int inAnimation = 16843127; // 0x1010177
field public static final int includeFontPadding = 16843103; // 0x101015f
field public static final int includeInGlobalSearch = 16843374; // 0x101026e
@@ -7580,6 +7581,8 @@
method public java.lang.String getShortcutId();
method public long getTimeStamp();
field public static final int CONFIGURATION_CHANGE = 5; // 0x5
+ field public static final int FOREGROUND_SERVICE_START = 19; // 0x13
+ field public static final int FOREGROUND_SERVICE_STOP = 20; // 0x14
field public static final int KEYGUARD_HIDDEN = 18; // 0x12
field public static final int KEYGUARD_SHOWN = 17; // 0x11
field public static final int MOVE_TO_BACKGROUND = 2; // 0x2
@@ -7597,9 +7600,11 @@
method public void add(android.app.usage.UsageStats);
method public int describeContents();
method public long getFirstTimeStamp();
+ method public long getLastTimeForegroundServiceUsed();
method public long getLastTimeStamp();
method public long getLastTimeUsed();
method public java.lang.String getPackageName();
+ method public long getTotalTimeForegroundServiceUsed();
method public long getTotalTimeInForeground();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.app.usage.UsageStats> CREATOR;
@@ -16628,6 +16633,8 @@
field public static final int SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_BGGR = 3; // 0x3
field public static final int SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_GBRG = 2; // 0x2
field public static final int SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_GRBG = 1; // 0x1
+ field public static final int SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_MONO = 5; // 0x5
+ field public static final int SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_NIR = 6; // 0x6
field public static final int SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_RGB = 4; // 0x4
field public static final int SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_RGGB = 0; // 0x0
field public static final int SENSOR_INFO_TIMESTAMP_SOURCE_REALTIME = 1; // 0x1
@@ -20712,6 +20719,51 @@
enum_constant public static final android.icu.text.TimeZoneNames.NameType SHORT_STANDARD;
}
+ public abstract class Transliterator {
+ method public static final android.icu.text.Transliterator createFromRules(java.lang.String, java.lang.String, int);
+ method public void filteredTransliterate(android.icu.text.Replaceable, android.icu.text.Transliterator.Position, boolean);
+ method public final void finishTransliteration(android.icu.text.Replaceable, android.icu.text.Transliterator.Position);
+ method public static final java.util.Enumeration<java.lang.String> getAvailableIDs();
+ method public static final java.util.Enumeration<java.lang.String> getAvailableSources();
+ method public static final java.util.Enumeration<java.lang.String> getAvailableTargets(java.lang.String);
+ method public static final java.util.Enumeration<java.lang.String> getAvailableVariants(java.lang.String, java.lang.String);
+ method public static final java.lang.String getDisplayName(java.lang.String);
+ method public static java.lang.String getDisplayName(java.lang.String, java.util.Locale);
+ method public static java.lang.String getDisplayName(java.lang.String, android.icu.util.ULocale);
+ method public android.icu.text.Transliterator[] getElements();
+ method public final android.icu.text.UnicodeFilter getFilter();
+ method public final java.lang.String getID();
+ method public static final android.icu.text.Transliterator getInstance(java.lang.String);
+ method public static android.icu.text.Transliterator getInstance(java.lang.String, int);
+ method public final android.icu.text.Transliterator getInverse();
+ method public final int getMaximumContextLength();
+ method public final android.icu.text.UnicodeSet getSourceSet();
+ method public android.icu.text.UnicodeSet getTargetSet();
+ method public void setFilter(android.icu.text.UnicodeFilter);
+ method public java.lang.String toRules(boolean);
+ method public final int transliterate(android.icu.text.Replaceable, int, int);
+ method public final void transliterate(android.icu.text.Replaceable);
+ method public final java.lang.String transliterate(java.lang.String);
+ method public final void transliterate(android.icu.text.Replaceable, android.icu.text.Transliterator.Position, java.lang.String);
+ method public final void transliterate(android.icu.text.Replaceable, android.icu.text.Transliterator.Position, int);
+ method public final void transliterate(android.icu.text.Replaceable, android.icu.text.Transliterator.Position);
+ field public static final int FORWARD = 0; // 0x0
+ field public static final int REVERSE = 1; // 0x1
+ }
+
+ public static class Transliterator.Position {
+ ctor public Transliterator.Position();
+ ctor public Transliterator.Position(int, int, int);
+ ctor public Transliterator.Position(int, int, int, int);
+ ctor public Transliterator.Position(android.icu.text.Transliterator.Position);
+ method public void set(android.icu.text.Transliterator.Position);
+ method public final void validate(int);
+ field public int contextLimit;
+ field public int contextStart;
+ field public int limit;
+ field public int start;
+ }
+
public abstract class UCharacterIterator implements java.lang.Cloneable {
ctor protected UCharacterIterator();
method public java.lang.Object clone() throws java.lang.CloneNotSupportedException;
@@ -24208,6 +24260,8 @@
method public static int getMaxSecurityLevel();
method public int getMaxSessionCount();
method public android.os.PersistableBundle getMetrics();
+ method public java.util.List<byte[]> getOfflineLicenseKeySetIds();
+ method public int getOfflineLicenseState(byte[]);
method public int getOpenSessionCount();
method public byte[] getPropertyByteArray(java.lang.String);
method public java.lang.String getPropertyString(java.lang.String);
@@ -24228,6 +24282,7 @@
method public void releaseSecureStops(byte[]);
method public void removeAllSecureStops();
method public void removeKeys(byte[]);
+ method public void removeOfflineLicense(byte[]);
method public void removeSecureStop(byte[]);
method public void restoreKeys(byte[], byte[]);
method public void setOnEventListener(android.media.MediaDrm.OnEventListener);
@@ -24250,6 +24305,9 @@
field public static final int KEY_TYPE_OFFLINE = 2; // 0x2
field public static final int KEY_TYPE_RELEASE = 3; // 0x3
field public static final int KEY_TYPE_STREAMING = 1; // 0x1
+ field public static final int OFFLINE_LICENSE_INACTIVE = 2; // 0x2
+ field public static final int OFFLINE_LICENSE_STATE_UNKNOWN = 0; // 0x0
+ field public static final int OFFLINE_LICENSE_USABLE = 1; // 0x1
field public static final java.lang.String PROPERTY_ALGORITHMS = "algorithms";
field public static final java.lang.String PROPERTY_DESCRIPTION = "description";
field public static final java.lang.String PROPERTY_DEVICE_UNIQUE_ID = "deviceUniqueId";
@@ -42083,7 +42141,7 @@
field public static final int CAPABILITY_CAN_PAUSE_VIDEO = 1048576; // 0x100000
field public static final int CAPABILITY_CAN_PULL_CALL = 16777216; // 0x1000000
field public static final int CAPABILITY_CAN_SEND_RESPONSE_VIA_CONNECTION = 4194304; // 0x400000
- field public static final int CAPABILITY_CAN_UPGRADE_TO_VIDEO = 524288; // 0x80000
+ field public static final deprecated int CAPABILITY_CAN_UPGRADE_TO_VIDEO = 524288; // 0x80000
field public static final int CAPABILITY_DISCONNECT_FROM_CONFERENCE = 8192; // 0x2000
field public static final int CAPABILITY_HOLD = 1; // 0x1
field public static final int CAPABILITY_MANAGE_CONFERENCE = 128; // 0x80
@@ -42503,6 +42561,7 @@
method public android.telecom.PhoneAccount getPhoneAccount(android.telecom.PhoneAccountHandle);
method public java.util.List<android.telecom.PhoneAccountHandle> getSelfManagedPhoneAccounts();
method public android.telecom.PhoneAccountHandle getSimCallManager();
+ method public java.lang.String getSystemDialerPackage();
method public java.lang.String getVoiceMailNumber(android.telecom.PhoneAccountHandle);
method public boolean handleMmi(java.lang.String);
method public boolean handleMmi(java.lang.String, android.telecom.PhoneAccountHandle);
@@ -48785,6 +48844,7 @@
method public int getId();
method public int getImportantForAccessibility();
method public int getImportantForAutofill();
+ method public int getImportantForContentCapture();
method public boolean getKeepScreenOn();
method public android.view.KeyEvent.DispatcherState getKeyDispatcherState();
method public int getLabelFor();
@@ -48918,6 +48978,7 @@
method public boolean isHovered();
method public boolean isImportantForAccessibility();
method public final boolean isImportantForAutofill();
+ method public final boolean isImportantForContentCapture();
method public boolean isInEditMode();
method public boolean isInLayout();
method public boolean isInTouchMode();
@@ -48992,6 +49053,7 @@
method public void onPopulateAccessibilityEvent(android.view.accessibility.AccessibilityEvent);
method public void onProvideAutofillStructure(android.view.ViewStructure, int);
method public void onProvideAutofillVirtualStructure(android.view.ViewStructure, int);
+ method public boolean onProvideContentCaptureStructure(android.view.ViewStructure, int);
method public void onProvideStructure(android.view.ViewStructure);
method public void onProvideVirtualStructure(android.view.ViewStructure);
method public android.view.PointerIcon onResolvePointerIcon(android.view.MotionEvent, int);
@@ -49110,6 +49172,7 @@
method public void setId(int);
method public void setImportantForAccessibility(int);
method public void setImportantForAutofill(int);
+ method public void setImportantForContentCapture(int);
method public void setKeepScreenOn(boolean);
method public void setKeyboardNavigationCluster(boolean);
method public void setLabelFor(int);
@@ -49280,6 +49343,11 @@
field public static final int IMPORTANT_FOR_AUTOFILL_NO_EXCLUDE_DESCENDANTS = 8; // 0x8
field public static final int IMPORTANT_FOR_AUTOFILL_YES = 1; // 0x1
field public static final int IMPORTANT_FOR_AUTOFILL_YES_EXCLUDE_DESCENDANTS = 4; // 0x4
+ field public static final int IMPORTANT_FOR_CONTENT_CAPTURE_AUTO = 0; // 0x0
+ field public static final int IMPORTANT_FOR_CONTENT_CAPTURE_NO = 2; // 0x2
+ field public static final int IMPORTANT_FOR_CONTENT_CAPTURE_NO_EXCLUDE_DESCENDANTS = 8; // 0x8
+ field public static final int IMPORTANT_FOR_CONTENT_CAPTURE_YES = 1; // 0x1
+ field public static final int IMPORTANT_FOR_CONTENT_CAPTURE_YES_EXCLUDE_DESCENDANTS = 4; // 0x4
field public static final int INVISIBLE = 4; // 0x4
field public static final int KEEP_SCREEN_ON = 67108864; // 0x4000000
field public static final int LAYER_TYPE_HARDWARE = 2; // 0x2
@@ -51734,6 +51802,10 @@
method public void disableContentCapture();
method public android.content.ComponentName getIntelligenceServiceComponentName();
method public boolean isContentCaptureEnabled();
+ method public android.view.ViewStructure newVirtualViewStructure(android.view.autofill.AutofillId, int);
+ method public void notifyViewAppeared(android.view.ViewStructure);
+ method public void notifyViewDisappeared(android.view.autofill.AutofillId);
+ method public void notifyViewTextChanged(android.view.autofill.AutofillId, java.lang.CharSequence, int);
field public static final int FLAG_USER_INPUT = 1; // 0x1
}
@@ -52047,6 +52119,7 @@
public final class TextLinks implements android.os.Parcelable {
method public int apply(android.text.Spannable, int, java.util.function.Function<android.view.textclassifier.TextLinks.TextLink, android.view.textclassifier.TextLinks.TextLinkSpan>);
method public int describeContents();
+ method public android.os.Bundle getExtras();
method public java.util.Collection<android.view.textclassifier.TextLinks.TextLink> getLinks();
method public void writeToParcel(android.os.Parcel, int);
field public static final int APPLY_STRATEGY_IGNORE = 0; // 0x0
@@ -52063,12 +52136,14 @@
method public android.view.textclassifier.TextLinks.Builder addLink(int, int, java.util.Map<java.lang.String, java.lang.Float>);
method public android.view.textclassifier.TextLinks build();
method public android.view.textclassifier.TextLinks.Builder clearTextLinks();
+ method public android.view.textclassifier.TextLinks.Builder setExtras(android.os.Bundle);
}
public static final class TextLinks.Request implements android.os.Parcelable {
method public int describeContents();
method public android.os.LocaleList getDefaultLocales();
method public android.view.textclassifier.TextClassifier.EntityConfig getEntityConfig();
+ method public android.os.Bundle getExtras();
method public java.lang.CharSequence getText();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.view.textclassifier.TextLinks.Request> CREATOR;
@@ -52079,6 +52154,7 @@
method public android.view.textclassifier.TextLinks.Request build();
method public android.view.textclassifier.TextLinks.Request.Builder setDefaultLocales(android.os.LocaleList);
method public android.view.textclassifier.TextLinks.Request.Builder setEntityConfig(android.view.textclassifier.TextClassifier.EntityConfig);
+ method public android.view.textclassifier.TextLinks.Request.Builder setExtras(android.os.Bundle);
}
public static final class TextLinks.TextLink implements android.os.Parcelable {
@@ -52103,6 +52179,7 @@
method public float getConfidenceScore(java.lang.String);
method public java.lang.String getEntity(int);
method public int getEntityCount();
+ method public android.os.Bundle getExtras();
method public java.lang.String getId();
method public int getSelectionEndIndex();
method public int getSelectionStartIndex();
@@ -52114,6 +52191,7 @@
ctor public TextSelection.Builder(int, int);
method public android.view.textclassifier.TextSelection build();
method public android.view.textclassifier.TextSelection.Builder setEntityType(java.lang.String, float);
+ method public android.view.textclassifier.TextSelection.Builder setExtras(android.os.Bundle);
method public android.view.textclassifier.TextSelection.Builder setId(java.lang.String);
}
@@ -52121,6 +52199,7 @@
method public int describeContents();
method public android.os.LocaleList getDefaultLocales();
method public int getEndIndex();
+ method public android.os.Bundle getExtras();
method public int getStartIndex();
method public java.lang.CharSequence getText();
method public void writeToParcel(android.os.Parcel, int);
@@ -52131,6 +52210,7 @@
ctor public TextSelection.Request.Builder(java.lang.CharSequence, int, int);
method public android.view.textclassifier.TextSelection.Request build();
method public android.view.textclassifier.TextSelection.Request.Builder setDefaultLocales(android.os.LocaleList);
+ method public android.view.textclassifier.TextSelection.Request.Builder setExtras(android.os.Bundle);
}
}
diff --git a/api/system-current.txt b/api/system-current.txt
index 576df268..7d82e25 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -5427,15 +5427,21 @@
method public int getAllPhoneAccountsCount();
method public int getCallState();
method public android.telecom.PhoneAccountHandle getConnectionManager();
+ method public int getCurrentTtyMode();
method public deprecated android.content.ComponentName getDefaultPhoneApp();
method public java.util.List<android.telecom.PhoneAccountHandle> getPhoneAccountsForPackage();
method public java.util.List<android.telecom.PhoneAccountHandle> getPhoneAccountsSupportingScheme(java.lang.String);
method public boolean isInEmergencyCall();
method public boolean isRinging();
method public boolean isTtySupported();
+ method public boolean setDefaultDialer(java.lang.String);
field public static final java.lang.String EXTRA_CALL_BACK_INTENT = "android.telecom.extra.CALL_BACK_INTENT";
field public static final java.lang.String EXTRA_CLEAR_MISSED_CALLS_INTENT = "android.telecom.extra.CLEAR_MISSED_CALLS_INTENT";
field public static final java.lang.String EXTRA_CONNECTION_SERVICE = "android.telecom.extra.CONNECTION_SERVICE";
+ field public static final int TTY_MODE_FULL = 1; // 0x1
+ field public static final int TTY_MODE_HCO = 2; // 0x2
+ field public static final int TTY_MODE_OFF = 0; // 0x0
+ field public static final int TTY_MODE_VCO = 3; // 0x3
}
}
@@ -5476,6 +5482,7 @@
method public int getDomain();
method public int getRegState();
method public int getRejectCause();
+ method public int getRoamingType();
method public int getTransportType();
method public boolean isEmergencyEnabled();
method public boolean isRoaming();
@@ -5535,6 +5542,10 @@
method public deprecated android.telephony.NetworkRegistrationState getNetworkRegistrationStates(int, int);
method public java.util.List<android.telephony.NetworkRegistrationState> getNetworkRegistrationStatesForDomain(int);
method public java.util.List<android.telephony.NetworkRegistrationState> getNetworkRegistrationStatesForTransportType(int);
+ field public static final int ROAMING_TYPE_DOMESTIC = 2; // 0x2
+ field public static final int ROAMING_TYPE_INTERNATIONAL = 3; // 0x3
+ field public static final int ROAMING_TYPE_NOT_ROAMING = 0; // 0x0
+ field public static final int ROAMING_TYPE_UNKNOWN = 1; // 0x1
}
public final class SmsManager {
@@ -6994,8 +7005,8 @@
field public static final int TYPE_ACTIVITY_RESUMED = 2; // 0x2
field public static final int TYPE_ACTIVITY_STARTED = 1; // 0x1
field public static final int TYPE_ACTIVITY_STOPPED = 4; // 0x4
- field public static final int TYPE_VIEW_ADDED = 5; // 0x5
- field public static final int TYPE_VIEW_REMOVED = 6; // 0x6
+ field public static final int TYPE_VIEW_APPEARED = 5; // 0x5
+ field public static final int TYPE_VIEW_DISAPPEARED = 6; // 0x6
field public static final int TYPE_VIEW_TEXT_CHANGED = 7; // 0x7
}
diff --git a/api/test-current.txt b/api/test-current.txt
index 8b8c542..c3920b8 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -25,6 +25,10 @@
package android.app {
+ public class Activity extends android.view.ContextThemeWrapper implements android.content.ComponentCallbacks2 android.view.KeyEvent.Callback android.view.LayoutInflater.Factory2 android.view.View.OnCreateContextMenuListener android.view.Window.Callback {
+ method public void onMovedToDisplay(int, android.content.res.Configuration);
+ }
+
public class ActivityManager {
method public void addOnUidImportanceListener(android.app.ActivityManager.OnUidImportanceListener, int);
method public void alwaysShowUnsupportedCompileSdkWarning(android.content.ComponentName);
@@ -710,6 +714,7 @@
public class Environment {
method public static java.io.File buildPath(java.io.File, java.lang.String...);
+ method public static java.io.File getProductDirectory();
method public static java.io.File getStorageDirectory();
}
@@ -1591,6 +1596,7 @@
public class KeyEvent extends android.view.InputEvent implements android.os.Parcelable {
method public static java.lang.String actionToString(int);
+ method public final void setDisplayId(int);
field public static final int LAST_KEYCODE = 288; // 0x120
}
@@ -1637,9 +1643,9 @@
}
public abstract interface WindowManager implements android.view.ViewManager {
- method public abstract void setShouldShowIme(int, boolean);
- method public abstract void setShouldShowWithInsecureKeyguard(int, boolean);
- method public abstract void setShouldShowSystemDecors(int, boolean);
+ method public default void setShouldShowIme(int, boolean);
+ method public default void setShouldShowSystemDecors(int, boolean);
+ method public default void setShouldShowWithInsecureKeyguard(int, boolean);
}
public static class WindowManager.LayoutParams extends android.view.ViewGroup.LayoutParams implements android.os.Parcelable {
diff --git a/cmds/idmap2/idmap2/Scan.cpp b/cmds/idmap2/idmap2/Scan.cpp
index 33c274e..00c49e3 100644
--- a/cmds/idmap2/idmap2/Scan.cpp
+++ b/cmds/idmap2/idmap2/Scan.cpp
@@ -138,7 +138,8 @@
std::stringstream stream;
for (auto iter = interesting_apks.cbegin(); iter != interesting_apks.cend(); ++iter) {
const std::string idmap_path = Idmap::CanonicalIdmapPathFor(output_directory, *iter);
- if (!Verify(std::vector<std::string>({"--idmap-path", idmap_path}), out_error) &&
+ std::stringstream dev_null;
+ if (!Verify(std::vector<std::string>({"--idmap-path", idmap_path}), dev_null) &&
!Create(std::vector<std::string>({
"--target-apk-path",
target_apk_path,
diff --git a/cmds/sm/src/com/android/commands/sm/Sm.java b/cmds/sm/src/com/android/commands/sm/Sm.java
index 09343f1..c6d717a 100644
--- a/cmds/sm/src/com/android/commands/sm/Sm.java
+++ b/cmds/sm/src/com/android/commands/sm/Sm.java
@@ -101,6 +101,8 @@
runFstrim();
} else if ("set-virtual-disk".equals(op)) {
runSetVirtualDisk();
+ } else if ("set-isolated-storage".equals(op)) {
+ runIsolatedStorage();
} else {
throw new IllegalArgumentException();
}
@@ -278,6 +280,20 @@
StorageManager.DEBUG_VIRTUAL_DISK);
}
+ public void runIsolatedStorage() throws RemoteException {
+ final boolean enableIsolatedStorage = Boolean.parseBoolean(nextArg());
+ // Toggling isolated-storage state will result in a device reboot. So to avoid this command
+ // from erroring out (DeadSystemException), call setDebugFlags() in a separate thread.
+ new Thread(() -> {
+ try {
+ mSm.setDebugFlags(enableIsolatedStorage ? StorageManager.DEBUG_ISOLATED_STORAGE : 0,
+ StorageManager.DEBUG_ISOLATED_STORAGE);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Encountered an error!", e);
+ }
+ }).start();
+ }
+
public void runIdleMaint() throws RemoteException {
final boolean im_run = "run".equals(nextArg());
if (im_run) {
@@ -316,6 +332,8 @@
System.err.println("");
System.err.println(" sm set-emulate-fbe [true|false]");
System.err.println("");
+ System.err.println(" sm set-isolated-storage [true|false]");
+ System.err.println("");
return 1;
}
}
diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.cpp b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
index c8b1cf0..a34df8aa 100644
--- a/cmds/statsd/src/metrics/ValueMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
@@ -64,21 +64,29 @@
const int FIELD_ID_DIMENSION_LEAF_IN_WHAT = 4;
const int FIELD_ID_DIMENSION_LEAF_IN_CONDITION = 5;
// for ValueBucketInfo
-const int FIELD_ID_VALUE_LONG = 7;
-const int FIELD_ID_VALUE_DOUBLE = 8;
+const int FIELD_ID_VALUE_INDEX = 1;
+const int FIELD_ID_VALUE_LONG = 2;
+const int FIELD_ID_VALUE_DOUBLE = 3;
+const int FIELD_ID_VALUES = 9;
const int FIELD_ID_BUCKET_NUM = 4;
const int FIELD_ID_START_BUCKET_ELAPSED_MILLIS = 5;
const int FIELD_ID_END_BUCKET_ELAPSED_MILLIS = 6;
// ValueMetric has a minimum bucket size of 10min so that we don't pull too frequently
-ValueMetricProducer::ValueMetricProducer(const ConfigKey& key, const ValueMetric& metric,
+ValueMetricProducer::ValueMetricProducer(const ConfigKey& key,
+ const ValueMetric& metric,
const int conditionIndex,
- const sp<ConditionWizard>& wizard, const int pullTagId,
- const int64_t timeBaseNs, const int64_t startTimeNs,
+ const sp<ConditionWizard>& conditionWizard,
+ const int whatMatcherIndex,
+ const sp<EventMatcherWizard>& matcherWizard,
+ const int pullTagId,
+ const int64_t timeBaseNs,
+ const int64_t startTimeNs,
const sp<StatsPullerManager>& pullerManager)
- : MetricProducer(metric.id(), key, timeBaseNs, conditionIndex, wizard),
+ : MetricProducer(metric.id(), key, timeBaseNs, conditionIndex, conditionWizard),
+ mWhatMatcherIndex(whatMatcherIndex),
+ mEventMatcherWizard(matcherWizard),
mPullerManager(pullerManager),
- mValueField(metric.value_field()),
mPullTagId(pullTagId),
mIsPulled(pullTagId != -1),
mMinBucketSizeNs(metric.min_bucket_size_nanos()),
@@ -103,6 +111,9 @@
}
mBucketSizeNs = bucketSizeMills * 1000000;
+
+ translateFieldMatcher(metric.value_field(), &mFieldMatchers);
+
if (metric.has_dimensions_in_what()) {
translateFieldMatcher(metric.dimensions_in_what(), &mDimensionsInWhat);
mContainANYPositionInDimensionsInWhat = HasPositionANY(metric.dimensions_in_what());
@@ -122,9 +133,6 @@
}
}
- if (mValueField.child_size() > 0) {
- mField = mValueField.child(0).field();
- }
mConditionSliced = (metric.links().size() > 0) || (mDimensionsInCondition.size() > 0);
mSliceByPositionALL = HasPositionALL(metric.dimensions_in_what()) ||
HasPositionALL(metric.dimensions_in_condition());
@@ -142,7 +150,7 @@
mCurrentBucketStartTimeNs = startTimeNs;
// Kicks off the puller immediately if condition is true and diff based.
if (mIsPulled && mCondition && mUseDiff) {
- pullLocked(startTimeNs);
+ pullAndMatchEventsLocked(startTimeNs);
}
VLOG("value metric %lld created. bucket size %lld start_time: %lld", (long long)metric.id(),
(long long)mBucketSizeNs, (long long)mTimeBaseNs);
@@ -259,18 +267,27 @@
protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_BUCKET_NUM,
(long long)(getBucketNumFromEndTimeNs(bucket.mBucketEndNs)));
}
- if (bucket.value.getType() == LONG) {
- protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_VALUE_LONG,
- (long long)bucket.value.long_value);
- VLOG("\t bucket [%lld - %lld] count: %lld", (long long)bucket.mBucketStartNs,
- (long long)bucket.mBucketEndNs, (long long)bucket.value.long_value);
- } else if (bucket.value.getType() == DOUBLE) {
- protoOutput->write(FIELD_TYPE_DOUBLE | FIELD_ID_VALUE_DOUBLE,
- bucket.value.double_value);
- VLOG("\t bucket [%lld - %lld] count: %.2f", (long long)bucket.mBucketStartNs,
- (long long)bucket.mBucketEndNs, bucket.value.double_value);
- } else {
- VLOG("Wrong value type for ValueMetric output: %d", bucket.value.getType());
+ for (int i = 0; i < (int)bucket.valueIndex.size(); i ++) {
+ int index = bucket.valueIndex[i];
+ const Value& value = bucket.values[i];
+ uint64_t valueToken = protoOutput->start(
+ FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_VALUES);
+ protoOutput->write(FIELD_TYPE_INT32 | FIELD_ID_VALUE_INDEX,
+ index);
+ if (value.getType() == LONG) {
+ protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_VALUE_LONG,
+ (long long)value.long_value);
+ VLOG("\t bucket [%lld - %lld] value %d: %lld", (long long)bucket.mBucketStartNs,
+ (long long)bucket.mBucketEndNs, index, (long long)value.long_value);
+ } else if (value.getType() == DOUBLE) {
+ protoOutput->write(FIELD_TYPE_DOUBLE | FIELD_ID_VALUE_DOUBLE,
+ value.double_value);
+ VLOG("\t bucket [%lld - %lld] value %d: %.2f", (long long)bucket.mBucketStartNs,
+ (long long)bucket.mBucketEndNs, index, value.double_value);
+ } else {
+ VLOG("Wrong value type for ValueMetric output: %d", value.getType());
+ }
+ protoOutput->end(valueToken);
}
protoOutput->end(bucketInfoToken);
}
@@ -297,27 +314,32 @@
// Pull on condition changes.
if (mIsPulled && (mCondition != condition)) {
- pullLocked(eventTimeNs);
+ pullAndMatchEventsLocked(eventTimeNs);
}
// when condition change from true to false, clear diff base
if (mUseDiff && mCondition && !condition) {
for (auto& slice : mCurrentSlicedBucket) {
- slice.second.hasBase = false;
+ for (auto& interval : slice.second) {
+ interval.hasBase = false;
+ }
}
}
mCondition = condition;
}
-void ValueMetricProducer::pullLocked(const int64_t timestampNs) {
+void ValueMetricProducer::pullAndMatchEventsLocked(const int64_t timestampNs) {
vector<std::shared_ptr<LogEvent>> allData;
if (mPullerManager->Pull(mPullTagId, timestampNs, &allData)) {
if (allData.size() == 0) {
return;
}
for (const auto& data : allData) {
- onMatchedLogEventLocked(0, *data);
+ if (mEventMatcherWizard->matchLogEvent(
+ *data, mWhatMatcherIndex) == MatchingState::kMatched) {
+ onMatchedLogEventLocked(mWhatMatcherIndex, *data);
+ }
}
}
}
@@ -328,9 +350,9 @@
void ValueMetricProducer::onDataPulled(const std::vector<std::shared_ptr<LogEvent>>& allData) {
std::lock_guard<std::mutex> lock(mMutex);
-
if (mCondition) {
if (allData.size() == 0) {
+ VLOG("Data pulled is empty");
return;
}
// For scheduled pulled data, the effective event time is snap to the nearest
@@ -348,9 +370,14 @@
return;
}
for (const auto& data : allData) {
- data->setElapsedTimestampNs(bucketEndTime);
- onMatchedLogEventLocked(0, *data);
+ if (mEventMatcherWizard->matchLogEvent(*data, mWhatMatcherIndex) ==
+ MatchingState::kMatched) {
+ data->setElapsedTimestampNs(bucketEndTime);
+ onMatchedLogEventLocked(mWhatMatcherIndex, *data);
+ }
}
+ } else {
+ VLOG("No need to commit data on condition false.");
}
}
@@ -363,10 +390,12 @@
(unsigned long)mCurrentSlicedBucket.size());
if (verbose) {
for (const auto& it : mCurrentSlicedBucket) {
+ for (const auto& interval : it.second) {
fprintf(out, "\t(what)%s\t(condition)%s (value)%s\n",
it.first.getDimensionKeyInWhat().toString().c_str(),
it.first.getDimensionKeyInCondition().toString().c_str(),
- it.second.value.toString().c_str());
+ interval.value.toString().c_str());
+ }
}
}
}
@@ -391,25 +420,29 @@
return false;
}
-const Value getDoubleOrLong(const Value& value) {
- Value v;
- switch (value.type) {
- case INT:
- v.setLong(value.int_value);
- break;
- case LONG:
- v.setLong(value.long_value);
- break;
- case FLOAT:
- v.setDouble(value.float_value);
- break;
- case DOUBLE:
- v.setDouble(value.double_value);
- break;
- default:
- break;
+bool getDoubleOrLong(const LogEvent& event, const Matcher& matcher, Value& ret) {
+ for (const FieldValue& value : event.getValues()) {
+ if (value.mField.matches(matcher)) {
+ switch (value.mValue.type) {
+ case INT:
+ ret.setLong(value.mValue.int_value);
+ break;
+ case LONG:
+ ret.setLong(value.mValue.long_value);
+ break;
+ case FLOAT:
+ ret.setDouble(value.mValue.float_value);
+ break;
+ case DOUBLE:
+ ret.setDouble(value.mValue.double_value);
+ break;
+ default:
+ break;
+ }
+ return true;
+ }
}
- return v;
+ return false;
}
void ValueMetricProducer::onMatchedLogEventInternalLocked(const size_t matcherIndex,
@@ -436,82 +469,90 @@
if (hitGuardRailLocked(eventKey)) {
return;
}
- Interval& interval = mCurrentSlicedBucket[eventKey];
-
- if (mField > event.size()) {
- VLOG("Failed to extract value field %d from atom %s. %d", mField, event.ToString().c_str(),
- (int)event.size());
- return;
+ vector<Interval>& multiIntervals = mCurrentSlicedBucket[eventKey];
+ if (multiIntervals.size() < mFieldMatchers.size()) {
+ VLOG("Resizing number of intervals to %d", (int)mFieldMatchers.size());
+ multiIntervals.resize(mFieldMatchers.size());
}
- Value value = getDoubleOrLong(event.getValues()[mField - 1].mValue);
- if (mUseDiff) {
- // no base. just update base and return.
- if (!interval.hasBase) {
- interval.base = value;
- interval.hasBase = true;
+ for (int i = 0; i < (int)mFieldMatchers.size(); i++) {
+ const Matcher& matcher = mFieldMatchers[i];
+ Interval& interval = multiIntervals[i];
+ interval.valueIndex = i;
+ Value value;
+ if (!getDoubleOrLong(event, matcher, value)) {
+ VLOG("Failed to get value %d from event %s", i, event.ToString().c_str());
return;
}
- Value diff;
- switch (mValueDirection) {
- case ValueMetric::INCREASING:
- if (value >= interval.base) {
- diff = value - interval.base;
- } else if (mUseAbsoluteValueOnReset) {
- diff = value;
- } else {
- VLOG("Unexpected decreasing value");
- StatsdStats::getInstance().notePullDataError(mPullTagId);
- interval.base = value;
- return;
- }
- break;
- case ValueMetric::DECREASING:
- if (interval.base >= value) {
- diff = interval.base - value;
- } else if (mUseAbsoluteValueOnReset) {
- diff = value;
- } else {
- VLOG("Unexpected increasing value");
- StatsdStats::getInstance().notePullDataError(mPullTagId);
- interval.base = value;
- return;
- }
- break;
- case ValueMetric::ANY:
- diff = value - interval.base;
- break;
- default:
- break;
- }
- interval.base = value;
- value = diff;
- }
- if (interval.hasValue) {
- switch (mAggregationType) {
- case ValueMetric::SUM:
- // for AVG, we add up and take average when flushing the bucket
- case ValueMetric::AVG:
- interval.value += value;
- break;
- case ValueMetric::MIN:
- interval.value = std::min(value, interval.value);
- break;
- case ValueMetric::MAX:
- interval.value = std::max(value, interval.value);
- break;
- default:
- break;
+ if (mUseDiff) {
+ // no base. just update base and return.
+ if (!interval.hasBase) {
+ interval.base = value;
+ interval.hasBase = true;
+ return;
+ }
+ Value diff;
+ switch (mValueDirection) {
+ case ValueMetric::INCREASING:
+ if (value >= interval.base) {
+ diff = value - interval.base;
+ } else if (mUseAbsoluteValueOnReset) {
+ diff = value;
+ } else {
+ VLOG("Unexpected decreasing value");
+ StatsdStats::getInstance().notePullDataError(mPullTagId);
+ interval.base = value;
+ return;
+ }
+ break;
+ case ValueMetric::DECREASING:
+ if (interval.base >= value) {
+ diff = interval.base - value;
+ } else if (mUseAbsoluteValueOnReset) {
+ diff = value;
+ } else {
+ VLOG("Unexpected increasing value");
+ StatsdStats::getInstance().notePullDataError(mPullTagId);
+ interval.base = value;
+ return;
+ }
+ break;
+ case ValueMetric::ANY:
+ diff = value - interval.base;
+ break;
+ default:
+ break;
+ }
+ interval.base = value;
+ value = diff;
}
- } else {
- interval.value = value;
- interval.hasValue = true;
+
+ if (interval.hasValue) {
+ switch (mAggregationType) {
+ case ValueMetric::SUM:
+ // for AVG, we add up and take average when flushing the bucket
+ case ValueMetric::AVG:
+ interval.value += value;
+ break;
+ case ValueMetric::MIN:
+ interval.value = std::min(value, interval.value);
+ break;
+ case ValueMetric::MAX:
+ interval.value = std::max(value, interval.value);
+ break;
+ default:
+ break;
+ }
+ } else {
+ interval.value = value;
+ interval.hasValue = true;
+ }
+ interval.sampleSize += 1;
}
- interval.sampleSize += 1;
// TODO: propgate proper values down stream when anomaly support doubles
- long wholeBucketVal = interval.value.long_value;
+ long wholeBucketVal = multiIntervals[0].value.long_value;
auto prev = mCurrentFullBucket.find(eventKey);
if (prev != mCurrentFullBucket.end()) {
wholeBucketVal += prev->second;
@@ -540,7 +581,9 @@
VLOG("Skipping forward %lld buckets", (long long)numBucketsForward);
// take base again in future good bucket.
for (auto& slice : mCurrentSlicedBucket) {
- slice.second.hasBase = false;
+ for (auto& interval : slice.second) {
+ interval.hasBase = false;
+ }
}
}
VLOG("metric %lld: new bucket start time: %lld", (long long)mMetricId,
@@ -552,37 +595,38 @@
(int)mCurrentSlicedBucket.size());
int64_t fullBucketEndTimeNs = getCurrentBucketEndTimeNs();
- ValueBucket info;
- info.mBucketStartNs = mCurrentBucketStartTimeNs;
- if (eventTimeNs < fullBucketEndTimeNs) {
- info.mBucketEndNs = eventTimeNs;
- } else {
- info.mBucketEndNs = fullBucketEndTimeNs;
- }
+ int64_t bucketEndTime = eventTimeNs < fullBucketEndTimeNs ? eventTimeNs : fullBucketEndTimeNs;
- if (info.mBucketEndNs - mCurrentBucketStartTimeNs >= mMinBucketSizeNs) {
+ if (bucketEndTime - mCurrentBucketStartTimeNs >= mMinBucketSizeNs) {
// The current bucket is large enough to keep.
for (const auto& slice : mCurrentSlicedBucket) {
- if (slice.second.hasValue) {
- // skip the output if the diff is zero
- if (mSkipZeroDiffOutput && mUseDiff && slice.second.value.isZero()) {
- continue;
+ ValueBucket bucket;
+ bucket.mBucketStartNs = mCurrentBucketStartTimeNs;
+ bucket.mBucketEndNs = bucketEndTime;
+ for (const auto& interval : slice.second) {
+ if (interval.hasValue) {
+ // skip the output if the diff is zero
+ if (mSkipZeroDiffOutput && mUseDiff && interval.value.isZero()) {
+ continue;
+ }
+ bucket.valueIndex.push_back(interval.valueIndex);
+ if (mAggregationType != ValueMetric::AVG) {
+ bucket.values.push_back(interval.value);
+ } else {
+ double sum = interval.value.type == LONG ? (double)interval.value.long_value
+ : interval.value.double_value;
+ bucket.values.push_back(Value((double)sum / interval.sampleSize));
+ }
}
- if (mAggregationType != ValueMetric::AVG) {
- info.value = slice.second.value;
- } else {
- double sum = slice.second.value.type == LONG
- ? (double)slice.second.value.long_value
- : slice.second.value.double_value;
- info.value.setDouble(sum / slice.second.sampleSize);
- }
- // it will auto create new vector of ValuebucketInfo if the key is not found.
+ }
+ // it will auto create new vector of ValuebucketInfo if the key is not found.
+ if (bucket.valueIndex.size() > 0) {
auto& bucketList = mPastBuckets[slice.first];
- bucketList.push_back(info);
+ bucketList.push_back(bucket);
}
}
} else {
- mSkippedBuckets.emplace_back(info.mBucketStartNs, info.mBucketEndNs);
+ mSkippedBuckets.emplace_back(mCurrentBucketStartTimeNs, bucketEndTime);
}
if (eventTimeNs > fullBucketEndTimeNs) { // If full bucket, send to anomaly tracker.
@@ -590,7 +634,7 @@
if (mCurrentFullBucket.size() > 0) {
for (const auto& slice : mCurrentSlicedBucket) {
// TODO: fix this when anomaly can accept double values
- mCurrentFullBucket[slice.first] += slice.second.value.long_value;
+ mCurrentFullBucket[slice.first] += slice.second[0].value.long_value;
}
for (const auto& slice : mCurrentFullBucket) {
for (auto& tracker : mAnomalyTrackers) {
@@ -606,7 +650,7 @@
for (auto& tracker : mAnomalyTrackers) {
if (tracker != nullptr) {
// TODO: fix this when anomaly can accept double values
- tracker->addPastBucket(slice.first, slice.second.value.long_value,
+ tracker->addPastBucket(slice.first, slice.second[0].value.long_value,
mCurrentBucketNum);
}
}
@@ -616,14 +660,16 @@
// Accumulate partial bucket.
for (const auto& slice : mCurrentSlicedBucket) {
// TODO: fix this when anomaly can accept double values
- mCurrentFullBucket[slice.first] += slice.second.value.long_value;
+ mCurrentFullBucket[slice.first] += slice.second[0].value.long_value;
}
}
// Reset counters
for (auto& slice : mCurrentSlicedBucket) {
- slice.second.hasValue = false;
- slice.second.sampleSize = 0;
+ for (auto& interval : slice.second) {
+ interval.hasValue = false;
+ interval.sampleSize = 0;
+ }
}
}
diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.h b/cmds/statsd/src/metrics/ValueMetricProducer.h
index 3416afe..c3912ee 100644
--- a/cmds/statsd/src/metrics/ValueMetricProducer.h
+++ b/cmds/statsd/src/metrics/ValueMetricProducer.h
@@ -23,6 +23,7 @@
#include "../condition/ConditionTracker.h"
#include "../external/PullDataReceiver.h"
#include "../external/StatsPullerManager.h"
+#include "../matchers/EventMatcherWizard.h"
#include "../stats_log_util.h"
#include "MetricProducer.h"
#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
@@ -34,13 +35,16 @@
struct ValueBucket {
int64_t mBucketStartNs;
int64_t mBucketEndNs;
- Value value;
+ std::vector<int> valueIndex;
+ std::vector<Value> values;
};
class ValueMetricProducer : public virtual MetricProducer, public virtual PullDataReceiver {
public:
ValueMetricProducer(const ConfigKey& key, const ValueMetric& valueMetric,
- const int conditionIndex, const sp<ConditionWizard>& wizard,
+ const int conditionIndex, const sp<ConditionWizard>& conditionWizard,
+ const int whatMatcherIndex,
+ const sp<EventMatcherWizard>& matcherWizard,
const int pullTagId, const int64_t timeBaseNs, const int64_t startTimeNs,
const sp<StatsPullerManager>& pullerManager);
@@ -54,7 +58,7 @@
const int64_t version) override {
std::lock_guard<std::mutex> lock(mMutex);
if (mIsPulled && mCondition) {
- pullLocked(eventTimeNs - 1);
+ pullAndMatchEventsLocked(eventTimeNs - 1);
}
flushCurrentBucketLocked(eventTimeNs);
mCurrentBucketStartTimeNs = eventTimeNs;
@@ -95,9 +99,14 @@
// Calculate previous bucket end time based on current time.
int64_t calcPreviousBucketEndTime(const int64_t currentTimeNs);
+ const int mWhatMatcherIndex;
+
+ sp<EventMatcherWizard> mEventMatcherWizard;
+
sp<StatsPullerManager> mPullerManager;
- const FieldMatcher mValueField;
+ // Value fields for matching.
+ std::vector<Matcher> mFieldMatchers;
// tagId for pulled data. -1 if this is not pulled
const int mPullTagId;
@@ -105,10 +114,10 @@
// if this is pulled metric
const bool mIsPulled;
- int mField;
-
- // internal state of a bucket.
+ // internal state of an ongoing aggregation bucket.
typedef struct {
+ // Index in multi value aggregation.
+ int valueIndex;
// Holds current base value of the dimension. Take diff and update if necessary.
Value base;
// Whether there is a base to diff to.
@@ -122,7 +131,7 @@
bool hasValue;
} Interval;
- std::unordered_map<MetricDimensionKey, Interval> mCurrentSlicedBucket;
+ std::unordered_map<MetricDimensionKey, std::vector<Interval>> mCurrentSlicedBucket;
std::unordered_map<MetricDimensionKey, int64_t> mCurrentFullBucket;
@@ -137,7 +146,7 @@
// Util function to check whether the specified dimension hits the guardrail.
bool hitGuardRailLocked(const MetricDimensionKey& newKey);
- void pullLocked(const int64_t timestampNs);
+ void pullAndMatchEventsLocked(const int64_t timestampNs);
static const size_t kBucketSize = sizeof(ValueBucket{});
@@ -156,6 +165,7 @@
const bool mSkipZeroDiffOutput;
FRIEND_TEST(ValueMetricProducerTest, TestPulledEventsNoCondition);
+ FRIEND_TEST(ValueMetricProducerTest, TestPulledEventsWithFiltering);
FRIEND_TEST(ValueMetricProducerTest, TestPulledEventsTakeAbsoluteValueOnReset);
FRIEND_TEST(ValueMetricProducerTest, TestPulledEventsTakeZeroOnReset);
FRIEND_TEST(ValueMetricProducerTest, TestEventsWithNonSlicedCondition);
diff --git a/cmds/statsd/src/metrics/metrics_manager_util.cpp b/cmds/statsd/src/metrics/metrics_manager_util.cpp
index 136ba07..47b0376 100644
--- a/cmds/statsd/src/metrics/metrics_manager_util.cpp
+++ b/cmds/statsd/src/metrics/metrics_manager_util.cpp
@@ -488,9 +488,9 @@
}
}
- sp<MetricProducer> valueProducer =
- new ValueMetricProducer(key, metric, conditionIndex, wizard, pullTagId,
- timeBaseTimeNs, currentTimeNs, pullerManager);
+ sp<MetricProducer> valueProducer = new ValueMetricProducer(
+ key, metric, conditionIndex, wizard, trackerIndex, matcherWizard, pullTagId,
+ timeBaseTimeNs, currentTimeNs, pullerManager);
allMetricProducers.push_back(valueProducer);
}
diff --git a/cmds/statsd/src/stats_log.proto b/cmds/statsd/src/stats_log.proto
index 3b42b31..5d0f3d1 100644
--- a/cmds/statsd/src/stats_log.proto
+++ b/cmds/statsd/src/stats_log.proto
@@ -108,12 +108,22 @@
optional int64 value = 3 [deprecated = true];
- oneof values {
- int64 value_long = 7;
+ oneof single_value {
+ int64 value_long = 7 [deprecated = true];
- double value_double = 8;
+ double value_double = 8 [deprecated = true];
}
+ message Value {
+ optional int32 index = 1;
+ oneof value {
+ int64 value_long = 2;
+ double value_double = 3;
+ }
+ }
+
+ repeated Value values = 9;
+
optional int64 bucket_num = 4;
optional int64 start_bucket_elapsed_millis = 5;
diff --git a/cmds/statsd/src/statsd_config.proto b/cmds/statsd/src/statsd_config.proto
index 5c46a29..aa789c7 100644
--- a/cmds/statsd/src/statsd_config.proto
+++ b/cmds/statsd/src/statsd_config.proto
@@ -274,6 +274,7 @@
optional bool use_diff = 12;
enum ValueDirection {
+ UNKNOWN = 0;
INCREASING = 1;
DECREASING = 2;
ANY = 3;
diff --git a/cmds/statsd/tests/e2e/ValueMetric_pull_e2e_test.cpp b/cmds/statsd/tests/e2e/ValueMetric_pull_e2e_test.cpp
index 095b401..abf1ab1 100644
--- a/cmds/statsd/tests/e2e/ValueMetric_pull_e2e_test.cpp
+++ b/cmds/statsd/tests/e2e/ValueMetric_pull_e2e_test.cpp
@@ -142,23 +142,23 @@
EXPECT_EQ(baseTimeNs + 2 * bucketSizeNs, data.bucket_info(0).start_bucket_elapsed_nanos());
EXPECT_EQ(baseTimeNs + 3 * bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos());
- EXPECT_TRUE(data.bucket_info(0).has_value_long());
+ EXPECT_EQ(1, data.bucket_info(0).values_size());
EXPECT_EQ(baseTimeNs + 3 * bucketSizeNs, data.bucket_info(1).start_bucket_elapsed_nanos());
EXPECT_EQ(baseTimeNs + 4 * bucketSizeNs, data.bucket_info(1).end_bucket_elapsed_nanos());
- EXPECT_TRUE(data.bucket_info(1).has_value_long());
+ EXPECT_EQ(1, data.bucket_info(1).values_size());
EXPECT_EQ(baseTimeNs + 4 * bucketSizeNs, data.bucket_info(2).start_bucket_elapsed_nanos());
EXPECT_EQ(baseTimeNs + 5 * bucketSizeNs, data.bucket_info(2).end_bucket_elapsed_nanos());
- EXPECT_TRUE(data.bucket_info(2).has_value_long());
+ EXPECT_EQ(1, data.bucket_info(2).values_size());
EXPECT_EQ(baseTimeNs + 6 * bucketSizeNs, data.bucket_info(3).start_bucket_elapsed_nanos());
EXPECT_EQ(baseTimeNs + 7 * bucketSizeNs, data.bucket_info(3).end_bucket_elapsed_nanos());
- EXPECT_TRUE(data.bucket_info(3).has_value_long());
+ EXPECT_EQ(1, data.bucket_info(3).values_size());
EXPECT_EQ(baseTimeNs + 7 * bucketSizeNs, data.bucket_info(4).start_bucket_elapsed_nanos());
EXPECT_EQ(baseTimeNs + 8 * bucketSizeNs, data.bucket_info(4).end_bucket_elapsed_nanos());
- EXPECT_TRUE(data.bucket_info(4).has_value_long());
+ EXPECT_EQ(1, data.bucket_info(4).values_size());
}
TEST(ValueMetricE2eTest, TestPulledEvents_LateAlarm) {
@@ -249,15 +249,15 @@
EXPECT_EQ(baseTimeNs + 2 * bucketSizeNs, data.bucket_info(0).start_bucket_elapsed_nanos());
EXPECT_EQ(baseTimeNs + 3 * bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos());
- EXPECT_TRUE(data.bucket_info(0).has_value_long());
+ EXPECT_EQ(1, data.bucket_info(0).values_size());
EXPECT_EQ(baseTimeNs + 8 * bucketSizeNs, data.bucket_info(1).start_bucket_elapsed_nanos());
EXPECT_EQ(baseTimeNs + 9 * bucketSizeNs, data.bucket_info(1).end_bucket_elapsed_nanos());
- EXPECT_TRUE(data.bucket_info(1).has_value_long());
+ EXPECT_EQ(1, data.bucket_info(1).values_size());
EXPECT_EQ(baseTimeNs + 9 * bucketSizeNs, data.bucket_info(2).start_bucket_elapsed_nanos());
EXPECT_EQ(baseTimeNs + 10 * bucketSizeNs, data.bucket_info(2).end_bucket_elapsed_nanos());
- EXPECT_TRUE(data.bucket_info(2).has_value_long());
+ EXPECT_EQ(1, data.bucket_info(2).values_size());
}
#else
diff --git a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
index ffa07081..44aa00b 100644
--- a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
@@ -12,6 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+#include "src/matchers/SimpleLogMatchingTracker.h"
#include "src/metrics/ValueMetricProducer.h"
#include "src/stats_log_util.h"
#include "metrics_test_helper.h"
@@ -40,6 +41,8 @@
const ConfigKey kConfigKey(0, 12345);
const int tagId = 1;
const int64_t metricId = 123;
+const int64_t atomMatcherId = 678;
+const int logEventMatcherIndex = 0;
const int64_t bucketStartTimeNs = 10000000000;
const int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL;
const int64_t bucket2StartTimeNs = bucketStartTimeNs + bucketSizeNs;
@@ -60,14 +63,20 @@
metric.mutable_value_field()->add_child()->set_field(2);
int64_t startTimeBase = 11;
-
+ UidMap uidMap;
+ SimpleAtomMatcher atomMatcher;
+ atomMatcher.set_atom_id(tagId);
+ sp<EventMatcherWizard> eventMatcherWizard =
+ new EventMatcherWizard({new SimpleLogMatchingTracker(
+ atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
// statsd started long ago.
// The metric starts in the middle of the bucket
ValueMetricProducer valueProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
- -1, startTimeBase, 22, pullerManager);
+ logEventMatcherIndex, eventMatcherWizard, -1, startTimeBase,
+ 22, pullerManager);
EXPECT_EQ(startTimeBase, valueProducer.calcPreviousBucketEndTime(60 * NS_PER_SEC + 10));
EXPECT_EQ(startTimeBase, valueProducer.calcPreviousBucketEndTime(60 * NS_PER_SEC + 10));
@@ -87,13 +96,20 @@
metric.mutable_value_field()->set_field(tagId);
metric.mutable_value_field()->add_child()->set_field(2);
+ UidMap uidMap;
+ SimpleAtomMatcher atomMatcher;
+ atomMatcher.set_atom_id(tagId);
+ sp<EventMatcherWizard> eventMatcherWizard =
+ new EventMatcherWizard({new SimpleLogMatchingTracker(
+ atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
// statsd started long ago.
// The metric starts in the middle of the bucket
ValueMetricProducer valueProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
- -1, 5, 600 * NS_PER_SEC + NS_PER_SEC/2, pullerManager);
+ logEventMatcherIndex, eventMatcherWizard, -1, 5,
+ 600 * NS_PER_SEC + NS_PER_SEC / 2, pullerManager);
EXPECT_EQ(600500000000, valueProducer.mCurrentBucketStartTimeNs);
EXPECT_EQ(10, valueProducer.mCurrentBucketNum);
@@ -110,6 +126,12 @@
metric.mutable_value_field()->set_field(tagId);
metric.mutable_value_field()->add_child()->set_field(2);
+ UidMap uidMap;
+ SimpleAtomMatcher atomMatcher;
+ atomMatcher.set_atom_id(tagId);
+ sp<EventMatcherWizard> eventMatcherWizard =
+ new EventMatcherWizard({new SimpleLogMatchingTracker(
+ atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
@@ -127,7 +149,8 @@
}));
ValueMetricProducer valueProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
- tagId, bucketStartTimeNs, bucketStartTimeNs, pullerManager);
+ logEventMatcherIndex, eventMatcherWizard, tagId,
+ bucketStartTimeNs, bucketStartTimeNs, pullerManager);
vector<shared_ptr<LogEvent>> allData;
allData.clear();
@@ -140,7 +163,7 @@
valueProducer.onDataPulled(allData);
// has one slice
EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
- ValueMetricProducer::Interval curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
+ ValueMetricProducer::Interval curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
EXPECT_EQ(true, curInterval.hasBase);
EXPECT_EQ(11, curInterval.base.long_value);
@@ -157,7 +180,7 @@
valueProducer.onDataPulled(allData);
// has one slice
EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
- curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
+ curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
EXPECT_EQ(true, curInterval.hasBase);
EXPECT_EQ(23, curInterval.base.long_value);
@@ -165,7 +188,7 @@
EXPECT_EQ(12, curInterval.value.long_value);
EXPECT_EQ(1UL, valueProducer.mPastBuckets.size());
EXPECT_EQ(1UL, valueProducer.mPastBuckets.begin()->second.size());
- EXPECT_EQ(8, valueProducer.mPastBuckets.begin()->second.back().value.long_value);
+ EXPECT_EQ(8, valueProducer.mPastBuckets.begin()->second.back().values[0].long_value);
allData.clear();
event = make_shared<LogEvent>(tagId, bucket4StartTimeNs + 1);
@@ -175,7 +198,7 @@
allData.push_back(event);
valueProducer.onDataPulled(allData);
EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
- curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
+ curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
EXPECT_EQ(true, curInterval.hasBase);
EXPECT_EQ(36, curInterval.base.long_value);
@@ -183,7 +206,103 @@
EXPECT_EQ(13, curInterval.value.long_value);
EXPECT_EQ(1UL, valueProducer.mPastBuckets.size());
EXPECT_EQ(2UL, valueProducer.mPastBuckets.begin()->second.size());
- EXPECT_EQ(12, valueProducer.mPastBuckets.begin()->second.back().value.long_value);
+ EXPECT_EQ(12, valueProducer.mPastBuckets.begin()->second.back().values[0].long_value);
+}
+
+/*
+ * Tests pulled atoms with filtering
+ */
+TEST(ValueMetricProducerTest, TestPulledEventsWithFiltering) {
+ ValueMetric metric;
+ metric.set_id(metricId);
+ metric.set_bucket(ONE_MINUTE);
+ metric.mutable_value_field()->set_field(tagId);
+ metric.mutable_value_field()->add_child()->set_field(2);
+
+ UidMap uidMap;
+ SimpleAtomMatcher atomMatcher;
+ atomMatcher.set_atom_id(tagId);
+ auto keyValue = atomMatcher.add_field_value_matcher();
+ keyValue->set_field(1);
+ keyValue->set_eq_int(3);
+ sp<EventMatcherWizard> eventMatcherWizard =
+ new EventMatcherWizard({new SimpleLogMatchingTracker(
+ atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
+ sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+ sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
+ EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
+ EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return());
+ EXPECT_CALL(*pullerManager, Pull(tagId, _, _))
+ .WillOnce(Invoke([](int tagId, int64_t timeNs,
+ vector<std::shared_ptr<LogEvent>>* data) {
+ data->clear();
+ shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs);
+ event->write(3);
+ event->write(3);
+ event->init();
+ data->push_back(event);
+ return true;
+ }));
+
+ ValueMetricProducer valueProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
+ logEventMatcherIndex, eventMatcherWizard, tagId,
+ bucketStartTimeNs, bucketStartTimeNs, pullerManager);
+
+ vector<shared_ptr<LogEvent>> allData;
+ allData.clear();
+ shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 1);
+ event->write(3);
+ event->write(11);
+ event->init();
+ allData.push_back(event);
+
+ valueProducer.onDataPulled(allData);
+ // has one slice
+ EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
+ ValueMetricProducer::Interval curInterval =
+ valueProducer.mCurrentSlicedBucket.begin()->second[0];
+
+ EXPECT_EQ(true, curInterval.hasBase);
+ EXPECT_EQ(11, curInterval.base.long_value);
+ EXPECT_EQ(true, curInterval.hasValue);
+ EXPECT_EQ(8, curInterval.value.long_value);
+ EXPECT_EQ(0UL, valueProducer.mPastBuckets.size());
+
+ allData.clear();
+ event = make_shared<LogEvent>(tagId, bucket3StartTimeNs + 1);
+ event->write(4);
+ event->write(23);
+ event->init();
+ allData.push_back(event);
+ valueProducer.onDataPulled(allData);
+ // has one slice
+ EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
+ curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
+
+ EXPECT_EQ(true, curInterval.hasBase);
+ EXPECT_EQ(11, curInterval.base.long_value);
+ // no events caused flush of bucket
+ EXPECT_EQ(true, curInterval.hasValue);
+ EXPECT_EQ(8, curInterval.value.long_value);
+ EXPECT_EQ(0UL, valueProducer.mPastBuckets.size());
+
+ allData.clear();
+ event = make_shared<LogEvent>(tagId, bucket4StartTimeNs + 1);
+ event->write(3);
+ event->write(36);
+ event->init();
+ allData.push_back(event);
+ valueProducer.onDataPulled(allData);
+ EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
+ curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
+
+ // the base was reset
+ EXPECT_EQ(true, curInterval.hasBase);
+ EXPECT_EQ(36, curInterval.base.long_value);
+ EXPECT_EQ(false, curInterval.hasValue);
+ EXPECT_EQ(1UL, valueProducer.mPastBuckets.size());
+ EXPECT_EQ(1UL, valueProducer.mPastBuckets.begin()->second.size());
+ EXPECT_EQ(8, valueProducer.mPastBuckets.begin()->second.back().values[0].long_value);
}
/*
@@ -197,6 +316,12 @@
metric.mutable_value_field()->add_child()->set_field(2);
metric.set_use_absolute_value_on_reset(true);
+ UidMap uidMap;
+ SimpleAtomMatcher atomMatcher;
+ atomMatcher.set_atom_id(tagId);
+ sp<EventMatcherWizard> eventMatcherWizard =
+ new EventMatcherWizard({new SimpleLogMatchingTracker(
+ atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
@@ -204,7 +329,8 @@
EXPECT_CALL(*pullerManager, Pull(tagId, _, _)).WillOnce(Return(true));
ValueMetricProducer valueProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
- tagId, bucketStartTimeNs, bucketStartTimeNs, pullerManager);
+ logEventMatcherIndex, eventMatcherWizard, tagId,
+ bucketStartTimeNs, bucketStartTimeNs, pullerManager);
vector<shared_ptr<LogEvent>> allData;
allData.clear();
@@ -217,7 +343,7 @@
valueProducer.onDataPulled(allData);
// has one slice
EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
- ValueMetricProducer::Interval curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
+ ValueMetricProducer::Interval curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
EXPECT_EQ(true, curInterval.hasBase);
EXPECT_EQ(11, curInterval.base.long_value);
@@ -233,7 +359,7 @@
valueProducer.onDataPulled(allData);
// has one slice
EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
- curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
+ curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
EXPECT_EQ(true, curInterval.hasBase);
EXPECT_EQ(10, curInterval.base.long_value);
EXPECT_EQ(true, curInterval.hasValue);
@@ -248,14 +374,14 @@
allData.push_back(event);
valueProducer.onDataPulled(allData);
EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
- curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
+ curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
EXPECT_EQ(true, curInterval.hasBase);
EXPECT_EQ(36, curInterval.base.long_value);
EXPECT_EQ(true, curInterval.hasValue);
EXPECT_EQ(26, curInterval.value.long_value);
EXPECT_EQ(1UL, valueProducer.mPastBuckets.size());
EXPECT_EQ(1UL, valueProducer.mPastBuckets.begin()->second.size());
- EXPECT_EQ(10, valueProducer.mPastBuckets.begin()->second.back().value.long_value);
+ EXPECT_EQ(10, valueProducer.mPastBuckets.begin()->second.back().values[0].long_value);
}
/*
@@ -268,6 +394,12 @@
metric.mutable_value_field()->set_field(tagId);
metric.mutable_value_field()->add_child()->set_field(2);
+ UidMap uidMap;
+ SimpleAtomMatcher atomMatcher;
+ atomMatcher.set_atom_id(tagId);
+ sp<EventMatcherWizard> eventMatcherWizard =
+ new EventMatcherWizard({new SimpleLogMatchingTracker(
+ atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
@@ -275,7 +407,8 @@
EXPECT_CALL(*pullerManager, Pull(tagId, _, _)).WillOnce(Return(false));
ValueMetricProducer valueProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
- tagId, bucketStartTimeNs, bucketStartTimeNs, pullerManager);
+ logEventMatcherIndex, eventMatcherWizard, tagId,
+ bucketStartTimeNs, bucketStartTimeNs, pullerManager);
vector<shared_ptr<LogEvent>> allData;
allData.clear();
@@ -288,7 +421,7 @@
valueProducer.onDataPulled(allData);
// has one slice
EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
- ValueMetricProducer::Interval curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
+ ValueMetricProducer::Interval curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
EXPECT_EQ(true, curInterval.hasBase);
EXPECT_EQ(11, curInterval.base.long_value);
@@ -304,7 +437,7 @@
valueProducer.onDataPulled(allData);
// has one slice
EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
- curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
+ curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
EXPECT_EQ(true, curInterval.hasBase);
EXPECT_EQ(10, curInterval.base.long_value);
EXPECT_EQ(false, curInterval.hasValue);
@@ -318,7 +451,7 @@
allData.push_back(event);
valueProducer.onDataPulled(allData);
EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
- curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
+ curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
EXPECT_EQ(true, curInterval.hasBase);
EXPECT_EQ(36, curInterval.base.long_value);
EXPECT_EQ(true, curInterval.hasValue);
@@ -337,6 +470,12 @@
metric.mutable_value_field()->add_child()->set_field(2);
metric.set_condition(StringToId("SCREEN_ON"));
+ UidMap uidMap;
+ SimpleAtomMatcher atomMatcher;
+ atomMatcher.set_atom_id(tagId);
+ sp<EventMatcherWizard> eventMatcherWizard =
+ new EventMatcherWizard({new SimpleLogMatchingTracker(
+ atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
@@ -364,13 +503,14 @@
return true;
}));
- ValueMetricProducer valueProducer(kConfigKey, metric, 1, wizard, tagId, bucketStartTimeNs,
+ ValueMetricProducer valueProducer(kConfigKey, metric, 1, wizard, logEventMatcherIndex,
+ eventMatcherWizard, tagId, bucketStartTimeNs,
bucketStartTimeNs, pullerManager);
valueProducer.onConditionChanged(true, bucketStartTimeNs + 8);
// has one slice
EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
- ValueMetricProducer::Interval curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
+ ValueMetricProducer::Interval curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
// startUpdated:false sum:0 start:100
EXPECT_EQ(true, curInterval.hasBase);
EXPECT_EQ(100, curInterval.base.long_value);
@@ -388,7 +528,7 @@
// has one slice
EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
- curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
+ curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
EXPECT_EQ(true, curInterval.hasBase);
EXPECT_EQ(110, curInterval.base.long_value);
EXPECT_EQ(true, curInterval.hasValue);
@@ -399,7 +539,7 @@
// has one slice
EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
- curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
+ curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
EXPECT_EQ(true, curInterval.hasValue);
EXPECT_EQ(10, curInterval.value.long_value);
EXPECT_EQ(false, curInterval.hasBase);
@@ -412,10 +552,17 @@
metric.mutable_value_field()->set_field(tagId);
metric.mutable_value_field()->add_child()->set_field(2);
+ UidMap uidMap;
+ SimpleAtomMatcher atomMatcher;
+ atomMatcher.set_atom_id(tagId);
+ sp<EventMatcherWizard> eventMatcherWizard =
+ new EventMatcherWizard({new SimpleLogMatchingTracker(
+ atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, -1, bucketStartTimeNs,
- bucketStartTimeNs, pullerManager);
+ ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, logEventMatcherIndex,
+ eventMatcherWizard, -1, bucketStartTimeNs, bucketStartTimeNs,
+ pullerManager);
shared_ptr<LogEvent> event1 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
event1->write(1);
@@ -453,6 +600,12 @@
metric.mutable_value_field()->set_field(tagId);
metric.mutable_value_field()->add_child()->set_field(2);
+ UidMap uidMap;
+ SimpleAtomMatcher atomMatcher;
+ atomMatcher.set_atom_id(tagId);
+ sp<EventMatcherWizard> eventMatcherWizard =
+ new EventMatcherWizard({new SimpleLogMatchingTracker(
+ atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
@@ -469,7 +622,8 @@
data->push_back(event);
return true;
}));
- ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, tagId, bucketStartTimeNs,
+ ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, logEventMatcherIndex,
+ eventMatcherWizard, tagId, bucketStartTimeNs,
bucketStartTimeNs, pullerManager);
vector<shared_ptr<LogEvent>> allData;
@@ -486,7 +640,7 @@
valueProducer.notifyAppUpgrade(bucket2StartTimeNs + 150, "ANY.APP", 1, 1);
EXPECT_EQ(1UL, valueProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
EXPECT_EQ(bucket2StartTimeNs + 150, valueProducer.mCurrentBucketStartTimeNs);
- EXPECT_EQ(20L, valueProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].value.long_value);
+ EXPECT_EQ(20L, valueProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].values[0].long_value);
allData.clear();
event = make_shared<LogEvent>(tagId, bucket3StartTimeNs + 1);
@@ -497,7 +651,7 @@
valueProducer.onDataPulled(allData);
EXPECT_EQ(1UL, valueProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
EXPECT_EQ(bucket2StartTimeNs + 150, valueProducer.mCurrentBucketStartTimeNs);
- EXPECT_EQ(20L, valueProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].value.long_value);
+ EXPECT_EQ(20L, valueProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].values[0].long_value);
}
TEST(ValueMetricProducerTest, TestPulledValueWithUpgradeWhileConditionFalse) {
@@ -508,6 +662,12 @@
metric.mutable_value_field()->add_child()->set_field(2);
metric.set_condition(StringToId("SCREEN_ON"));
+ UidMap uidMap;
+ SimpleAtomMatcher atomMatcher;
+ atomMatcher.set_atom_id(tagId);
+ sp<EventMatcherWizard> eventMatcherWizard =
+ new EventMatcherWizard({new SimpleLogMatchingTracker(
+ atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
@@ -533,7 +693,8 @@
data->push_back(event);
return true;
}));
- ValueMetricProducer valueProducer(kConfigKey, metric, 1, wizard, tagId, bucketStartTimeNs,
+ ValueMetricProducer valueProducer(kConfigKey, metric, 1, wizard, logEventMatcherIndex,
+ eventMatcherWizard, tagId, bucketStartTimeNs,
bucketStartTimeNs, pullerManager);
valueProducer.onConditionChanged(true, bucketStartTimeNs + 1);
@@ -545,7 +706,7 @@
EXPECT_EQ(bucket2StartTimeNs-50, valueProducer.mCurrentBucketStartTimeNs);
EXPECT_EQ(1UL, valueProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
EXPECT_EQ(bucketStartTimeNs, valueProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].mBucketStartNs);
- EXPECT_EQ(20L, valueProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].value.long_value);
+ EXPECT_EQ(20L, valueProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].values[0].long_value);
EXPECT_FALSE(valueProducer.mCondition);
}
@@ -556,11 +717,18 @@
metric.mutable_value_field()->set_field(tagId);
metric.mutable_value_field()->add_child()->set_field(2);
+ UidMap uidMap;
+ SimpleAtomMatcher atomMatcher;
+ atomMatcher.set_atom_id(tagId);
+ sp<EventMatcherWizard> eventMatcherWizard =
+ new EventMatcherWizard({new SimpleLogMatchingTracker(
+ atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, -1, bucketStartTimeNs,
- bucketStartTimeNs, pullerManager);
+ ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, logEventMatcherIndex,
+ eventMatcherWizard, -1, bucketStartTimeNs, bucketStartTimeNs,
+ pullerManager);
shared_ptr<LogEvent> event1 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
event1->write(1);
@@ -573,7 +741,7 @@
valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event1);
// has one slice
EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
- ValueMetricProducer::Interval curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
+ ValueMetricProducer::Interval curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
EXPECT_EQ(10, curInterval.value.long_value);
EXPECT_EQ(true, curInterval.hasValue);
@@ -581,13 +749,13 @@
// has one slice
EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
- curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
+ curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
EXPECT_EQ(30, curInterval.value.long_value);
valueProducer.flushIfNeededLocked(bucket3StartTimeNs);
EXPECT_EQ(1UL, valueProducer.mPastBuckets.size());
EXPECT_EQ(1UL, valueProducer.mPastBuckets.begin()->second.size());
- EXPECT_EQ(30, valueProducer.mPastBuckets.begin()->second.back().value.long_value);
+ EXPECT_EQ(30, valueProducer.mPastBuckets.begin()->second.back().values[0].long_value);
}
TEST(ValueMetricProducerTest, TestPushedEventsWithCondition) {
@@ -597,11 +765,18 @@
metric.mutable_value_field()->set_field(tagId);
metric.mutable_value_field()->add_child()->set_field(2);
+ UidMap uidMap;
+ SimpleAtomMatcher atomMatcher;
+ atomMatcher.set_atom_id(tagId);
+ sp<EventMatcherWizard> eventMatcherWizard =
+ new EventMatcherWizard({new SimpleLogMatchingTracker(
+ atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- ValueMetricProducer valueProducer(kConfigKey, metric, 1, wizard, -1, bucketStartTimeNs,
- bucketStartTimeNs, pullerManager);
+ ValueMetricProducer valueProducer(kConfigKey, metric, 1, wizard, logEventMatcherIndex,
+ eventMatcherWizard, -1, bucketStartTimeNs, bucketStartTimeNs,
+ pullerManager);
shared_ptr<LogEvent> event1 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
event1->write(1);
@@ -620,8 +795,8 @@
// has one slice
EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
- ValueMetricProducer::Interval curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
- curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
+ ValueMetricProducer::Interval curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
+ curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
EXPECT_EQ(20, curInterval.value.long_value);
shared_ptr<LogEvent> event3 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 30);
@@ -632,7 +807,7 @@
// has one slice
EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
- curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
+ curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
EXPECT_EQ(50, curInterval.value.long_value);
valueProducer.onConditionChangedLocked(false, bucketStartTimeNs + 35);
@@ -644,13 +819,13 @@
// has one slice
EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
- curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
+ curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
EXPECT_EQ(50, curInterval.value.long_value);
valueProducer.flushIfNeededLocked(bucket3StartTimeNs);
EXPECT_EQ(1UL, valueProducer.mPastBuckets.size());
EXPECT_EQ(1UL, valueProducer.mPastBuckets.begin()->second.size());
- EXPECT_EQ(50, valueProducer.mPastBuckets.begin()->second.back().value.long_value);
+ EXPECT_EQ(50, valueProducer.mPastBuckets.begin()->second.back().values[0].long_value);
}
TEST(ValueMetricProducerTest, TestAnomalyDetection) {
@@ -669,11 +844,17 @@
metric.mutable_value_field()->set_field(tagId);
metric.mutable_value_field()->add_child()->set_field(2);
+ UidMap uidMap;
+ SimpleAtomMatcher atomMatcher;
+ atomMatcher.set_atom_id(tagId);
+ sp<EventMatcherWizard> eventMatcherWizard =
+ new EventMatcherWizard({new SimpleLogMatchingTracker(
+ atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
ValueMetricProducer valueProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
- -1 /*not pulled*/, bucketStartTimeNs, bucketStartTimeNs,
- pullerManager);
+ logEventMatcherIndex, eventMatcherWizard, -1 /*not pulled*/,
+ bucketStartTimeNs, bucketStartTimeNs, pullerManager);
sp<AnomalyTracker> anomalyTracker = valueProducer.addAnomalyTracker(alert, alarmMonitor);
@@ -744,6 +925,12 @@
metric.mutable_value_field()->set_field(tagId);
metric.mutable_value_field()->add_child()->set_field(2);
+ UidMap uidMap;
+ SimpleAtomMatcher atomMatcher;
+ atomMatcher.set_atom_id(tagId);
+ sp<EventMatcherWizard> eventMatcherWizard =
+ new EventMatcherWizard({new SimpleLogMatchingTracker(
+ atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
@@ -751,7 +938,8 @@
EXPECT_CALL(*pullerManager, Pull(tagId, _, _)).WillOnce(Return(true));
ValueMetricProducer valueProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
- tagId, bucketStartTimeNs, bucketStartTimeNs, pullerManager);
+ logEventMatcherIndex, eventMatcherWizard, tagId,
+ bucketStartTimeNs, bucketStartTimeNs, pullerManager);
vector<shared_ptr<LogEvent>> allData;
// pull 1
@@ -765,7 +953,7 @@
valueProducer.onDataPulled(allData);
// has one slice
EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
- ValueMetricProducer::Interval curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
+ ValueMetricProducer::Interval curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
// startUpdated:true sum:0 start:11
EXPECT_EQ(true, curInterval.hasBase);
@@ -783,7 +971,7 @@
valueProducer.onDataPulled(allData);
// has one slice
EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
- curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
+ curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
// tartUpdated:false sum:12
EXPECT_EQ(true, curInterval.hasBase);
EXPECT_EQ(23, curInterval.base.long_value);
@@ -803,14 +991,14 @@
allData.push_back(event);
valueProducer.onDataPulled(allData);
EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
- curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
+ curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
// startUpdated:false sum:12
EXPECT_EQ(true, curInterval.hasBase);
EXPECT_EQ(36, curInterval.base.long_value);
EXPECT_EQ(false, curInterval.hasValue);
EXPECT_EQ(1UL, valueProducer.mPastBuckets.size());
EXPECT_EQ(1UL, valueProducer.mPastBuckets.begin()->second.size());
- EXPECT_EQ(12, valueProducer.mPastBuckets.begin()->second.back().value.long_value);
+ EXPECT_EQ(12, valueProducer.mPastBuckets.begin()->second.back().values[0].long_value);
}
/*
@@ -825,6 +1013,12 @@
metric.mutable_value_field()->add_child()->set_field(2);
metric.set_condition(StringToId("SCREEN_ON"));
+ UidMap uidMap;
+ SimpleAtomMatcher atomMatcher;
+ atomMatcher.set_atom_id(tagId);
+ sp<EventMatcherWizard> eventMatcherWizard =
+ new EventMatcherWizard({new SimpleLogMatchingTracker(
+ atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
@@ -854,13 +1048,14 @@
return true;
}));
- ValueMetricProducer valueProducer(kConfigKey, metric, 1, wizard, tagId, bucketStartTimeNs,
+ ValueMetricProducer valueProducer(kConfigKey, metric, 1, wizard, logEventMatcherIndex,
+ eventMatcherWizard, tagId, bucketStartTimeNs,
bucketStartTimeNs, pullerManager);
valueProducer.onConditionChanged(true, bucketStartTimeNs + 8);
// has one slice
EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
- ValueMetricProducer::Interval curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
+ ValueMetricProducer::Interval curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
EXPECT_EQ(true, curInterval.hasBase);
EXPECT_EQ(100, curInterval.base.long_value);
EXPECT_EQ(false, curInterval.hasValue);
@@ -868,7 +1063,7 @@
// pull on bucket boundary come late, condition change happens before it
valueProducer.onConditionChanged(false, bucket2StartTimeNs + 1);
- curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
+ curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
EXPECT_EQ(false, curInterval.hasBase);
EXPECT_EQ(true, curInterval.hasValue);
EXPECT_EQ(20, curInterval.value.long_value);
@@ -885,7 +1080,7 @@
allData.push_back(event);
valueProducer.onDataPulled(allData);
- curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
+ curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
EXPECT_EQ(false, curInterval.hasBase);
EXPECT_EQ(true, curInterval.hasValue);
EXPECT_EQ(20, curInterval.value.long_value);
@@ -904,6 +1099,12 @@
metric.mutable_value_field()->add_child()->set_field(2);
metric.set_condition(StringToId("SCREEN_ON"));
+ UidMap uidMap;
+ SimpleAtomMatcher atomMatcher;
+ atomMatcher.set_atom_id(tagId);
+ sp<EventMatcherWizard> eventMatcherWizard =
+ new EventMatcherWizard({new SimpleLogMatchingTracker(
+ atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillRepeatedly(Return());
@@ -944,13 +1145,14 @@
return true;
}));
- ValueMetricProducer valueProducer(kConfigKey, metric, 1, wizard, tagId, bucketStartTimeNs,
+ ValueMetricProducer valueProducer(kConfigKey, metric, 1, wizard, logEventMatcherIndex,
+ eventMatcherWizard, tagId, bucketStartTimeNs,
bucketStartTimeNs, pullerManager);
valueProducer.onConditionChanged(true, bucketStartTimeNs + 8);
// has one slice
EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
- ValueMetricProducer::Interval curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
+ ValueMetricProducer::Interval curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
// startUpdated:false sum:0 start:100
EXPECT_EQ(true, curInterval.hasBase);
EXPECT_EQ(100, curInterval.base.long_value);
@@ -959,7 +1161,7 @@
// pull on bucket boundary come late, condition change happens before it
valueProducer.onConditionChanged(false, bucket2StartTimeNs + 1);
- curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
+ curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
EXPECT_EQ(false, curInterval.hasBase);
EXPECT_EQ(true, curInterval.hasValue);
EXPECT_EQ(20, curInterval.value.long_value);
@@ -967,7 +1169,7 @@
// condition changed to true again, before the pull alarm is delivered
valueProducer.onConditionChanged(true, bucket2StartTimeNs + 25);
- curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
+ curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
EXPECT_EQ(true, curInterval.hasBase);
EXPECT_EQ(130, curInterval.base.long_value);
EXPECT_EQ(true, curInterval.hasValue);
@@ -984,7 +1186,7 @@
allData.push_back(event);
valueProducer.onDataPulled(allData);
- curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
+ curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
EXPECT_EQ(true, curInterval.hasBase);
EXPECT_EQ(130, curInterval.base.long_value);
EXPECT_EQ(true, curInterval.hasValue);
@@ -1000,11 +1202,18 @@
metric.mutable_value_field()->add_child()->set_field(2);
metric.set_aggregation_type(ValueMetric::MIN);
+ UidMap uidMap;
+ SimpleAtomMatcher atomMatcher;
+ atomMatcher.set_atom_id(tagId);
+ sp<EventMatcherWizard> eventMatcherWizard =
+ new EventMatcherWizard({new SimpleLogMatchingTracker(
+ atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, -1, bucketStartTimeNs,
- bucketStartTimeNs, pullerManager);
+ ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, logEventMatcherIndex,
+ eventMatcherWizard, -1, bucketStartTimeNs, bucketStartTimeNs,
+ pullerManager);
shared_ptr<LogEvent> event1 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
event1->write(1);
@@ -1017,7 +1226,7 @@
valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event1);
// has one slice
EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
- ValueMetricProducer::Interval curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
+ ValueMetricProducer::Interval curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
EXPECT_EQ(10, curInterval.value.long_value);
EXPECT_EQ(true, curInterval.hasValue);
@@ -1025,13 +1234,13 @@
// has one slice
EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
- curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
+ curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
EXPECT_EQ(10, curInterval.value.long_value);
valueProducer.flushIfNeededLocked(bucket3StartTimeNs);
EXPECT_EQ(1UL, valueProducer.mPastBuckets.size());
EXPECT_EQ(1UL, valueProducer.mPastBuckets.begin()->second.size());
- EXPECT_EQ(10, valueProducer.mPastBuckets.begin()->second.back().value.long_value);
+ EXPECT_EQ(10, valueProducer.mPastBuckets.begin()->second.back().values[0].long_value);
}
TEST(ValueMetricProducerTest, TestPushedAggregateMax) {
@@ -1042,11 +1251,18 @@
metric.mutable_value_field()->add_child()->set_field(2);
metric.set_aggregation_type(ValueMetric::MAX);
+ UidMap uidMap;
+ SimpleAtomMatcher atomMatcher;
+ atomMatcher.set_atom_id(tagId);
+ sp<EventMatcherWizard> eventMatcherWizard =
+ new EventMatcherWizard({new SimpleLogMatchingTracker(
+ atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, -1, bucketStartTimeNs,
- bucketStartTimeNs, pullerManager);
+ ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, logEventMatcherIndex,
+ eventMatcherWizard, -1, bucketStartTimeNs, bucketStartTimeNs,
+ pullerManager);
shared_ptr<LogEvent> event1 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
event1->write(1);
@@ -1059,7 +1275,7 @@
valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event1);
// has one slice
EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
- ValueMetricProducer::Interval curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
+ ValueMetricProducer::Interval curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
EXPECT_EQ(10, curInterval.value.long_value);
EXPECT_EQ(true, curInterval.hasValue);
@@ -1067,13 +1283,13 @@
// has one slice
EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
- curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
+ curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
EXPECT_EQ(20, curInterval.value.long_value);
valueProducer.flushIfNeededLocked(bucket3StartTimeNs);
EXPECT_EQ(1UL, valueProducer.mPastBuckets.size());
EXPECT_EQ(1UL, valueProducer.mPastBuckets.begin()->second.size());
- EXPECT_EQ(20, valueProducer.mPastBuckets.begin()->second.back().value.long_value);
+ EXPECT_EQ(20, valueProducer.mPastBuckets.begin()->second.back().values[0].long_value);
}
TEST(ValueMetricProducerTest, TestPushedAggregateAvg) {
@@ -1084,11 +1300,18 @@
metric.mutable_value_field()->add_child()->set_field(2);
metric.set_aggregation_type(ValueMetric::AVG);
+ UidMap uidMap;
+ SimpleAtomMatcher atomMatcher;
+ atomMatcher.set_atom_id(tagId);
+ sp<EventMatcherWizard> eventMatcherWizard =
+ new EventMatcherWizard({new SimpleLogMatchingTracker(
+ atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, -1, bucketStartTimeNs,
- bucketStartTimeNs, pullerManager);
+ ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, logEventMatcherIndex,
+ eventMatcherWizard, -1, bucketStartTimeNs, bucketStartTimeNs,
+ pullerManager);
shared_ptr<LogEvent> event1 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
event1->write(1);
@@ -1102,7 +1325,7 @@
// has one slice
EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
ValueMetricProducer::Interval curInterval;
- curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
+ curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
EXPECT_EQ(10, curInterval.value.long_value);
EXPECT_EQ(true, curInterval.hasValue);
EXPECT_EQ(1, curInterval.sampleSize);
@@ -1111,14 +1334,14 @@
// has one slice
EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
- curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
+ curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
EXPECT_EQ(25, curInterval.value.long_value);
EXPECT_EQ(2, curInterval.sampleSize);
valueProducer.flushIfNeededLocked(bucket3StartTimeNs);
EXPECT_EQ(1UL, valueProducer.mPastBuckets.size());
EXPECT_EQ(1UL, valueProducer.mPastBuckets.begin()->second.size());
- EXPECT_TRUE(std::abs(valueProducer.mPastBuckets.begin()->second.back().value.double_value - 12.5) < epsilon);
+ EXPECT_TRUE(std::abs(valueProducer.mPastBuckets.begin()->second.back().values[0].double_value - 12.5) < epsilon);
}
TEST(ValueMetricProducerTest, TestPushedAggregateSum) {
@@ -1129,11 +1352,18 @@
metric.mutable_value_field()->add_child()->set_field(2);
metric.set_aggregation_type(ValueMetric::SUM);
+ UidMap uidMap;
+ SimpleAtomMatcher atomMatcher;
+ atomMatcher.set_atom_id(tagId);
+ sp<EventMatcherWizard> eventMatcherWizard =
+ new EventMatcherWizard({new SimpleLogMatchingTracker(
+ atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, -1, bucketStartTimeNs,
- bucketStartTimeNs, pullerManager);
+ ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, logEventMatcherIndex,
+ eventMatcherWizard, -1, bucketStartTimeNs, bucketStartTimeNs,
+ pullerManager);
shared_ptr<LogEvent> event1 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
event1->write(1);
@@ -1146,7 +1376,7 @@
valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event1);
// has one slice
EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
- ValueMetricProducer::Interval curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
+ ValueMetricProducer::Interval curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
EXPECT_EQ(10, curInterval.value.long_value);
EXPECT_EQ(true, curInterval.hasValue);
@@ -1154,13 +1384,13 @@
// has one slice
EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
- curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
+ curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
EXPECT_EQ(25, curInterval.value.long_value);
valueProducer.flushIfNeededLocked(bucket3StartTimeNs);
EXPECT_EQ(1UL, valueProducer.mPastBuckets.size());
EXPECT_EQ(1UL, valueProducer.mPastBuckets.begin()->second.size());
- EXPECT_EQ(25, valueProducer.mPastBuckets.begin()->second.back().value.long_value);
+ EXPECT_EQ(25, valueProducer.mPastBuckets.begin()->second.back().values[0].long_value);
}
TEST(ValueMetricProducerTest, TestSkipZeroDiffOutput) {
@@ -1172,11 +1402,18 @@
metric.set_aggregation_type(ValueMetric::MIN);
metric.set_use_diff(true);
+ UidMap uidMap;
+ SimpleAtomMatcher atomMatcher;
+ atomMatcher.set_atom_id(tagId);
+ sp<EventMatcherWizard> eventMatcherWizard =
+ new EventMatcherWizard({new SimpleLogMatchingTracker(
+ atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
- ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, -1, bucketStartTimeNs,
- bucketStartTimeNs, pullerManager);
+ ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, logEventMatcherIndex,
+ eventMatcherWizard, -1, bucketStartTimeNs, bucketStartTimeNs,
+ pullerManager);
shared_ptr<LogEvent> event1 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
event1->write(1);
@@ -1189,7 +1426,7 @@
valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event1);
// has one slice
EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
- ValueMetricProducer::Interval curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
+ ValueMetricProducer::Interval curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
EXPECT_EQ(true, curInterval.hasBase);
EXPECT_EQ(10, curInterval.base.long_value);
EXPECT_EQ(false, curInterval.hasValue);
@@ -1198,7 +1435,7 @@
// has one slice
EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
- curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
+ curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
EXPECT_EQ(true, curInterval.hasValue);
EXPECT_EQ(5, curInterval.value.long_value);
@@ -1209,7 +1446,7 @@
event3->init();
valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event3);
EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
- curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
+ curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
EXPECT_EQ(true, curInterval.hasBase);
EXPECT_EQ(15, curInterval.base.long_value);
EXPECT_EQ(true, curInterval.hasValue);
@@ -1220,7 +1457,7 @@
event4->init();
valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event4);
EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
- curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
+ curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
EXPECT_EQ(true, curInterval.hasBase);
EXPECT_EQ(15, curInterval.base.long_value);
EXPECT_EQ(true, curInterval.hasValue);
@@ -1228,7 +1465,7 @@
valueProducer.flushIfNeededLocked(bucket3StartTimeNs);
EXPECT_EQ(1UL, valueProducer.mPastBuckets.size());
EXPECT_EQ(1UL, valueProducer.mPastBuckets.begin()->second.size());
- EXPECT_EQ(5, valueProducer.mPastBuckets.begin()->second.back().value.long_value);
+ EXPECT_EQ(5, valueProducer.mPastBuckets.begin()->second.back().values[0].long_value);
}
} // namespace statsd
diff --git a/config/hiddenapi-light-greylist.txt b/config/hiddenapi-light-greylist.txt
index 0997566..851e35b 100644
--- a/config/hiddenapi-light-greylist.txt
+++ b/config/hiddenapi-light-greylist.txt
@@ -441,6 +441,8 @@
Landroid/hardware/location/IContextHubService$Stub;->asInterface(Landroid/os/IBinder;)Landroid/hardware/location/IContextHubService;
Landroid/hardware/usb/IUsbManager$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
Landroid/hardware/usb/IUsbManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/hardware/usb/IUsbManager;
+Landroid/inputmethodservice/IInputMethodSessionWrapper;->mCaller:Lcom/android/internal/os/HandlerCaller;
+Landroid/inputmethodservice/IInputMethodWrapper;->mCaller:Lcom/android/internal/os/HandlerCaller;
Landroid/location/ICountryDetector$Stub;->asInterface(Landroid/os/IBinder;)Landroid/location/ICountryDetector;
Landroid/location/ICountryListener$Stub;-><init>()V
Landroid/location/IGeocodeProvider$Stub;-><init>()V
@@ -4210,170 +4212,6 @@
Lcom/android/okhttp/Response;->message:Ljava/lang/String;
Lcom/android/okhttp/Response;->networkResponse:Lcom/android/okhttp/Response;
Lcom/android/okhttp/Response;->protocol:Lcom/android/okhttp/Protocol;
-Lcom/android/org/bouncycastle/asn1/ASN1EncodableVector;-><init>()V
-Lcom/android/org/bouncycastle/asn1/ASN1EncodableVector;->add(Lcom/android/org/bouncycastle/asn1/ASN1Encodable;)V
-Lcom/android/org/bouncycastle/asn1/ASN1InputStream;-><init>(Ljava/io/InputStream;)V
-Lcom/android/org/bouncycastle/asn1/ASN1InputStream;-><init>([B)V
-Lcom/android/org/bouncycastle/asn1/ASN1InputStream;->readObject()Lcom/android/org/bouncycastle/asn1/ASN1Primitive;
-Lcom/android/org/bouncycastle/asn1/ASN1Integer;-><init>(Ljava/math/BigInteger;)V
-Lcom/android/org/bouncycastle/asn1/DERBitString;-><init>([B)V
-Lcom/android/org/bouncycastle/asn1/DEREncodableVector;-><init>()V
-Lcom/android/org/bouncycastle/asn1/DERInteger;-><init>(J)V
-Lcom/android/org/bouncycastle/asn1/DERInteger;-><init>(Ljava/math/BigInteger;)V
-Lcom/android/org/bouncycastle/asn1/DERNull;->INSTANCE:Lcom/android/org/bouncycastle/asn1/DERNull;
-Lcom/android/org/bouncycastle/asn1/DERObjectIdentifier;-><init>(Ljava/lang/String;)V
-Lcom/android/org/bouncycastle/asn1/DEROctetString;-><init>([B)V
-Lcom/android/org/bouncycastle/asn1/DEROutputStream;-><init>(Ljava/io/OutputStream;)V
-Lcom/android/org/bouncycastle/asn1/DERSequence;-><init>()V
-Lcom/android/org/bouncycastle/asn1/DERSequence;-><init>(Lcom/android/org/bouncycastle/asn1/ASN1EncodableVector;)V
-Lcom/android/org/bouncycastle/asn1/DERSet;-><init>(Lcom/android/org/bouncycastle/asn1/ASN1EncodableVector;)V
-Lcom/android/org/bouncycastle/asn1/pkcs/PKCSObjectIdentifiers;->sha256WithRSAEncryption:Lcom/android/org/bouncycastle/asn1/ASN1ObjectIdentifier;
-Lcom/android/org/bouncycastle/asn1/x509/AlgorithmIdentifier;-><init>(Lcom/android/org/bouncycastle/asn1/ASN1ObjectIdentifier;)V
-Lcom/android/org/bouncycastle/asn1/x509/AlgorithmIdentifier;-><init>(Lcom/android/org/bouncycastle/asn1/ASN1ObjectIdentifier;Lcom/android/org/bouncycastle/asn1/ASN1Encodable;)V
-Lcom/android/org/bouncycastle/asn1/x509/Certificate;->getInstance(Ljava/lang/Object;)Lcom/android/org/bouncycastle/asn1/x509/Certificate;
-Lcom/android/org/bouncycastle/asn1/x509/DigestInfo;-><init>(Lcom/android/org/bouncycastle/asn1/x509/AlgorithmIdentifier;[B)V
-Lcom/android/org/bouncycastle/asn1/x509/SubjectPublicKeyInfo;->getInstance(Ljava/lang/Object;)Lcom/android/org/bouncycastle/asn1/x509/SubjectPublicKeyInfo;
-Lcom/android/org/bouncycastle/asn1/x509/Time;-><init>(Ljava/util/Date;)V
-Lcom/android/org/bouncycastle/asn1/x509/V3TBSCertificateGenerator;-><init>()V
-Lcom/android/org/bouncycastle/asn1/x509/V3TBSCertificateGenerator;->generateTBSCertificate()Lcom/android/org/bouncycastle/asn1/x509/TBSCertificate;
-Lcom/android/org/bouncycastle/asn1/x509/V3TBSCertificateGenerator;->setEndDate(Lcom/android/org/bouncycastle/asn1/x509/Time;)V
-Lcom/android/org/bouncycastle/asn1/x509/V3TBSCertificateGenerator;->setIssuer(Lcom/android/org/bouncycastle/asn1/x509/X509Name;)V
-Lcom/android/org/bouncycastle/asn1/x509/V3TBSCertificateGenerator;->setSerialNumber(Lcom/android/org/bouncycastle/asn1/ASN1Integer;)V
-Lcom/android/org/bouncycastle/asn1/x509/V3TBSCertificateGenerator;->setSignature(Lcom/android/org/bouncycastle/asn1/x509/AlgorithmIdentifier;)V
-Lcom/android/org/bouncycastle/asn1/x509/V3TBSCertificateGenerator;->setStartDate(Lcom/android/org/bouncycastle/asn1/x509/Time;)V
-Lcom/android/org/bouncycastle/asn1/x509/V3TBSCertificateGenerator;->setSubject(Lcom/android/org/bouncycastle/asn1/x509/X509Name;)V
-Lcom/android/org/bouncycastle/asn1/x509/V3TBSCertificateGenerator;->setSubjectPublicKeyInfo(Lcom/android/org/bouncycastle/asn1/x509/SubjectPublicKeyInfo;)V
-Lcom/android/org/bouncycastle/asn1/x509/X509Name;-><init>(Lcom/android/org/bouncycastle/asn1/ASN1Sequence;)V
-Lcom/android/org/bouncycastle/asn1/x509/X509Name;-><init>(Ljava/lang/String;)V
-Lcom/android/org/bouncycastle/asn1/x509/X509Name;->CN:Lcom/android/org/bouncycastle/asn1/ASN1ObjectIdentifier;
-Lcom/android/org/bouncycastle/asn1/x509/X509Name;->getOIDs()Ljava/util/Vector;
-Lcom/android/org/bouncycastle/asn1/x509/X509Name;->getValues()Ljava/util/Vector;
-Lcom/android/org/bouncycastle/asn1/x9/X9ObjectIdentifiers;->ecdsa_with_SHA256:Lcom/android/org/bouncycastle/asn1/ASN1ObjectIdentifier;
-Lcom/android/org/bouncycastle/jce/provider/BouncyCastleProvider;-><init>()V
-Lcom/android/org/bouncycastle/jce/provider/X509CertificateObject;-><init>(Lcom/android/org/bouncycastle/asn1/x509/Certificate;)V
-Lcom/android/org/bouncycastle/jce/X509Principal;-><init>([B)V
-Lcom/android/org/bouncycastle/x509/X509V3CertificateGenerator;-><init>()V
-Lcom/android/org/bouncycastle/x509/X509V3CertificateGenerator;->generate(Ljava/security/PrivateKey;)Ljava/security/cert/X509Certificate;
-Lcom/android/org/bouncycastle/x509/X509V3CertificateGenerator;->setIssuerDN(Lcom/android/org/bouncycastle/asn1/x509/X509Name;)V
-Lcom/android/org/bouncycastle/x509/X509V3CertificateGenerator;->setIssuerDN(Ljavax/security/auth/x500/X500Principal;)V
-Lcom/android/org/bouncycastle/x509/X509V3CertificateGenerator;->setNotAfter(Ljava/util/Date;)V
-Lcom/android/org/bouncycastle/x509/X509V3CertificateGenerator;->setNotBefore(Ljava/util/Date;)V
-Lcom/android/org/bouncycastle/x509/X509V3CertificateGenerator;->setPublicKey(Ljava/security/PublicKey;)V
-Lcom/android/org/bouncycastle/x509/X509V3CertificateGenerator;->setSerialNumber(Ljava/math/BigInteger;)V
-Lcom/android/org/bouncycastle/x509/X509V3CertificateGenerator;->setSignatureAlgorithm(Ljava/lang/String;)V
-Lcom/android/org/bouncycastle/x509/X509V3CertificateGenerator;->setSubjectDN(Lcom/android/org/bouncycastle/asn1/x509/X509Name;)V
-Lcom/android/org/bouncycastle/x509/X509V3CertificateGenerator;->setSubjectDN(Ljavax/security/auth/x500/X500Principal;)V
-Lcom/android/org/conscrypt/AbstractConscryptSocket;->getAlpnSelectedProtocol()[B
-Lcom/android/org/conscrypt/AbstractConscryptSocket;->getApplicationProtocols()[Ljava/lang/String;
-Lcom/android/org/conscrypt/AbstractConscryptSocket;->getChannelId()[B
-Lcom/android/org/conscrypt/AbstractConscryptSocket;->getHostname()Ljava/lang/String;
-Lcom/android/org/conscrypt/AbstractConscryptSocket;->getHostnameOrIP()Ljava/lang/String;
-Lcom/android/org/conscrypt/AbstractConscryptSocket;->getNpnSelectedProtocol()[B
-Lcom/android/org/conscrypt/AbstractConscryptSocket;->getSoWriteTimeout()I
-Lcom/android/org/conscrypt/AbstractConscryptSocket;->setAlpnProtocols([B)V
-Lcom/android/org/conscrypt/AbstractConscryptSocket;->setAlpnProtocols([Ljava/lang/String;)V
-Lcom/android/org/conscrypt/AbstractConscryptSocket;->setApplicationProtocols([Ljava/lang/String;)V
-Lcom/android/org/conscrypt/AbstractConscryptSocket;->setChannelIdEnabled(Z)V
-Lcom/android/org/conscrypt/AbstractConscryptSocket;->setChannelIdPrivateKey(Ljava/security/PrivateKey;)V
-Lcom/android/org/conscrypt/AbstractConscryptSocket;->setHandshakeTimeout(I)V
-Lcom/android/org/conscrypt/AbstractConscryptSocket;->setHostname(Ljava/lang/String;)V
-Lcom/android/org/conscrypt/AbstractConscryptSocket;->setNpnProtocols([B)V
-Lcom/android/org/conscrypt/AbstractConscryptSocket;->setSoWriteTimeout(I)V
-Lcom/android/org/conscrypt/AbstractConscryptSocket;->setUseSessionTickets(Z)V
-Lcom/android/org/conscrypt/ClientSessionContext;->getSession(Ljava/lang/String;I)Lcom/android/org/conscrypt/NativeSslSession;
-Lcom/android/org/conscrypt/ClientSessionContext;->setPersistentCache(Lcom/android/org/conscrypt/SSLClientSessionCache;)V
-Lcom/android/org/conscrypt/ConscryptFileDescriptorSocket;->setHostname(Ljava/lang/String;)V
-Lcom/android/org/conscrypt/ConscryptFileDescriptorSocket;->setUseSessionTickets(Z)V
-Lcom/android/org/conscrypt/FileClientSessionCache$Impl;->getSessionData(Ljava/lang/String;I)[B
-Lcom/android/org/conscrypt/FileClientSessionCache;->usingDirectory(Ljava/io/File;)Lcom/android/org/conscrypt/SSLClientSessionCache;
-Lcom/android/org/conscrypt/NativeCrypto;->ASN1_seq_pack_X509([J)[B
-Lcom/android/org/conscrypt/NativeCrypto;->ASN1_seq_unpack_X509_bio(J)[J
-Lcom/android/org/conscrypt/NativeCrypto;->ASN1_TIME_to_Calendar(JLjava/util/Calendar;)V
-Lcom/android/org/conscrypt/NativeCrypto;->BIO_free_all(J)V
-Lcom/android/org/conscrypt/NativeCrypto;->create_BIO_InputStream(Lcom/android/org/conscrypt/OpenSSLBIOInputStream;Z)J
-Lcom/android/org/conscrypt/NativeCrypto;->create_BIO_OutputStream(Ljava/io/OutputStream;)J
-Lcom/android/org/conscrypt/NativeCrypto;->d2i_PKCS7_bio(JI)[J
-Lcom/android/org/conscrypt/NativeCrypto;->d2i_SSL_SESSION([B)J
-Lcom/android/org/conscrypt/NativeCrypto;->d2i_X509([B)J
-Lcom/android/org/conscrypt/NativeCrypto;->d2i_X509_bio(J)J
-Lcom/android/org/conscrypt/NativeCrypto;->d2i_X509_CRL_bio(J)J
-Lcom/android/org/conscrypt/NativeCrypto;->EC_GROUP_clear_free(J)V
-Lcom/android/org/conscrypt/NativeCrypto;->EC_GROUP_new_by_curve_name(Ljava/lang/String;)J
-Lcom/android/org/conscrypt/NativeCrypto;->EC_POINT_clear_free(J)V
-Lcom/android/org/conscrypt/NativeCrypto;->EVP_CIPHER_CTX_new()J
-Lcom/android/org/conscrypt/NativeCrypto;->EVP_CIPHER_iv_length(J)I
-Lcom/android/org/conscrypt/NativeCrypto;->EVP_get_cipherbyname(Ljava/lang/String;)J
-Lcom/android/org/conscrypt/NativeCrypto;->EVP_get_digestbyname(Ljava/lang/String;)J
-Lcom/android/org/conscrypt/NativeCrypto;->EVP_MD_CTX_create()J
-Lcom/android/org/conscrypt/NativeCrypto;->EVP_MD_CTX_destroy(J)V
-Lcom/android/org/conscrypt/NativeCrypto;->EVP_MD_size(J)I
-Lcom/android/org/conscrypt/NativeCrypto;->EVP_PKEY_free(J)V
-Lcom/android/org/conscrypt/NativeCrypto;->EVP_PKEY_new_RSA([B[B[B[B[B[B[B[B)J
-Lcom/android/org/conscrypt/NativeCrypto;->get_X509_REVOKED_ext_oids(JI)[Ljava/lang/String;
-Lcom/android/org/conscrypt/NativeCrypto;->get_X509_REVOKED_revocationDate(J)J
-Lcom/android/org/conscrypt/NativeCrypto;->i2d_PKCS7([J)[B
-Lcom/android/org/conscrypt/NativeCrypto;->i2d_SSL_SESSION(J)[B
-Lcom/android/org/conscrypt/NativeCrypto;->i2d_X509_REVOKED(J)[B
-Lcom/android/org/conscrypt/NativeCrypto;->PEM_read_bio_PKCS7(JI)[J
-Lcom/android/org/conscrypt/NativeCrypto;->PEM_read_bio_X509(J)J
-Lcom/android/org/conscrypt/NativeCrypto;->PEM_read_bio_X509_CRL(J)J
-Lcom/android/org/conscrypt/NativeCrypto;->RAND_bytes([B)V
-Lcom/android/org/conscrypt/NativeCrypto;->RSA_generate_key_ex(I[B)J
-Lcom/android/org/conscrypt/NativeCrypto;->SSL_CTX_new()J
-Lcom/android/org/conscrypt/NativeCrypto;->SSL_SESSION_cipher(J)Ljava/lang/String;
-Lcom/android/org/conscrypt/NativeCrypto;->SSL_SESSION_free(J)V
-Lcom/android/org/conscrypt/NativeCrypto;->SSL_SESSION_get_time(J)J
-Lcom/android/org/conscrypt/NativeCrypto;->SSL_SESSION_get_version(J)Ljava/lang/String;
-Lcom/android/org/conscrypt/NativeCrypto;->SSL_SESSION_session_id(J)[B
-Lcom/android/org/conscrypt/NativeCrypto;->X509_REVOKED_dup(J)J
-Lcom/android/org/conscrypt/NativeCrypto;->X509_REVOKED_get_ext(JLjava/lang/String;)J
-Lcom/android/org/conscrypt/NativeCrypto;->X509_REVOKED_get_ext_oid(JLjava/lang/String;)[B
-Lcom/android/org/conscrypt/NativeCrypto;->X509_REVOKED_get_serialNumber(J)[B
-Lcom/android/org/conscrypt/NativeCrypto;->X509_REVOKED_print(JJ)V
-Lcom/android/org/conscrypt/NativeCrypto;->X509_supported_extension(J)I
-Lcom/android/org/conscrypt/OpenSSLBIOInputStream;-><init>(Ljava/io/InputStream;Z)V
-Lcom/android/org/conscrypt/OpenSSLBIOInputStream;->getBioContext()J
-Lcom/android/org/conscrypt/OpenSSLBIOInputStream;->release()V
-Lcom/android/org/conscrypt/OpenSSLContextImpl$TLSv12;-><init>()V
-Lcom/android/org/conscrypt/OpenSSLContextImpl;-><init>()V
-Lcom/android/org/conscrypt/OpenSSLContextImpl;->engineGetClientSessionContext()Lcom/android/org/conscrypt/ClientSessionContext;
-Lcom/android/org/conscrypt/OpenSSLContextImpl;->getPreferred()Lcom/android/org/conscrypt/OpenSSLContextImpl;
-Lcom/android/org/conscrypt/OpenSSLKey;-><init>(J)V
-Lcom/android/org/conscrypt/OpenSSLKey;->fromPrivateKey(Ljava/security/PrivateKey;)Lcom/android/org/conscrypt/OpenSSLKey;
-Lcom/android/org/conscrypt/OpenSSLKey;->getNativeRef()Lcom/android/org/conscrypt/NativeRef$EVP_PKEY;
-Lcom/android/org/conscrypt/OpenSSLKey;->getPublicKey()Ljava/security/PublicKey;
-Lcom/android/org/conscrypt/OpenSSLKeyHolder;->getOpenSSLKey()Lcom/android/org/conscrypt/OpenSSLKey;
-Lcom/android/org/conscrypt/OpenSSLProvider;-><init>()V
-Lcom/android/org/conscrypt/OpenSSLRandom;-><init>()V
-Lcom/android/org/conscrypt/OpenSSLSocketFactoryImpl;-><init>()V
-Lcom/android/org/conscrypt/OpenSSLSocketFactoryImpl;->sslParameters:Lcom/android/org/conscrypt/SSLParametersImpl;
-Lcom/android/org/conscrypt/OpenSSLSocketImpl;->getAlpnSelectedProtocol()[B
-Lcom/android/org/conscrypt/OpenSSLSocketImpl;->getChannelId()[B
-Lcom/android/org/conscrypt/OpenSSLSocketImpl;->getHostname()Ljava/lang/String;
-Lcom/android/org/conscrypt/OpenSSLSocketImpl;->getHostnameOrIP()Ljava/lang/String;
-Lcom/android/org/conscrypt/OpenSSLSocketImpl;->getNpnSelectedProtocol()[B
-Lcom/android/org/conscrypt/OpenSSLSocketImpl;->getSoWriteTimeout()I
-Lcom/android/org/conscrypt/OpenSSLSocketImpl;->setAlpnProtocols([B)V
-Lcom/android/org/conscrypt/OpenSSLSocketImpl;->setAlpnProtocols([Ljava/lang/String;)V
-Lcom/android/org/conscrypt/OpenSSLSocketImpl;->setChannelIdEnabled(Z)V
-Lcom/android/org/conscrypt/OpenSSLSocketImpl;->setChannelIdPrivateKey(Ljava/security/PrivateKey;)V
-Lcom/android/org/conscrypt/OpenSSLSocketImpl;->setHandshakeTimeout(I)V
-Lcom/android/org/conscrypt/OpenSSLSocketImpl;->setHostname(Ljava/lang/String;)V
-Lcom/android/org/conscrypt/OpenSSLSocketImpl;->setNpnProtocols([B)V
-Lcom/android/org/conscrypt/OpenSSLSocketImpl;->setSoWriteTimeout(I)V
-Lcom/android/org/conscrypt/OpenSSLSocketImpl;->setUseSessionTickets(Z)V
-Lcom/android/org/conscrypt/OpenSSLX509Certificate;->fromX509PemInputStream(Ljava/io/InputStream;)Lcom/android/org/conscrypt/OpenSSLX509Certificate;
-Lcom/android/org/conscrypt/OpenSSLX509Certificate;->mContext:J
-Lcom/android/org/conscrypt/SSLParametersImpl;->getDefault()Lcom/android/org/conscrypt/SSLParametersImpl;
-Lcom/android/org/conscrypt/SSLParametersImpl;->getDefaultX509TrustManager()Ljavax/net/ssl/X509TrustManager;
-Lcom/android/org/conscrypt/SSLParametersImpl;->getX509TrustManager()Ljavax/net/ssl/X509TrustManager;
-Lcom/android/org/conscrypt/SSLParametersImpl;->setEnabledProtocols([Ljava/lang/String;)V
-Lcom/android/org/conscrypt/SSLParametersImpl;->x509TrustManager:Ljavax/net/ssl/X509TrustManager;
-Lcom/android/org/conscrypt/TrustedCertificateStore;-><init>()V
-Lcom/android/org/conscrypt/TrustedCertificateStore;->getCertificateChain(Ljava/security/cert/X509Certificate;)Ljava/util/List;
-Lcom/android/org/conscrypt/TrustManagerImpl;-><init>(Ljava/security/KeyStore;)V
-Lcom/android/org/conscrypt/TrustManagerImpl;->checkServerTrusted([Ljava/security/cert/X509Certificate;Ljava/lang/String;Ljava/lang/String;)Ljava/util/List;
-Lcom/android/org/conscrypt/X509PublicKey;-><init>(Ljava/lang/String;[B)V
Lcom/android/server/net/BaseNetworkObserver;-><init>()V
Lcom/android/server/net/NetlinkTracker;-><init>(Ljava/lang/String;Lcom/android/server/net/NetlinkTracker$Callback;)V
Lcom/android/server/net/NetlinkTracker;->clearLinkProperties()V
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index fade574..553acc8 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -31,6 +31,7 @@
import android.annotation.RequiresPermission;
import android.annotation.StyleRes;
import android.annotation.SystemApi;
+import android.annotation.TestApi;
import android.annotation.UnsupportedAppUsage;
import android.app.VoiceInteractor.Request;
import android.app.admin.DevicePolicyManager;
@@ -2346,6 +2347,7 @@
* @see View#onMovedToDisplay(int, Configuration)
* @hide
*/
+ @TestApi
public void onMovedToDisplay(int displayId, Configuration config) {
}
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index d415132..0e5b976 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -161,13 +161,6 @@
public abstract List<ProcessMemoryState> getMemoryStateForProcesses();
/**
- * Returns a list that contains the memory stats for monitored native processes.
- *
- * The list of the monitored processes is defined in MemoryStatUtil class.
- */
- public abstract List<ProcessMemoryState> getMemoryStateForNativeProcesses();
-
- /**
* Checks to see if the calling pid is allowed to handle the user. Returns adjusted user id as
* needed.
*/
@@ -285,4 +278,7 @@
*/
public abstract void setDebugFlagsForStartingActivity(ActivityInfo aInfo, int startFlags,
ProfilerInfo profilerInfo, Object wmLock);
+
+ /** Checks if process running with given pid has access to full external storage or not */
+ public abstract boolean isAppStorageSandboxed(int pid, int uid);
}
diff --git a/core/java/android/app/Instrumentation.java b/core/java/android/app/Instrumentation.java
index ac16442..015bc6c 100644
--- a/core/java/android/app/Instrumentation.java
+++ b/core/java/android/app/Instrumentation.java
@@ -1009,7 +1009,7 @@
* from its event processing, though it may <em>not</em> have completely
* finished reacting from the event -- for example, if it needs to update
* its display as a result, it may still be in the process of doing that.
- *
+ *
* @param event The event to send to the current focus.
*/
public void sendKeySync(KeyEvent event) {
@@ -1017,14 +1017,7 @@
long downTime = event.getDownTime();
long eventTime = event.getEventTime();
- int action = event.getAction();
- int code = event.getKeyCode();
- int repeatCount = event.getRepeatCount();
- int metaState = event.getMetaState();
- int deviceId = event.getDeviceId();
- int scancode = event.getScanCode();
int source = event.getSource();
- int flags = event.getFlags();
if (source == InputDevice.SOURCE_UNKNOWN) {
source = InputDevice.SOURCE_KEYBOARD;
}
@@ -1034,12 +1027,14 @@
if (downTime == 0) {
downTime = eventTime;
}
- KeyEvent newEvent = new KeyEvent(downTime, eventTime, action, code, repeatCount, metaState,
- deviceId, scancode, flags | KeyEvent.FLAG_FROM_SYSTEM, source);
+ KeyEvent newEvent = new KeyEvent(event);
+ newEvent.setTime(downTime, eventTime);
+ newEvent.setSource(source);
+ newEvent.setFlags(event.getFlags() | KeyEvent.FLAG_FROM_SYSTEM);
InputManager.getInstance().injectInputEvent(newEvent,
InputManager.INJECT_INPUT_EVENT_MODE_WAIT_FOR_FINISH);
}
-
+
/**
* Sends an up and down key event sync to the currently focused window.
*
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index 5850540..0123551 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -153,6 +153,7 @@
import android.telephony.TelephonyManager;
import android.telephony.euicc.EuiccCardManager;
import android.telephony.euicc.EuiccManager;
+import android.telephony.rcs.RcsManager;
import android.util.ArrayMap;
import android.util.Log;
import android.view.ContextThemeWrapper;
@@ -547,6 +548,14 @@
return new SubscriptionManager(ctx.getOuterContext());
}});
+ registerService(Context.TELEPHONY_RCS_SERVICE, RcsManager.class,
+ new CachedServiceFetcher<RcsManager>() {
+ @Override
+ public RcsManager createService(ContextImpl ctx) {
+ return new RcsManager();
+ }
+ });
+
registerService(Context.CARRIER_CONFIG_SERVICE, CarrierConfigManager.class,
new CachedServiceFetcher<CarrierConfigManager>() {
@Override
diff --git a/core/java/android/app/usage/UsageEvents.java b/core/java/android/app/usage/UsageEvents.java
index 308b39e..3a5975a 100644
--- a/core/java/android/app/usage/UsageEvents.java
+++ b/core/java/android/app/usage/UsageEvents.java
@@ -50,12 +50,20 @@
public static final int NONE = 0;
/**
- * An event type denoting that a component moved to the foreground.
+ * An event type denoting that an {@link android.app.Activity} moved to the foreground.
+ * This event has a package name and class name associated with it and can be retrieved
+ * using {@link #getPackageName()} and {@link #getClassName()}.
+ * If a package has multiple activities, this event is reported for each activity that moves
+ * to foreground.
*/
public static final int MOVE_TO_FOREGROUND = 1;
/**
- * An event type denoting that a component moved to the background.
+ * An event type denoting that an {@link android.app.Activity} moved to the background.
+ * This event has a package name and class name associated with it and can be retrieved
+ * using {@link #getPackageName()} and {@link #getClassName()}.
+ * If a package has multiple activities, this event is reported for each activity that moves
+ * to background.
*/
public static final int MOVE_TO_BACKGROUND = 2;
@@ -166,10 +174,43 @@
public static final int KEYGUARD_HIDDEN = 18;
/**
+ * An event type denoting start of a foreground service.
+ * This event has a package name and class name associated with it and can be retrieved
+ * using {@link #getPackageName()} and {@link #getClassName()}.
+ * If a package has multiple foreground services, this event is reported for each service
+ * that is started.
+ */
+ public static final int FOREGROUND_SERVICE_START = 19;
+
+ /**
+ * An event type denoting stop of a foreground service.
+ * This event has a package name and class name associated with it and can be retrieved
+ * using {@link #getPackageName()} and {@link #getClassName()}.
+ * If a package has multiple foreground services, this event is reported for each service
+ * that is stopped.
+ */
+ public static final int FOREGROUND_SERVICE_STOP = 20;
+
+ /**
+ * An event type denoting that a foreground service is at started state at beginning of a
+ * time interval.
+ * This is effectively treated as a {@link #FOREGROUND_SERVICE_START}.
+ * {@hide}
+ */
+ public static final int CONTINUING_FOREGROUND_SERVICE = 21;
+
+ /**
+ * An event type denoting that a foreground service is at started state when the stats
+ * rolled-over at the end of a time interval.
+ * {@hide}
+ */
+ public static final int ROLLOVER_FOREGROUND_SERVICE = 22;
+
+ /**
* Keep in sync with the greatest event type value.
* @hide
*/
- public static final int MAX_EVENT_TYPE = 18;
+ public static final int MAX_EVENT_TYPE = 22;
/** @hide */
public static final int FLAG_IS_PACKAGE_INSTANT_APP = 1 << 0;
diff --git a/core/java/android/app/usage/UsageStats.java b/core/java/android/app/usage/UsageStats.java
index 0659a23..73426e4 100644
--- a/core/java/android/app/usage/UsageStats.java
+++ b/core/java/android/app/usage/UsageStats.java
@@ -16,6 +16,15 @@
package android.app.usage;
+import static android.app.usage.UsageEvents.Event.CONTINUE_PREVIOUS_DAY;
+import static android.app.usage.UsageEvents.Event.CONTINUING_FOREGROUND_SERVICE;
+import static android.app.usage.UsageEvents.Event.END_OF_DAY;
+import static android.app.usage.UsageEvents.Event.FOREGROUND_SERVICE_START;
+import static android.app.usage.UsageEvents.Event.FOREGROUND_SERVICE_STOP;
+import static android.app.usage.UsageEvents.Event.MOVE_TO_BACKGROUND;
+import static android.app.usage.UsageEvents.Event.MOVE_TO_FOREGROUND;
+import static android.app.usage.UsageEvents.Event.ROLLOVER_FOREGROUND_SERVICE;
+
import android.annotation.SystemApi;
import android.annotation.UnsupportedAppUsage;
import android.os.Bundle;
@@ -48,19 +57,32 @@
public long mEndTimeStamp;
/**
- * Last time used by the user with an explicit action (notification, activity launch).
+ * Last time used by the user with an explicit action (notification, activity launch)
* {@hide}
*/
@UnsupportedAppUsage
public long mLastTimeUsed;
/**
+ * Total time this package's activity is in foreground.
* {@hide}
*/
@UnsupportedAppUsage
public long mTotalTimeInForeground;
/**
+ * Last time foreground service is started.
+ * {@hide}
+ */
+ public long mLastTimeForegroundServiceUsed;
+
+ /**
+ * Total time this package's foreground service is started.
+ * {@hide}
+ */
+ public long mTotalTimeForegroundServiceUsed;
+
+ /**
* {@hide}
*/
@UnsupportedAppUsage
@@ -71,16 +93,36 @@
*/
public int mAppLaunchCount;
- /**
+ /** Last activity MOVE_TO_FOREGROUND or MOVE_TO_BACKGROUND event.
* {@hide}
+ * @deprecated use {@link #mLastForegroundActivityEventMap} instead.
*/
@UnsupportedAppUsage
+ @Deprecated
public int mLastEvent;
/**
+ * If an activity is in foreground, it has one entry in this map.
+ * When activity moves to background, it is removed from this map.
+ * Key is activity class name.
+ * Value is last time this activity MOVE_TO_FOREGROUND or MOVE_TO_BACKGROUND event.
+ * {@hide}
+ */
+ public ArrayMap<String, Integer> mLastForegroundActivityEventMap = new ArrayMap<>();
+
+ /**
+ * If a foreground service is started, it has one entry in this map.
+ * When a foreground service is stopped, it is removed from this map.
+ * Key is foreground service class name.
+ * Value is last foreground service FOREGROUND_SERVICE_START ot FOREGROUND_SERVICE_STOP event.
+ * {@hide}
+ */
+ public ArrayMap<String, Integer> mLastForegroundServiceEventMap = new ArrayMap<>();
+
+ /**
* {@hide}
*/
- public ArrayMap<String, ArrayMap<String, Integer>> mChooserCounts;
+ public ArrayMap<String, ArrayMap<String, Integer>> mChooserCounts = new ArrayMap<>();
/**
* {@hide}
@@ -93,10 +135,14 @@
mBeginTimeStamp = stats.mBeginTimeStamp;
mEndTimeStamp = stats.mEndTimeStamp;
mLastTimeUsed = stats.mLastTimeUsed;
+ mLastTimeForegroundServiceUsed = stats.mLastTimeForegroundServiceUsed;
mTotalTimeInForeground = stats.mTotalTimeInForeground;
+ mTotalTimeForegroundServiceUsed = stats.mTotalTimeForegroundServiceUsed;
mLaunchCount = stats.mLaunchCount;
mAppLaunchCount = stats.mAppLaunchCount;
mLastEvent = stats.mLastEvent;
+ mLastForegroundActivityEventMap = stats.mLastForegroundActivityEventMap;
+ mLastForegroundServiceEventMap = stats.mLastForegroundServiceEventMap;
mChooserCounts = stats.mChooserCounts;
}
@@ -136,7 +182,7 @@
}
/**
- * Get the last time this package was used, measured in milliseconds since the epoch.
+ * Get the last time this package's activity was used, measured in milliseconds since the epoch.
* <p/>
* See {@link System#currentTimeMillis()}.
*/
@@ -152,6 +198,23 @@
}
/**
+ * Get the last time this package's foreground service was used, measured in milliseconds since
+ * the epoch.
+ * <p/>
+ * See {@link System#currentTimeMillis()}.
+ */
+ public long getLastTimeForegroundServiceUsed() {
+ return mLastTimeForegroundServiceUsed;
+ }
+
+ /**
+ * Get the total time this package's foreground services are started, measured in milliseconds.
+ */
+ public long getTotalTimeForegroundServiceUsed() {
+ return mTotalTimeForegroundServiceUsed;
+ }
+
+ /**
* Returns the number of times the app was launched as an activity from outside of the app.
* Excludes intra-app activity transitions.
* @hide
@@ -161,6 +224,19 @@
return mAppLaunchCount;
}
+ private void mergeEventMap(ArrayMap<String, Integer> left, ArrayMap<String, Integer> right) {
+ final int size = right.size();
+ for (int i = 0; i < size; i++) {
+ final String className = right.keyAt(i);
+ final Integer event = right.valueAt(i);
+ if (left.containsKey(className)) {
+ left.put(className, Math.max(left.get(className), event));
+ } else {
+ left.put(className, event);
+ }
+ }
+ }
+
/**
* Add the statistics from the right {@link UsageStats} to the left. The package name for
* both {@link UsageStats} objects must be the same.
@@ -179,12 +255,16 @@
if (right.mBeginTimeStamp > mBeginTimeStamp) {
// Even though incoming UsageStat begins after this one, its last time used fields
// may somehow be empty or chronologically preceding the older UsageStat.
- mLastEvent = Math.max(mLastEvent, right.mLastEvent);
+ mergeEventMap(mLastForegroundActivityEventMap, right.mLastForegroundActivityEventMap);
+ mergeEventMap(mLastForegroundServiceEventMap, right.mLastForegroundServiceEventMap);
mLastTimeUsed = Math.max(mLastTimeUsed, right.mLastTimeUsed);
+ mLastTimeForegroundServiceUsed = Math.max(mLastTimeForegroundServiceUsed,
+ right.mLastTimeForegroundServiceUsed);
}
mBeginTimeStamp = Math.min(mBeginTimeStamp, right.mBeginTimeStamp);
mEndTimeStamp = Math.max(mEndTimeStamp, right.mEndTimeStamp);
mTotalTimeInForeground += right.mTotalTimeInForeground;
+ mTotalTimeForegroundServiceUsed += right.mTotalTimeForegroundServiceUsed;
mLaunchCount += right.mLaunchCount;
mAppLaunchCount += right.mAppLaunchCount;
if (mChooserCounts == null) {
@@ -209,6 +289,161 @@
}
}
+ /**
+ * Tell if an event indicate activity is in foreground or not.
+ * @param event the activity event.
+ * @return true if activity is in foreground, false otherwise.
+ * @hide
+ */
+ private boolean isActivityInForeground(int event) {
+ return event == MOVE_TO_FOREGROUND
+ || event == CONTINUE_PREVIOUS_DAY;
+ }
+
+ /**
+ * Tell if an event indicate foreground sevice is started or not.
+ * @param event the foreground service event.
+ * @return true if foreground service is started, false if stopped.
+ * @hide
+ */
+ private boolean isForegroundServiceStarted(int event) {
+ return event == FOREGROUND_SERVICE_START
+ || event == CONTINUING_FOREGROUND_SERVICE;
+ }
+
+ /**
+ * If any activity in foreground or any foreground service is started, the app is considered in
+ * use.
+ * @return true if in use, false otherwise.
+ * @hide
+ */
+ private boolean isAppInUse() {
+ return !mLastForegroundActivityEventMap.isEmpty()
+ || !mLastForegroundServiceEventMap.isEmpty();
+ }
+
+ /**
+ * Update by an event of an activity.
+ * @param className className of the activity.
+ * @param timeStamp timeStamp of the event.
+ * @param eventType type of the event.
+ * @hide
+ */
+ private void updateForegroundActivity(String className, long timeStamp, int eventType) {
+ if (eventType != MOVE_TO_BACKGROUND
+ && eventType != MOVE_TO_FOREGROUND
+ && eventType != END_OF_DAY) {
+ return;
+ }
+
+ final Integer lastEvent = mLastForegroundActivityEventMap.get(className);
+ if (lastEvent != null) {
+ if (isActivityInForeground(lastEvent)) {
+ if (timeStamp > mLastTimeUsed) {
+ mTotalTimeInForeground += timeStamp - mLastTimeUsed;
+ mLastTimeUsed = timeStamp;
+ }
+ }
+ if (eventType == MOVE_TO_BACKGROUND) {
+ mLastForegroundActivityEventMap.remove(className);
+ } else {
+ mLastForegroundActivityEventMap.put(className, eventType);
+ }
+ } else if (eventType == MOVE_TO_FOREGROUND) {
+ if (!isAppInUse()) {
+ mLastTimeUsed = timeStamp;
+ }
+ mLastForegroundActivityEventMap.put(className, eventType);
+ }
+ }
+
+ /**
+ * Update by an event of an foreground service.
+ * @param className className of the foreground service.
+ * @param timeStamp timeStamp of the event.
+ * @param eventType type of the event.
+ * @hide
+ */
+ private void updateForegroundService(String className, long timeStamp, int eventType) {
+ if (eventType != FOREGROUND_SERVICE_STOP
+ && eventType != FOREGROUND_SERVICE_START
+ && eventType != ROLLOVER_FOREGROUND_SERVICE) {
+ return;
+ }
+ final Integer lastEvent = mLastForegroundServiceEventMap.get(className);
+ if (lastEvent != null) {
+ if (isForegroundServiceStarted(lastEvent)) {
+ if (timeStamp > mLastTimeForegroundServiceUsed) {
+ mTotalTimeForegroundServiceUsed +=
+ timeStamp - mLastTimeForegroundServiceUsed;
+ mLastTimeForegroundServiceUsed = timeStamp;
+ }
+ }
+ if (eventType == FOREGROUND_SERVICE_STOP) {
+ mLastForegroundServiceEventMap.remove(className);
+ } else {
+ mLastForegroundServiceEventMap.put(className, eventType);
+ }
+ } else if (eventType == FOREGROUND_SERVICE_START) {
+ if (!isAppInUse()) {
+ mLastTimeForegroundServiceUsed = timeStamp;
+ }
+ mLastForegroundServiceEventMap.put(className, eventType);
+ }
+ }
+
+ /**
+ * Update the UsageStats by a activity or foreground service event.
+ * @param className class name of a activity or foreground service, could be null to mark
+ * END_OF_DAY or rollover.
+ * @param timeStamp Epoch timestamp in milliseconds.
+ * @param eventType event type as in {@link UsageEvents.Event}
+ * @hide
+ */
+ public void update(String className, long timeStamp, int eventType) {
+ switch(eventType) {
+ case MOVE_TO_BACKGROUND:
+ case MOVE_TO_FOREGROUND:
+ updateForegroundActivity(className, timeStamp, eventType);
+ break;
+ case END_OF_DAY:
+ // END_OF_DAY means updating all activities.
+ final int size = mLastForegroundActivityEventMap.size();
+ for (int i = 0; i < size; i++) {
+ final String name = mLastForegroundActivityEventMap.keyAt(i);
+ updateForegroundActivity(name, timeStamp, eventType);
+ }
+ break;
+ case CONTINUE_PREVIOUS_DAY:
+ mLastTimeUsed = timeStamp;
+ mLastForegroundActivityEventMap.put(className, eventType);
+ break;
+ case FOREGROUND_SERVICE_STOP:
+ case FOREGROUND_SERVICE_START:
+ updateForegroundService(className, timeStamp, eventType);
+ break;
+ case ROLLOVER_FOREGROUND_SERVICE:
+ // ROLLOVER_FOREGROUND_SERVICE means updating all foreground services.
+ final int size2 = mLastForegroundServiceEventMap.size();
+ for (int i = 0; i < size2; i++) {
+ final String name = mLastForegroundServiceEventMap.keyAt(i);
+ updateForegroundService(name, timeStamp, eventType);
+ }
+ break;
+ case CONTINUING_FOREGROUND_SERVICE:
+ mLastTimeForegroundServiceUsed = timeStamp;
+ mLastForegroundServiceEventMap.put(className, eventType);
+ break;
+ default:
+ break;
+ }
+ mEndTimeStamp = timeStamp;
+
+ if (eventType == MOVE_TO_FOREGROUND) {
+ mLaunchCount += 1;
+ }
+ }
+
@Override
public int describeContents() {
return 0;
@@ -220,7 +455,9 @@
dest.writeLong(mBeginTimeStamp);
dest.writeLong(mEndTimeStamp);
dest.writeLong(mLastTimeUsed);
+ dest.writeLong(mLastTimeForegroundServiceUsed);
dest.writeLong(mTotalTimeInForeground);
+ dest.writeLong(mTotalTimeForegroundServiceUsed);
dest.writeInt(mLaunchCount);
dest.writeInt(mAppLaunchCount);
dest.writeInt(mLastEvent);
@@ -239,6 +476,22 @@
}
}
dest.writeBundle(allCounts);
+
+ final Bundle foregroundActivityEventBundle = new Bundle();
+ final int foregroundEventSize = mLastForegroundActivityEventMap.size();
+ for (int i = 0; i < foregroundEventSize; i++) {
+ foregroundActivityEventBundle.putInt(mLastForegroundActivityEventMap.keyAt(i),
+ mLastForegroundActivityEventMap.valueAt(i));
+ }
+ dest.writeBundle(foregroundActivityEventBundle);
+
+ final Bundle foregroundServiceEventBundle = new Bundle();
+ final int foregroundServiceEventSize = mLastForegroundServiceEventMap.size();
+ for (int i = 0; i < foregroundServiceEventSize; i++) {
+ foregroundServiceEventBundle.putInt(mLastForegroundServiceEventMap.keyAt(i),
+ mLastForegroundServiceEventMap.valueAt(i));
+ }
+ dest.writeBundle(foregroundServiceEventBundle);
}
public static final Creator<UsageStats> CREATOR = new Creator<UsageStats>() {
@@ -249,7 +502,9 @@
stats.mBeginTimeStamp = in.readLong();
stats.mEndTimeStamp = in.readLong();
stats.mLastTimeUsed = in.readLong();
+ stats.mLastTimeForegroundServiceUsed = in.readLong();
stats.mTotalTimeInForeground = in.readLong();
+ stats.mTotalTimeForegroundServiceUsed = in.readLong();
stats.mLaunchCount = in.readInt();
stats.mAppLaunchCount = in.readInt();
stats.mLastEvent = in.readInt();
@@ -272,9 +527,20 @@
}
}
}
+ readBundleToEventMap(stats.mLastForegroundActivityEventMap, in.readBundle());
+ readBundleToEventMap(stats.mLastForegroundServiceEventMap, in.readBundle());
return stats;
}
+ private void readBundleToEventMap(ArrayMap<String, Integer> eventMap, Bundle bundle) {
+ if (bundle != null) {
+ for (String className : bundle.keySet()) {
+ final int event = bundle.getInt(className);
+ eventMap.put(className, event);
+ }
+ }
+ }
+
@Override
public UsageStats[] newArray(int size) {
return new UsageStats[size];
diff --git a/core/java/android/app/usage/UsageStatsManager.java b/core/java/android/app/usage/UsageStatsManager.java
index 6d7400e..5514851 100644
--- a/core/java/android/app/usage/UsageStatsManager.java
+++ b/core/java/android/app/usage/UsageStatsManager.java
@@ -192,7 +192,10 @@
public static final int REASON_SUB_USAGE_EXEMPTED_SYNC_SCHEDULED_DOZE = 0x000C;
/** @hide */
public static final int REASON_SUB_USAGE_EXEMPTED_SYNC_START = 0x000D;
-
+ /** @hide */
+ public static final int REASON_SUB_USAGE_FOREGROUND_SERVICE_START = 0x000E;
+ /** @hide */
+ public static final int REASON_SUB_USAGE_FOREGROUND_SERVICE_STOP = 0x000F;
/** @hide */
public static final int REASON_SUB_PREDICTED_RESTORED = 0x0001;
diff --git a/core/java/android/appwidget/AppWidgetHostView.java b/core/java/android/appwidget/AppWidgetHostView.java
index c6e94c7..318dbee 100644
--- a/core/java/android/appwidget/AppWidgetHostView.java
+++ b/core/java/android/appwidget/AppWidgetHostView.java
@@ -24,6 +24,8 @@
import android.content.ContextWrapper;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
+import android.content.pm.LauncherActivityInfo;
+import android.content.pm.LauncherApps;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.res.Resources;
import android.graphics.Color;
@@ -622,6 +624,7 @@
}
}
defaultView = inflater.inflate(layoutId, this, false);
+ defaultView.setOnClickListener(this::onDefaultViewClicked);
} else {
Log.w(TAG, "can't inflate defaultView because mInfo is missing");
}
@@ -641,6 +644,19 @@
return defaultView;
}
+ private void onDefaultViewClicked(View view) {
+ if (mInfo != null) {
+ LauncherApps launcherApps = getContext().getSystemService(LauncherApps.class);
+ List<LauncherActivityInfo> activities = launcherApps.getActivityList(
+ mInfo.provider.getPackageName(), mInfo.getProfile());
+ if (!activities.isEmpty()) {
+ LauncherActivityInfo ai = activities.get(0);
+ launcherApps.startMainActivity(ai.getComponentName(), ai.getUser(),
+ RemoteViews.getSourceBounds(view), null);
+ }
+ }
+ }
+
/**
* Inflate and return a view that represents an error state.
*/
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 2aa32c4..9f8ae0b 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -4367,6 +4367,13 @@
public static final String APP_BINDING_SERVICE = "app_binding";
/**
+ * Use with {@link #getSystemService(String)} to retrieve an
+ * {@link android.telephony.rcs.RcsManager}.
+ * @hide
+ */
+ public static final String TELEPHONY_RCS_SERVICE = "ircs";
+
+ /**
* Determine whether the given permission is allowed for a particular
* process and user ID running in the system.
*
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index 6b5c659..48240db 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -27,7 +27,6 @@
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.res.Resources;
import android.graphics.drawable.Drawable;
-import android.os.Build;
import android.os.Environment;
import android.os.Parcel;
import android.os.Parcelable;
@@ -1171,11 +1170,11 @@
* system apps.
* @hide
*/
- public static final int HIDDEN_API_ENFORCEMENT_NONE = 0;
+ public static final int HIDDEN_API_ENFORCEMENT_DISABLED = 0;
/**
* No API enforcement, but enable the detection logic and warnings. Observed behaviour is the
- * same as {@link #HIDDEN_API_ENFORCEMENT_NONE} but you may see warnings in the log when APIs
- * are accessed.
+ * same as {@link #HIDDEN_API_ENFORCEMENT_DISABLED} but you may see warnings in the log when
+ * APIs are accessed.
* @hide
* */
public static final int HIDDEN_API_ENFORCEMENT_JUST_WARN = 1;
@@ -1183,14 +1182,10 @@
* Dark grey list enforcement. Enforces the dark grey and black lists
* @hide
*/
- public static final int HIDDEN_API_ENFORCEMENT_DARK_GREY_AND_BLACK = 2;
- /**
- * Blacklist enforcement only.
- * @hide
- */
- public static final int HIDDEN_API_ENFORCEMENT_BLACK = 3;
+ public static final int HIDDEN_API_ENFORCEMENT_ENABLED = 2;
- private static final int HIDDEN_API_ENFORCEMENT_MAX = HIDDEN_API_ENFORCEMENT_BLACK;
+ private static final int HIDDEN_API_ENFORCEMENT_MIN = HIDDEN_API_ENFORCEMENT_DEFAULT;
+ private static final int HIDDEN_API_ENFORCEMENT_MAX = HIDDEN_API_ENFORCEMENT_ENABLED;
/**
* Values in this IntDef MUST be kept in sync with enum hiddenapi::EnforcementPolicy in
@@ -1199,17 +1194,16 @@
*/
@IntDef(prefix = { "HIDDEN_API_ENFORCEMENT_" }, value = {
HIDDEN_API_ENFORCEMENT_DEFAULT,
- HIDDEN_API_ENFORCEMENT_NONE,
+ HIDDEN_API_ENFORCEMENT_DISABLED,
HIDDEN_API_ENFORCEMENT_JUST_WARN,
- HIDDEN_API_ENFORCEMENT_DARK_GREY_AND_BLACK,
- HIDDEN_API_ENFORCEMENT_BLACK,
+ HIDDEN_API_ENFORCEMENT_ENABLED,
})
@Retention(RetentionPolicy.SOURCE)
public @interface HiddenApiEnforcementPolicy {}
/** @hide */
public static boolean isValidHiddenApiEnforcementPolicy(int policy) {
- return policy >= HIDDEN_API_ENFORCEMENT_DEFAULT && policy <= HIDDEN_API_ENFORCEMENT_MAX;
+ return policy >= HIDDEN_API_ENFORCEMENT_MIN && policy <= HIDDEN_API_ENFORCEMENT_MAX;
}
private int mHiddenApiPolicy = HIDDEN_API_ENFORCEMENT_DEFAULT;
@@ -1749,16 +1743,12 @@
*/
public @HiddenApiEnforcementPolicy int getHiddenApiEnforcementPolicy() {
if (isAllowedToUseHiddenApis()) {
- return HIDDEN_API_ENFORCEMENT_NONE;
+ return HIDDEN_API_ENFORCEMENT_DISABLED;
}
if (mHiddenApiPolicy != HIDDEN_API_ENFORCEMENT_DEFAULT) {
return mHiddenApiPolicy;
}
- if (targetSdkVersion < Build.VERSION_CODES.P) {
- return HIDDEN_API_ENFORCEMENT_BLACK;
- } else {
- return HIDDEN_API_ENFORCEMENT_DARK_GREY_AND_BLACK;
- }
+ return HIDDEN_API_ENFORCEMENT_ENABLED;
}
/**
@@ -1777,23 +1767,15 @@
* This will have no effect if this app is not subject to hidden API enforcement, i.e. if it
* is on the package whitelist.
*
- * @param policyPreP configured policy for pre-P apps, or {@link
- * #HIDDEN_API_ENFORCEMENT_DEFAULT} if nothing configured.
- * @param policyP configured policy for apps targeting P or later, or {@link
- * #HIDDEN_API_ENFORCEMENT_DEFAULT} if nothing configured.
+ * @param policy configured policy for this app, or {@link #HIDDEN_API_ENFORCEMENT_DEFAULT}
+ * if nothing configured.
* @hide
*/
- public void maybeUpdateHiddenApiEnforcementPolicy(
- @HiddenApiEnforcementPolicy int policyPreP, @HiddenApiEnforcementPolicy int policyP) {
+ public void maybeUpdateHiddenApiEnforcementPolicy(@HiddenApiEnforcementPolicy int policy) {
if (isPackageWhitelistedForHiddenApis()) {
return;
}
- if (targetSdkVersion < Build.VERSION_CODES.P) {
- setHiddenApiEnforcementPolicy(policyPreP);
- } else if (targetSdkVersion >= Build.VERSION_CODES.P) {
- setHiddenApiEnforcementPolicy(policyP);
- }
-
+ setHiddenApiEnforcementPolicy(policy);
}
/**
diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java
index 444ca87..7148b12 100644
--- a/core/java/android/hardware/camera2/CameraCharacteristics.java
+++ b/core/java/android/hardware/camera2/CameraCharacteristics.java
@@ -28,8 +28,8 @@
import android.hardware.camera2.utils.TypeReference;
import android.util.Rational;
-import java.util.Arrays;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
@@ -2668,7 +2668,8 @@
/**
* <p>The arrangement of color filters on sensor;
* represents the colors in the top-left 2x2 section of
- * the sensor, in reading order.</p>
+ * the sensor, in reading order, for a Bayer camera, or the
+ * light spectrum it captures for MONOCHROME camera.</p>
* <p><b>Possible values:</b>
* <ul>
* <li>{@link #SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_RGGB RGGB}</li>
@@ -2676,6 +2677,8 @@
* <li>{@link #SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_GBRG GBRG}</li>
* <li>{@link #SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_BGGR BGGR}</li>
* <li>{@link #SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_RGB RGB}</li>
+ * <li>{@link #SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_MONO MONO}</li>
+ * <li>{@link #SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_NIR NIR}</li>
* </ul></p>
* <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
* <p><b>Full capability</b> -
@@ -2688,6 +2691,8 @@
* @see #SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_GBRG
* @see #SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_BGGR
* @see #SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_RGB
+ * @see #SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_MONO
+ * @see #SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_NIR
*/
@PublicKey
public static final Key<Integer> SENSOR_INFO_COLOR_FILTER_ARRANGEMENT =
@@ -2919,6 +2924,8 @@
* <p>Some devices may choose to provide a second set of calibration
* information for improved quality, including
* {@link CameraCharacteristics#SENSOR_REFERENCE_ILLUMINANT2 android.sensor.referenceIlluminant2} and its corresponding matrices.</p>
+ * <p>Starting from Android Q, this key will not be present for a MONOCHROME camera, even if
+ * the camera device has RAW capability.</p>
* <p><b>Possible values:</b>
* <ul>
* <li>{@link #SENSOR_REFERENCE_ILLUMINANT1_DAYLIGHT DAYLIGHT}</li>
@@ -2981,6 +2988,8 @@
* <p>If this key is present, then {@link CameraCharacteristics#SENSOR_COLOR_TRANSFORM2 android.sensor.colorTransform2},
* {@link CameraCharacteristics#SENSOR_CALIBRATION_TRANSFORM2 android.sensor.calibrationTransform2}, and
* {@link CameraCharacteristics#SENSOR_FORWARD_MATRIX2 android.sensor.forwardMatrix2} will also be present.</p>
+ * <p>Starting from Android Q, this key will not be present for a MONOCHROME camera, even if
+ * the camera device has RAW capability.</p>
* <p><b>Range of valid values:</b><br>
* Any value listed in {@link CameraCharacteristics#SENSOR_REFERENCE_ILLUMINANT1 android.sensor.referenceIlluminant1}</p>
* <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
@@ -3006,6 +3015,8 @@
* colorspace) into this camera device's native sensor color
* space under the first reference illuminant
* ({@link CameraCharacteristics#SENSOR_REFERENCE_ILLUMINANT1 android.sensor.referenceIlluminant1}).</p>
+ * <p>Starting from Android Q, this key will not be present for a MONOCHROME camera, even if
+ * the camera device has RAW capability.</p>
* <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
* <p><b>Permission {@link android.Manifest.permission#CAMERA } is needed to access this property</b></p>
*
@@ -3029,6 +3040,8 @@
* ({@link CameraCharacteristics#SENSOR_REFERENCE_ILLUMINANT2 android.sensor.referenceIlluminant2}).</p>
* <p>This matrix will only be present if the second reference
* illuminant is present.</p>
+ * <p>Starting from Android Q, this key will not be present for a MONOCHROME camera, even if
+ * the camera device has RAW capability.</p>
* <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
* <p><b>Permission {@link android.Manifest.permission#CAMERA } is needed to access this property</b></p>
*
@@ -3053,6 +3066,8 @@
* and the CIE XYZ colorspace when calculating this transform will
* match the standard white point for the first reference illuminant
* (i.e. no chromatic adaptation will be applied by this transform).</p>
+ * <p>Starting from Android Q, this key will not be present for a MONOCHROME camera, even if
+ * the camera device has RAW capability.</p>
* <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
* <p><b>Permission {@link android.Manifest.permission#CAMERA } is needed to access this property</b></p>
*
@@ -3079,6 +3094,8 @@
* (i.e. no chromatic adaptation will be applied by this transform).</p>
* <p>This matrix will only be present if the second reference
* illuminant is present.</p>
+ * <p>Starting from Android Q, this key will not be present for a MONOCHROME camera, even if
+ * the camera device has RAW capability.</p>
* <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
* <p><b>Permission {@link android.Manifest.permission#CAMERA } is needed to access this property</b></p>
*
@@ -3101,6 +3118,8 @@
* this matrix is chosen so that the standard white point for this reference
* illuminant in the reference sensor colorspace is mapped to D50 in the
* CIE XYZ colorspace.</p>
+ * <p>Starting from Android Q, this key will not be present for a MONOCHROME camera, even if
+ * the camera device has RAW capability.</p>
* <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
* <p><b>Permission {@link android.Manifest.permission#CAMERA } is needed to access this property</b></p>
*
@@ -3125,6 +3144,8 @@
* CIE XYZ colorspace.</p>
* <p>This matrix will only be present if the second reference
* illuminant is present.</p>
+ * <p>Starting from Android Q, this key will not be present for a MONOCHROME camera, even if
+ * the camera device has RAW capability.</p>
* <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
* <p><b>Permission {@link android.Manifest.permission#CAMERA } is needed to access this property</b></p>
*
@@ -3153,6 +3174,7 @@
* level values. For raw capture in particular, it is recommended to use
* pixels from {@link CameraCharacteristics#SENSOR_OPTICAL_BLACK_REGIONS android.sensor.opticalBlackRegions} to calculate black
* level values for each frame.</p>
+ * <p>For a MONOCHROME camera device, all of the 2x2 channels must have the same values.</p>
* <p><b>Range of valid values:</b><br>
* >= 0 for each.</p>
* <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
diff --git a/core/java/android/hardware/camera2/CameraDevice.java b/core/java/android/hardware/camera2/CameraDevice.java
index ac00f14..dc6cffc 100644
--- a/core/java/android/hardware/camera2/CameraDevice.java
+++ b/core/java/android/hardware/camera2/CameraDevice.java
@@ -357,7 +357,7 @@
* </p>
*
* <p>MONOCHROME-capability ({@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES}
- * includes {@link CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_MONOCHROME MONOCHROME})
+ * includes {@link CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_MONOCHROME MONOCHROME}) devices
* supporting {@link android.graphics.ImageFormat#Y8 Y8} support substituting {@code YUV}
* streams with {@code Y8} in all guaranteed stream combinations for the device's hardware level
* and capabilities.</p>
diff --git a/core/java/android/hardware/camera2/CameraMetadata.java b/core/java/android/hardware/camera2/CameraMetadata.java
index ffc2264..402472a 100644
--- a/core/java/android/hardware/camera2/CameraMetadata.java
+++ b/core/java/android/hardware/camera2/CameraMetadata.java
@@ -880,11 +880,15 @@
/**
* <p>The camera device is a monochrome camera that doesn't contain a color filter array,
- * and the pixel values on U and V planes are all 128.</p>
+ * and for YUV_420_888 stream, the pixel values on U and V planes are all 128.</p>
* <p>A MONOCHROME camera must support the guaranteed stream combinations required for
* its device level and capabilities. Additionally, if the monochrome camera device
* supports Y8 format, all mandatory stream combination requirements related to {@link android.graphics.ImageFormat#YUV_420_888 YUV_420_888} apply
- * to {@link android.graphics.ImageFormat#Y8 Y8} as well.</p>
+ * to {@link android.graphics.ImageFormat#Y8 Y8} as well. There are no
+ * mandatory stream combination requirements with regard to
+ * {@link android.graphics.ImageFormat#Y8 Y8} for Bayer camera devices.</p>
+ * <p>Starting from Android Q, the SENSOR_INFO_COLOR_FILTER_ARRANGEMENT of a MONOCHROME
+ * camera will be either MONO or NIR.</p>
* @see CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES
*/
public static final int REQUEST_AVAILABLE_CAPABILITIES_MONOCHROME = 12;
@@ -937,6 +941,23 @@
*/
public static final int SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_RGB = 4;
+ /**
+ * <p>Sensor doesn't have any Bayer color filter.
+ * Such sensor captures visible light in monochrome. The exact weighting and
+ * wavelengths captured is not specified, but generally only includes the visible
+ * frequencies. This value implies a MONOCHROME camera.</p>
+ * @see CameraCharacteristics#SENSOR_INFO_COLOR_FILTER_ARRANGEMENT
+ */
+ public static final int SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_MONO = 5;
+
+ /**
+ * <p>Sensor has a near infrared filter capturing light with wavelength between
+ * roughly 750nm and 1400nm, and the same filter covers the whole sensor array. This
+ * value implies a MONOCHROME camera.</p>
+ * @see CameraCharacteristics#SENSOR_INFO_COLOR_FILTER_ARRANGEMENT
+ */
+ public static final int SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_NIR = 6;
+
//
// Enumeration values for CameraCharacteristics#SENSOR_INFO_TIMESTAMP_SOURCE
//
diff --git a/core/java/android/hardware/camera2/CaptureRequest.java b/core/java/android/hardware/camera2/CaptureRequest.java
index 2744e91..8ebaf2f 100644
--- a/core/java/android/hardware/camera2/CaptureRequest.java
+++ b/core/java/android/hardware/camera2/CaptureRequest.java
@@ -24,8 +24,8 @@
import android.hardware.camera2.impl.SyntheticKey;
import android.hardware.camera2.params.OutputConfiguration;
import android.hardware.camera2.utils.HashCodeHelpers;
-import android.hardware.camera2.utils.TypeReference;
import android.hardware.camera2.utils.SurfaceUtils;
+import android.hardware.camera2.utils.TypeReference;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.ArraySet;
@@ -2947,8 +2947,8 @@
* of points can be less than max (that is, the request doesn't have to
* always provide a curve with number of points equivalent to
* {@link CameraCharacteristics#TONEMAP_MAX_CURVE_POINTS android.tonemap.maxCurvePoints}).</p>
- * <p>For devices with MONOCHROME capability, only red channel is used. Green and blue channels
- * are ignored.</p>
+ * <p>For devices with MONOCHROME capability, all three channels must have the same set of
+ * control points.</p>
* <p>A few examples, and their corresponding graphical mappings; these
* only specify the red channel and the precision is limited to 4
* digits, for conciseness.</p>
@@ -3011,8 +3011,8 @@
* of points can be less than max (that is, the request doesn't have to
* always provide a curve with number of points equivalent to
* {@link CameraCharacteristics#TONEMAP_MAX_CURVE_POINTS android.tonemap.maxCurvePoints}).</p>
- * <p>For devices with MONOCHROME capability, only red channel is used. Green and blue channels
- * are ignored.</p>
+ * <p>For devices with MONOCHROME capability, all three channels must have the same set of
+ * control points.</p>
* <p>A few examples, and their corresponding graphical mappings; these
* only specify the red channel and the precision is limited to 4
* digits, for conciseness.</p>
diff --git a/core/java/android/hardware/camera2/CaptureResult.java b/core/java/android/hardware/camera2/CaptureResult.java
index 2b67f3e..3d70c51 100644
--- a/core/java/android/hardware/camera2/CaptureResult.java
+++ b/core/java/android/hardware/camera2/CaptureResult.java
@@ -3417,6 +3417,8 @@
* used to interpolate between the provided color transforms when
* processing raw sensor data.</p>
* <p>The order of the values is R, G, B; where R is in the lowest index.</p>
+ * <p>Starting from Android Q, this key will not be present for a MONOCHROME camera, even if
+ * the camera device has RAW capability.</p>
* <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
*/
@PublicKey
@@ -3442,6 +3444,8 @@
* that channel.</p>
* <p>A more detailed description of the noise model can be found in the
* Adobe DNG specification for the NoiseProfile tag.</p>
+ * <p>For a MONOCHROME camera, there is only one color channel. So the noise model coefficients
+ * will only contain one S and one O.</p>
* <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
*
* @see CameraCharacteristics#SENSOR_INFO_COLOR_FILTER_ARRANGEMENT
@@ -3482,6 +3486,8 @@
* <li>R > 1.20 will require strong software correction to produce
* a usuable image (>20% divergence).</li>
* </ul>
+ * <p>Starting from Android Q, this key will not be present for a MONOCHROME camera, even if
+ * the camera device has RAW capability.</p>
* <p><b>Range of valid values:</b><br></p>
* <p>>= 0</p>
* <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
@@ -3592,6 +3598,7 @@
* layout key (see {@link CameraCharacteristics#SENSOR_INFO_COLOR_FILTER_ARRANGEMENT android.sensor.info.colorFilterArrangement}), i.e. the
* nth value given corresponds to the black level offset for the nth
* color channel listed in the CFA.</p>
+ * <p>For a MONOCHROME camera, all of the 2x2 channels must have the same values.</p>
* <p>This key will be available if {@link CameraCharacteristics#SENSOR_OPTICAL_BLACK_REGIONS android.sensor.opticalBlackRegions} is available or the
* camera device advertises this key via {@link android.hardware.camera2.CameraCharacteristics#getAvailableCaptureResultKeys }.</p>
* <p><b>Range of valid values:</b><br>
@@ -3852,6 +3859,17 @@
* <p>As a visualization only, inverting the full-color map to recover an
* image of a gray wall (using bicubic interpolation for visual quality) as captured by the sensor gives:</p>
* <p><img alt="Image of a uniform white wall (inverse shading map)" src="/reference/images/camera2/metadata/android.statistics.lensShadingMap/inv_shading.png" /></p>
+ * <p>For a MONOCHROME camera, all of the 2x2 channels must have the same values. An example
+ * shading map for such a camera is defined as:</p>
+ * <pre><code>android.lens.info.shadingMapSize = [ 4, 3 ]
+ * android.statistics.lensShadingMap =
+ * [ 1.3, 1.3, 1.3, 1.3, 1.2, 1.2, 1.2, 1.2,
+ * 1.1, 1.1, 1.1, 1.1, 1.3, 1.3, 1.3, 1.3,
+ * 1.2, 1.2, 1.2, 1.2, 1.1, 1.1, 1.1, 1.1,
+ * 1.0, 1.0, 1.0, 1.0, 1.2, 1.2, 1.2, 1.2,
+ * 1.3, 1.3, 1.3, 1.3, 1.2, 1.2, 1.2, 1.2,
+ * 1.2, 1.2, 1.2, 1.2, 1.3, 1.3, 1.3, 1.3 ]
+ * </code></pre>
* <p><b>Range of valid values:</b><br>
* Each gain factor is >= 1</p>
* <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
@@ -3894,13 +3912,13 @@
* (x,y) ϵ (0 ... N-1, 0 ... M-1) is the value of the shading map at
* pixel ( ((W-1)/(N-1)) * x, ((H-1)/(M-1)) * y) for the four color channels.
* The map is assumed to be bilinearly interpolated between the sample points.</p>
- * <p>The channel order is [R, Geven, Godd, B], where Geven is the green
- * channel for the even rows of a Bayer pattern, and Godd is the odd rows.
+ * <p>For a Bayer camera, the channel order is [R, Geven, Godd, B], where Geven is
+ * the green channel for the even rows of a Bayer pattern, and Godd is the odd rows.
* The shading map is stored in a fully interleaved format, and its size
* is provided in the camera static metadata by android.lens.info.shadingMapSize.</p>
* <p>The shading map will generally have on the order of 30-40 rows and columns,
* and will be smaller than 64x64.</p>
- * <p>As an example, given a very small map defined as:</p>
+ * <p>As an example, given a very small map for a Bayer camera defined as:</p>
* <pre><code>android.lens.info.shadingMapSize = [ 4, 3 ]
* android.statistics.lensShadingMap =
* [ 1.3, 1.2, 1.15, 1.2, 1.2, 1.2, 1.15, 1.2,
@@ -3920,6 +3938,17 @@
* image of a gray wall (using bicubic interpolation for visual quality)
* as captured by the sensor gives:</p>
* <p><img alt="Image of a uniform white wall (inverse shading map)" src="/reference/images/camera2/metadata/android.statistics.lensShadingMap/inv_shading.png" /></p>
+ * <p>For a MONOCHROME camera, all of the 2x2 channels must have the same values. An example
+ * shading map for such a camera is defined as:</p>
+ * <pre><code>android.lens.info.shadingMapSize = [ 4, 3 ]
+ * android.statistics.lensShadingMap =
+ * [ 1.3, 1.3, 1.3, 1.3, 1.2, 1.2, 1.2, 1.2,
+ * 1.1, 1.1, 1.1, 1.1, 1.3, 1.3, 1.3, 1.3,
+ * 1.2, 1.2, 1.2, 1.2, 1.1, 1.1, 1.1, 1.1,
+ * 1.0, 1.0, 1.0, 1.0, 1.2, 1.2, 1.2, 1.2,
+ * 1.3, 1.3, 1.3, 1.3, 1.2, 1.2, 1.2, 1.2,
+ * 1.2, 1.2, 1.2, 1.2, 1.3, 1.3, 1.3, 1.3 ]
+ * </code></pre>
* <p>Note that the RAW image data might be subject to lens shading
* correction not reported on this map. Query
* {@link CameraCharacteristics#SENSOR_INFO_LENS_SHADING_APPLIED android.sensor.info.lensShadingApplied} to see if RAW image data has subject
@@ -4250,8 +4279,8 @@
* of points can be less than max (that is, the request doesn't have to
* always provide a curve with number of points equivalent to
* {@link CameraCharacteristics#TONEMAP_MAX_CURVE_POINTS android.tonemap.maxCurvePoints}).</p>
- * <p>For devices with MONOCHROME capability, only red channel is used. Green and blue channels
- * are ignored.</p>
+ * <p>For devices with MONOCHROME capability, all three channels must have the same set of
+ * control points.</p>
* <p>A few examples, and their corresponding graphical mappings; these
* only specify the red channel and the precision is limited to 4
* digits, for conciseness.</p>
@@ -4314,8 +4343,8 @@
* of points can be less than max (that is, the request doesn't have to
* always provide a curve with number of points equivalent to
* {@link CameraCharacteristics#TONEMAP_MAX_CURVE_POINTS android.tonemap.maxCurvePoints}).</p>
- * <p>For devices with MONOCHROME capability, only red channel is used. Green and blue channels
- * are ignored.</p>
+ * <p>For devices with MONOCHROME capability, all three channels must have the same set of
+ * control points.</p>
* <p>A few examples, and their corresponding graphical mappings; these
* only specify the red channel and the precision is limited to 4
* digits, for conciseness.</p>
diff --git a/core/java/android/hardware/camera2/params/BlackLevelPattern.java b/core/java/android/hardware/camera2/params/BlackLevelPattern.java
index 6d6c094..283977f 100644
--- a/core/java/android/hardware/camera2/params/BlackLevelPattern.java
+++ b/core/java/android/hardware/camera2/params/BlackLevelPattern.java
@@ -16,13 +16,17 @@
package android.hardware.camera2.params;
-import java.util.Arrays;
-
import static com.android.internal.util.Preconditions.checkNotNull;
+import java.util.Arrays;
+
/**
* Immutable class to store a 4-element vector of integers corresponding to a 2x2 pattern
* of color channel offsets used for the black level offsets of each color channel.
+ *
+ * For a camera device with
+ * {@link android.hardware.camera2.CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES_MONOCHROME
+ * MONOCHROME} capability, all 4 elements of the pattern will have the same value.
*/
public final class BlackLevelPattern {
@@ -133,6 +137,12 @@
* {@link android.hardware.camera2.CameraCharacteristics#SENSOR_INFO_COLOR_FILTER_ARRANGEMENT}).
* </p>
*
+ * <p>A {@link
+ * android.hardware.camera2.CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES_MONOCHROME
+ * MONOCHROME} camera only has one channel. As a result, the returned string will contain 4
+ * identical values.
+ * </p>
+ *
* @return string representation of {@link BlackLevelPattern}
*
* @see android.hardware.camera2.CameraCharacteristics#SENSOR_INFO_COLOR_FILTER_ARRANGEMENT
diff --git a/core/java/android/hardware/camera2/params/TonemapCurve.java b/core/java/android/hardware/camera2/params/TonemapCurve.java
index 71e68a5..90e6355 100644
--- a/core/java/android/hardware/camera2/params/TonemapCurve.java
+++ b/core/java/android/hardware/camera2/params/TonemapCurve.java
@@ -34,6 +34,10 @@
* use as the tonemapping/contrast/gamma curve when {@link CaptureRequest#TONEMAP_MODE} is
* set to {@link CameraMetadata#TONEMAP_MODE_CONTRAST_CURVE}.</p>
*
+ * <p>For a camera device with
+ * {@link android.hardware.camera2.CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES_MONOCHROME
+ * MONOCHROME} capability, all 3 channels will contain the same set of control points.
+ *
* <p>The total number of points {@code (Pin, Pout)} for each color channel can be no more than
* {@link CameraCharacteristics#TONEMAP_MAX_CURVE_POINTS}.</p>
*
diff --git a/core/java/android/inputmethodservice/MultiClientInputMethodClientCallbackAdaptor.java b/core/java/android/inputmethodservice/MultiClientInputMethodClientCallbackAdaptor.java
new file mode 100644
index 0000000..b4b541d
--- /dev/null
+++ b/core/java/android/inputmethodservice/MultiClientInputMethodClientCallbackAdaptor.java
@@ -0,0 +1,469 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.inputmethodservice;
+
+import android.annotation.Nullable;
+import android.annotation.WorkerThread;
+import android.graphics.Rect;
+import android.os.Bundle;
+import android.os.Debug;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.ResultReceiver;
+import android.util.Log;
+import android.view.InputChannel;
+import android.view.InputDevice;
+import android.view.InputEvent;
+import android.view.InputEventReceiver;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+import android.view.WindowManager.LayoutParams.SoftInputModeFlags;
+import android.view.inputmethod.CompletionInfo;
+import android.view.inputmethod.CursorAnchorInfo;
+import android.view.inputmethod.EditorInfo;
+import android.view.inputmethod.ExtractedText;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.inputmethod.IMultiClientInputMethodSession;
+import com.android.internal.os.SomeArgs;
+import com.android.internal.util.function.pooled.PooledLambda;
+import com.android.internal.view.IInputContext;
+import com.android.internal.view.IInputMethodSession;
+import com.android.internal.view.InputConnectionWrapper;
+
+import java.lang.ref.WeakReference;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+/**
+ * Re-dispatches all the incoming per-client events to the specified {@link Looper} thread.
+ *
+ * <p>There are three types of per-client callbacks.</p>
+ *
+ * <ul>
+ * <li>{@link IInputMethodSession} - from the IME client</li>
+ * <li>{@link IMultiClientInputMethodSession} - from MultiClientInputMethodManagerService</li>
+ * <li>{@link InputChannel} - from the IME client</li>
+ * </ul>
+ *
+ * <p>This class serializes all the incoming events among those channels onto
+ * {@link MultiClientInputMethodServiceDelegate.ClientCallback} on the specified {@link Looper}
+ * thread.</p>
+ */
+final class MultiClientInputMethodClientCallbackAdaptor {
+ static final boolean DEBUG = false;
+ static final String TAG = MultiClientInputMethodClientCallbackAdaptor.class.getSimpleName();
+
+ private final Object mSessionLock = new Object();
+ @GuardedBy("mSessionLock")
+ CallbackImpl mCallbackImpl;
+ @GuardedBy("mSessionLock")
+ InputChannel mReadChannel;
+ @GuardedBy("mSessionLock")
+ KeyEvent.DispatcherState mDispatcherState;
+ @GuardedBy("mSessionLock")
+ Handler mHandler;
+ @GuardedBy("mSessionLock")
+ @Nullable
+ InputEventReceiver mInputEventReceiver;
+
+ private final AtomicBoolean mFinished = new AtomicBoolean(false);
+
+ IInputMethodSession.Stub createIInputMethodSession() {
+ synchronized (mSessionLock) {
+ return new InputMethodSessionImpl(
+ mSessionLock, mCallbackImpl, mHandler, mFinished);
+ }
+ }
+
+ IMultiClientInputMethodSession.Stub createIMultiClientInputMethodSession() {
+ synchronized (mSessionLock) {
+ return new MultiClientInputMethodSessionImpl(
+ mSessionLock, mCallbackImpl, mHandler, mFinished);
+ }
+ }
+
+ MultiClientInputMethodClientCallbackAdaptor(
+ MultiClientInputMethodServiceDelegate.ClientCallback clientCallback, Looper looper,
+ KeyEvent.DispatcherState dispatcherState, InputChannel readChannel) {
+ synchronized (mSessionLock) {
+ mCallbackImpl = new CallbackImpl(this, clientCallback);
+ mDispatcherState = dispatcherState;
+ mHandler = new Handler(looper, null, true);
+ mReadChannel = readChannel;
+ mInputEventReceiver = new ImeInputEventReceiver(mReadChannel, mHandler.getLooper(),
+ mFinished, mDispatcherState, mCallbackImpl.mOriginalCallback);
+ }
+ }
+
+ private static final class KeyEventCallbackAdaptor implements KeyEvent.Callback {
+ private final MultiClientInputMethodServiceDelegate.ClientCallback mLocalCallback;
+
+ KeyEventCallbackAdaptor(
+ MultiClientInputMethodServiceDelegate.ClientCallback callback) {
+ mLocalCallback = callback;
+ }
+
+ @Override
+ public boolean onKeyDown(int keyCode, KeyEvent event) {
+ return mLocalCallback.onKeyDown(keyCode, event);
+ }
+
+ @Override
+ public boolean onKeyLongPress(int keyCode, KeyEvent event) {
+ return mLocalCallback.onKeyLongPress(keyCode, event);
+ }
+
+ @Override
+ public boolean onKeyUp(int keyCode, KeyEvent event) {
+ return mLocalCallback.onKeyUp(keyCode, event);
+ }
+
+ @Override
+ public boolean onKeyMultiple(int keyCode, int count, KeyEvent event) {
+ return mLocalCallback.onKeyMultiple(keyCode, event);
+ }
+ }
+
+ private static final class ImeInputEventReceiver extends InputEventReceiver {
+ private final AtomicBoolean mFinished;
+ private final KeyEvent.DispatcherState mDispatcherState;
+ private final MultiClientInputMethodServiceDelegate.ClientCallback mClientCallback;
+ private final KeyEventCallbackAdaptor mKeyEventCallbackAdaptor;
+
+ ImeInputEventReceiver(InputChannel readChannel, Looper looper, AtomicBoolean finished,
+ KeyEvent.DispatcherState dispatcherState,
+ MultiClientInputMethodServiceDelegate.ClientCallback callback) {
+ super(readChannel, looper);
+ mFinished = finished;
+ mDispatcherState = dispatcherState;
+ mClientCallback = callback;
+ mKeyEventCallbackAdaptor = new KeyEventCallbackAdaptor(callback);
+ }
+
+ @Override
+ public void onInputEvent(InputEvent event) {
+ if (mFinished.get()) {
+ // The session has been finished.
+ finishInputEvent(event, false);
+ return;
+ }
+ boolean handled = false;
+ try {
+ if (event instanceof KeyEvent) {
+ final KeyEvent keyEvent = (KeyEvent) event;
+ handled = keyEvent.dispatch(mKeyEventCallbackAdaptor, mDispatcherState,
+ mKeyEventCallbackAdaptor);
+ } else {
+ final MotionEvent motionEvent = (MotionEvent) event;
+ if (motionEvent.isFromSource(InputDevice.SOURCE_CLASS_TRACKBALL)) {
+ handled = mClientCallback.onTrackballEvent(motionEvent);
+ } else {
+ handled = mClientCallback.onGenericMotionEvent(motionEvent);
+ }
+ }
+ } finally {
+ finishInputEvent(event, handled);
+ }
+ }
+ }
+
+ private static final class InputMethodSessionImpl extends IInputMethodSession.Stub {
+ private final Object mSessionLock;
+ @GuardedBy("mSessionLock")
+ private CallbackImpl mCallbackImpl;
+ @GuardedBy("mSessionLock")
+ private Handler mHandler;
+ private final AtomicBoolean mSessionFinished;
+
+ InputMethodSessionImpl(Object lock, CallbackImpl callback, Handler handler,
+ AtomicBoolean sessionFinished) {
+ mSessionLock = lock;
+ mCallbackImpl = callback;
+ mHandler = handler;
+ mSessionFinished = sessionFinished;
+ }
+
+ @Override
+ public void updateExtractedText(int token, ExtractedText text) {
+ reportNotSupported();
+ }
+
+ @Override
+ public void updateSelection(int oldSelStart, int oldSelEnd,
+ int newSelStart, int newSelEnd,
+ int candidatesStart, int candidatesEnd) {
+ synchronized (mSessionLock) {
+ if (mCallbackImpl == null || mHandler == null) {
+ return;
+ }
+ final SomeArgs args = SomeArgs.obtain();
+ args.argi1 = oldSelStart;
+ args.argi2 = oldSelEnd;
+ args.argi3 = newSelStart;
+ args.argi4 = newSelEnd;
+ args.argi5 = candidatesStart;
+ args.argi6 = candidatesEnd;
+ mHandler.sendMessage(PooledLambda.obtainMessage(
+ CallbackImpl::updateSelection, mCallbackImpl, args));
+ }
+ }
+
+ @Override
+ public void viewClicked(boolean focusChanged) {
+ reportNotSupported();
+ }
+
+ @Override
+ public void updateCursor(Rect newCursor) {
+ reportNotSupported();
+ }
+
+ @Override
+ public void displayCompletions(CompletionInfo[] completions) {
+ synchronized (mSessionLock) {
+ if (mCallbackImpl == null || mHandler == null) {
+ return;
+ }
+ mHandler.sendMessage(PooledLambda.obtainMessage(
+ CallbackImpl::displayCompletions, mCallbackImpl, completions));
+ }
+ }
+
+ @Override
+ public void appPrivateCommand(String action, Bundle data) {
+ synchronized (mSessionLock) {
+ if (mCallbackImpl == null || mHandler == null) {
+ return;
+ }
+ mHandler.sendMessage(PooledLambda.obtainMessage(
+ CallbackImpl::appPrivateCommand, mCallbackImpl, action, data));
+ }
+ }
+
+ @Override
+ public void toggleSoftInput(int showFlags, int hideFlags) {
+ synchronized (mSessionLock) {
+ if (mCallbackImpl == null || mHandler == null) {
+ return;
+ }
+ mHandler.sendMessage(PooledLambda.obtainMessage(
+ CallbackImpl::toggleSoftInput, mCallbackImpl, showFlags,
+ hideFlags));
+ }
+ }
+
+ @Override
+ public void finishSession() {
+ synchronized (mSessionLock) {
+ if (mCallbackImpl == null || mHandler == null) {
+ return;
+ }
+ mSessionFinished.set(true);
+ mHandler.sendMessage(PooledLambda.obtainMessage(
+ CallbackImpl::finishSession, mCallbackImpl));
+ mCallbackImpl = null;
+ mHandler = null;
+ }
+ }
+
+ @Override
+ public void updateCursorAnchorInfo(CursorAnchorInfo info) {
+ synchronized (mSessionLock) {
+ if (mCallbackImpl == null || mHandler == null) {
+ return;
+ }
+ mHandler.sendMessage(PooledLambda.obtainMessage(
+ CallbackImpl::updateCursorAnchorInfo, mCallbackImpl, info));
+ }
+ }
+ }
+
+ private static final class MultiClientInputMethodSessionImpl
+ extends IMultiClientInputMethodSession.Stub {
+ private final Object mSessionLock;
+ @GuardedBy("mSessionLock")
+ private CallbackImpl mCallbackImpl;
+ @GuardedBy("mSessionLock")
+ private Handler mHandler;
+ private final AtomicBoolean mSessionFinished;
+
+ MultiClientInputMethodSessionImpl(Object lock, CallbackImpl callback,
+ Handler handler, AtomicBoolean sessionFinished) {
+ mSessionLock = lock;
+ mCallbackImpl = callback;
+ mHandler = handler;
+ mSessionFinished = sessionFinished;
+ }
+
+ @Override
+ public void startInputOrWindowGainedFocus(@Nullable IInputContext inputContext,
+ int missingMethods, @Nullable EditorInfo editorInfo, int controlFlags,
+ @SoftInputModeFlags int softInputMode, int windowHandle) {
+ synchronized (mSessionLock) {
+ if (mCallbackImpl == null || mHandler == null) {
+ return;
+ }
+ final SomeArgs args = SomeArgs.obtain();
+ // TODO(Bug 119211536): Remove dependency on AbstractInputMethodService from ICW
+ final WeakReference<AbstractInputMethodService> fakeIMS =
+ new WeakReference<>(null);
+ args.arg1 = (inputContext == null) ? null
+ : new InputConnectionWrapper(fakeIMS, inputContext, missingMethods,
+ mSessionFinished);
+ args.arg2 = editorInfo;
+ args.argi1 = controlFlags;
+ args.argi2 = softInputMode;
+ args.argi3 = windowHandle;
+ mHandler.sendMessage(PooledLambda.obtainMessage(
+ CallbackImpl::startInputOrWindowGainedFocus, mCallbackImpl, args));
+ }
+ }
+
+ @Override
+ public void showSoftInput(int flags, ResultReceiver resultReceiver) {
+ synchronized (mSessionLock) {
+ if (mCallbackImpl == null || mHandler == null) {
+ return;
+ }
+ mHandler.sendMessage(PooledLambda.obtainMessage(
+ CallbackImpl::showSoftInput, mCallbackImpl, flags,
+ resultReceiver));
+ }
+ }
+
+ @Override
+ public void hideSoftInput(int flags, ResultReceiver resultReceiver) {
+ synchronized (mSessionLock) {
+ if (mCallbackImpl == null || mHandler == null) {
+ return;
+ }
+ mHandler.sendMessage(PooledLambda.obtainMessage(
+ CallbackImpl::hideSoftInput, mCallbackImpl, flags,
+ resultReceiver));
+ }
+ }
+ }
+
+ /**
+ * The maim part of adaptor to {@link MultiClientInputMethodServiceDelegate.ClientCallback}.
+ */
+ @WorkerThread
+ private static final class CallbackImpl {
+ private final MultiClientInputMethodClientCallbackAdaptor mCallbackAdaptor;
+ private final MultiClientInputMethodServiceDelegate.ClientCallback mOriginalCallback;
+ private boolean mFinished = false;
+
+ CallbackImpl(MultiClientInputMethodClientCallbackAdaptor callbackAdaptor,
+ MultiClientInputMethodServiceDelegate.ClientCallback callback) {
+ mCallbackAdaptor = callbackAdaptor;
+ mOriginalCallback = callback;
+ }
+
+ void updateSelection(SomeArgs args) {
+ try {
+ if (mFinished) {
+ return;
+ }
+ mOriginalCallback.onUpdateSelection(args.argi1, args.argi2, args.argi3,
+ args.argi4, args.argi5, args.argi6);
+ } finally {
+ args.recycle();
+ }
+ }
+
+ void displayCompletions(CompletionInfo[] completions) {
+ if (mFinished) {
+ return;
+ }
+ mOriginalCallback.onDisplayCompletions(completions);
+ }
+
+ void appPrivateCommand(String action, Bundle data) {
+ if (mFinished) {
+ return;
+ }
+ mOriginalCallback.onAppPrivateCommand(action, data);
+ }
+
+ void toggleSoftInput(int showFlags, int hideFlags) {
+ if (mFinished) {
+ return;
+ }
+ mOriginalCallback.onToggleSoftInput(showFlags, hideFlags);
+ }
+
+ void finishSession() {
+ if (mFinished) {
+ return;
+ }
+ mFinished = true;
+ mOriginalCallback.onFinishSession();
+ synchronized (mCallbackAdaptor.mSessionLock) {
+ mCallbackAdaptor.mDispatcherState = null;
+ if (mCallbackAdaptor.mReadChannel != null) {
+ mCallbackAdaptor.mReadChannel.dispose();
+ mCallbackAdaptor.mReadChannel = null;
+ }
+ mCallbackAdaptor.mInputEventReceiver = null;
+ }
+ }
+
+ void updateCursorAnchorInfo(CursorAnchorInfo info) {
+ if (mFinished) {
+ return;
+ }
+ mOriginalCallback.onUpdateCursorAnchorInfo(info);
+ }
+
+ void startInputOrWindowGainedFocus(SomeArgs args) {
+ try {
+ if (mFinished) {
+ return;
+ }
+ final InputConnectionWrapper inputConnection = (InputConnectionWrapper) args.arg1;
+ final EditorInfo editorInfo = (EditorInfo) args.arg2;
+ final int startInputFlags = args.argi1;
+ final int softInputMode = args.argi2;
+ final int windowHandle = args.argi3;
+ mOriginalCallback.onStartInputOrWindowGainedFocus(inputConnection, editorInfo,
+ startInputFlags, softInputMode, windowHandle);
+ } finally {
+ args.recycle();
+ }
+ }
+
+ void showSoftInput(int flags, ResultReceiver resultReceiver) {
+ if (mFinished) {
+ return;
+ }
+ mOriginalCallback.onShowSoftInput(flags, resultReceiver);
+ }
+
+ void hideSoftInput(int flags, ResultReceiver resultReceiver) {
+ if (mFinished) {
+ return;
+ }
+ mOriginalCallback.onHideSoftInput(flags, resultReceiver);
+ }
+ }
+
+ private static void reportNotSupported() {
+ if (DEBUG) {
+ Log.d(TAG, Debug.getCaller() + " is not supported");
+ }
+ }
+}
diff --git a/core/java/android/inputmethodservice/MultiClientInputMethodServiceDelegate.java b/core/java/android/inputmethodservice/MultiClientInputMethodServiceDelegate.java
new file mode 100644
index 0000000..0604f6a
--- /dev/null
+++ b/core/java/android/inputmethodservice/MultiClientInputMethodServiceDelegate.java
@@ -0,0 +1,377 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.inputmethodservice;
+
+import android.annotation.Nullable;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.ResultReceiver;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.WindowManager.LayoutParams.SoftInputModeFlags;
+import android.view.inputmethod.CompletionInfo;
+import android.view.inputmethod.CursorAnchorInfo;
+import android.view.inputmethod.EditorInfo;
+import android.view.inputmethod.InputConnection;
+
+import com.android.internal.inputmethod.StartInputFlags;
+
+/**
+ * Defines all the public APIs and interfaces that are necessary to implement multi-client IMEs.
+ *
+ * <p>Actual implementation is further delegated to
+ * {@link MultiClientInputMethodServiceDelegateImpl}.</p>
+ *
+ * @hide
+ */
+public final class MultiClientInputMethodServiceDelegate {
+ // @SdkConstant(SdkConstantType.SERVICE_ACTION)
+ public static final String SERVICE_INTERFACE =
+ "android.inputmethodservice.MultiClientInputMethodService";
+
+ /**
+ * Special value that is guaranteed to be not used for IME client ID.
+ */
+ public static final int INVALID_CLIENT_ID = -1;
+
+ /**
+ * Special value that is guaranteed to be not used for window handle.
+ */
+ public static final int INVALID_WINDOW_HANDLE = -1;
+
+ private final MultiClientInputMethodServiceDelegateImpl mImpl;
+
+ /**
+ * Top-level callbacks for this {@link MultiClientInputMethodServiceDelegate}.
+ */
+ public interface ServiceCallback {
+ /**
+ * Called when this {@link MultiClientInputMethodServiceDelegate} is recognized by the
+ * system and privileged operations like {@link #createInputMethodWindowToken(int)} are
+ * ready to be called.
+ */
+ void initialized();
+
+ /**
+ * Called when a new IME client is recognized by the system.
+ *
+ * <p>Once the IME receives this callback, the IME can start interacting with the IME client
+ * by calling {@link #acceptClient(int, ClientCallback, KeyEvent.DispatcherState, Looper)}.
+ * </p>
+ *
+ * @param clientId ID of the client.
+ * @param uid UID of the IME client.
+ * @param pid PID of the IME client.
+ * @param selfReportedDisplayId display ID reported from the IME client. Since the system
+ * does not validate this display ID, and at any time the IME client can lose the
+ * access to this display ID, the IME needs to call
+ * {@link #isUidAllowedOnDisplay(int, int)} to check whether the IME client still
+ * has access to this display or not.
+ */
+ void addClient(int clientId, int uid, int pid, int selfReportedDisplayId);
+
+ /**
+ * Called when an IME client is being destroyed.
+ *
+ * @param clientId ID of the client.
+ */
+ void removeClient(int clientId);
+ }
+
+ /**
+ * Per-client callbacks.
+ */
+ public interface ClientCallback {
+ /**
+ * Called when the associated IME client called {@link
+ * android.view.inputmethod.InputMethodManager#sendAppPrivateCommand(View, String, Bundle)}.
+ *
+ * @param action Name of the command to be performed.
+ * @param data Any data to include with the command.
+ * @see android.inputmethodservice.InputMethodService#onAppPrivateCommand(String, Bundle)
+ */
+ void onAppPrivateCommand(String action, Bundle data);
+
+ /**
+ * Called when the associated IME client called {@link
+ * android.view.inputmethod.InputMethodManager#displayCompletions(View, CompletionInfo[])}.
+ *
+ * @param completions Completion information provided from the IME client.
+ * @see android.inputmethodservice.InputMethodService#onDisplayCompletions(CompletionInfo[])
+ */
+ void onDisplayCompletions(CompletionInfo[] completions);
+
+ /**
+ * Called when this callback session is closed. No further callback should not happen on
+ * this callback object.
+ */
+ void onFinishSession();
+
+ /**
+ * Called when the associated IME client called {@link
+ * android.view.inputmethod.InputMethodManager#hideSoftInputFromWindow(IBinder, int)} or
+ * {@link android.view.inputmethod.InputMethodManager#hideSoftInputFromWindow(IBinder, int,
+ * ResultReceiver)}.
+ *
+ * @param flags The flag passed by the client.
+ * @param resultReceiver The {@link ResultReceiver} passed by the client.
+ * @see android.inputmethodservice.InputMethodService#onWindowHidden()
+ */
+ void onHideSoftInput(int flags, ResultReceiver resultReceiver);
+
+ /**
+ * Called when the associated IME client called {@link
+ * android.view.inputmethod.InputMethodManager#showSoftInput(View, int)} or {@link
+ * android.view.inputmethod.InputMethodManager#showSoftInput(View, int, ResultReceiver)}.
+ *
+ * @param flags The flag passed by the client.
+ * @param resultReceiver The {@link ResultReceiver} passed by the client.
+ * @see android.inputmethodservice.InputMethodService#onWindowShown()
+ */
+ void onShowSoftInput(int flags, ResultReceiver resultReceiver);
+
+ /**
+ * A generic callback when {@link InputConnection} is being established.
+ *
+ * @param inputConnection The {@link InputConnection} to be established.
+ * @param editorInfo The {@link EditorInfo} reported from the IME client.
+ * @param startInputFlags Any combinations of {@link StartInputFlags}.
+ * @param softInputMode SoftWindowMode specified to this window.
+ * @param targetWindowHandle A unique Window token.
+ * @see android.inputmethodservice.InputMethodService#onStartInput(EditorInfo, boolean)
+ */
+ void onStartInputOrWindowGainedFocus(
+ @Nullable InputConnection inputConnection,
+ @Nullable EditorInfo editorInfo,
+ @StartInputFlags int startInputFlags,
+ @SoftInputModeFlags int softInputMode,
+ int targetWindowHandle);
+
+ /**
+ * Called when the associated IME client called {@link
+ * android.view.inputmethod.InputMethodManager#toggleSoftInput(int, int)}.
+ *
+ * @param showFlags The flag passed by the client.
+ * @param hideFlags The flag passed by the client.
+ * @see android.inputmethodservice.InputMethodService#onToggleSoftInput(int, int)
+ */
+ void onToggleSoftInput(int showFlags, int hideFlags);
+
+ /**
+ * Called when the associated IME client called {@link
+ * android.view.inputmethod.InputMethodManager#updateCursorAnchorInfo(View,
+ * CursorAnchorInfo)}.
+ *
+ * @param info The {@link CursorAnchorInfo} passed by the client.
+ * @see android.inputmethodservice.InputMethodService#onUpdateCursorAnchorInfo(
+ * CursorAnchorInfo)
+ */
+ void onUpdateCursorAnchorInfo(CursorAnchorInfo info);
+
+ /**
+ * Called when the associated IME client called {@link
+ * android.view.inputmethod.InputMethodManager#updateSelection(View, int, int, int, int)}.
+ *
+ * @param oldSelStart The previous selection start index.
+ * @param oldSelEnd The previous selection end index.
+ * @param newSelStart The new selection start index.
+ * @param newSelEnd The new selection end index.
+ * @param candidatesStart The new candidate start index.
+ * @param candidatesEnd The new candidate end index.
+ * @see android.inputmethodservice.InputMethodService#onUpdateSelection(int, int, int, int,
+ * int, int)
+ */
+ void onUpdateSelection(int oldSelStart, int oldSelEnd, int newSelStart, int newSelEnd,
+ int candidatesStart, int candidatesEnd);
+
+ /**
+ * Called to give a chance for the IME to intercept generic motion events before they are
+ * processed by the application.
+ *
+ * @param event {@link MotionEvent} that is about to be handled by the IME client.
+ * @return {@code true} to tell the IME client that the IME handled this event.
+ * @see android.inputmethodservice.InputMethodService#onGenericMotionEvent(MotionEvent)
+ */
+ boolean onGenericMotionEvent(MotionEvent event);
+
+ /**
+ * Called to give a chance for the IME to intercept key down events before they are
+ * processed by the application.
+ *
+ * @param keyCode The value in {@link KeyEvent#getKeyCode()}.
+ * @param event {@link KeyEvent} for this key down event.
+ * @return {@code true} to tell the IME client that the IME handled this event.
+ * @see android.inputmethodservice.InputMethodService#onKeyDown(int, KeyEvent)
+ */
+ boolean onKeyDown(int keyCode, KeyEvent event);
+
+ /**
+ * Called to give a chance for the IME to intercept key long press events before they are
+ * processed by the application.
+ *
+ * @param keyCode The value in {@link KeyEvent#getKeyCode()}.
+ * @param event {@link KeyEvent} for this key long press event.
+ * @return {@code true} to tell the IME client that the IME handled this event.
+ * @see android.inputmethodservice.InputMethodService#onKeyLongPress(int, KeyEvent)
+ */
+ boolean onKeyLongPress(int keyCode, KeyEvent event);
+
+ /**
+ * Called to give a chance for the IME to intercept key multiple events before they are
+ * processed by the application.
+ *
+ * @param keyCode The value in {@link KeyEvent#getKeyCode()}.
+ * @param event {@link KeyEvent} for this key multiple event.
+ * @return {@code true} to tell the IME client that the IME handled this event.
+ * @see android.inputmethodservice.InputMethodService#onKeyMultiple(int, int, KeyEvent)
+ */
+ boolean onKeyMultiple(int keyCode, KeyEvent event);
+
+ /**
+ * Called to give a chance for the IME to intercept key up events before they are processed
+ * by the application.
+ *
+ * @param keyCode The value in {@link KeyEvent#getKeyCode()}.
+ * @param event {@link KeyEvent} for this key up event.
+ * @return {@code true} to tell the IME client that the IME handled this event.
+ * @see android.inputmethodservice.InputMethodService#onKeyUp(int, KeyEvent)
+ */
+ boolean onKeyUp(int keyCode, KeyEvent event);
+
+ /**
+ * Called to give a chance for the IME to intercept generic motion events before they are
+ * processed by the application.
+ *
+ * @param event {@link MotionEvent} that is about to be handled by the IME client.
+ * @return {@code true} to tell the IME client that the IME handled this event.
+ * @see android.inputmethodservice.InputMethodService#onTrackballEvent(MotionEvent)
+ */
+ boolean onTrackballEvent(MotionEvent event);
+ }
+
+ private MultiClientInputMethodServiceDelegate(Context context,
+ ServiceCallback serviceCallback) {
+ mImpl = new MultiClientInputMethodServiceDelegateImpl(context, serviceCallback);
+ }
+
+ /**
+ * Must be called by the multi-client IME implementer to create
+ * {@link MultiClientInputMethodServiceDelegate}.
+ *
+ * @param context {@link Context} with which the delegate should interact with the system.
+ * @param serviceCallback {@link ServiceCallback} to receive service-level callbacks.
+ * @return A new instance of {@link MultiClientInputMethodServiceDelegate}.
+ */
+ public static MultiClientInputMethodServiceDelegate create(Context context,
+ ServiceCallback serviceCallback) {
+ return new MultiClientInputMethodServiceDelegate(context, serviceCallback);
+ }
+
+ /**
+ * Must be called by the multi-client IME service when {@link android.app.Service#onDestroy()}
+ * is called.
+ */
+ public void onDestroy() {
+ mImpl.onDestroy();
+ }
+
+ /**
+ * Must be called by the multi-client IME service when
+ * {@link android.app.Service#onBind(Intent)} is called.
+ *
+ * @param intent {@link Intent} passed to {@link android.app.Service#onBind(Intent)}.
+ * @return An {@link IBinder} object that needs to be returned from
+ * {@link android.app.Service#onBind(Intent)}.
+ */
+ public IBinder onBind(Intent intent) {
+ return mImpl.onBind(intent);
+ }
+
+ /**
+ * Must be called by the multi-client IME service when
+ * {@link android.app.Service#onUnbind(Intent)} is called.
+ *
+ * @param intent {@link Intent} passed to {@link android.app.Service#onUnbind(Intent)}.
+ * @return A boolean value that needs to be returned from
+ * {@link android.app.Service#onUnbind(Intent)}.
+ */
+ public boolean onUnbind(Intent intent) {
+ return mImpl.onUnbind(intent);
+ }
+
+ /**
+ * Must be called by the multi-client IME service to create a special window token for IME
+ * window.
+ *
+ * <p>This method is available only after {@link ServiceCallback#initialized()}.</p>
+ *
+ * @param displayId display ID on which the IME window will be shown.
+ * @return Window token to be specified to the IME window/
+ */
+ public IBinder createInputMethodWindowToken(int displayId) {
+ return mImpl.createInputMethodWindowToken(displayId);
+ }
+
+ /**
+ * Must be called by the multi-client IME service to notify the system when the IME is ready to
+ * accept callback events from the specified IME client.
+ *
+ * @param clientId The IME client ID specified in
+ * {@link ServiceCallback#addClient(int, int, int, int)}.
+ * @param clientCallback The {@link ClientCallback} to receive callback events from this IME
+ * client.
+ * @param dispatcherState {@link KeyEvent.DispatcherState} to be used when receiving key-related
+ * callbacks in {@link ClientCallback}.
+ * @param looper {@link Looper} on which {@link ClientCallback} will be called back.
+ */
+ public void acceptClient(int clientId, ClientCallback clientCallback,
+ KeyEvent.DispatcherState dispatcherState, Looper looper) {
+ mImpl.acceptClient(clientId, clientCallback, dispatcherState, looper);
+ }
+
+ /**
+ * Must be called by the multi-client IME service to notify the system when the IME is ready to
+ * interact with the window in the IME client.
+ *
+ * @param clientId The IME client ID specified in
+ * {@link ServiceCallback#addClient(int, int, int, int)}.
+ * @param targetWindowHandle The window handle specified in
+ * {@link ClientCallback#onStartInputOrWindowGainedFocus}.
+ * @param imeWindowToken The IME window token returned from
+ * {@link #createInputMethodWindowToken(int)}.
+ */
+ public void reportImeWindowTarget(int clientId, int targetWindowHandle,
+ IBinder imeWindowToken) {
+ mImpl.reportImeWindowTarget(clientId, targetWindowHandle, imeWindowToken);
+ }
+
+ /**
+ * Can be called by the multi-client IME service to check if the given {@code uid} is allowed
+ * to access to {@code displayId}.
+ *
+ * @param displayId Display ID to be queried.
+ * @param uid UID to be queried.
+ * @return {@code true} if {@code uid} is allowed to access to {@code displayId}.
+ */
+ public boolean isUidAllowedOnDisplay(int displayId, int uid) {
+ return mImpl.isUidAllowedOnDisplay(displayId, uid);
+ }
+}
diff --git a/core/java/android/inputmethodservice/MultiClientInputMethodServiceDelegateImpl.java b/core/java/android/inputmethodservice/MultiClientInputMethodServiceDelegateImpl.java
new file mode 100644
index 0000000..bbe3a7f
--- /dev/null
+++ b/core/java/android/inputmethodservice/MultiClientInputMethodServiceDelegateImpl.java
@@ -0,0 +1,193 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.inputmethodservice;
+
+import static java.lang.annotation.RetentionPolicy.SOURCE;
+
+import android.annotation.IntDef;
+import android.content.Context;
+import android.content.Intent;
+import android.os.IBinder;
+import android.os.Looper;
+import android.util.Log;
+import android.view.InputChannel;
+import android.view.KeyEvent;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.inputmethod.IMultiClientInputMethod;
+import com.android.internal.inputmethod.IMultiClientInputMethodPrivilegedOperations;
+import com.android.internal.inputmethod.MultiClientInputMethodPrivilegedOperations;
+
+import java.lang.annotation.Retention;
+import java.lang.ref.WeakReference;
+
+final class MultiClientInputMethodServiceDelegateImpl {
+ private static final String TAG = "MultiClientInputMethodServiceDelegateImpl";
+
+ private final Object mLock = new Object();
+
+ @Retention(SOURCE)
+ @IntDef({InitializationPhase.INSTANTIATED,
+ InitializationPhase.ON_BIND_CALLED,
+ InitializationPhase.INITIALIZE_CALLED,
+ InitializationPhase.ON_UNBIND_CALLED,
+ InitializationPhase.ON_DESTROY_CALLED})
+ private @interface InitializationPhase {
+ int INSTANTIATED = 1;
+ int ON_BIND_CALLED = 2;
+ int INITIALIZE_CALLED = 3;
+ int ON_UNBIND_CALLED = 4;
+ int ON_DESTROY_CALLED = 5;
+ }
+
+ @GuardedBy("mLock")
+ @InitializationPhase
+ private int mInitializationPhase;
+
+ private final MultiClientInputMethodPrivilegedOperations mPrivOps =
+ new MultiClientInputMethodPrivilegedOperations();
+
+ private final MultiClientInputMethodServiceDelegate.ServiceCallback mServiceCallback;
+
+ private final Context mContext;
+
+ MultiClientInputMethodServiceDelegateImpl(Context context,
+ MultiClientInputMethodServiceDelegate.ServiceCallback serviceCallback) {
+ mInitializationPhase = InitializationPhase.INSTANTIATED;
+ mContext = context;
+ mServiceCallback = serviceCallback;
+ }
+
+ void onDestroy() {
+ synchronized (mLock) {
+ switch (mInitializationPhase) {
+ case InitializationPhase.INSTANTIATED:
+ case InitializationPhase.ON_UNBIND_CALLED:
+ mInitializationPhase = InitializationPhase.ON_DESTROY_CALLED;
+ break;
+ default:
+ Log.e(TAG, "unexpected state=" + mInitializationPhase);
+ break;
+ }
+ }
+ }
+
+ private static final class ServiceImpl extends IMultiClientInputMethod.Stub {
+ private final WeakReference<MultiClientInputMethodServiceDelegateImpl> mImpl;
+
+ ServiceImpl(MultiClientInputMethodServiceDelegateImpl service) {
+ mImpl = new WeakReference<>(service);
+ }
+
+ @Override
+ public void initialize(IMultiClientInputMethodPrivilegedOperations privOps) {
+ final MultiClientInputMethodServiceDelegateImpl service = mImpl.get();
+ if (service == null) {
+ return;
+ }
+ synchronized (service.mLock) {
+ switch (service.mInitializationPhase) {
+ case InitializationPhase.ON_BIND_CALLED:
+ service.mPrivOps.set(privOps);
+ service.mInitializationPhase = InitializationPhase.INITIALIZE_CALLED;
+ service.mServiceCallback.initialized();
+ break;
+ default:
+ Log.e(TAG, "unexpected state=" + service.mInitializationPhase);
+ break;
+ }
+ }
+ }
+
+ @Override
+ public void addClient(int clientId, int uid, int pid, int selfReportedDisplayId) {
+ final MultiClientInputMethodServiceDelegateImpl service = mImpl.get();
+ if (service == null) {
+ return;
+ }
+ service.mServiceCallback.addClient(clientId, uid, pid, selfReportedDisplayId);
+ }
+
+ @Override
+ public void removeClient(int clientId) {
+ final MultiClientInputMethodServiceDelegateImpl service = mImpl.get();
+ if (service == null) {
+ return;
+ }
+ service.mServiceCallback.removeClient(clientId);
+ }
+ }
+
+ IBinder onBind(Intent intent) {
+ synchronized (mLock) {
+ switch (mInitializationPhase) {
+ case InitializationPhase.INSTANTIATED:
+ mInitializationPhase = InitializationPhase.ON_BIND_CALLED;
+ return new ServiceImpl(this);
+ default:
+ Log.e(TAG, "unexpected state=" + mInitializationPhase);
+ break;
+ }
+ }
+ return null;
+ }
+
+ boolean onUnbind(Intent intent) {
+ synchronized (mLock) {
+ switch (mInitializationPhase) {
+ case InitializationPhase.ON_BIND_CALLED:
+ case InitializationPhase.INITIALIZE_CALLED:
+ mInitializationPhase = InitializationPhase.ON_UNBIND_CALLED;
+ mPrivOps.dispose();
+ break;
+ default:
+ Log.e(TAG, "unexpected state=" + mInitializationPhase);
+ break;
+ }
+ }
+ return false;
+ }
+
+ IBinder createInputMethodWindowToken(int displayId) {
+ return mPrivOps.createInputMethodWindowToken(displayId);
+ }
+
+ void acceptClient(int clientId,
+ MultiClientInputMethodServiceDelegate.ClientCallback clientCallback,
+ KeyEvent.DispatcherState dispatcherState, Looper looper) {
+ final InputChannel[] channels = InputChannel.openInputChannelPair("MSIMS-session");
+ final InputChannel writeChannel = channels[0];
+ final InputChannel readChannel = channels[1];
+ try {
+ final MultiClientInputMethodClientCallbackAdaptor callbackAdaptor =
+ new MultiClientInputMethodClientCallbackAdaptor(clientCallback, looper,
+ dispatcherState, readChannel);
+ mPrivOps.acceptClient(clientId, callbackAdaptor.createIInputMethodSession(),
+ callbackAdaptor.createIMultiClientInputMethodSession(), writeChannel);
+ } finally {
+ writeChannel.dispose();
+ }
+ }
+
+ void reportImeWindowTarget(int clientId, int targetWindowHandle, IBinder imeWindowToken) {
+ mPrivOps.reportImeWindowTarget(clientId, targetWindowHandle, imeWindowToken);
+ }
+
+ boolean isUidAllowedOnDisplay(int displayId, int uid) {
+ return mPrivOps.isUidAllowedOnDisplay(displayId, uid);
+ }
+}
diff --git a/core/java/android/os/Environment.java b/core/java/android/os/Environment.java
index 0c4a0b3e..0c1aae8 100644
--- a/core/java/android/os/Environment.java
+++ b/core/java/android/os/Environment.java
@@ -199,6 +199,7 @@
* @hide
*/
@SystemApi
+ @TestApi
public static File getProductDirectory() {
return DIR_PRODUCT_ROOT;
}
@@ -468,6 +469,14 @@
}
/**
+ * Returns location of packages cache directory.
+ * {@hide}
+ */
+ public static File getPackageCacheDirectory() {
+ return new File(getDataSystemDirectory(), "package_cache");
+ }
+
+ /**
* Return the primary shared/external storage directory. This directory may
* not currently be accessible if it has been mounted by the user on their
* computer, has been removed from the device, or some other problem has
diff --git a/core/java/android/os/FileUtils.java b/core/java/android/os/FileUtils.java
index 0b90f54..1f47f93 100644
--- a/core/java/android/os/FileUtils.java
+++ b/core/java/android/os/FileUtils.java
@@ -1156,11 +1156,16 @@
public static @Nullable File createDir(File baseDir, String name) {
final File dir = new File(baseDir, name);
+ return createDir(dir) ? dir : null;
+ }
+
+ /** @hide */
+ public static boolean createDir(File dir) {
if (dir.exists()) {
- return dir.isDirectory() ? dir : null;
+ return dir.isDirectory();
}
- return dir.mkdir() ? dir : null;
+ return dir.mkdir();
}
/**
diff --git a/core/java/android/os/storage/IStorageManager.aidl b/core/java/android/os/storage/IStorageManager.aidl
index 33b2676..bf988ae 100644
--- a/core/java/android/os/storage/IStorageManager.aidl
+++ b/core/java/android/os/storage/IStorageManager.aidl
@@ -188,6 +188,6 @@
void allocateBytes(String volumeUuid, long bytes, int flags, String callingPackage) = 78;
void runIdleMaintenance() = 79;
void abortIdleMaintenance() = 80;
- String translateAppToSystem(String path, String packageName, int userId) = 81;
- String translateSystemToApp(String path, String packageName, int userId) = 82;
+ String translateAppToSystem(String path, int pid, int uid) = 81;
+ String translateSystemToApp(String path, int pid, int uid) = 82;
}
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index c91cda6..8a36a78 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -225,6 +225,8 @@
public static final int DEBUG_SDCARDFS_FORCE_OFF = 1 << 4;
/** {@hide} */
public static final int DEBUG_VIRTUAL_DISK = 1 << 5;
+ /** {@hide} */
+ public static final int DEBUG_ISOLATED_STORAGE = 1 << 6;
/** {@hide} */
public static final int FLAG_STORAGE_DE = IInstalld.FLAG_STORAGE_DE;
@@ -1546,13 +1548,13 @@
*
* @hide
*/
- public File translateAppToSystem(File file, String packageName) {
+ public File translateAppToSystem(File file, int pid, int uid) {
// We can only translate absolute paths
if (!file.isAbsolute()) return file;
try {
return new File(mStorageManager.translateAppToSystem(file.getAbsolutePath(),
- packageName, mContext.getUserId()));
+ pid, uid));
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -1564,13 +1566,13 @@
*
* @hide
*/
- public File translateSystemToApp(File file, String packageName) {
+ public File translateSystemToApp(File file, int pid, int uid) {
// We can only translate absolute paths
if (!file.isAbsolute()) return file;
try {
return new File(mStorageManager.translateSystemToApp(file.getAbsolutePath(),
- packageName, mContext.getUserId()));
+ pid, uid));
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 0aa3a7b..f840792 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -12636,27 +12636,14 @@
"hidden_api_access_log_sampling_rate";
/**
- * Hidden API enforcement policy for apps targeting SDK versions prior to the latest
- * version.
+ * Hidden API enforcement policy for apps.
*
* Values correspond to @{@link
* android.content.pm.ApplicationInfo.HiddenApiEnforcementPolicy}
*
* @hide
*/
- public static final String HIDDEN_API_POLICY_PRE_P_APPS =
- "hidden_api_policy_pre_p_apps";
-
- /**
- * Hidden API enforcement policy for apps targeting the current SDK version.
- *
- * Values correspond to @{@link
- * android.content.pm.ApplicationInfo.HiddenApiEnforcementPolicy}
- *
- * @hide
- */
- public static final String HIDDEN_API_POLICY_P_APPS =
- "hidden_api_policy_p_apps";
+ public static final String HIDDEN_API_POLICY = "hidden_api_policy";
/**
* Timeout for a single {@link android.media.soundtrigger.SoundTriggerDetectionService}
diff --git a/core/java/android/view/KeyEvent.java b/core/java/android/view/KeyEvent.java
index 907f385..0739516 100644
--- a/core/java/android/view/KeyEvent.java
+++ b/core/java/android/view/KeyEvent.java
@@ -1978,6 +1978,7 @@
}
/** @hide */
+ @TestApi
@Override
public final void setDisplayId(int displayId) {
mDisplayId = displayId;
@@ -2042,6 +2043,16 @@
}
/**
+ * Modifies the flags of the event.
+ *
+ * @param newFlags New flags for the event, replacing the entire value.
+ * @hide
+ */
+ public final void setFlags(int newFlags) {
+ mFlags = newFlags;
+ }
+
+ /**
* Returns the flags for this key event.
*
* @see #FLAG_WOKE_HERE
@@ -2542,6 +2553,20 @@
}
/**
+ * Modifies the down time and the event time of the event.
+ *
+ * @param downTime The new down time (in {@link android.os.SystemClock#uptimeMillis}) of the
+ * event.
+ * @param eventTime The new event time (in {@link android.os.SystemClock#uptimeMillis}) of the
+ * event.
+ * @hide
+ */
+ public final void setTime(long downTime, long eventTime) {
+ mDownTime = downTime;
+ mEventTime = eventTime;
+ }
+
+ /**
* Retrieve the time of the most recent key down event,
* in the {@link android.os.SystemClock#uptimeMillis} time base. If this
* is a down event, this will be the same as {@link #getEventTime()}.
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 453d788..ec16828 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -112,6 +112,7 @@
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputConnection;
import android.view.inputmethod.InputMethodManager;
+import android.view.intelligence.IntelligenceManager;
import android.widget.Checkable;
import android.widget.FrameLayout;
import android.widget.ScrollBarDrawable;
@@ -799,6 +800,11 @@
private static final String AUTOFILL_LOG_TAG = "View.Autofill";
/**
+ * The logging tag used by this class when logging content capture-related messages.
+ */
+ private static final String CONTENT_CAPTURE_LOG_TAG = "View.ContentCapture";
+
+ /**
* When set to true, apps will draw debugging information about their layouts.
*
* @hide
@@ -1337,6 +1343,59 @@
*/
public static final int AUTOFILL_FLAG_INCLUDE_NOT_IMPORTANT_VIEWS = 0x1;
+ /** @hide */
+ @IntDef(prefix = { "IMPORTANT_FOR_CONTENT_CAPTURE_" }, value = {
+ IMPORTANT_FOR_CONTENT_CAPTURE_AUTO,
+ IMPORTANT_FOR_CONTENT_CAPTURE_YES,
+ IMPORTANT_FOR_CONTENT_CAPTURE_NO,
+ IMPORTANT_FOR_CONTENT_CAPTURE_YES_EXCLUDE_DESCENDANTS,
+ IMPORTANT_FOR_CONTENT_CAPTURE_NO_EXCLUDE_DESCENDANTS
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface ContentCaptureImportance {}
+
+ /**
+ * Automatically determine whether a view is important for content capture.
+ *
+ * @see #isImportantForContentCapture()
+ * @see #setImportantForContentCapture(int)
+ */
+ public static final int IMPORTANT_FOR_CONTENT_CAPTURE_AUTO = 0x0;
+
+ /**
+ * The view is important for content capture, and its children (if any) will be traversed.
+ *
+ * @see #isImportantForContentCapture()
+ * @see #setImportantForContentCapture(int)
+ */
+ public static final int IMPORTANT_FOR_CONTENT_CAPTURE_YES = 0x1;
+
+ /**
+ * The view is not important for content capture, but its children (if any) will be traversed.
+ *
+ * @see #isImportantForContentCapture()
+ * @see #setImportantForContentCapture(int)
+ */
+ public static final int IMPORTANT_FOR_CONTENT_CAPTURE_NO = 0x2;
+
+ /**
+ * The view is important for content capture, but its children (if any) will not be traversed.
+ *
+ * @see #isImportantForContentCapture()
+ * @see #setImportantForContentCapture(int)
+ */
+ public static final int IMPORTANT_FOR_CONTENT_CAPTURE_YES_EXCLUDE_DESCENDANTS = 0x4;
+
+ /**
+ * The view is not important for content capture, and its children (if any) will not be
+ * traversed.
+ *
+ * @see #isImportantForContentCapture()
+ * @see #setImportantForContentCapture(int)
+ */
+ public static final int IMPORTANT_FOR_CONTENT_CAPTURE_NO_EXCLUDE_DESCENDANTS = 0x8;
+
+
/**
* This view is enabled. Interpretation varies by subclass.
* Use with ENABLED_MASK when calling setFlags.
@@ -2243,7 +2302,44 @@
@UnsupportedAppUsage
protected Object mTag = null;
- // for mPrivateFlags:
+ /*
+ * Masks for mPrivateFlags, as generated by dumpFlags():
+ *
+ * |-------|-------|-------|-------|
+ * 1 PFLAG_WANTS_FOCUS
+ * 1 PFLAG_FOCUSED
+ * 1 PFLAG_SELECTED
+ * 1 PFLAG_IS_ROOT_NAMESPACE
+ * 1 PFLAG_HAS_BOUNDS
+ * 1 PFLAG_DRAWN
+ * 1 PFLAG_DRAW_ANIMATION
+ * 1 PFLAG_SKIP_DRAW
+ * 1 PFLAG_REQUEST_TRANSPARENT_REGIONS
+ * 1 PFLAG_DRAWABLE_STATE_DIRTY
+ * 1 PFLAG_MEASURED_DIMENSION_SET
+ * 1 PFLAG_FORCE_LAYOUT
+ * 1 PFLAG_LAYOUT_REQUIRED
+ * 1 PFLAG_PRESSED
+ * 1 PFLAG_DRAWING_CACHE_VALID
+ * 1 PFLAG_ANIMATION_STARTED
+ * 1 PFLAG_SAVE_STATE_CALLED
+ * 1 PFLAG_ALPHA_SET
+ * 1 PFLAG_SCROLL_CONTAINER
+ * 1 PFLAG_SCROLL_CONTAINER_ADDED
+ * 1 PFLAG_DIRTY
+ * 1 PFLAG_DIRTY_MASK
+ * 1 PFLAG_OPAQUE_BACKGROUND
+ * 1 PFLAG_OPAQUE_SCROLLBARS
+ * 11 PFLAG_OPAQUE_MASK
+ * 1 PFLAG_PREPRESSED
+ * 1 PFLAG_CANCEL_NEXT_UP_EVENT
+ * 1 PFLAG_AWAKEN_SCROLL_BARS_ON_ATTACH
+ * 1 PFLAG_HOVERED
+ * 1 PFLAG_NOTIFY_AUTOFILL_MANAGER_ON_CLICK
+ * 1 PFLAG_ACTIVATED
+ * 1 PFLAG_INVALIDATED
+ * |-------|-------|-------|-------|
+ */
/** {@hide} */
static final int PFLAG_WANTS_FOCUS = 0x00000001;
/** {@hide} */
@@ -2393,7 +2489,9 @@
*/
static final int PFLAG_INVALIDATED = 0x80000000;
- /**
+ /* End of masks for mPrivateFlags */
+
+ /*
* Masks for mPrivateFlags2, as generated by dumpFlags():
*
* |-------|-------|-------|-------|
@@ -2934,7 +3032,7 @@
/* End of masks for mPrivateFlags2 */
- /**
+ /*
* Masks for mPrivateFlags3, as generated by dumpFlags():
*
* |-------|-------|-------|-------|
@@ -3270,6 +3368,57 @@
/* End of masks for mPrivateFlags3 */
+ /*
+ * Masks for mPrivateFlags4, as generated by dumpFlags():
+ *
+ * |-------|-------|-------|-------|
+ * 1111 PFLAG4_IMPORTANT_FOR_CONTENT_CAPTURE_MASK
+ * 1 PFLAG4_NOTIFIED_CONTENT_CAPTURE_ON_LAYOUT
+ * 1 PFLAG4_NOTIFIED_CONTENT_CAPTURE_ADDED
+ * 1 PFLAG4_LAST_CONTENT_CAPTURE_NOTIFICATION_TYPE
+ * |-------|-------|-------|-------|
+ */
+
+ /**
+ * Mask for obtaining the bits which specify how to determine
+ * whether a view is important for autofill.
+ *
+ * <p>NOTE: the important for content capture values were the first flags added and are set in
+ * the rightmost position, so we don't need to shift them
+ */
+ private static final int PFLAG4_IMPORTANT_FOR_CONTENT_CAPTURE_MASK =
+ IMPORTANT_FOR_CONTENT_CAPTURE_AUTO | IMPORTANT_FOR_CONTENT_CAPTURE_YES
+ | IMPORTANT_FOR_CONTENT_CAPTURE_NO
+ | IMPORTANT_FOR_CONTENT_CAPTURE_YES_EXCLUDE_DESCENDANTS
+ | IMPORTANT_FOR_CONTENT_CAPTURE_NO_EXCLUDE_DESCENDANTS;
+
+ /*
+ * Variables used to control when the IntelligenceManager.notifyNodeAdded()/removed() methods
+ * should be called.
+ *
+ * The idea is to call notifyNodeAdded() after the view is layout and visible, then call
+ * notifyNodeRemoved() when it's gone (without known when it was removed from the parent).
+ *
+ * TODO(b/111276913): the current algortighm could probably be optimized and some of them
+ * removed
+ */
+ private static final int PFLAG4_NOTIFIED_CONTENT_CAPTURE_ON_LAYOUT = 0x10;
+ private static final int PFLAG4_NOTIFIED_CONTENT_CAPTURE_ADDED = 0x20;
+ private static final int PFLAG4_LAST_CONTENT_CAPTURE_NOTIFICATION_TYPE = 0x40;
+
+ private static final int CONTENT_CAPTURE_NOTIFICATION_TYPE_APPEARED = 1;
+ private static final int CONTENT_CAPTURE_NOTIFICATION_TYPE_DISAPPEARED = 0;
+
+ /** @hide */
+ @IntDef(flag = true, prefix = { "CONTENT_CAPTURE_NOTIFICATION_TYPE_" }, value = {
+ CONTENT_CAPTURE_NOTIFICATION_TYPE_APPEARED,
+ CONTENT_CAPTURE_NOTIFICATION_TYPE_DISAPPEARED
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface ContentCaptureNotificationType {}
+
+ /* End of masks for mPrivateFlags4 */
+
/**
* Always allow a user to over-scroll this view, provided it is a
* view that can scroll.
@@ -3861,6 +4010,8 @@
@UnsupportedAppUsage
int mPrivateFlags3;
+ private int mPrivateFlags4;
+
/**
* This view's request for the visibility of the status bar.
* @hide
@@ -5803,6 +5954,7 @@
mAttributes = trimmed;
}
+ @Override
public String toString() {
StringBuilder out = new StringBuilder(128);
out.append(getClass().getName());
@@ -5875,6 +6027,9 @@
}
}
}
+ if (mAutofillId != null) {
+ out.append(" aid="); out.append(mAutofillId);
+ }
out.append("}");
return out.toString();
}
@@ -7888,7 +8043,8 @@
* fills in all data that can be inferred from the view itself.
*/
public void onProvideStructure(ViewStructure structure) {
- onProvideStructureForAssistOrAutofill(structure, false, 0);
+ onProvideStructureForAssistOrAutofillOrViewCapture(structure, /* forAutofill = */ false,
+ /* forViewCapture= */ false, /* flags= */ 0);
}
/**
@@ -7961,11 +8117,46 @@
* @see #AUTOFILL_FLAG_INCLUDE_NOT_IMPORTANT_VIEWS
*/
public void onProvideAutofillStructure(ViewStructure structure, @AutofillFlags int flags) {
- onProvideStructureForAssistOrAutofill(structure, true, flags);
+ onProvideStructureForAssistOrAutofillOrViewCapture(structure, /* forAutofill = */ true,
+ /* forViewCapture= */ false, flags);
}
- private void onProvideStructureForAssistOrAutofill(ViewStructure structure,
- boolean forAutofill, @AutofillFlags int flags) {
+ /**
+ * Populates a {@link ViewStructure} for Content Capture.
+ *
+ * <p>This method is called after a view is that is eligible for Content Capture
+ * (for example, if it {@link #isImportantForAutofill()}, an intelligence service is enabled for
+ * the user, and the activity rendering the view is enabled for Content Capture) is laid out and
+ * is visible.
+ *
+ * <p><b>Note: </b>the following methods of the {@code structure} will be ignored:
+ * <ul>
+ * <li>{@link ViewStructure#setChildCount(int)}
+ * <li>{@link ViewStructure#addChildCount(int)}
+ * <li>{@link ViewStructure#getChildCount()}
+ * <li>{@link ViewStructure#newChild(int)}
+ * <li>{@link ViewStructure#asyncNewChild(int)}
+ * <li>{@link ViewStructure#asyncCommit()}
+ * <li>{@link ViewStructure#setWebDomain(String)}
+ * <li>{@link ViewStructure#newHtmlInfoBuilder(String)}
+ * <li>{@link ViewStructure#setHtmlInfo(android.view.ViewStructure.HtmlInfo)}
+ * <li>{@link ViewStructure#setDataIsSensitive(boolean)}
+ * </ul>
+ *
+ * @return whether the IntelligenceService should be notified that the view was added (through
+ * the {@link IntelligenceManager#notifyViewAppeared(ViewStructure)} method) to the view
+ * hierarchy. Most views should return {@code true} here, but views that contains virtual
+ * hierarchy might opt to return {@code false} and notify the manager independently, as the
+ * virtual views are rendered.
+ */
+ public boolean onProvideContentCaptureStructure(@NonNull ViewStructure structure, int flags) {
+ onProvideStructureForAssistOrAutofillOrViewCapture(structure, /* forAutofill = */ false,
+ /* forViewCapture= */ true, flags);
+ return true;
+ }
+
+ private void onProvideStructureForAssistOrAutofillOrViewCapture(ViewStructure structure,
+ boolean forAutofill, boolean forViewCapture, @AutofillFlags int flags) {
final int id = mID;
if (id != NO_ID && !isViewIdGenerated(id)) {
String pkg, type, entry;
@@ -7981,8 +8172,11 @@
} else {
structure.setId(id, null, null, null);
}
+ if (forViewCapture) {
+ structure.setDataIsSensitive(false);
+ }
- if (forAutofill) {
+ if (forAutofill || forViewCapture) {
final @AutofillType int autofillType = getAutofillType();
// Don't need to fill autofill info if view does not support it.
// For example, only TextViews that are editable support autofill
@@ -8530,9 +8724,8 @@
if (parentImportance == IMPORTANT_FOR_AUTOFILL_NO_EXCLUDE_DESCENDANTS
|| parentImportance == IMPORTANT_FOR_AUTOFILL_YES_EXCLUDE_DESCENDANTS) {
if (Log.isLoggable(AUTOFILL_LOG_TAG, Log.VERBOSE)) {
- Log.v(AUTOFILL_LOG_TAG, "View (autofillId=" + getAutofillViewId() + ", "
- + getClass() + ") is not important for autofill because parent "
- + parent + "'s importance is " + parentImportance);
+ Log.v(AUTOFILL_LOG_TAG, "View (" + this + ") is not important for autofill "
+ + "because parent " + parent + "'s importance is " + parentImportance);
}
return false;
}
@@ -8549,14 +8742,18 @@
if (importance == IMPORTANT_FOR_AUTOFILL_NO_EXCLUDE_DESCENDANTS
|| importance == IMPORTANT_FOR_AUTOFILL_NO) {
if (Log.isLoggable(AUTOFILL_LOG_TAG, Log.VERBOSE)) {
- Log.v(AUTOFILL_LOG_TAG, "View (autofillId=" + getAutofillViewId() + ", "
- + getClass() + ") is not important for autofill because its "
- + "importance is " + importance);
+ Log.v(AUTOFILL_LOG_TAG, "View (" + this + ") is not important for autofill "
+ + "because its importance is " + importance);
}
return false;
}
// Then use some heuristics to handle AUTO.
+ if (importance != IMPORTANT_FOR_AUTOFILL_AUTO) {
+ Log.w(AUTOFILL_LOG_TAG, "invalid autofill importance (" + importance + " on view "
+ + this);
+ return false;
+ }
// Always include views that have an explicit resource id.
final int id = mID;
@@ -8584,6 +8781,198 @@
return false;
}
+ /**
+ * Gets the mode for determining whether this view is important for content capture.
+ *
+ * <p>See {@link #setImportantForContentCapture(int)} and
+ * {@link #isImportantForContentCapture()} for more info about this mode.
+ *
+ * @return {@link #IMPORTANT_FOR_CONTENT_CAPTURE_AUTO} by default, or value passed to
+ * {@link #setImportantForContentCapture(int)}.
+ *
+ * @attr ref android.R.styleable#View_importantForContentCapture
+ */
+ @ViewDebug.ExportedProperty(mapping = {
+ @ViewDebug.IntToString(from = IMPORTANT_FOR_CONTENT_CAPTURE_AUTO, to = "auto"),
+ @ViewDebug.IntToString(from = IMPORTANT_FOR_CONTENT_CAPTURE_YES, to = "yes"),
+ @ViewDebug.IntToString(from = IMPORTANT_FOR_CONTENT_CAPTURE_NO, to = "no"),
+ @ViewDebug.IntToString(from = IMPORTANT_FOR_CONTENT_CAPTURE_YES_EXCLUDE_DESCENDANTS,
+ to = "yesExcludeDescendants"),
+ @ViewDebug.IntToString(from = IMPORTANT_FOR_CONTENT_CAPTURE_NO_EXCLUDE_DESCENDANTS,
+ to = "noExcludeDescendants")})
+ public @ContentCaptureImportance int getImportantForContentCapture() {
+ // NOTE: the important for content capture values were the first flags added and are set in
+ // the rightmost position, so we don't need to shift them
+ return mPrivateFlags4 & PFLAG4_IMPORTANT_FOR_CONTENT_CAPTURE_MASK;
+ }
+
+ /**
+ * Sets the mode for determining whether this view is considered important for content capture.
+ *
+ * <p>The platform determines the importance for autofill automatically but you
+ * can use this method to customize the behavior. Typically, a view that provides text should
+ * be marked as {@link #IMPORTANT_FOR_CONTENT_CAPTURE_YES}.
+ *
+ * @param mode {@link #IMPORTANT_FOR_CONTENT_CAPTURE_AUTO},
+ * {@link #IMPORTANT_FOR_CONTENT_CAPTURE_YES}, {@link #IMPORTANT_FOR_CONTENT_CAPTURE_NO},
+ * {@link #IMPORTANT_FOR_CONTENT_CAPTURE_YES_EXCLUDE_DESCENDANTS},
+ * or {@link #IMPORTANT_FOR_CONTENT_CAPTURE_NO_EXCLUDE_DESCENDANTS}.
+ *
+ * @attr ref android.R.styleable#View_importantForContentCapture
+ */
+ public void setImportantForContentCapture(@ContentCaptureImportance int mode) {
+ // Reset first
+ mPrivateFlags4 &= ~PFLAG4_IMPORTANT_FOR_CONTENT_CAPTURE_MASK;
+ // Then set again
+ // NOTE: the important for content capture values were the first flags added and are set in
+ // the rightmost position, so we don't need to shift them
+ mPrivateFlags4 |= (mode & PFLAG4_IMPORTANT_FOR_CONTENT_CAPTURE_MASK);
+ }
+
+ /**
+ * Hints the Android System whether this view is considered important for Content Capture, based
+ * on the value explicitly set by {@link #setImportantForContentCapture(int)} and heuristics
+ * when it's {@link #IMPORTANT_FOR_CONTENT_CAPTURE_AUTO}.
+ *
+ * @return whether the view is considered important for autofill.
+ *
+ * @see #setImportantForContentCapture(int)
+ * @see #IMPORTANT_FOR_CONTENT_CAPTURE_AUTO
+ * @see #IMPORTANT_FOR_CONTENT_CAPTURE_YES
+ * @see #IMPORTANT_FOR_CONTENT_CAPTURE_NO
+ * @see #IMPORTANT_FOR_CONTENT_CAPTURE_YES_EXCLUDE_DESCENDANTS
+ * @see #IMPORTANT_FOR_CONTENT_CAPTURE_NO_EXCLUDE_DESCENDANTS
+ */
+ public final boolean isImportantForContentCapture() {
+ // Check parent mode to ensure we're important
+ ViewParent parent = mParent;
+ while (parent instanceof View) {
+ final int parentImportance = ((View) parent).getImportantForContentCapture();
+ if (parentImportance == IMPORTANT_FOR_CONTENT_CAPTURE_NO_EXCLUDE_DESCENDANTS
+ || parentImportance == IMPORTANT_FOR_CONTENT_CAPTURE_YES_EXCLUDE_DESCENDANTS) {
+ if (Log.isLoggable(CONTENT_CAPTURE_LOG_TAG, Log.VERBOSE)) {
+ Log.v(CONTENT_CAPTURE_LOG_TAG, "View (" + this + ") is not important for "
+ + "content capture because parent " + parent + "'s importance is "
+ + parentImportance);
+ }
+ return false;
+ }
+ parent = parent.getParent();
+ }
+
+ final int importance = getImportantForContentCapture();
+
+ // First, check the explicit states.
+ if (importance == IMPORTANT_FOR_CONTENT_CAPTURE_YES_EXCLUDE_DESCENDANTS
+ || importance == IMPORTANT_FOR_CONTENT_CAPTURE_YES) {
+ return true;
+ }
+ if (importance == IMPORTANT_FOR_CONTENT_CAPTURE_NO_EXCLUDE_DESCENDANTS
+ || importance == IMPORTANT_FOR_CONTENT_CAPTURE_NO) {
+ if (Log.isLoggable(CONTENT_CAPTURE_LOG_TAG, Log.VERBOSE)) {
+ Log.v(CONTENT_CAPTURE_LOG_TAG, "View (" + this + ") is not important for content "
+ + "capture because its importance is " + importance);
+ }
+ return false;
+ }
+
+ // Then use some heuristics to handle AUTO.
+ if (importance != IMPORTANT_FOR_CONTENT_CAPTURE_AUTO) {
+ Log.w(CONTENT_CAPTURE_LOG_TAG, "invalid content capture importance (" + importance
+ + " on view " + this);
+ return false;
+ }
+
+ // View group is important if at least one children also is
+ //TODO(b/111276913): decide if we really need to send the relevant parents or just the
+ // leaves (with absolute coordinates). If it's the latter, then we need to update this
+ // javadoc and ViewGroup's implementation.
+ if (this instanceof ViewGroup) {
+ final ViewGroup group = (ViewGroup) this;
+ for (int i = 0; i < group.getChildCount(); i++) {
+ final View child = group.getChildAt(i);
+ if (child.isImportantForContentCapture()) {
+ return true;
+ }
+ }
+ }
+
+ // If the app developer explicitly set hints or autofill hintsfor it, it's important.
+ if (getAutofillHints() != null) {
+ return true;
+ }
+
+ // Otherwise, assume it's not important...
+ return false;
+ }
+
+ /**
+ * Helper used to notify the {@link IntelligenceManager}anager when the view is removed or
+ * added, based on whether it's laid out and visible, and without knowing if the parent removed
+ * it from the view
+ * hierarchy.
+ */
+ // TODO(b/111276913): make sure the current algorithm covers all cases. For example, it should
+ // probably be called every time notifyEnterOrExitForAutoFillIfNeeded() is called as well.
+ private void notifyNodeAddedOrRemovedForContentCaptureIfNeeded(
+ @ContentCaptureNotificationType int type) {
+ if (type != CONTENT_CAPTURE_NOTIFICATION_TYPE_APPEARED
+ && type != CONTENT_CAPTURE_NOTIFICATION_TYPE_DISAPPEARED) {
+ // Sanity check so it does not screw up the flags
+ Log.wtf(CONTENT_CAPTURE_LOG_TAG, "notifyNodeAddedOrRemovedForContentCaptureIfNeeded(): "
+ + "invalid type " + type + " for " + this);
+ return;
+ }
+
+ if (!isImportantForContentCapture()) return;
+
+ final IntelligenceManager im = mContext.getSystemService(IntelligenceManager.class);
+ if (im == null || !im.isContentCaptureEnabled()) return;
+
+ // Make sure event is notified just once, and reset the
+ // PFLAG4_LAST_CONTENT_CAPTURE_NOTIFICATION_TYPE flag
+ boolean ignoreNotification = false;
+ if (type == CONTENT_CAPTURE_NOTIFICATION_TYPE_APPEARED) {
+ if ((mPrivateFlags4 & PFLAG4_LAST_CONTENT_CAPTURE_NOTIFICATION_TYPE)
+ == CONTENT_CAPTURE_NOTIFICATION_TYPE_APPEARED) {
+ ignoreNotification = true;
+ } else {
+ mPrivateFlags4 |= PFLAG4_LAST_CONTENT_CAPTURE_NOTIFICATION_TYPE;
+ }
+ } else {
+ if ((mPrivateFlags4 & PFLAG4_LAST_CONTENT_CAPTURE_NOTIFICATION_TYPE)
+ == CONTENT_CAPTURE_NOTIFICATION_TYPE_DISAPPEARED) {
+ ignoreNotification = true;
+ } else {
+ mPrivateFlags4 &= ~PFLAG4_LAST_CONTENT_CAPTURE_NOTIFICATION_TYPE;
+ }
+ }
+ if (ignoreNotification) {
+ if (Log.isLoggable(CONTENT_CAPTURE_LOG_TAG, Log.VERBOSE)) {
+ // TODO(b/111276913): remove this log statement if the algorithm is not improved
+ // (right now it's called too many times when the activity is stopped and/or views
+ // disappear
+ Log.v(CONTENT_CAPTURE_LOG_TAG, "notifyNodeAddedOrRemovedForContentCaptureIfNeeded("
+ + type + "): ignoring repeated notification on " + this);
+ }
+ return;
+ }
+
+ if (type == CONTENT_CAPTURE_NOTIFICATION_TYPE_APPEARED) {
+ final ViewStructure structure = im.newViewStructure(this);
+ boolean notifyMgr = onProvideContentCaptureStructure(structure, /* flags= */ 0);
+ if (notifyMgr) {
+ im.notifyViewAppeared(structure);
+ }
+ mPrivateFlags4 |= PFLAG4_NOTIFIED_CONTENT_CAPTURE_ADDED;
+ } else {
+ if ((mPrivateFlags4 & PFLAG4_NOTIFIED_CONTENT_CAPTURE_ADDED) == 0) {
+ return; // skip initial notification
+ }
+ im.notifyViewDisappeared(getAutofillId());
+ }
+ }
+
@Nullable
private AutofillManager getAutofillManager() {
return mContext.getSystemService(AutofillManager.class);
@@ -13094,6 +13483,9 @@
: AccessibilityEvent.CONTENT_CHANGE_TYPE_PANE_DISAPPEARED);
}
}
+ notifyNodeAddedOrRemovedForContentCaptureIfNeeded(isVisible
+ ? CONTENT_CAPTURE_NOTIFICATION_TYPE_APPEARED
+ : CONTENT_CAPTURE_NOTIFICATION_TYPE_DISAPPEARED);
}
/**
@@ -18630,6 +19022,8 @@
}
notifyEnterOrExitForAutoFillIfNeeded(false);
+ notifyNodeAddedOrRemovedForContentCaptureIfNeeded(
+ CONTENT_CAPTURE_NOTIFICATION_TYPE_DISAPPEARED);
}
/**
@@ -20934,6 +21328,13 @@
mPrivateFlags3 &= ~PFLAG3_NOTIFY_AUTOFILL_ENTER_ON_LAYOUT;
notifyEnterOrExitForAutoFillIfNeeded(true);
}
+
+ if ((mViewFlags & VISIBILITY_MASK) == VISIBLE
+ && (mPrivateFlags4 & PFLAG4_NOTIFIED_CONTENT_CAPTURE_ON_LAYOUT) == 0) {
+ notifyNodeAddedOrRemovedForContentCaptureIfNeeded(
+ CONTENT_CAPTURE_NOTIFICATION_TYPE_APPEARED);
+ mPrivateFlags4 |= PFLAG4_NOTIFIED_CONTENT_CAPTURE_ON_LAYOUT;
+ }
}
private boolean hasParentWantsFocus() {
@@ -24122,7 +24523,7 @@
* @param outShadowSize A {@link android.graphics.Point} containing the width and height
* of the shadow image. Your application must set {@link android.graphics.Point#x} to the
* desired width and must set {@link android.graphics.Point#y} to the desired height of the
- * image.
+ * image. Since Android P, the width and height must be positive values.
*
* @param outShadowTouchPoint A {@link android.graphics.Point} for the position within the
* shadow image that should be underneath the touch point during the drag and drop
diff --git a/core/java/android/view/ViewStructure.java b/core/java/android/view/ViewStructure.java
index 38dcdd3..6efb6f3 100644
--- a/core/java/android/view/ViewStructure.java
+++ b/core/java/android/view/ViewStructure.java
@@ -24,7 +24,6 @@
import android.os.LocaleList;
import android.util.Pair;
import android.view.View.AutofillImportance;
-import android.view.ViewStructure.HtmlInfo;
import android.view.autofill.AutofillId;
import android.view.autofill.AutofillValue;
diff --git a/core/java/android/view/intelligence/ContentCaptureEvent.java b/core/java/android/view/intelligence/ContentCaptureEvent.java
index 2530ae3..befcb55 100644
--- a/core/java/android/view/intelligence/ContentCaptureEvent.java
+++ b/core/java/android/view/intelligence/ContentCaptureEvent.java
@@ -16,12 +16,17 @@
package android.view.intelligence;
import android.annotation.IntDef;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
+import android.os.SystemClock;
import android.view.autofill.AutofillId;
+import com.android.internal.util.Preconditions;
+
+import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -60,14 +65,14 @@
*
* <p>The metadata of the node is available through {@link #getViewNode()}.
*/
- public static final int TYPE_VIEW_ADDED = 5;
+ public static final int TYPE_VIEW_APPEARED = 5;
/**
* Called when a node has been removed from the screen and is not visible to the user anymore.
*
* <p>The id of the node is available through {@link #getId()}.
*/
- public static final int TYPE_VIEW_REMOVED = 6;
+ public static final int TYPE_VIEW_DISAPPEARED = 6;
/**
* Called when the text of a node has been changed.
@@ -85,8 +90,8 @@
TYPE_ACTIVITY_PAUSED,
TYPE_ACTIVITY_RESUMED,
TYPE_ACTIVITY_STOPPED,
- TYPE_VIEW_ADDED,
- TYPE_VIEW_REMOVED,
+ TYPE_VIEW_APPEARED,
+ TYPE_VIEW_DISAPPEARED,
TYPE_VIEW_TEXT_CHANGED
})
@Retention(RetentionPolicy.SOURCE)
@@ -95,7 +100,9 @@
private final int mType;
private final long mEventTime;
private final int mFlags;
-
+ private @Nullable AutofillId mId;
+ private @Nullable ViewNode mNode;
+ private @Nullable CharSequence mText;
/** @hide */
public ContentCaptureEvent(int type, long eventTime, int flags) {
@@ -104,12 +111,42 @@
mFlags = flags;
}
+
+ /** @hide */
+ public ContentCaptureEvent(int type, int flags) {
+ this(type, SystemClock.uptimeMillis(), flags);
+ }
+
+ /** @hide */
+ public ContentCaptureEvent(int type) {
+ this(type, /* flags= */ 0);
+ }
+
+ /** @hide */
+ public ContentCaptureEvent setAutofillId(@NonNull AutofillId id) {
+ mId = Preconditions.checkNotNull(id);
+ return this;
+ }
+
+ /** @hide */
+ public ContentCaptureEvent setViewNode(@NonNull ViewNode node) {
+ mNode = Preconditions.checkNotNull(node);
+ return this;
+ }
+
+ /** @hide */
+ public ContentCaptureEvent setText(@Nullable CharSequence text) {
+ mText = text;
+ return this;
+ }
+
/**
* Gets the type of the event.
*
* @return one of {@link #TYPE_ACTIVITY_STARTED}, {@link #TYPE_ACTIVITY_RESUMED},
* {@link #TYPE_ACTIVITY_PAUSED}, {@link #TYPE_ACTIVITY_STOPPED},
- * {@link #TYPE_VIEW_ADDED}, {@link #TYPE_VIEW_REMOVED}, or {@link #TYPE_VIEW_TEXT_CHANGED}.
+ * {@link #TYPE_VIEW_APPEARED}, {@link #TYPE_VIEW_DISAPPEARED},
+ * or {@link #TYPE_VIEW_TEXT_CHANGED}.
*/
public @EventType int getType() {
return mType;
@@ -135,21 +172,21 @@
/**
* Gets the whole metadata of the node associated with the event.
*
- * <p>Only set on {@link #TYPE_VIEW_ADDED} events.
+ * <p>Only set on {@link #TYPE_VIEW_APPEARED} events.
*/
@Nullable
public ViewNode getViewNode() {
- return null;
+ return mNode;
}
/**
* Gets the {@link AutofillId} of the node associated with the event.
*
- * <p>Only set on {@link #TYPE_VIEW_REMOVED} and {@link #TYPE_VIEW_TEXT_CHANGED} events.
+ * <p>Only set on {@link #TYPE_VIEW_DISAPPEARED} and {@link #TYPE_VIEW_TEXT_CHANGED} events.
*/
@Nullable
public AutofillId getId() {
- return null;
+ return mId;
}
/**
@@ -159,16 +196,41 @@
*/
@Nullable
public CharSequence getText() {
- return null;
+ return mText;
+ }
+
+ /** @hide */
+ public void dump(@NonNull PrintWriter pw) {
+ pw.print("type="); pw.print(getTypeAsString(mType));
+ pw.print(", time="); pw.print(mEventTime);
+ if (mFlags > 0) {
+ pw.print(", flags="); pw.print(mFlags);
+ }
+ if (mId != null) {
+ pw.print(", id="); pw.print(mId);
+ }
+ if (mNode != null) {
+ pw.print(", id="); pw.print(mNode.getAutofillId());
+ }
}
@Override
public String toString() {
final StringBuilder string = new StringBuilder("ContentCaptureEvent[type=")
- .append(getTypeAsString(mType)).append(", time=").append(mEventTime);
+ .append(getTypeAsString(mType));
if (mFlags > 0) {
string.append(", flags=").append(mFlags);
}
+ if (mId != null) {
+ string.append(", id=").append(mId);
+ }
+ if (mNode != null) {
+ final String className = mNode.getClassName();
+ if (mNode != null) {
+ string.append(", class=").append(className);
+ }
+ string.append(", id=").append(mNode.getAutofillId());
+ }
return string.append(']').toString();
}
@@ -182,6 +244,9 @@
parcel.writeInt(mType);
parcel.writeLong(mEventTime);
parcel.writeInt(mFlags);
+ parcel.writeParcelable(mId, flags);
+ ViewNode.writeToParcel(parcel, mNode, flags);
+ parcel.writeCharSequence(mText);
}
public static final Parcelable.Creator<ContentCaptureEvent> CREATOR =
@@ -192,7 +257,17 @@
final int type = parcel.readInt();
final long eventTime = parcel.readLong();
final int flags = parcel.readInt();
- return new ContentCaptureEvent(type, eventTime, flags);
+ final ContentCaptureEvent event = new ContentCaptureEvent(type, eventTime, flags);
+ final AutofillId id = parcel.readParcelable(null);
+ if (id != null) {
+ event.setAutofillId(id);
+ }
+ final ViewNode node = ViewNode.readFromParcel(parcel);
+ if (node != null) {
+ event.setViewNode(node);
+ }
+ event.setText(parcel.readCharSequence());
+ return event;
}
@Override
@@ -201,7 +276,6 @@
}
};
-
/** @hide */
public static String getTypeAsString(@EventType int type) {
switch (type) {
@@ -213,10 +287,10 @@
return "ACTIVITY_PAUSED";
case TYPE_ACTIVITY_STOPPED:
return "ACTIVITY_STOPPED";
- case TYPE_VIEW_ADDED:
- return "VIEW_ADDED";
- case TYPE_VIEW_REMOVED:
- return "VIEW_REMOVED";
+ case TYPE_VIEW_APPEARED:
+ return "VIEW_APPEARED";
+ case TYPE_VIEW_DISAPPEARED:
+ return "VIEW_DISAPPEARED";
case TYPE_VIEW_TEXT_CHANGED:
return "VIEW_TEXT_CHANGED";
default:
diff --git a/core/java/android/view/intelligence/IntelligenceManager.java b/core/java/android/view/intelligence/IntelligenceManager.java
index 9bf6c2c..c02fb32 100644
--- a/core/java/android/view/intelligence/IntelligenceManager.java
+++ b/core/java/android/view/intelligence/IntelligenceManager.java
@@ -15,6 +15,12 @@
*/
package android.view.intelligence;
+import static android.view.intelligence.ContentCaptureEvent.TYPE_VIEW_APPEARED;
+import static android.view.intelligence.ContentCaptureEvent.TYPE_VIEW_DISAPPEARED;
+import static android.view.intelligence.ContentCaptureEvent.TYPE_VIEW_TEXT_CHANGED;
+
+import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
@@ -22,11 +28,15 @@
import android.content.ComponentName;
import android.content.Context;
import android.os.Bundle;
+import android.os.Handler;
+import android.os.HandlerThread;
import android.os.IBinder;
import android.os.RemoteException;
-import android.os.SystemClock;
import android.service.intelligence.InteractionSessionId;
import android.util.Log;
+import android.view.View;
+import android.view.ViewStructure;
+import android.view.autofill.AutofillId;
import android.view.intelligence.ContentCaptureEvent.EventType;
import com.android.internal.annotations.GuardedBy;
@@ -34,8 +44,7 @@
import com.android.internal.util.Preconditions;
import java.io.PrintWriter;
-import java.util.Arrays;
-import java.util.List;
+import java.util.ArrayList;
import java.util.Set;
/**
@@ -46,8 +55,9 @@
private static final String TAG = "IntelligenceManager";
- // TODO(b/111276913): define a way to dynamically set it (for example, using settings?)
+ // TODO(b/111276913): define a way to dynamically set them(for example, using settings?)
private static final boolean VERBOSE = false;
+ private static final boolean DEBUG = true; // STOPSHIP if not set to false
/**
* Used to indicate that a text change was caused by user input (for example, through IME).
@@ -55,7 +65,6 @@
//TODO(b/111276913): link to notifyTextChanged() method once available
public static final int FLAG_USER_INPUT = 0x1;
-
/**
* Initial state, when there is no session.
*
@@ -77,6 +86,15 @@
*/
public static final int STATE_ACTIVE = 2;
+ private static final String BG_THREAD_NAME = "intel_svc_streamer_thread";
+
+ /**
+ * Maximum number of events that are delayed for an app.
+ *
+ * <p>If the session is not started after the limit is reached, it's discarded.
+ */
+ private static final int MAX_DELAYED_SIZE = 20;
+
private final Context mContext;
@Nullable
@@ -99,10 +117,24 @@
@GuardedBy("mLock")
private ComponentName mComponentName;
+ // TODO(b/111276913): create using maximum batch size as capacity
+ /**
+ * List of events held to be sent as a batch.
+ */
+ @GuardedBy("mLock")
+ private final ArrayList<ContentCaptureEvent> mEvents = new ArrayList<>();
+
+ private final Handler mHandler;
+
/** @hide */
public IntelligenceManager(@NonNull Context context, @Nullable IIntelligenceManager service) {
mContext = Preconditions.checkNotNull(context, "context cannot be null");
mService = service;
+
+ // TODO(b/111276913): use an existing bg thread instead...
+ final HandlerThread bgThread = new HandlerThread(BG_THREAD_NAME);
+ bgThread.start();
+ mHandler = Handler.createAsync(bgThread.getLooper());
}
/** @hide */
@@ -111,8 +143,9 @@
synchronized (mLock) {
if (mState != STATE_UNKNOWN) {
+ // TODO(b/111276913): revisit this scenario
Log.w(TAG, "ignoring onActivityStarted(" + token + ") while on state "
- + getStateAsStringLocked());
+ + getStateAsString(mState));
return;
}
mState = STATE_WAITING_FOR_SERVER;
@@ -121,8 +154,8 @@
mComponentName = componentName;
if (VERBOSE) {
- Log.v(TAG, "onActivityStarted(): token=" + token + ", act=" + componentName
- + ", id=" + mId);
+ Log.v(TAG, "onActivityCreated(): token=" + token + ", act="
+ + getActivityDebugNameLocked() + ", id=" + mId);
}
final int flags = 0; // TODO(b/111276913): get proper flags
@@ -138,12 +171,12 @@
} else {
// TODO(b/111276913): handle other cases like disabled by
// service
- mState = STATE_UNKNOWN;
+ resetStateLocked();
}
if (VERBOSE) {
Log.v(TAG, "onActivityStarted() result: code=" + resultCode
+ ", id=" + mId
- + ", state=" + getStateAsStringLocked());
+ + ", state=" + getStateAsString(mState));
}
}
}
@@ -154,6 +187,60 @@
}
}
+ //TODO(b/111276913): should buffer event (and call service on handler thread), instead of
+ // calling right away
+ private void sendEvent(@NonNull ContentCaptureEvent event) {
+ mHandler.sendMessage(obtainMessage(IntelligenceManager::handleSendEvent, this, event));
+ }
+
+ private void handleSendEvent(@NonNull ContentCaptureEvent event) {
+
+ synchronized (mLock) {
+ mEvents.add(event);
+ final int numberEvents = mEvents.size();
+ if (mState != STATE_ACTIVE) {
+ if (numberEvents >= MAX_DELAYED_SIZE) {
+ // Typically happens on system apps that are started before the system service
+ // is ready (like com.android.settings/.FallbackHome)
+ //TODO(b/111276913): try to ignore session while system is not ready / boot
+ // not complete instead.
+ Log.w(TAG, "Closing session for " + getActivityDebugNameLocked()
+ + " after " + numberEvents + " delayed events");
+ // TODO(b/111276913): blacklist activity / use special flag to indicate that
+ // when it's launched again
+ resetStateLocked();
+ return;
+ }
+
+ if (VERBOSE) {
+ Log.v(TAG, "Delaying " + numberEvents + " events for "
+ + getActivityDebugNameLocked() + " while on state "
+ + getStateAsString(mState));
+ }
+ return;
+ }
+
+ if (mId == null) {
+ // Sanity check - should not happen
+ Log.wtf(TAG, "null session id for " + mComponentName);
+ return;
+ }
+
+ //TODO(b/111276913): right now we're sending sending right away (unless not ready), but
+ // we should hold the events and flush later.
+ try {
+ if (DEBUG) {
+ Log.d(TAG, "Sending " + numberEvents + " event(s) for "
+ + getActivityDebugNameLocked());
+ }
+ mService.sendEvents(mContext.getUserId(), mId, mEvents);
+ mEvents.clear();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ }
+
/**
* Used for intermediate events (i.e, other than created and destroyed).
*
@@ -161,28 +248,11 @@
*/
public void onActivityLifecycleEvent(@EventType int type) {
if (!isContentCaptureEnabled()) return;
-
- //TODO(b/111276913): should buffer event (and call service on handler thread), instead of
- // calling right away
- final ContentCaptureEvent event = new ContentCaptureEvent(type, SystemClock.uptimeMillis(),
- 0);
- final List<ContentCaptureEvent> events = Arrays.asList(event);
-
- synchronized (mLock) {
- //TODO(b/111276913): check session state; for example, how to handle if it's waiting for
- // remote id
-
- if (VERBOSE) {
- Log.v(TAG, "onActivityLifecycleEvent() for " + mComponentName.flattenToShortString()
- + ": " + ContentCaptureEvent.getTypeAsString(type));
- }
-
- try {
- mService.sendEvents(mContext.getUserId(), mId, events);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
+ if (VERBOSE) {
+ Log.v(TAG, "onActivityLifecycleEvent() for " + getActivityDebugNameLocked()
+ + ": " + ContentCaptureEvent.getTypeAsString(type));
}
+ sendEvent(new ContentCaptureEvent(type));
}
/** @hide */
@@ -194,22 +264,105 @@
// id) and send it to the cache of batched commands
if (VERBOSE) {
- Log.v(TAG, "onActivityDestroyed(): state=" + getStateAsStringLocked()
+ Log.v(TAG, "onActivityDestroyed(): state=" + getStateAsString(mState)
+ ", mId=" + mId);
}
try {
mService.finishSession(mContext.getUserId(), mId);
- mState = STATE_UNKNOWN;
- mId = null;
- mApplicationToken = null;
- mComponentName = null;
+ resetStateLocked();
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
}
+ @GuardedBy("mLock")
+ private void resetStateLocked() {
+ mState = STATE_UNKNOWN;
+ mId = null;
+ mApplicationToken = null;
+ mComponentName = null;
+ mEvents.clear();
+ }
+
+ /**
+ * Notifies the Intelligence Service that a node has been added to the view structure.
+ *
+ * <p>Typically called "manually" by views that handle their own virtual view hierarchy, or
+ * automatically by the Android System for views that return {@code true} on
+ * {@link View#onProvideContentCaptureStructure(ViewStructure, int)}.
+ *
+ * @param node node that has been added.
+ */
+ public void notifyViewAppeared(@NonNull ViewStructure node) {
+ Preconditions.checkNotNull(node);
+ if (!isContentCaptureEnabled()) return;
+
+ if (!(node instanceof ViewNode.ViewStructureImpl)) {
+ throw new IllegalArgumentException("Invalid node class: " + node.getClass());
+ }
+ sendEvent(new ContentCaptureEvent(TYPE_VIEW_APPEARED)
+ .setViewNode(((ViewNode.ViewStructureImpl) node).mNode));
+ }
+
+ /**
+ * Notifies the Intelligence Service that a node has been removed from the view structure.
+ *
+ * <p>Typically called "manually" by views that handle their own virtual view hierarchy, or
+ * automatically by the Android System for standard views.
+ *
+ * @param id id of the node that has been removed.
+ */
+ public void notifyViewDisappeared(@NonNull AutofillId id) {
+ Preconditions.checkNotNull(id);
+ if (!isContentCaptureEnabled()) return;
+
+ sendEvent(new ContentCaptureEvent(TYPE_VIEW_DISAPPEARED).setAutofillId(id));
+ }
+
+ /**
+ * Notifies the Intelligence Service that the value of a text node has been changed.
+ *
+ * @param id of the node.
+ * @param text new text.
+ * @param flags either {@code 0} or {@link #FLAG_USER_INPUT} when the value was explicitly
+ * changed by the user (for example, through the keyboard).
+ */
+ public void notifyViewTextChanged(@NonNull AutofillId id, @Nullable CharSequence text,
+ int flags) {
+ Preconditions.checkNotNull(id);
+ if (!isContentCaptureEnabled()) return;
+
+ sendEvent(new ContentCaptureEvent(TYPE_VIEW_TEXT_CHANGED, flags).setAutofillId(id)
+ .setText(text));
+ }
+
+ /**
+ * Creates a {@link ViewStructure} for a "standard" view.
+ *
+ * @hide
+ */
+ @NonNull
+ public ViewStructure newViewStructure(@NonNull View view) {
+ return new ViewNode.ViewStructureImpl(view);
+ }
+
+ /**
+ * Creates a {@link ViewStructure} for a "virtual" view, so it can be passed to
+ * {@link #notifyViewAppeared(ViewStructure)} by the view managing the virtual view hierarchy.
+ *
+ * @param parentId id of the virtual view parent (it can be obtained by calling
+ * {@link ViewStructure#getAutofillId()} on the parent).
+ * @param virtualId id of the virtual child, relative to the parent.
+ *
+ * @return a new {@link ViewStructure} that can be used for Content Capture purposes.
+ */
+ @NonNull
+ public ViewStructure newVirtualViewStructure(@NonNull AutofillId parentId, int virtualId) {
+ return new ViewNode.ViewStructureImpl(parentId, virtualId);
+ }
+
/**
* Returns the component name of the {@code android.service.intelligence.IntelligenceService}
* that is enabled for the current user.
@@ -322,15 +475,29 @@
pw.print(prefix2); pw.print("enabled: "); pw.println(isContentCaptureEnabled());
pw.print(prefix2); pw.print("id: "); pw.println(mId);
pw.print(prefix2); pw.print("state: "); pw.print(mState); pw.print(" (");
- pw.print(getStateAsStringLocked()); pw.println(")");
- pw.print(prefix2); pw.print("appToken: "); pw.println(mApplicationToken);
- pw.print(prefix2); pw.print("componentName: "); pw.println(mComponentName);
+ pw.print(getStateAsString(mState)); pw.println(")");
+ pw.print(prefix2); pw.print("app token: "); pw.println(mApplicationToken);
+ pw.print(prefix2); pw.print("component name: ");
+ pw.println(mComponentName == null ? "null" : mComponentName.flattenToShortString());
+ final int numberEvents = mEvents.size();
+ pw.print(prefix2); pw.print("batched events: "); pw.println(numberEvents);
+ if (numberEvents > 0) {
+ for (int i = 0; i < numberEvents; i++) {
+ final ContentCaptureEvent event = mEvents.get(i);
+ pw.println(i); pw.print(": "); event.dump(pw); pw.println();
+ }
+
+ }
}
}
+ /**
+ * Gets a string that can be used to identify the activity on logging statements.
+ */
@GuardedBy("mLock")
- private String getStateAsStringLocked() {
- return getStateAsString(mState);
+ private String getActivityDebugNameLocked() {
+ return mComponentName == null ? mContext.getPackageName()
+ : mComponentName.flattenToShortString();
}
@NonNull
diff --git a/core/java/android/view/intelligence/ViewNode.java b/core/java/android/view/intelligence/ViewNode.java
index 357ecf5..cc78e6b 100644
--- a/core/java/android/view/intelligence/ViewNode.java
+++ b/core/java/android/view/intelligence/ViewNode.java
@@ -15,10 +15,24 @@
*/
package android.view.intelligence;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.app.assist.AssistStructure;
+import android.graphics.Matrix;
+import android.graphics.Rect;
+import android.os.Bundle;
+import android.os.LocaleList;
+import android.os.Parcel;
+import android.util.Log;
+import android.view.View;
+import android.view.ViewParent;
+import android.view.ViewStructure;
+import android.view.ViewStructure.HtmlInfo.Builder;
import android.view.autofill.AutofillId;
+import android.view.autofill.AutofillValue;
+
+import com.android.internal.util.Preconditions;
//TODO(b/111276913): add javadocs / implement Parcelable / implement
//TODO(b/111276913): for now it's extending ViewNode directly as it needs most of its properties,
@@ -28,6 +42,16 @@
@SystemApi
public final class ViewNode extends AssistStructure.ViewNode {
+ private static final String TAG = "ViewNode";
+
+ private AutofillId mParentAutofillId;
+
+ // TODO(b/111276913): temporarily setting some fields here while they're not accessible from the
+ // superclass
+ private AutofillId mAutofillId;
+ private CharSequence mText;
+ private String mClassName;
+
/** @hide */
public ViewNode() {
}
@@ -38,7 +62,343 @@
*/
@Nullable
public AutofillId getParentAutofillId() {
- //TODO(b/111276913): implement
- return null;
+ return mParentAutofillId;
+ }
+
+ // TODO(b/111276913): temporarily overwriting some methods
+ @Override
+ public AutofillId getAutofillId() {
+ return mAutofillId;
+ }
+ @Override
+ public CharSequence getText() {
+ return mText;
+ }
+ @Override
+ public String getClassName() {
+ return mClassName;
+ }
+
+ /** @hide */
+ public static void writeToParcel(@NonNull Parcel parcel, @Nullable ViewNode node, int flags) {
+ if (node == null) {
+ parcel.writeParcelable(null, flags);
+ return;
+ }
+ parcel.writeParcelable(node.mAutofillId, flags);
+ parcel.writeParcelable(node.mParentAutofillId, flags);
+ parcel.writeCharSequence(node.mText);
+ parcel.writeString(node.mClassName);
+ }
+
+ /** @hide */
+ public static @Nullable ViewNode readFromParcel(@NonNull Parcel parcel) {
+ final AutofillId id = parcel.readParcelable(null);
+ if (id == null) return null;
+
+ final ViewNode node = new ViewNode();
+
+ node.mAutofillId = id;
+ node.mParentAutofillId = parcel.readParcelable(null);
+ node.mText = parcel.readCharSequence();
+ node.mClassName = parcel.readString();
+
+ return node;
+ }
+
+ /** @hide */
+ static final class ViewStructureImpl extends ViewStructure {
+
+ final ViewNode mNode = new ViewNode();
+
+ ViewStructureImpl(@NonNull View view) {
+ mNode.mAutofillId = Preconditions.checkNotNull(view).getAutofillId();
+ final ViewParent parent = view.getParent();
+ if (parent instanceof View) {
+ mNode.mParentAutofillId = ((View) parent).getAutofillId();
+ }
+ }
+
+ ViewStructureImpl(@NonNull AutofillId parentId, int virtualId) {
+ mNode.mParentAutofillId = Preconditions.checkNotNull(parentId);
+ mNode.mAutofillId = new AutofillId(parentId, virtualId);
+ }
+
+ @Override
+ public void setId(int id, String packageName, String typeName, String entryName) {
+ // TODO(b/111276913): implement or move to superclass
+ }
+
+ @Override
+ public void setDimens(int left, int top, int scrollX, int scrollY, int width, int height) {
+ // TODO(b/111276913): implement or move to superclass
+ }
+
+ @Override
+ public void setTransformation(Matrix matrix) {
+ // TODO(b/111276913): implement or move to superclass
+ }
+
+ @Override
+ public void setElevation(float elevation) {
+ // TODO(b/111276913): implement or move to superclass
+ }
+
+ @Override
+ public void setAlpha(float alpha) {
+ // TODO(b/111276913): implement or move to superclass
+ }
+
+ @Override
+ public void setVisibility(int visibility) {
+ // TODO(b/111276913): implement or move to superclass
+ }
+
+ @Override
+ public void setAssistBlocked(boolean state) {
+ // TODO(b/111276913): implement or move to superclass
+ }
+
+ @Override
+ public void setEnabled(boolean state) {
+ // TODO(b/111276913): implement or move to superclass
+ }
+
+ @Override
+ public void setClickable(boolean state) {
+ // TODO(b/111276913): implement or move to superclass
+ }
+
+ @Override
+ public void setLongClickable(boolean state) {
+ // TODO(b/111276913): implement or move to superclass
+ }
+
+ @Override
+ public void setContextClickable(boolean state) {
+ // TODO(b/111276913): implement or move to superclass
+ }
+
+ @Override
+ public void setFocusable(boolean state) {
+ // TODO(b/111276913): implement or move to superclass
+ }
+
+ @Override
+ public void setFocused(boolean state) {
+ // TODO(b/111276913): implement or move to superclass
+ }
+
+ @Override
+ public void setAccessibilityFocused(boolean state) {
+ // TODO(b/111276913): implement or move to superclass
+ }
+
+ @Override
+ public void setCheckable(boolean state) {
+ // TODO(b/111276913): implement or move to superclass
+ }
+
+ @Override
+ public void setChecked(boolean state) {
+ // TODO(b/111276913): implement or move to superclass
+ }
+
+ @Override
+ public void setSelected(boolean state) {
+ // TODO(b/111276913): implement or move to superclass
+ }
+
+ @Override
+ public void setActivated(boolean state) {
+ // TODO(b/111276913): implement or move to superclass
+ }
+
+ @Override
+ public void setOpaque(boolean opaque) {
+ // TODO(b/111276913): implement or move to superclass
+ }
+
+ @Override
+ public void setClassName(String className) {
+ // TODO(b/111276913): temporarily setting directly; should be done on superclass instead
+ mNode.mClassName = className;
+ }
+
+ @Override
+ public void setContentDescription(CharSequence contentDescription) {
+ // TODO(b/111276913): implement or move to superclass
+ }
+
+ @Override
+ public void setText(CharSequence text) {
+ // TODO(b/111276913): temporarily setting directly; should be done on superclass instead
+ mNode.mText = text;
+ }
+
+ @Override
+ public void setText(CharSequence text, int selectionStart, int selectionEnd) {
+ // TODO(b/111276913): implement or move to superclass
+ }
+
+ @Override
+ public void setTextStyle(float size, int fgColor, int bgColor, int style) {
+ // TODO(b/111276913): implement or move to superclass
+ }
+
+ @Override
+ public void setTextLines(int[] charOffsets, int[] baselines) {
+ // TODO(b/111276913): implement or move to superclass
+ }
+
+ @Override
+ public void setHint(CharSequence hint) {
+ // TODO(b/111276913): implement or move to superclass
+ }
+
+ @Override
+ public CharSequence getText() {
+ // TODO(b/111276913): temporarily getting directly; should be done on superclass instead
+ return mNode.mText;
+ }
+
+ @Override
+ public int getTextSelectionStart() {
+ // TODO(b/111276913): implement or move to superclass
+ return 0;
+ }
+
+ @Override
+ public int getTextSelectionEnd() {
+ // TODO(b/111276913): implement or move to superclass
+ return 0;
+ }
+
+ @Override
+ public CharSequence getHint() {
+ // TODO(b/111276913): implement or move to superclass
+ return null;
+ }
+
+ @Override
+ public Bundle getExtras() {
+ // TODO(b/111276913): implement or move to superclass
+ return null;
+ }
+
+ @Override
+ public boolean hasExtras() {
+ // TODO(b/111276913): implement or move to superclass
+ return false;
+ }
+
+ @Override
+ public void setChildCount(int num) {
+ Log.w(TAG, "setChildCount() is not supported");
+ }
+
+ @Override
+ public int addChildCount(int num) {
+ Log.w(TAG, "addChildCount() is not supported");
+ return 0;
+ }
+
+ @Override
+ public int getChildCount() {
+ Log.w(TAG, "getChildCount() is not supported");
+ return 0;
+ }
+
+ @Override
+ public ViewStructure newChild(int index) {
+ Log.w(TAG, "newChild() is not supported");
+ return null;
+ }
+
+ @Override
+ public ViewStructure asyncNewChild(int index) {
+ Log.w(TAG, "asyncNewChild() is not supported");
+ return null;
+ }
+
+ @Override
+ public AutofillId getAutofillId() {
+ // TODO(b/111276913): temporarily getting directly; should be done on superclass instead
+ return mNode.mAutofillId;
+ }
+
+ @Override
+ public void setAutofillId(AutofillId id) {
+ // TODO(b/111276913): temporarily setting directly; should be done on superclass instead
+ mNode.mAutofillId = id;
+ }
+
+ @Override
+ public void setAutofillId(AutofillId parentId, int virtualId) {
+ // TODO(b/111276913): temporarily setting directly; should be done on superclass instead
+ mNode.mAutofillId = new AutofillId(parentId, virtualId);
+ }
+
+ @Override
+ public void setAutofillType(int type) {
+ // TODO(b/111276913): implement or move to superclass
+ }
+
+ @Override
+ public void setAutofillHints(String[] hint) {
+ // TODO(b/111276913): implement or move to superclass
+ }
+
+ @Override
+ public void setAutofillValue(AutofillValue value) {
+ // TODO(b/111276913): implement or move to superclass
+ }
+
+ @Override
+ public void setAutofillOptions(CharSequence[] options) {
+ // TODO(b/111276913): implement or move to superclass
+ }
+
+ @Override
+ public void setInputType(int inputType) {
+ // TODO(b/111276913): implement or move to superclass
+ }
+
+ @Override
+ public void setDataIsSensitive(boolean sensitive) {
+ // TODO(b/111276913): implement or move to superclass
+ }
+
+ @Override
+ public void asyncCommit() {
+ Log.w(TAG, "asyncCommit() is not supported");
+ }
+
+ @Override
+ public Rect getTempRect() {
+ // TODO(b/111276913): implement or move to superclass
+ return null;
+ }
+
+ @Override
+ public void setWebDomain(String domain) {
+ Log.w(TAG, "setWebDomain() is not supported");
+ }
+
+ @Override
+ public void setLocaleList(LocaleList localeList) {
+ // TODO(b/111276913): implement or move to superclass
+ }
+
+ @Override
+ public Builder newHtmlInfoBuilder(String tagName) {
+ Log.w(TAG, "newHtmlInfoBuilder() is not supported");
+ return null;
+ }
+
+ @Override
+ public void setHtmlInfo(HtmlInfo htmlInfo) {
+ Log.w(TAG, "setHtmlInfo() is not supported");
+ }
}
}
diff --git a/core/java/android/view/textclassifier/ModelFileManager.java b/core/java/android/view/textclassifier/ModelFileManager.java
index adea125..896b516 100644
--- a/core/java/android/view/textclassifier/ModelFileManager.java
+++ b/core/java/android/view/textclassifier/ModelFileManager.java
@@ -74,10 +74,9 @@
* @param localeList the required locales, use {@code null} if there is no preference.
*/
public ModelFile findBestModelFile(@Nullable LocaleList localeList) {
- // Specified localeList takes priority over the system default, so it is listed first.
final String languages = localeList == null || localeList.isEmpty()
? LocaleList.getDefault().toLanguageTags()
- : localeList.toLanguageTags() + "," + LocaleList.getDefault().toLanguageTags();
+ : localeList.toLanguageTags();
final List<Locale.LanguageRange> languageRangeList = Locale.LanguageRange.parse(languages);
ModelFile bestModel = null;
diff --git a/core/java/android/view/textclassifier/TextClassificationConstants.java b/core/java/android/view/textclassifier/TextClassificationConstants.java
index 2fc7422..50801a2 100644
--- a/core/java/android/view/textclassifier/TextClassificationConstants.java
+++ b/core/java/android/view/textclassifier/TextClassificationConstants.java
@@ -90,6 +90,10 @@
"entity_list_not_editable";
private static final String ENTITY_LIST_EDITABLE =
"entity_list_editable";
+ private static final String IN_APP_CONVERSATION_ACTION_TYPES_DEFAULT =
+ "in_app_conversation_action_types_default";
+ private static final String NOTIFICATION_CONVERSATION_ACTION_TYPES_DEFAULT =
+ "notification_conversation_action_types_default";
private static final boolean LOCAL_TEXT_CLASSIFIER_ENABLED_DEFAULT = true;
private static final boolean SYSTEM_TEXT_CLASSIFIER_ENABLED_DEFAULT = true;
@@ -111,6 +115,18 @@
.add(TextClassifier.TYPE_DATE)
.add(TextClassifier.TYPE_DATE_TIME)
.add(TextClassifier.TYPE_FLIGHT_NUMBER).toString();
+ private static final String CONVERSATION_ACTIONS_TYPES_DEFAULT_VALUES =
+ new StringJoiner(ENTITY_LIST_DELIMITER)
+ .add(ConversationActions.TYPE_TEXT_REPLY)
+ .add(ConversationActions.TYPE_CREATE_REMINDER)
+ .add(ConversationActions.TYPE_CALL_PHONE)
+ .add(ConversationActions.TYPE_OPEN_URL)
+ .add(ConversationActions.TYPE_SEND_EMAIL)
+ .add(ConversationActions.TYPE_SEND_SMS)
+ .add(ConversationActions.TYPE_TRACK_FLIGHT)
+ .add(ConversationActions.TYPE_VIEW_CALENDAR)
+ .add(ConversationActions.TYPE_VIEW_MAP)
+ .toString();
private final boolean mSystemTextClassifierEnabled;
private final boolean mLocalTextClassifierEnabled;
@@ -126,6 +142,8 @@
private final List<String> mEntityListDefault;
private final List<String> mEntityListNotEditable;
private final List<String> mEntityListEditable;
+ private final List<String> mInAppConversationActionTypesDefault;
+ private final List<String> mNotificationConversationActionTypesDefault;
private TextClassificationConstants(@Nullable String settings) {
final KeyValueListParser parser = new KeyValueListParser(',');
@@ -177,6 +195,12 @@
mEntityListEditable = parseEntityList(parser.getString(
ENTITY_LIST_EDITABLE,
ENTITY_LIST_DEFAULT_VALUE));
+ mInAppConversationActionTypesDefault = parseEntityList(parser.getString(
+ IN_APP_CONVERSATION_ACTION_TYPES_DEFAULT,
+ CONVERSATION_ACTIONS_TYPES_DEFAULT_VALUES));
+ mNotificationConversationActionTypesDefault = parseEntityList(parser.getString(
+ NOTIFICATION_CONVERSATION_ACTION_TYPES_DEFAULT,
+ CONVERSATION_ACTIONS_TYPES_DEFAULT_VALUES));
}
/** Load from a settings string. */
@@ -240,6 +264,14 @@
return mEntityListEditable;
}
+ public List<String> getInAppConversationActionTypes() {
+ return mInAppConversationActionTypesDefault;
+ }
+
+ public List<String> getNotificationConversationActionTypes() {
+ return mNotificationConversationActionTypesDefault;
+ }
+
private static List<String> parseEntityList(String listStr) {
return Collections.unmodifiableList(Arrays.asList(listStr.split(ENTITY_LIST_DELIMITER)));
}
@@ -261,6 +293,9 @@
pw.printPair("getEntityListDefault", mEntityListDefault);
pw.printPair("getEntityListNotEditable", mEntityListNotEditable);
pw.printPair("getEntityListEditable", mEntityListEditable);
+ pw.printPair("getInAppConversationActionTypes", mInAppConversationActionTypesDefault);
+ pw.printPair("getNotificationConversationActionTypes",
+ mNotificationConversationActionTypesDefault);
pw.decreaseIndent();
pw.println();
}
diff --git a/core/java/android/view/textclassifier/TextClassifier.java b/core/java/android/view/textclassifier/TextClassifier.java
index e675744..524f709 100644
--- a/core/java/android/view/textclassifier/TextClassifier.java
+++ b/core/java/android/view/textclassifier/TextClassifier.java
@@ -48,6 +48,9 @@
/**
* Interface for providing text classification related features.
+ * <p>
+ * The TextClassifier may be used to understand the meaning of text, as well as generating predicted
+ * next actions based on the text.
*
* <p><strong>NOTE: </strong>Unless otherwise stated, methods of this interface are blocking
* operations. Call on a worker thread.
diff --git a/core/java/android/view/textclassifier/TextClassifierImpl.java b/core/java/android/view/textclassifier/TextClassifierImpl.java
index 159bfaa..798a820 100644
--- a/core/java/android/view/textclassifier/TextClassifierImpl.java
+++ b/core/java/android/view/textclassifier/TextClassifierImpl.java
@@ -40,11 +40,13 @@
import android.provider.Browser;
import android.provider.CalendarContract;
import android.provider.ContactsContract;
+import android.text.TextUtils;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.Preconditions;
+import com.google.android.textclassifier.ActionsSuggestionsModel;
import com.google.android.textclassifier.AnnotatorModel;
import com.google.android.textclassifier.LangIdModel;
@@ -90,6 +92,11 @@
private static final File UPDATED_LANG_ID_MODEL_FILE =
new File("/data/misc/textclassifier/lang_id.model");
+ // Actions
+ private static final String ACTIONS_FACTORY_MODEL_FILENAME_REGEX = "actions_suggestions.model";
+ private static final File UPDATED_ACTIONS_MODEL =
+ new File("/data/misc/textclassifier/actions_suggestions.model");
+
private final Context mContext;
private final TextClassifier mFallback;
private final GenerateLinksLogger mGenerateLinksLogger;
@@ -101,6 +108,8 @@
private AnnotatorModel mAnnotatorImpl;
@GuardedBy("mLock") // Do not access outside this lock.
private LangIdModel mLangIdImpl;
+ @GuardedBy("mLock") // Do not access outside this lock.
+ private ActionsSuggestionsModel mActionsImpl;
private final Object mLoggerLock = new Object();
@GuardedBy("mLoggerLock") // Do not access outside this lock.
@@ -110,6 +119,7 @@
private final ModelFileManager mAnnotatorModelFileManager;
private final ModelFileManager mLangIdModelFileManager;
+ private final ModelFileManager mActionsModelFileManager;
public TextClassifierImpl(
Context context, TextClassificationConstants settings, TextClassifier fallback) {
@@ -131,6 +141,13 @@
UPDATED_LANG_ID_MODEL_FILE,
fd -> -1, // TODO: Replace this with LangIdModel.getVersion(fd)
fd -> ModelFileManager.ModelFile.LANGUAGE_INDEPENDENT));
+ mActionsModelFileManager = new ModelFileManager(
+ new ModelFileManager.ModelFileSupplierImpl(
+ FACTORY_MODEL_DIR,
+ ACTIONS_FACTORY_MODEL_FILENAME_REGEX,
+ UPDATED_ACTIONS_MODEL,
+ ActionsSuggestionsModel::getVersion,
+ ActionsSuggestionsModel::getLocales));
}
public TextClassifierImpl(Context context, TextClassificationConstants settings) {
@@ -346,10 +363,69 @@
return mFallback.detectLanguage(request);
}
+ @Override
+ public ConversationActions suggestConversationActions(ConversationActions.Request request) {
+ Preconditions.checkNotNull(request);
+ Utils.checkMainThread();
+ try {
+ ActionsSuggestionsModel actionsImpl = getActionsImpl();
+ if (actionsImpl == null) {
+ // Actions model is optional, fallback if it is not available.
+ return mFallback.suggestConversationActions(request);
+ }
+ List<ActionsSuggestionsModel.ConversationMessage> nativeMessages = new ArrayList<>();
+ for (ConversationActions.Message message : request.getConversation()) {
+ if (TextUtils.isEmpty(message.getText())) {
+ continue;
+ }
+ // TODO: We need to map the Person object to user id.
+ int userId = 1;
+ nativeMessages.add(
+ new ActionsSuggestionsModel.ConversationMessage(
+ userId, message.getText().toString()));
+ }
+ ActionsSuggestionsModel.Conversation nativeConversation =
+ new ActionsSuggestionsModel.Conversation(nativeMessages.toArray(
+ new ActionsSuggestionsModel.ConversationMessage[0]));
+
+ ActionsSuggestionsModel.ActionSuggestion[] nativeSuggestions =
+ actionsImpl.suggestActions(nativeConversation, null);
+
+ Collection<String> expectedTypes = resolveActionTypesFromRequest(request);
+ List<ConversationActions.ConversationAction> conversationActions = new ArrayList<>();
+ int maxSuggestions = Math.min(request.getMaxSuggestions(), nativeSuggestions.length);
+ for (int i = 0; i < maxSuggestions; i++) {
+ ActionsSuggestionsModel.ActionSuggestion nativeSuggestion = nativeSuggestions[i];
+ String actionType = nativeSuggestion.getActionType();
+ if (!expectedTypes.contains(actionType)) {
+ continue;
+ }
+ conversationActions.add(
+ new ConversationActions.ConversationAction.Builder(actionType)
+ .setTextReply(nativeSuggestion.getResponseText())
+ .setConfidenceScore(nativeSuggestion.getScore())
+ .build());
+ }
+ return new ConversationActions(conversationActions);
+ } catch (Throwable t) {
+ // Avoid throwing from this method. Log the error.
+ Log.e(LOG_TAG, "Error suggesting conversation actions.", t);
+ }
+ return mFallback.suggestConversationActions(request);
+ }
+
+ private Collection<String> resolveActionTypesFromRequest(ConversationActions.Request request) {
+ List<String> defaultActionTypes =
+ request.getHints().contains(ConversationActions.HINT_FOR_NOTIFICATION)
+ ? mSettings.getNotificationConversationActionTypes()
+ : mSettings.getInAppConversationActionTypes();
+ return request.getTypeConfig().resolveTypes(defaultActionTypes);
+ }
+
private AnnotatorModel getAnnotatorImpl(LocaleList localeList)
throws FileNotFoundException {
synchronized (mLock) {
- localeList = localeList == null ? LocaleList.getEmptyLocaleList() : localeList;
+ localeList = localeList == null ? LocaleList.getDefault() : localeList;
final ModelFileManager.ModelFile bestModel =
mAnnotatorModelFileManager.findBestModelFile(localeList);
if (bestModel == null) {
@@ -386,7 +462,7 @@
synchronized (mLock) {
if (mLangIdImpl == null) {
final ModelFileManager.ModelFile bestModel =
- mLangIdModelFileManager.findBestModelFile(LocaleList.getEmptyLocaleList());
+ mLangIdModelFileManager.findBestModelFile(null);
if (bestModel == null) {
throw new FileNotFoundException("No LangID model is found");
}
@@ -404,6 +480,30 @@
}
}
+ @Nullable
+ private ActionsSuggestionsModel getActionsImpl() throws FileNotFoundException {
+ synchronized (mLock) {
+ if (mActionsImpl == null) {
+ // TODO: Use LangID to determine the locale we should use here?
+ final ModelFileManager.ModelFile bestModel =
+ mActionsModelFileManager.findBestModelFile(LocaleList.getDefault());
+ if (bestModel == null) {
+ return null;
+ }
+ final ParcelFileDescriptor pfd = ParcelFileDescriptor.open(
+ new File(bestModel.getPath()), ParcelFileDescriptor.MODE_READ_ONLY);
+ try {
+ if (pfd != null) {
+ mActionsImpl = new ActionsSuggestionsModel(pfd.getFd());
+ }
+ } finally {
+ maybeCloseAndLogError(pfd);
+ }
+ }
+ return mActionsImpl;
+ }
+ }
+
private String createId(String text, int start, int end) {
synchronized (mLock) {
return SelectionSessionLogger.createId(text, start, end, mContext,
@@ -471,11 +571,19 @@
}
printWriter.decreaseIndent();
printWriter.println("LangID model file(s):");
+ printWriter.increaseIndent();
for (ModelFileManager.ModelFile modelFile :
mLangIdModelFileManager.listModelFiles()) {
printWriter.println(modelFile.toString());
}
printWriter.decreaseIndent();
+ printWriter.println("Actions model file(s):");
+ printWriter.increaseIndent();
+ for (ModelFileManager.ModelFile modelFile :
+ mActionsModelFileManager.listModelFiles()) {
+ printWriter.println(modelFile.toString());
+ }
+ printWriter.decreaseIndent();
printWriter.printPair("mFallback", mFallback);
printWriter.decreaseIndent();
printWriter.println();
diff --git a/core/java/android/view/textclassifier/TextLinks.java b/core/java/android/view/textclassifier/TextLinks.java
index b31438f..02aee50 100644
--- a/core/java/android/view/textclassifier/TextLinks.java
+++ b/core/java/android/view/textclassifier/TextLinks.java
@@ -21,6 +21,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
+import android.os.Bundle;
import android.os.LocaleList;
import android.os.Parcel;
import android.os.Parcelable;
@@ -92,10 +93,12 @@
private final String mFullText;
private final List<TextLink> mLinks;
+ private final Bundle mExtras;
- private TextLinks(String fullText, ArrayList<TextLink> links) {
+ private TextLinks(String fullText, ArrayList<TextLink> links, Bundle extras) {
mFullText = fullText;
mLinks = Collections.unmodifiableList(links);
+ mExtras = extras;
}
/**
@@ -116,6 +119,18 @@
}
/**
+ * Returns the extended data.
+ *
+ * <p><b>NOTE: </b>Each call to this method returns a new bundle copy so clients should
+ * prefer to hold a reference to the returned bundle rather than frequently calling this
+ * method.
+ */
+ @NonNull
+ public Bundle getExtras() {
+ return mExtras.deepCopy();
+ }
+
+ /**
* Annotates the given text with the generated links. It will fail if the provided text doesn't
* match the original text used to create the TextLinks.
*
@@ -158,6 +173,7 @@
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(mFullText);
dest.writeTypedList(mLinks);
+ dest.writeBundle(mExtras);
}
public static final Parcelable.Creator<TextLinks> CREATOR =
@@ -176,6 +192,7 @@
private TextLinks(Parcel in) {
mFullText = in.readString();
mLinks = in.createTypedArrayList(TextLink.CREATOR);
+ mExtras = in.readBundle();
}
/**
@@ -304,18 +321,21 @@
@Nullable private final TextClassifier.EntityConfig mEntityConfig;
private final boolean mLegacyFallback;
private String mCallingPackageName;
+ private final Bundle mExtras;
private Request(
CharSequence text,
LocaleList defaultLocales,
TextClassifier.EntityConfig entityConfig,
boolean legacyFallback,
- String callingPackageName) {
+ String callingPackageName,
+ Bundle extras) {
mText = text;
mDefaultLocales = defaultLocales;
mEntityConfig = entityConfig;
mLegacyFallback = legacyFallback;
mCallingPackageName = callingPackageName;
+ mExtras = extras;
}
/**
@@ -362,6 +382,18 @@
}
/**
+ * Returns the extended data.
+ *
+ * <p><b>NOTE: </b>Each call to this method returns a new bundle copy so clients should
+ * prefer to hold a reference to the returned bundle rather than frequently calling this
+ * method.
+ */
+ @NonNull
+ public Bundle getExtras() {
+ return mExtras.deepCopy();
+ }
+
+ /**
* A builder for building TextLinks requests.
*/
public static final class Builder {
@@ -372,6 +404,7 @@
@Nullable private TextClassifier.EntityConfig mEntityConfig;
private boolean mLegacyFallback = true; // Use legacy fall back by default.
private String mCallingPackageName;
+ @Nullable private Bundle mExtras;
public Builder(@NonNull CharSequence text) {
mText = Preconditions.checkNotNull(text);
@@ -431,15 +464,25 @@
}
/**
+ * Sets the extended data.
+ *
+ * @return this builder
+ */
+ public Builder setExtras(@Nullable Bundle extras) {
+ mExtras = extras;
+ return this;
+ }
+
+ /**
* Builds and returns the request object.
*/
@NonNull
public Request build() {
return new Request(
mText, mDefaultLocales, mEntityConfig,
- mLegacyFallback, mCallingPackageName);
+ mLegacyFallback, mCallingPackageName,
+ mExtras == null ? Bundle.EMPTY : mExtras.deepCopy());
}
-
}
/**
@@ -469,6 +512,7 @@
mEntityConfig.writeToParcel(dest, flags);
}
dest.writeString(mCallingPackageName);
+ dest.writeBundle(mExtras);
}
public static final Parcelable.Creator<Request> CREATOR =
@@ -491,6 +535,7 @@
? null : TextClassifier.EntityConfig.CREATOR.createFromParcel(in);
mLegacyFallback = true;
mCallingPackageName = in.readString();
+ mExtras = in.readBundle();
}
}
@@ -575,6 +620,7 @@
public static final class Builder {
private final String mFullText;
private final ArrayList<TextLink> mLinks;
+ private Bundle mExtras;
/**
* Create a new TextLinks.Builder.
@@ -622,13 +668,24 @@
}
/**
+ * Sets the extended data.
+ *
+ * @return this builder
+ */
+ public Builder setExtras(@Nullable Bundle extras) {
+ mExtras = extras;
+ return this;
+ }
+
+ /**
* Constructs a TextLinks instance.
*
* @return the constructed TextLinks
*/
@NonNull
public TextLinks build() {
- return new TextLinks(mFullText, mLinks);
+ return new TextLinks(mFullText, mLinks,
+ mExtras == null ? Bundle.EMPTY : mExtras.deepCopy());
}
}
}
diff --git a/core/java/android/view/textclassifier/TextSelection.java b/core/java/android/view/textclassifier/TextSelection.java
index 52d01ea..f236915 100644
--- a/core/java/android/view/textclassifier/TextSelection.java
+++ b/core/java/android/view/textclassifier/TextSelection.java
@@ -20,6 +20,7 @@
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.os.Bundle;
import android.os.LocaleList;
import android.os.Parcel;
import android.os.Parcelable;
@@ -41,13 +42,16 @@
private final int mEndIndex;
private final EntityConfidence mEntityConfidence;
@Nullable private final String mId;
+ private final Bundle mExtras;
private TextSelection(
- int startIndex, int endIndex, Map<String, Float> entityConfidence, String id) {
+ int startIndex, int endIndex, Map<String, Float> entityConfidence, String id,
+ Bundle extras) {
mStartIndex = startIndex;
mEndIndex = endIndex;
mEntityConfidence = new EntityConfidence(entityConfidence);
mId = id;
+ mExtras = extras;
}
/**
@@ -103,6 +107,18 @@
return mId;
}
+ /**
+ * Returns the extended data.
+ *
+ * <p><b>NOTE: </b>Each call to this method returns a new bundle copy so clients should
+ * prefer to hold a reference to the returned bundle rather than frequently calling this
+ * method.
+ */
+ @NonNull
+ public Bundle getExtras() {
+ return mExtras.deepCopy();
+ }
+
@Override
public String toString() {
return String.format(
@@ -120,6 +136,8 @@
private final int mEndIndex;
private final Map<String, Float> mEntityConfidence = new ArrayMap<>();
@Nullable private String mId;
+ @Nullable
+ private Bundle mExtras;
/**
* Creates a builder used to build {@link TextSelection} objects.
@@ -160,12 +178,23 @@
}
/**
+ * Sets the extended data.
+ *
+ * @return this builder
+ */
+ public Builder setExtras(@Nullable Bundle extras) {
+ mExtras = extras;
+ return this;
+ }
+
+ /**
* Builds and returns {@link TextSelection} object.
*/
@NonNull
public TextSelection build() {
return new TextSelection(
- mStartIndex, mEndIndex, mEntityConfidence, mId);
+ mStartIndex, mEndIndex, mEntityConfidence, mId,
+ mExtras == null ? Bundle.EMPTY : mExtras.deepCopy());
}
}
@@ -179,18 +208,21 @@
private final int mEndIndex;
@Nullable private final LocaleList mDefaultLocales;
private final boolean mDarkLaunchAllowed;
+ private final Bundle mExtras;
private Request(
CharSequence text,
int startIndex,
int endIndex,
LocaleList defaultLocales,
- boolean darkLaunchAllowed) {
+ boolean darkLaunchAllowed,
+ Bundle extras) {
mText = text;
mStartIndex = startIndex;
mEndIndex = endIndex;
mDefaultLocales = defaultLocales;
mDarkLaunchAllowed = darkLaunchAllowed;
+ mExtras = extras;
}
/**
@@ -238,6 +270,18 @@
}
/**
+ * Returns the extended data.
+ *
+ * <p><b>NOTE: </b>Each call to this method returns a new bundle copy so clients should
+ * prefer to hold a reference to the returned bundle rather than frequently calling this
+ * method.
+ */
+ @NonNull
+ public Bundle getExtras() {
+ return mExtras.deepCopy();
+ }
+
+ /**
* A builder for building TextSelection requests.
*/
public static final class Builder {
@@ -248,6 +292,7 @@
@Nullable private LocaleList mDefaultLocales;
private boolean mDarkLaunchAllowed;
+ private Bundle mExtras;
/**
* @param text text providing context for the selected text (which is specified by the
@@ -296,12 +341,23 @@
}
/**
+ * Sets the extended data.
+ *
+ * @return this builder
+ */
+ public Builder setExtras(@Nullable Bundle extras) {
+ mExtras = extras;
+ return this;
+ }
+
+ /**
* Builds and returns the request object.
*/
@NonNull
public Request build() {
return new Request(mText, mStartIndex, mEndIndex,
- mDefaultLocales, mDarkLaunchAllowed);
+ mDefaultLocales, mDarkLaunchAllowed,
+ mExtras == null ? Bundle.EMPTY : mExtras.deepCopy());
}
}
@@ -319,6 +375,7 @@
if (mDefaultLocales != null) {
mDefaultLocales.writeToParcel(dest, flags);
}
+ dest.writeBundle(mExtras);
}
public static final Parcelable.Creator<Request> CREATOR =
@@ -340,6 +397,7 @@
mEndIndex = in.readInt();
mDefaultLocales = in.readInt() == 0 ? null : LocaleList.CREATOR.createFromParcel(in);
mDarkLaunchAllowed = false;
+ mExtras = in.readBundle();
}
}
@@ -354,6 +412,7 @@
dest.writeInt(mEndIndex);
mEntityConfidence.writeToParcel(dest, flags);
dest.writeString(mId);
+ dest.writeBundle(mExtras);
}
public static final Parcelable.Creator<TextSelection> CREATOR =
@@ -374,5 +433,6 @@
mEndIndex = in.readInt();
mEntityConfidence = EntityConfidence.CREATOR.createFromParcel(in);
mId = in.readString();
+ mExtras = in.readBundle();
}
}
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index a93604f..c0979fe 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -196,12 +196,6 @@
private boolean mIsRoot = true;
/**
- * Optional theme resource id applied in inflateView(). When 0, Theme.DeviceDefault will be
- * used.
- */
- private int mApplyThemeResId;
-
- /**
* Whether reapply is disallowed on this remoteview. This maybe be true if some actions modify
* the layout in a way that isn't recoverable, since views are being removed.
*/
@@ -3262,14 +3256,6 @@
}
/**
- * Set the theme used in apply() and applyASync().
- * @hide
- */
- public void setApplyTheme(@StyleRes int themeResId) {
- mApplyThemeResId = themeResId;
- }
-
- /**
* Inflates the view hierarchy represented by this object and applies
* all of the actions.
*
@@ -3290,11 +3276,25 @@
View result = inflateView(context, rvToApply, parent);
rvToApply.performApply(result, parent, handler);
+ return result;
+ }
+ /** @hide */
+ public View applyWithTheme(Context context, ViewGroup parent, OnClickHandler handler,
+ @StyleRes int applyThemeResId) {
+ RemoteViews rvToApply = getRemoteViewsToApply(context);
+
+ View result = inflateView(context, rvToApply, parent, applyThemeResId);
+ rvToApply.performApply(result, parent, handler);
return result;
}
private View inflateView(Context context, RemoteViews rv, ViewGroup parent) {
+ return inflateView(context, rv, parent, 0);
+ }
+
+ private View inflateView(Context context, RemoteViews rv, ViewGroup parent,
+ @StyleRes int applyThemeResId) {
// RemoteViews may be built by an application installed in another
// user. So build a context that loads resources from that user but
// still returns the current users userId so settings like data / time formats
@@ -3303,8 +3303,8 @@
Context inflationContext = new RemoteViewsContextWrapper(context, contextForResources);
// If mApplyThemeResId is not given, Theme.DeviceDefault will be used.
- if (mApplyThemeResId != 0) {
- inflationContext = new ContextThemeWrapper(inflationContext, mApplyThemeResId);
+ if (applyThemeResId != 0) {
+ inflationContext = new ContextThemeWrapper(inflationContext, applyThemeResId);
}
LayoutInflater inflater = (LayoutInflater)
context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
diff --git a/core/java/android/widget/Switch.java b/core/java/android/widget/Switch.java
index 8bfc151..d55c09f 100644
--- a/core/java/android/widget/Switch.java
+++ b/core/java/android/widget/Switch.java
@@ -1422,17 +1422,24 @@
@Override
public void onProvideStructure(ViewStructure structure) {
super.onProvideStructure(structure);
- onProvideAutoFillStructureForAssistOrAutofill(structure);
+ onProvideStructureForAssistOrAutofillOrViewCapture(structure);
}
@Override
public void onProvideAutofillStructure(ViewStructure structure, int flags) {
super.onProvideAutofillStructure(structure, flags);
- onProvideAutoFillStructureForAssistOrAutofill(structure);
+ onProvideStructureForAssistOrAutofillOrViewCapture(structure);
}
- // NOTE: currently there is no difference for Assist or AutoFill, so it doesn't take flags
- private void onProvideAutoFillStructureForAssistOrAutofill(ViewStructure structure) {
+ @Override
+ public boolean onProvideContentCaptureStructure(ViewStructure structure, int flags) {
+ final boolean notifyManager = super.onProvideContentCaptureStructure(structure, flags);
+ onProvideStructureForAssistOrAutofillOrViewCapture(structure);
+ return notifyManager;
+ }
+
+ // NOTE: currently there is no difference for any type, so it doesn't take flags
+ private void onProvideStructureForAssistOrAutofillOrViewCapture(ViewStructure structure) {
CharSequence switchText = isChecked() ? mTextOn : mTextOff;
if (!TextUtils.isEmpty(switchText)) {
CharSequence oldText = structure.getText();
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 572670f..3bdd7b8 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -166,6 +166,7 @@
import android.view.inputmethod.ExtractedTextRequest;
import android.view.inputmethod.InputConnection;
import android.view.inputmethod.InputMethodManager;
+import android.view.intelligence.IntelligenceManager;
import android.view.textclassifier.TextClassification;
import android.view.textclassifier.TextClassificationContext;
import android.view.textclassifier.TextClassificationManager;
@@ -948,6 +949,9 @@
if (getImportantForAutofill() == IMPORTANT_FOR_AUTOFILL_AUTO) {
setImportantForAutofill(IMPORTANT_FOR_AUTOFILL_YES);
}
+ if (getImportantForContentCapture() == IMPORTANT_FOR_CONTENT_CAPTURE_AUTO) {
+ setImportantForContentCapture(IMPORTANT_FOR_CONTENT_CAPTURE_YES);
+ }
setTextInternal("");
@@ -6072,7 +6076,7 @@
if (needEditableForNotification) {
sendAfterTextChanged((Editable) text);
} else {
- notifyAutoFillManagerAfterTextChanged();
+ notifyManagersAfterTextChanged();
}
// SelectionModifierCursorController depends on textCanBeSelected, which depends on text
@@ -10121,23 +10125,33 @@
}
// Always notify AutoFillManager - it will return right away if autofill is disabled.
- notifyAutoFillManagerAfterTextChanged();
+ notifyManagersAfterTextChanged();
hideErrorIfUnchanged();
}
- private void notifyAutoFillManagerAfterTextChanged() {
- // It is important to not check whether the view is important for autofill
- // since the user can trigger autofill manually on not important views.
- if (!isAutofillable()) {
- return;
- }
- final AutofillManager afm = mContext.getSystemService(AutofillManager.class);
- if (afm != null) {
- if (android.view.autofill.Helper.sVerbose) {
- Log.v(LOG_TAG, "notifyAutoFillManagerAfterTextChanged");
+ private void notifyManagersAfterTextChanged() {
+
+ // Autofill
+ if (isAutofillable()) {
+ // It is important to not check whether the view is important for autofill
+ // since the user can trigger autofill manually on not important views.
+ final AutofillManager afm = mContext.getSystemService(AutofillManager.class);
+ if (afm != null) {
+ if (android.view.autofill.Helper.sVerbose) {
+ Log.v(LOG_TAG, "notifyAutoFillManagerAfterTextChanged");
+ }
+ afm.notifyValueChanged(TextView.this);
}
- afm.notifyValueChanged(TextView.this);
+ }
+
+ // ContentCapture
+ if (isImportantForContentCapture() && isTextEditable()) {
+ final IntelligenceManager im = mContext.getSystemService(IntelligenceManager.class);
+ if (im != null && im.isContentCaptureEnabled()) {
+ // TODO(b/111276913): pass flags when edited by user / add CTS test
+ im.notifyViewTextChanged(getAutofillId(), getText(), /* flags= */ 0);
+ }
}
}
@@ -10900,21 +10914,33 @@
@Override
public void onProvideStructure(ViewStructure structure) {
super.onProvideStructure(structure);
- onProvideAutoStructureForAssistOrAutofill(structure, false);
+ onProvideStructureForAssistOrAutofillOrViewCapture(structure, /* forAutofill = */ false,
+ /* forViewCapture= */ false);
}
@Override
public void onProvideAutofillStructure(ViewStructure structure, int flags) {
super.onProvideAutofillStructure(structure, flags);
- onProvideAutoStructureForAssistOrAutofill(structure, true);
+ onProvideStructureForAssistOrAutofillOrViewCapture(structure, /* forAutofill = */ true,
+ /* forViewCapture= */ false);
}
- private void onProvideAutoStructureForAssistOrAutofill(ViewStructure structure,
- boolean forAutofill) {
+ @Override
+ public boolean onProvideContentCaptureStructure(ViewStructure structure, int flags) {
+ final boolean notifyManager = super.onProvideContentCaptureStructure(structure, flags);
+ onProvideStructureForAssistOrAutofillOrViewCapture(structure, /* forAutofill = */ false,
+ /* forViewCapture= */ true);
+ return notifyManager;
+ }
+
+ private void onProvideStructureForAssistOrAutofillOrViewCapture(ViewStructure structure,
+ boolean forAutofill, boolean forViewCapture) {
final boolean isPassword = hasPasswordTransformationMethod()
|| isPasswordInputType(getInputType());
- if (forAutofill) {
- structure.setDataIsSensitive(!mTextSetFromXmlOrResourceId);
+ if (forAutofill || forViewCapture) {
+ if (forAutofill) {
+ structure.setDataIsSensitive(!mTextSetFromXmlOrResourceId);
+ }
if (mTextId != ResourceId.ID_NULL) {
try {
structure.setTextIdEntry(getResources().getResourceEntryName(mTextId));
@@ -10927,7 +10953,7 @@
}
}
- if (!isPassword || forAutofill) {
+ if (!isPassword || forAutofill || forViewCapture) {
if (mLayout == null) {
assumeLayout();
}
@@ -11043,7 +11069,8 @@
// of the View (and can be any drawable) or a BackgroundColorSpan inside the text.
structure.setTextStyle(getTextSize(), getCurrentTextColor(),
AssistStructure.ViewNode.TEXT_COLOR_UNDEFINED /* bgColor */, style);
- } else {
+ }
+ if (forAutofill || forViewCapture) {
structure.setMinTextEms(getMinEms());
structure.setMaxTextEms(getMaxEms());
int maxLength = -1;
diff --git a/telephony/java/com/android/internal/telephony/IRcs.aidl b/core/java/com/android/internal/inputmethod/IMultiClientInputMethod.aidl
similarity index 60%
copy from telephony/java/com/android/internal/telephony/IRcs.aidl
copy to core/java/com/android/internal/inputmethod/IMultiClientInputMethod.aidl
index ede8695..2f782ba 100644
--- a/telephony/java/com/android/internal/telephony/IRcs.aidl
+++ b/core/java/com/android/internal/inputmethod/IMultiClientInputMethod.aidl
@@ -14,8 +14,13 @@
* limitations under the License.
*/
-package com.android.internal.telephony;
+package com.android.internal.inputmethod;
-interface IRcs {
- void deleteThread(int threadId);
-}
\ No newline at end of file
+import android.os.IBinder;
+import com.android.internal.inputmethod.IMultiClientInputMethodPrivilegedOperations;
+
+oneway interface IMultiClientInputMethod {
+ void initialize(in IMultiClientInputMethodPrivilegedOperations privOps);
+ void addClient(int clientId, int uid, int pid, int selfReportedDisplayId);
+ void removeClient(int clientId);
+}
diff --git a/core/java/com/android/internal/inputmethod/IMultiClientInputMethodPrivilegedOperations.aidl b/core/java/com/android/internal/inputmethod/IMultiClientInputMethodPrivilegedOperations.aidl
new file mode 100644
index 0000000..69d9ccc
--- /dev/null
+++ b/core/java/com/android/internal/inputmethod/IMultiClientInputMethodPrivilegedOperations.aidl
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.inputmethod;
+
+import android.view.InputChannel;
+import com.android.internal.view.IInputMethodSession;
+import com.android.internal.inputmethod.IMultiClientInputMethodSession;
+
+/**
+ * Defines priviledged operations that only the current MSIMS is allowed to call.
+ * Actual operations are implemented and handled by MultiClientInputMethodManagerService.
+ */
+interface IMultiClientInputMethodPrivilegedOperations {
+ IBinder createInputMethodWindowToken(int displayId);
+ void deleteInputMethodWindowToken(IBinder token);
+ void acceptClient(int clientId, in IInputMethodSession session,
+ in IMultiClientInputMethodSession multiClientSession, in InputChannel writeChannel);
+ void reportImeWindowTarget(int clientId, int targetWindowHandle, in IBinder imeWindowToken);
+ boolean isUidAllowedOnDisplay(int displayId, int uid);
+}
diff --git a/core/java/com/android/internal/inputmethod/IMultiClientInputMethodSession.aidl b/core/java/com/android/internal/inputmethod/IMultiClientInputMethodSession.aidl
new file mode 100644
index 0000000..b40e82c
--- /dev/null
+++ b/core/java/com/android/internal/inputmethod/IMultiClientInputMethodSession.aidl
@@ -0,0 +1,29 @@
+/*
+ * 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.internal.inputmethod;
+
+import android.os.ResultReceiver;
+import android.view.inputmethod.EditorInfo;
+import com.android.internal.view.IInputContext;
+
+oneway interface IMultiClientInputMethodSession {
+ void startInputOrWindowGainedFocus(
+ in IInputContext inputContext, int missingMethods, in EditorInfo attribute,
+ int controlFlags, int softInputMode, int targetWindowHandle);
+ void showSoftInput(int flags, in ResultReceiver resultReceiver);
+ void hideSoftInput(int flags, in ResultReceiver resultReceiver);
+}
diff --git a/core/java/com/android/internal/inputmethod/MultiClientInputMethodPrivilegedOperations.java b/core/java/com/android/internal/inputmethod/MultiClientInputMethodPrivilegedOperations.java
new file mode 100644
index 0000000..9220117
--- /dev/null
+++ b/core/java/com/android/internal/inputmethod/MultiClientInputMethodPrivilegedOperations.java
@@ -0,0 +1,215 @@
+/*
+ * 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.internal.inputmethod;
+
+import android.annotation.AnyThread;
+import android.annotation.Nullable;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Log;
+import android.view.InputChannel;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.view.IInputMethodSession;
+
+/**
+ * A utility class to take care of boilerplate code around IPCs.
+ *
+ * <p>Note: All public methods are designed to be thread-safe.</p>
+ */
+public class MultiClientInputMethodPrivilegedOperations {
+ private static final String TAG = "MultiClientInputMethodPrivilegedOperations";
+
+ private static final class OpsHolder {
+ @Nullable
+ @GuardedBy("this")
+ private IMultiClientInputMethodPrivilegedOperations mPrivOps;
+
+ /**
+ * Sets {@link IMultiClientInputMethodPrivilegedOperations}.
+ *
+ * <p>This method can be called only once.</p>
+ *
+ * @param privOps Binder interface to be set.
+ */
+ @AnyThread
+ public synchronized void set(IMultiClientInputMethodPrivilegedOperations privOps) {
+ if (mPrivOps != null) {
+ throw new IllegalStateException(
+ "IMultiClientInputMethodPrivilegedOperations must be set at most once."
+ + " privOps=" + privOps);
+ }
+ mPrivOps = privOps;
+ }
+
+ /**
+ * A simplified version of {@link android.os.Debug#getCaller()}.
+ *
+ * @return method name of the caller.
+ */
+ @AnyThread
+ private static String getCallerMethodName() {
+ final StackTraceElement[] callStack = Thread.currentThread().getStackTrace();
+ if (callStack.length <= 4) {
+ return "<bottom of call stack>";
+ }
+ return callStack[4].getMethodName();
+ }
+
+ @AnyThread
+ public synchronized void dispose() {
+ mPrivOps = null;
+ }
+
+ @AnyThread
+ @Nullable
+ public synchronized IMultiClientInputMethodPrivilegedOperations getAndWarnIfNull() {
+ if (mPrivOps == null) {
+ Log.e(TAG, getCallerMethodName() + " is ignored."
+ + " Call it within attachToken() and InputMethodService.onDestroy()");
+ }
+ return mPrivOps;
+ }
+ }
+ private final OpsHolder mOps = new OpsHolder();
+
+ /**
+ * Sets {@link IMultiClientInputMethodPrivilegedOperations}.
+ *
+ * <p>This method can be called only once.</p>
+ *
+ * @param privOps Binder interface to be set.
+ */
+ @AnyThread
+ public void set(IMultiClientInputMethodPrivilegedOperations privOps) {
+ mOps.set(privOps);
+ }
+
+ /**
+ * Disposes internal Binder proxy so that the real Binder object can be garbage collected.
+ */
+ @AnyThread
+ public void dispose() {
+ mOps.dispose();
+ }
+
+ /**
+
+ * Calls {@link IMultiClientInputMethodPrivilegedOperations#createInputMethodWindowToken(int)}.
+ *
+ * @param displayId display ID on which the IME window will be shown.
+ * @return Window token to be specified to the IME window.
+ */
+ @AnyThread
+ public IBinder createInputMethodWindowToken(int displayId) {
+ IMultiClientInputMethodPrivilegedOperations ops = mOps.getAndWarnIfNull();
+ if (ops == null) {
+ return null;
+ }
+ try {
+ return ops.createInputMethodWindowToken(displayId);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Calls {@link
+ * IMultiClientInputMethodPrivilegedOperations#deleteInputMethodWindowToken(IBinder)}.
+ *
+ * @param token Window token that is to be deleted.
+ */
+ @AnyThread
+ public void deleteInputMethodWindowToken(IBinder token) {
+ IMultiClientInputMethodPrivilegedOperations ops = mOps.getAndWarnIfNull();
+ if (ops == null) {
+ return;
+ }
+ try {
+ ops.deleteInputMethodWindowToken(token);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Calls {@link IMultiClientInputMethodPrivilegedOperations#acceptClient(int,
+ * IInputMethodSession, IMultiClientInputMethodSession, InputChannel)}.
+ *
+ * @param clientId client ID to be accepted.
+ * @param session {@link IInputMethodSession} that is also used for traditional IME protocol.
+ * @param multiClientSession {@link IMultiClientInputMethodSession} that defines new callbacks
+ * for multi-client scenarios.
+ * @param writeChannel {@link InputChannel} that is also used for traditional IME protocol.
+ */
+ @AnyThread
+ public void acceptClient(int clientId, IInputMethodSession session,
+ IMultiClientInputMethodSession multiClientSession, InputChannel writeChannel) {
+ final IMultiClientInputMethodPrivilegedOperations ops = mOps.getAndWarnIfNull();
+ if (ops == null) {
+ return;
+ }
+ try {
+ ops.acceptClient(clientId, session, multiClientSession, writeChannel);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Calls {@link IMultiClientInputMethodPrivilegedOperations#reportImeWindowTarget(int, int,
+ * IBinder)}.
+ *
+ * @param clientId client ID about which new IME target window is reported.
+ * @param targetWindowHandle integer handle of the target window.
+ * @param imeWindowToken {@link IBinder} window token of the IME window.
+ */
+ @AnyThread
+ public void reportImeWindowTarget(int clientId, int targetWindowHandle,
+ IBinder imeWindowToken) {
+ final IMultiClientInputMethodPrivilegedOperations ops = mOps.getAndWarnIfNull();
+ if (ops == null) {
+ return;
+ }
+ try {
+ ops.reportImeWindowTarget(clientId, targetWindowHandle, imeWindowToken);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Calls {@link IMultiClientInputMethodPrivilegedOperations#isUidAllowedOnDisplay(int, int)}.
+ *
+ * @param displayId display ID to be verified.
+ * @param uid UID to be verified.
+ * @return {@code true} when {@code uid} is allowed to access to {@code displayId}.
+ */
+ @AnyThread
+ public boolean isUidAllowedOnDisplay(int displayId, int uid) {
+ final IMultiClientInputMethodPrivilegedOperations ops = mOps.getAndWarnIfNull();
+ if (ops == null) {
+ return false;
+ }
+ try {
+ return ops.isUidAllowedOnDisplay(displayId, uid);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+}
diff --git a/core/java/com/android/internal/os/KernelCpuProcStringReader.java b/core/java/com/android/internal/os/KernelCpuProcStringReader.java
new file mode 100644
index 0000000..22435ae
--- /dev/null
+++ b/core/java/com/android/internal/os/KernelCpuProcStringReader.java
@@ -0,0 +1,269 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.os;
+
+import android.os.StrictMode;
+import android.os.SystemClock;
+import android.util.Slog;
+
+import java.io.BufferedReader;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.nio.CharBuffer;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Arrays;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+
+/**
+ * Reads human-readable cpu time proc files.
+ *
+ * It is implemented as singletons for built-in kernel proc files. Get___Instance() method will
+ * return corresponding reader instance. In order to prevent frequent GC, it reuses the same char[]
+ * to store data read from proc files.
+ *
+ * A KernelCpuProcStringReader instance keeps an error counter. When the number of read errors
+ * within that instance accumulates to 5, this instance will reject all further read requests.
+ *
+ * Data fetched within last 500ms is considered fresh, since the reading lifecycle can take up to
+ * 100ms. KernelCpuProcStringReader always tries to use cache if it is fresh and valid, but it can
+ * be disabled through a parameter.
+ *
+ * A KernelCpuProcReader instance is thread-safe. It acquires a write lock when reading the proc
+ * file, releases it right after, then acquires a read lock before returning a ProcFileIterator.
+ * Caller is responsible for closing ProcFileIterator (also auto-closable) after reading, otherwise
+ * deadlock will occur.
+ */
+public class KernelCpuProcStringReader {
+ private static final String TAG = KernelCpuProcStringReader.class.getSimpleName();
+ private static final int ERROR_THRESHOLD = 5;
+ // Data read within the last 500ms is considered fresh.
+ private static final long FRESHNESS = 500L;
+ private static final int MAX_BUFFER_SIZE = 1024 * 1024;
+
+ private static final String PROC_UID_FREQ_TIME = "/proc/uid_time_in_state";
+ private static final String PROC_UID_ACTIVE_TIME = "/proc/uid_concurrent_active_time";
+ private static final String PROC_UID_CLUSTER_TIME = "/proc/uid_concurrent_policy_time";
+
+ private static final KernelCpuProcStringReader FREQ_TIME_READER =
+ new KernelCpuProcStringReader(PROC_UID_FREQ_TIME);
+ private static final KernelCpuProcStringReader ACTIVE_TIME_READER =
+ new KernelCpuProcStringReader(PROC_UID_ACTIVE_TIME);
+ private static final KernelCpuProcStringReader CLUSTER_TIME_READER =
+ new KernelCpuProcStringReader(PROC_UID_CLUSTER_TIME);
+
+ public static KernelCpuProcStringReader getFreqTimeReaderInstance() {
+ return FREQ_TIME_READER;
+ }
+
+ public static KernelCpuProcStringReader getActiveTimeReaderInstance() {
+ return ACTIVE_TIME_READER;
+ }
+
+ public static KernelCpuProcStringReader getClusterTimeReaderInstance() {
+ return CLUSTER_TIME_READER;
+ }
+
+ private int mErrors = 0;
+ private final Path mFile;
+ private char[] mBuf;
+ private int mSize;
+ private long mLastReadTime = 0;
+ private final ReentrantReadWriteLock mLock = new ReentrantReadWriteLock();
+ private final ReentrantReadWriteLock.ReadLock mReadLock = mLock.readLock();
+ private final ReentrantReadWriteLock.WriteLock mWriteLock = mLock.writeLock();
+
+ public KernelCpuProcStringReader(String file) {
+ mFile = Paths.get(file);
+ }
+
+ /**
+ * @see #open(boolean) Default behavior is trying to use cache.
+ */
+ public ProcFileIterator open() {
+ return open(false);
+ }
+
+ /**
+ * Opens the proc file and buffers all its content, which can be traversed through a
+ * ProcFileIterator.
+ *
+ * This method will tolerate at most 5 errors. After that, it will always return null. This is
+ * to save resources and to prevent log spam.
+ *
+ * This method is thread-safe. It first checks if there are other threads holding read/write
+ * lock. If there are, it assumes data is fresh and reuses the data.
+ *
+ * A read lock is automatically acquired when a valid ProcFileIterator is returned. Caller MUST
+ * call {@link ProcFileIterator#close()} when it is done to release the lock.
+ *
+ * @param ignoreCache If true, ignores the cache and refreshes the data anyway.
+ * @return A {@link ProcFileIterator} to iterate through the file content, or null if there is
+ * error.
+ */
+ public ProcFileIterator open(boolean ignoreCache) {
+ if (mErrors >= ERROR_THRESHOLD) {
+ return null;
+ }
+
+ if (ignoreCache) {
+ mWriteLock.lock();
+ } else {
+ mReadLock.lock();
+ if (dataValid()) {
+ return new ProcFileIterator(mSize);
+ }
+ mReadLock.unlock();
+ mWriteLock.lock();
+ if (dataValid()) {
+ // Recheck because another thread might have written data just before we did.
+ mReadLock.lock();
+ mWriteLock.unlock();
+ return new ProcFileIterator(mSize);
+ }
+ }
+
+ // At this point, write lock is held and data is invalid.
+ int total = 0;
+ int curr;
+ mSize = 0;
+ final int oldMask = StrictMode.allowThreadDiskReadsMask();
+ try (BufferedReader r = Files.newBufferedReader(mFile)) {
+ if (mBuf == null) {
+ mBuf = new char[1024];
+ }
+ while ((curr = r.read(mBuf, total, mBuf.length - total)) >= 0) {
+ total += curr;
+ if (total == mBuf.length) {
+ // Hit the limit. Resize buffer.
+ if (mBuf.length == MAX_BUFFER_SIZE) {
+ mErrors++;
+ Slog.e(TAG, "Proc file too large: " + mFile);
+ return null;
+ }
+ mBuf = Arrays.copyOf(mBuf, Math.min(mBuf.length << 1, MAX_BUFFER_SIZE));
+ }
+ }
+ mSize = total;
+ mLastReadTime = SystemClock.elapsedRealtime();
+ // ReentrantReadWriteLock allows lock downgrading.
+ mReadLock.lock();
+ return new ProcFileIterator(total);
+ } catch (FileNotFoundException e) {
+ mErrors++;
+ Slog.w(TAG, "File not found. It's normal if not implemented: " + mFile);
+ } catch (IOException e) {
+ mErrors++;
+ Slog.e(TAG, "Error reading: " + mFile, e);
+ } finally {
+ StrictMode.setThreadPolicyMask(oldMask);
+ mWriteLock.unlock();
+ }
+ return null;
+ }
+
+ private boolean dataValid() {
+ return mSize > 0 && (SystemClock.elapsedRealtime() - mLastReadTime < FRESHNESS);
+ }
+
+ /**
+ * An autoCloseable iterator to iterate through a string proc file line by line. User must call
+ * close() when finish using to prevent deadlock.
+ */
+ public class ProcFileIterator implements AutoCloseable {
+ private final int mSize;
+ private int mPos;
+
+ public ProcFileIterator(int size) {
+ mSize = size;
+ }
+
+ /**
+ * Fetches the next line. Note that all subsequent return values share the same char[]
+ * under the hood.
+ *
+ * @return A {@link java.nio.CharBuffer} containing the next line without the new line
+ * symbol.
+ */
+ public CharBuffer nextLine() {
+ if (mPos >= mSize) {
+ return null;
+ }
+ int i = mPos;
+ // Move i to the next new line symbol, which is always '\n' in Android.
+ while (i < mSize && mBuf[i] != '\n') {
+ i++;
+ }
+ int start = mPos;
+ mPos = i + 1;
+ return CharBuffer.wrap(mBuf, start, i - start);
+ }
+
+ /**
+ * Fetches the next line, converts all numbers into long, and puts into the given long[].
+ * To avoid GC, caller should try to use the same array for all calls. All non-numeric
+ * chars are treated as delimiters. All numbers are non-negative.
+ *
+ * @param array An array to store the parsed numbers.
+ * @return The number of elements written to the given array. -1 if there is no more line.
+ */
+ public int nextLineAsArray(long[] array) {
+ CharBuffer buf = nextLine();
+ if (buf == null) {
+ return -1;
+ }
+ int count = 0;
+ long num = -1;
+ char c;
+
+ while (buf.remaining() > 0 && count < array.length) {
+ c = buf.get();
+ if (num < 0) {
+ if (isNumber(c)) {
+ num = c - '0';
+ }
+ } else {
+ if (isNumber(c)) {
+ num = num * 10 + c - '0';
+ } else {
+ array[count++] = num;
+ num = -1;
+ }
+ }
+ }
+ if (num >= 0) {
+ array[count++] = num;
+ }
+ return count;
+ }
+
+ /** Total size of the proc file in chars. */
+ public int size() {
+ return mSize;
+ }
+
+ /** Must call close at the end to release the read lock! Or use try-with-resources. */
+ public void close() {
+ mReadLock.unlock();
+ }
+
+ private boolean isNumber(char c) {
+ return c >= '0' && c <= '9';
+ }
+ }
+}
diff --git a/core/java/com/android/internal/view/InputBindResult.java b/core/java/com/android/internal/view/InputBindResult.java
index ec8e8da..901cfe3 100644
--- a/core/java/com/android/internal/view/InputBindResult.java
+++ b/core/java/com/android/internal/view/InputBindResult.java
@@ -54,6 +54,7 @@
ResultCode.ERROR_NO_EDITOR,
ResultCode.ERROR_DISPLAY_ID_MISMATCH,
ResultCode.ERROR_INVALID_DISPLAY_ID,
+ ResultCode.ERROR_INVALID_CLIENT,
})
public @interface ResultCode {
/**
@@ -158,6 +159,10 @@
* display.
*/
int ERROR_INVALID_DISPLAY_ID = 14;
+ /**
+ * Indicates that the client is not recognized by the system.
+ */
+ int ERROR_INVALID_CLIENT = 15;
}
@ResultCode
@@ -287,6 +292,8 @@
return "ERROR_DISPLAY_ID_MISMATCH";
case ResultCode.ERROR_INVALID_DISPLAY_ID:
return "ERROR_INVALID_DISPLAY_ID";
+ case ResultCode.ERROR_INVALID_CLIENT:
+ return "ERROR_INVALID_CLIENT";
default:
return "Unknown(" + result + ")";
}
@@ -343,4 +350,9 @@
*/
public static final InputBindResult INVALID_DISPLAY_ID =
error(ResultCode.ERROR_INVALID_DISPLAY_ID);
+
+ /**
+ * Predefined error object for {@link ResultCode#ERROR_INVALID_CLIENT}.
+ */
+ public static final InputBindResult INVALID_CLIENT = error(ResultCode.ERROR_INVALID_CLIENT);
}
diff --git a/core/jni/android_hardware_camera2_DngCreator.cpp b/core/jni/android_hardware_camera2_DngCreator.cpp
index c977437..29051f1 100644
--- a/core/jni/android_hardware_camera2_DngCreator.cpp
+++ b/core/jni/android_hardware_camera2_DngCreator.cpp
@@ -892,6 +892,13 @@
cfaOut[3] = 0;
break;
}
+ // MONO and NIR are degenerate case of RGGB pattern: only Red channel
+ // will be used.
+ case ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_MONO:
+ case ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_NIR: {
+ cfaOut[0] = 0;
+ break;
+ }
default: {
return BAD_VALUE;
}
@@ -1063,6 +1070,8 @@
uint32_t preWidth = 0;
uint32_t preHeight = 0;
+ uint8_t colorFilter = 0;
+ bool isBayer = true;
{
// Check dimensions
camera_metadata_entry entry =
@@ -1083,10 +1092,25 @@
"either the preCorrectionActiveArraySize or the pixelArraySize.");
return nullptr;
}
+
+ camera_metadata_entry colorFilterEntry =
+ characteristics.find(ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT);
+ colorFilter = colorFilterEntry.data.u8[0];
+ camera_metadata_entry capabilitiesEntry =
+ characteristics.find(ANDROID_REQUEST_AVAILABLE_CAPABILITIES);
+ size_t capsCount = capabilitiesEntry.count;
+ uint8_t* caps = capabilitiesEntry.data.u8;
+ if (std::find(caps, caps+capsCount, ANDROID_REQUEST_AVAILABLE_CAPABILITIES_MONOCHROME)
+ != caps+capsCount) {
+ isBayer = false;
+ } else if (colorFilter == ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_MONO ||
+ colorFilter == ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_NIR) {
+ jniThrowException(env, "java/lang/AssertionError",
+ "A camera device with MONO/NIR color filter must have MONOCHROME capability.");
+ return nullptr;
+ }
}
-
-
writer->addIfd(TIFF_IFD_0);
status_t err = OK;
@@ -1094,9 +1118,12 @@
const uint32_t samplesPerPixel = 1;
const uint32_t bitsPerSample = BITS_PER_SAMPLE;
- OpcodeListBuilder::CfaLayout opcodeCfaLayout = OpcodeListBuilder::CFA_RGGB;
+ OpcodeListBuilder::CfaLayout opcodeCfaLayout = OpcodeListBuilder::CFA_NONE;
uint8_t cfaPlaneColor[3] = {0, 1, 2};
- uint8_t cfaEnum = -1;
+ camera_metadata_entry cfaEntry =
+ characteristics.find(ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT);
+ BAIL_IF_EMPTY_RET_NULL_SP(cfaEntry, env, TAG_CFAPATTERN, writer);
+ uint8_t cfaEnum = cfaEntry.data.u8[0];
// TODO: Greensplit.
// TODO: Add remaining non-essential tags
@@ -1141,12 +1168,20 @@
{
// Set photometric interpretation
- uint16_t interpretation = 32803; // CFA
+ uint16_t interpretation = isBayer ? 32803 /* CFA */ :
+ 34892; /* Linear Raw */;
BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_PHOTOMETRICINTERPRETATION, 1,
&interpretation, TIFF_IFD_0), env, TAG_PHOTOMETRICINTERPRETATION, writer);
}
{
+ uint16_t repeatDim[2] = {2, 2};
+ if (!isBayer) {
+ repeatDim[0] = repeatDim[1] = 1;
+ }
+ BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_BLACKLEVELREPEATDIM, 2, repeatDim,
+ TIFF_IFD_0), env, TAG_BLACKLEVELREPEATDIM, writer);
+
// Set blacklevel tags, using dynamic black level if available
camera_metadata_entry entry =
results.find(ANDROID_SENSOR_DYNAMIC_BLACK_LEVEL);
@@ -1165,14 +1200,9 @@
blackLevelRational[i * 2] = static_cast<uint32_t>(entry.data.i32[i]);
blackLevelRational[i * 2 + 1] = 1;
}
-
}
- BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_BLACKLEVEL, 4, blackLevelRational,
- TIFF_IFD_0), env, TAG_BLACKLEVEL, writer);
-
- uint16_t repeatDim[2] = {2, 2};
- BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_BLACKLEVELREPEATDIM, 2, repeatDim,
- TIFF_IFD_0), env, TAG_BLACKLEVELREPEATDIM, writer);
+ BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_BLACKLEVEL, repeatDim[0]*repeatDim[1],
+ blackLevelRational, TIFF_IFD_0), env, TAG_BLACKLEVEL, writer);
}
{
@@ -1189,21 +1219,15 @@
TIFF_IFD_0), env, TAG_PLANARCONFIGURATION, writer);
}
- {
+ // All CFA pattern tags are not necessary for monochrome cameras.
+ if (isBayer) {
// Set CFA pattern dimensions
uint16_t repeatDim[2] = {2, 2};
BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_CFAREPEATPATTERNDIM, 2, repeatDim,
TIFF_IFD_0), env, TAG_CFAREPEATPATTERNDIM, writer);
- }
- {
// Set CFA pattern
- camera_metadata_entry entry =
- characteristics.find(ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT);
- BAIL_IF_EMPTY_RET_NULL_SP(entry, env, TAG_CFAPATTERN, writer);
-
const int cfaLength = 4;
- cfaEnum = entry.data.u8[0];
uint8_t cfa[cfaLength];
if ((err = convertCFA(cfaEnum, /*out*/cfa)) != OK) {
jniThrowExceptionFmt(env, "java/lang/IllegalStateException",
@@ -1214,15 +1238,11 @@
env, TAG_CFAPATTERN, writer);
opcodeCfaLayout = convertCFAEnumToOpcodeLayout(cfaEnum);
- }
- {
// Set CFA plane color
BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_CFAPLANECOLOR, 3, cfaPlaneColor,
TIFF_IFD_0), env, TAG_CFAPLANECOLOR, writer);
- }
- {
// Set CFA layout
uint16_t cfaLayout = 1;
BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_CFALAYOUT, 1, &cfaLayout, TIFF_IFD_0),
@@ -1442,7 +1462,7 @@
}
bool singleIlluminant = false;
- {
+ if (isBayer) {
// Set calibration illuminants
camera_metadata_entry entry1 =
characteristics.find(ANDROID_SENSOR_REFERENCE_ILLUMINANT1);
@@ -1464,7 +1484,7 @@
}
}
- {
+ if (isBayer) {
// Set color transforms
camera_metadata_entry entry1 =
characteristics.find(ANDROID_SENSOR_COLOR_TRANSFORM1);
@@ -1497,7 +1517,7 @@
}
}
- {
+ if (isBayer) {
// Set calibration transforms
camera_metadata_entry entry1 =
characteristics.find(ANDROID_SENSOR_CALIBRATION_TRANSFORM1);
@@ -1531,7 +1551,7 @@
}
}
- {
+ if (isBayer) {
// Set forward transforms
camera_metadata_entry entry1 =
characteristics.find(ANDROID_SENSOR_FORWARD_MATRIX1);
@@ -1565,7 +1585,7 @@
}
}
- {
+ if (isBayer) {
// Set camera neutral
camera_metadata_entry entry =
results.find(ANDROID_SENSOR_NEUTRAL_COLOR_POINT);
@@ -1632,8 +1652,8 @@
camera_metadata_entry entry =
results.find(ANDROID_SENSOR_NOISE_PROFILE);
- const status_t numPlaneColors = 3;
- const status_t numCfaChannels = 4;
+ const status_t numPlaneColors = isBayer ? 3 : 1;
+ const status_t numCfaChannels = isBayer ? 4 : 1;
uint8_t cfaOut[numCfaChannels];
if ((err = convertCFA(cfaEnum, /*out*/cfaOut)) != OK) {
@@ -1710,42 +1730,44 @@
}
}
+ // Hot pixel map is specific to bayer camera per DNG spec.
+ if (isBayer) {
+ // Set up bad pixel correction list
+ camera_metadata_entry entry3 = characteristics.find(ANDROID_STATISTICS_HOT_PIXEL_MAP);
- // Set up bad pixel correction list
- camera_metadata_entry entry3 = characteristics.find(ANDROID_STATISTICS_HOT_PIXEL_MAP);
-
- if ((entry3.count % 2) != 0) {
- ALOGE("%s: Hot pixel map contains odd number of values, cannot map to pairs!",
- __FUNCTION__);
- jniThrowRuntimeException(env, "failed to add hotpixel map.");
- return nullptr;
- }
-
- // Adjust the bad pixel coordinates to be relative to the origin of the active area DNG tag
- std::vector<uint32_t> v;
- for (size_t i = 0; i < entry3.count; i += 2) {
- int32_t x = entry3.data.i32[i];
- int32_t y = entry3.data.i32[i + 1];
- x -= static_cast<int32_t>(xmin);
- y -= static_cast<int32_t>(ymin);
- if (x < 0 || y < 0 || static_cast<uint32_t>(x) >= width ||
- static_cast<uint32_t>(y) >= height) {
- continue;
- }
- v.push_back(x);
- v.push_back(y);
- }
- const uint32_t* badPixels = &v[0];
- uint32_t badPixelCount = v.size();
-
- if (badPixelCount > 0) {
- err = builder.addBadPixelListForMetadata(badPixels, badPixelCount, opcodeCfaLayout);
-
- if (err != OK) {
- ALOGE("%s: Could not add hotpixel map.", __FUNCTION__);
+ if ((entry3.count % 2) != 0) {
+ ALOGE("%s: Hot pixel map contains odd number of values, cannot map to pairs!",
+ __FUNCTION__);
jniThrowRuntimeException(env, "failed to add hotpixel map.");
return nullptr;
}
+
+ // Adjust the bad pixel coordinates to be relative to the origin of the active area DNG tag
+ std::vector<uint32_t> v;
+ for (size_t i = 0; i < entry3.count; i += 2) {
+ int32_t x = entry3.data.i32[i];
+ int32_t y = entry3.data.i32[i + 1];
+ x -= static_cast<int32_t>(xmin);
+ y -= static_cast<int32_t>(ymin);
+ if (x < 0 || y < 0 || static_cast<uint32_t>(x) >= width ||
+ static_cast<uint32_t>(y) >= height) {
+ continue;
+ }
+ v.push_back(x);
+ v.push_back(y);
+ }
+ const uint32_t* badPixels = &v[0];
+ uint32_t badPixelCount = v.size();
+
+ if (badPixelCount > 0) {
+ err = builder.addBadPixelListForMetadata(badPixels, badPixelCount, opcodeCfaLayout);
+
+ if (err != OK) {
+ ALOGE("%s: Could not add hotpixel map.", __FUNCTION__);
+ jniThrowRuntimeException(env, "failed to add hotpixel map.");
+ return nullptr;
+ }
+ }
}
if (builder.getCount() > 0) {
@@ -1960,10 +1982,12 @@
tagsToMove.add(TAG_BLACKLEVELREPEATDIM);
tagsToMove.add(TAG_SAMPLESPERPIXEL);
tagsToMove.add(TAG_PLANARCONFIGURATION);
- tagsToMove.add(TAG_CFAREPEATPATTERNDIM);
- tagsToMove.add(TAG_CFAPATTERN);
- tagsToMove.add(TAG_CFAPLANECOLOR);
- tagsToMove.add(TAG_CFALAYOUT);
+ if (isBayer) {
+ tagsToMove.add(TAG_CFAREPEATPATTERNDIM);
+ tagsToMove.add(TAG_CFAPATTERN);
+ tagsToMove.add(TAG_CFAPLANECOLOR);
+ tagsToMove.add(TAG_CFALAYOUT);
+ }
tagsToMove.add(TAG_XRESOLUTION);
tagsToMove.add(TAG_YRESOLUTION);
tagsToMove.add(TAG_RESOLUTIONUNIT);
diff --git a/core/jni/com_android_internal_net_NetworkStatsFactory.cpp b/core/jni/com_android_internal_net_NetworkStatsFactory.cpp
index b3ff4db..b708735 100644
--- a/core/jni/com_android_internal_net_NetworkStatsFactory.cpp
+++ b/core/jni/com_android_internal_net_NetworkStatsFactory.cpp
@@ -175,7 +175,7 @@
}
}
s.tag = rawTag >> 32;
- if (limitTag != -1 && s.tag != limitTag) {
+ if (limitTag != -1 && s.tag != static_cast<uint32_t>(limitTag)) {
//ALOGI("skipping due to tag: %s", buffer);
continue;
}
@@ -188,7 +188,7 @@
if (sscanf(pos, "%u %u %" PRIu64 " %" PRIu64 " %" PRIu64 " %" PRIu64,
&s.uid, &s.set, &s.rxBytes, &s.rxPackets,
&s.txBytes, &s.txPackets) == 6) {
- if (limitUid != -1 && limitUid != s.uid) {
+ if (limitUid != -1 && static_cast<uint32_t>(limitUid) != s.uid) {
//ALOGI("skipping due to uid: %s", buffer);
continue;
}
diff --git a/core/proto/android/providers/settings/secure.proto b/core/proto/android/providers/settings/secure.proto
index f8acc33..6e661e1 100644
--- a/core/proto/android/providers/settings/secure.proto
+++ b/core/proto/android/providers/settings/secure.proto
@@ -72,11 +72,6 @@
// List of the accessibility services to which the user has granted
// permission to put the device into touch exploration mode.
optional SettingProto touch_exploration_granted_accessibility_services = 31;
- reserved 32; // minimum_ui_timeout_enabled
- reserved 33; // minimum_ui_timeout_ms
- optional SettingProto non_interactive_ui_timeout_ms = 34 [ (android.privacy).dest = DEST_AUTOMATIC ];
- optional SettingProto interactive_ui_timeout_ms = 35 [ (android.privacy).dest = DEST_AUTOMATIC ];
-
}
optional Accessibility accessibility = 2;
diff --git a/core/proto/android/server/jobscheduler.proto b/core/proto/android/server/jobscheduler.proto
index 54f0934..e83a2bf 100644
--- a/core/proto/android/server/jobscheduler.proto
+++ b/core/proto/android/server/jobscheduler.proto
@@ -609,6 +609,17 @@
repeated Constraint unsatisfied_constraints = 9;
optional bool is_doze_whitelisted = 10;
+ message ImplicitConstraints {
+ // The device isn't Dozing or this job will be in the foreground. This
+ // implicit constraint must be satisfied for the job to run.
+ optional bool is_not_dozing = 1;
+ // The job is not restricted from running in the background (due to
+ // Battery Saver). This implicit constraint must be satisfied for the
+ // job to run.
+ optional bool is_not_restricted_in_bg = 2;
+ }
+ optional ImplicitConstraints implicit_constraints = 25;
+
enum TrackingController {
TRACKING_BATTERY = 0;
TRACKING_CONNECTIVITY = 1;
@@ -662,4 +673,6 @@
optional int64 last_failed_run_time = 23;
optional int64 internal_flags = 24;
+
+ // Next tag: 26
}
diff --git a/core/proto/android/server/usagestatsservice.proto b/core/proto/android/server/usagestatsservice.proto
index 941c81f..3d60a86 100644
--- a/core/proto/android/server/usagestatsservice.proto
+++ b/core/proto/android/server/usagestatsservice.proto
@@ -45,11 +45,15 @@
optional string package = 1;
// package_index contains the index + 1 of the package name in the string pool
optional int32 package_index = 2;
+ // Time attributes stored as an offset of the IntervalStats's beginTime.
optional int64 last_time_active_ms = 3;
optional int64 total_time_active_ms = 4;
optional int32 last_event = 5;
optional int32 app_launch_count = 6;
repeated ChooserAction chooser_actions = 7;
+ // Time attributes stored as an offset of the IntervalStats's beginTime.
+ optional int64 last_time_service_used_ms = 8;
+ optional int64 total_time_service_used_ms = 9;
}
// Stores the relevant information an IntervalStats will have about a Configuration
@@ -86,6 +90,8 @@
// stringpool contains all the package and class names used by UsageStats and Event
// They will hold a number that is equal to the index + 1 of their string in the pool
optional StringPool stringpool = 2;
+ optional int32 major_version = 3;
+ optional int32 minor_version = 4;
// The following fields contain aggregated usage stats data
optional CountAndTime interactive = 10;
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 68ec342..a99b942 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -2448,6 +2448,25 @@
<flag name="noExcludeDescendants" value="0x8" />
</attr>
+ <!-- Hints the Android System whether the view node associated with this View should be
+ use for content capture purposes. -->
+ <attr name="importantForContentCapture">
+ <!-- Let the Android System use its heuristics to determine if the view is important for content capture. -->
+ <flag name="auto" value="0" />
+ <!-- Hint the Android System that this view is important for content capture,
+ and its children (if any) will be traversed.. -->
+ <flag name="yes" value="0x1" />
+ <!-- Hint the Android System that this view is *not* important for content capture,
+ but its children (if any) will be traversed.. -->
+ <flag name="no" value="0x2" />
+ <!-- Hint the Android System that this view is important for content capture,
+ but its children (if any) will not be traversed. -->
+ <flag name="yesExcludeDescendants" value="0x4" />
+ <!-- Hint the Android System that this view is *not* important for content capture,
+ and its children (if any) will not be traversed. -->
+ <flag name="noExcludeDescendants" value="0x8" />
+ </attr>
+
<!-- Boolean that controls whether a view can take focus while in touch mode.
If this is true for a view, that view can gain focus when clicked on, and can keep
focus if another view is clicked on that doesn't have this attribute set to true. -->
diff --git a/core/res/res/values/locale_config.xml b/core/res/res/values/locale_config.xml
index 35eee6a..d6c0a10 100644
--- a/core/res/res/values/locale_config.xml
+++ b/core/res/res/values/locale_config.xml
@@ -23,80 +23,81 @@
<item>ak-GH</item> <!-- Akan (Ghana) -->
<item>am-ET</item> <!-- Amharic (Ethiopia) -->
<item>ar-AE</item> <!-- Arabic (United Arab Emirates) -->
- <item>ar-AE-u-nu-latn</item> <!-- Arabic (United Arab Emirates,Western Digits) -->
+ <item>ar-AE-u-nu-latn</item> <!-- Arabic (United Arab Emirates, Western Digits) -->
<item>ar-BH</item> <!-- Arabic (Bahrain) -->
- <item>ar-BH-u-nu-latn</item> <!-- Arabic (Bahrain,Western Digits) -->
+ <item>ar-BH-u-nu-latn</item> <!-- Arabic (Bahrain, Western Digits) -->
<item>ar-DJ</item> <!-- Arabic (Djibouti) -->
- <item>ar-DJ-u-nu-latn</item> <!-- Arabic (Djibouti,Western Digits) -->
+ <item>ar-DJ-u-nu-latn</item> <!-- Arabic (Djibouti, Western Digits) -->
<item>ar-DZ</item> <!-- Arabic (Algeria) -->
- <item>ar-DZ-u-nu-arab</item> <!-- Arabic (Algeria,Arabic-Indic Digits) -->
+ <item>ar-DZ-u-nu-arab</item> <!-- Arabic (Algeria, Arabic-Indic Digits) -->
<item>ar-EG</item> <!-- Arabic (Egypt) -->
- <item>ar-EG-u-nu-latn</item> <!-- Arabic (Egypt,Western Digits) -->
+ <item>ar-EG-u-nu-latn</item> <!-- Arabic (Egypt, Western Digits) -->
<item>ar-EH</item> <!-- Arabic (Western Sahara) -->
- <item>ar-EH-u-nu-arab</item> <!-- Arabic (Western Sahara,Arabic-Indic Digits) -->
+ <item>ar-EH-u-nu-arab</item> <!-- Arabic (Western Sahara, Arabic-Indic Digits) -->
<item>ar-ER</item> <!-- Arabic (Eritrea) -->
- <item>ar-ER-u-nu-latn</item> <!-- Arabic (Eritrea,Western Digits) -->
+ <item>ar-ER-u-nu-latn</item> <!-- Arabic (Eritrea, Western Digits) -->
<item>ar-IL</item> <!-- Arabic (Israel) -->
- <item>ar-IL-u-nu-latn</item> <!-- Arabic (Israel,Western Digits) -->
+ <item>ar-IL-u-nu-latn</item> <!-- Arabic (Israel, Western Digits) -->
<item>ar-IQ</item> <!-- Arabic (Iraq) -->
- <item>ar-IQ-u-nu-latn</item> <!-- Arabic (Iraq,Western Digits) -->
+ <item>ar-IQ-u-nu-latn</item> <!-- Arabic (Iraq, Western Digits) -->
<item>ar-JO</item> <!-- Arabic (Jordan) -->
- <item>ar-JO-u-nu-latn</item> <!-- Arabic (Jordan,Western Digits) -->
+ <item>ar-JO-u-nu-latn</item> <!-- Arabic (Jordan, Western Digits) -->
<item>ar-KM</item> <!-- Arabic (Comoros) -->
- <item>ar-KM-u-nu-latn</item> <!-- Arabic (Comoros,Western Digits) -->
+ <item>ar-KM-u-nu-latn</item> <!-- Arabic (Comoros, Western Digits) -->
<item>ar-KW</item> <!-- Arabic (Kuwait) -->
- <item>ar-KW-u-nu-latn</item> <!-- Arabic (Kuwait,Western Digits) -->
+ <item>ar-KW-u-nu-latn</item> <!-- Arabic (Kuwait, Western Digits) -->
<item>ar-LB</item> <!-- Arabic (Lebanon) -->
- <item>ar-LB-u-nu-latn</item> <!-- Arabic (Lebanon,Western Digits) -->
+ <item>ar-LB-u-nu-latn</item> <!-- Arabic (Lebanon, Western Digits) -->
<item>ar-LY</item> <!-- Arabic (Libya) -->
- <item>ar-LY-u-nu-arab</item> <!-- Arabic (Libya,Arabic-Indic Digits) -->
+ <item>ar-LY-u-nu-arab</item> <!-- Arabic (Libya, Arabic-Indic Digits) -->
<item>ar-MA</item> <!-- Arabic (Morocco) -->
- <item>ar-MA-u-nu-arab</item> <!-- Arabic (Morocco,Arabic-Indic Digits) -->
+ <item>ar-MA-u-nu-arab</item> <!-- Arabic (Morocco, Arabic-Indic Digits) -->
<item>ar-MR</item> <!-- Arabic (Mauritania) -->
- <item>ar-MR-u-nu-latn</item> <!-- Arabic (Mauritania,Western Digits) -->
+ <item>ar-MR-u-nu-latn</item> <!-- Arabic (Mauritania, Western Digits) -->
<item>ar-OM</item> <!-- Arabic (Oman) -->
- <item>ar-OM-u-nu-latn</item> <!-- Arabic (Oman,Western Digits) -->
+ <item>ar-OM-u-nu-latn</item> <!-- Arabic (Oman, Western Digits) -->
<item>ar-PS</item> <!-- Arabic (Palestine) -->
- <item>ar-PS-u-nu-latn</item> <!-- Arabic (Palestine,Western Digits) -->
+ <item>ar-PS-u-nu-latn</item> <!-- Arabic (Palestine, Western Digits) -->
<item>ar-QA</item> <!-- Arabic (Qatar) -->
- <item>ar-QA-u-nu-latn</item> <!-- Arabic (Qatar,Western Digits) -->
+ <item>ar-QA-u-nu-latn</item> <!-- Arabic (Qatar, Western Digits) -->
<item>ar-SA</item> <!-- Arabic (Saudi Arabia) -->
- <item>ar-SA-u-nu-latn</item> <!-- Arabic (Saudi Arabia,Western Digits) -->
+ <item>ar-SA-u-nu-latn</item> <!-- Arabic (Saudi Arabia, Western Digits) -->
<item>ar-SD</item> <!-- Arabic (Sudan) -->
- <item>ar-SD-u-nu-latn</item> <!-- Arabic (Sudan,Western Digits) -->
+ <item>ar-SD-u-nu-latn</item> <!-- Arabic (Sudan, Western Digits) -->
<item>ar-SO</item> <!-- Arabic (Somalia) -->
- <item>ar-SO-u-nu-latn</item> <!-- Arabic (Somalia,Western Digits) -->
+ <item>ar-SO-u-nu-latn</item> <!-- Arabic (Somalia, Western Digits) -->
<item>ar-SS</item> <!-- Arabic (South Sudan) -->
- <item>ar-SS-u-nu-latn</item> <!-- Arabic (South Sudan,Western Digits) -->
+ <item>ar-SS-u-nu-latn</item> <!-- Arabic (South Sudan, Western Digits) -->
<item>ar-SY</item> <!-- Arabic (Syria) -->
- <item>ar-SY-u-nu-latn</item> <!-- Arabic (Syria,Western Digits) -->
+ <item>ar-SY-u-nu-latn</item> <!-- Arabic (Syria, Western Digits) -->
<item>ar-TD</item> <!-- Arabic (Chad) -->
- <item>ar-TD-u-nu-latn</item> <!-- Arabic (Chad,Western Digits) -->
+ <item>ar-TD-u-nu-latn</item> <!-- Arabic (Chad, Western Digits) -->
<item>ar-TN</item> <!-- Arabic (Tunisia) -->
- <item>ar-TN-u-nu-arab</item> <!-- Arabic (Tunisia,Arabic-Indic Digits) -->
+ <item>ar-TN-u-nu-arab</item> <!-- Arabic (Tunisia, Arabic-Indic Digits) -->
<item>ar-XB</item> <!-- Right-to-left pseudolocale -->
<item>ar-YE</item> <!-- Arabic (Yemen) -->
- <item>ar-YE-u-nu-latn</item> <!-- Arabic (Yemen,Western Digits) -->
+ <item>ar-YE-u-nu-latn</item> <!-- Arabic (Yemen, Western Digits) -->
<item>as-IN</item> <!-- Assamese (India) -->
<item>asa-TZ</item> <!-- Asu (Tanzania) -->
- <item>az-Cyrl-AZ</item> <!-- Azerbaijani (Cyrillic,Azerbaijan) -->
- <item>az-Latn-AZ</item> <!-- Azerbaijani (Latin,Azerbaijan) -->
+ <item>ast-ES</item> <!-- Asturian (Spain) -->
+ <item>az-Cyrl-AZ</item> <!-- Azerbaijani (Cyrillic, Azerbaijan) -->
+ <item>az-Latn-AZ</item> <!-- Azerbaijani (Latin, Azerbaijan) -->
<item>bas-CM</item> <!-- Basaa (Cameroon) -->
<item>be-BY</item> <!-- Belarusian (Belarus) -->
<item>bem-ZM</item> <!-- Bemba (Zambia) -->
<item>bez-TZ</item> <!-- Bena (Tanzania) -->
<item>bg-BG</item> <!-- Bulgarian (Bulgaria) -->
<item>bm-ML</item> <!-- Bambara (Mali) -->
- <item>bn-BD</item> <!-- Bengali (Bangladesh) -->
- <item>bn-BD-u-nu-latn</item> <!-- Bengali (Bangladesh,Western Digits) -->
- <item>bn-IN</item> <!-- Bengali (India) -->
- <item>bn-IN-u-nu-latn</item> <!-- Bengali (India,Western Digits) -->
+ <item>bn-BD</item> <!-- Bangla (Bangladesh) -->
+ <item>bn-BD-u-nu-latn</item> <!-- Bangla (Bangladesh, Western Digits) -->
+ <item>bn-IN</item> <!-- Bangla (India) -->
+ <item>bn-IN-u-nu-latn</item> <!-- Bangla (India, Western Digits) -->
<item>bo-CN</item> <!-- Tibetan (China) -->
<item>bo-IN</item> <!-- Tibetan (India) -->
<item>br-FR</item> <!-- Breton (France) -->
<item>brx-IN</item> <!-- Bodo (India) -->
- <item>bs-Cyrl-BA</item> <!-- Bosnian (Cyrillic,Bosnia & Herzegovina) -->
- <item>bs-Latn-BA</item> <!-- Bosnian (Latin,Bosnia & Herzegovina) -->
+ <item>bs-Cyrl-BA</item> <!-- Bosnian (Cyrillic, Bosnia & Herzegovina) -->
+ <item>bs-Latn-BA</item> <!-- Bosnian (Latin, Bosnia & Herzegovina) -->
<item>ca-AD</item> <!-- Catalan (Andorra) -->
<item>ca-ES</item> <!-- Catalan (Spain) -->
<item>ca-FR</item> <!-- Catalan (France) -->
@@ -113,6 +114,7 @@
<item>de-BE</item> <!-- German (Belgium) -->
<item>de-CH</item> <!-- German (Switzerland) -->
<item>de-DE</item> <!-- German (Germany) -->
+ <item>de-IT</item> <!-- German (Italy) -->
<item>de-LI</item> <!-- German (Liechtenstein) -->
<item>de-LU</item> <!-- German (Luxembourg) -->
<item>dje-NE</item> <!-- Zarma (Niger) -->
@@ -230,6 +232,8 @@
<item>en-ZW</item> <!-- English (Zimbabwe) -->
<item>es-AR</item> <!-- Spanish (Argentina) -->
<item>es-BO</item> <!-- Spanish (Bolivia) -->
+ <item>es-BR</item> <!-- Spanish (Brazil) -->
+ <item>es-BZ</item> <!-- Spanish (Belize) -->
<item>es-CL</item> <!-- Spanish (Chile) -->
<item>es-CO</item> <!-- Spanish (Colombia) -->
<item>es-CR</item> <!-- Spanish (Costa Rica) -->
@@ -257,9 +261,9 @@
<item>eu-ES</item> <!-- Basque (Spain) -->
<item>ewo-CM</item> <!-- Ewondo (Cameroon) -->
<item>fa-AF</item> <!-- Persian (Afghanistan) -->
- <item>fa-AF-u-nu-latn</item> <!-- Persian (Afghanistan,Western Digits) -->
+ <item>fa-AF-u-nu-latn</item> <!-- Persian (Afghanistan, Western Digits) -->
<item>fa-IR</item> <!-- Persian (Iran) -->
- <item>fa-IR-u-nu-latn</item> <!-- Persian (Iran,Western Digits) -->
+ <item>fa-IR-u-nu-latn</item> <!-- Persian (Iran, Western Digits) -->
<item>ff-CM</item> <!-- Fulah (Cameroon) -->
<item>ff-GN</item> <!-- Fulah (Guinea) -->
<item>ff-MR</item> <!-- Fulah (Mauritania) -->
@@ -274,9 +278,9 @@
<item>fr-BJ</item> <!-- French (Benin) -->
<item>fr-BL</item> <!-- French (St. Barthélemy) -->
<item>fr-CA</item> <!-- French (Canada) -->
- <item>fr-CD</item> <!-- French (Congo (DRC)) -->
+ <item>fr-CD</item> <!-- French (Congo - Kinshasa) -->
<item>fr-CF</item> <!-- French (Central African Republic) -->
- <item>fr-CG</item> <!-- French (Congo (Republic)) -->
+ <item>fr-CG</item> <!-- French (Congo - Brazzaville) -->
<item>fr-CH</item> <!-- French (Switzerland) -->
<item>fr-CI</item> <!-- French (Côte d’Ivoire) -->
<item>fr-CM</item> <!-- French (Cameroon) -->
@@ -329,20 +333,20 @@
<item>ha-NE</item> <!-- Hausa (Niger) -->
<item>ha-NG</item> <!-- Hausa (Nigeria) -->
<item>haw-US</item> <!-- Hawaiian (United States) -->
- <item>iw-IL</item> <!-- Hebrew (Israel) -->
<item>hi-IN</item> <!-- Hindi (India) -->
<item>hr-BA</item> <!-- Croatian (Bosnia & Herzegovina) -->
<item>hr-HR</item> <!-- Croatian (Croatia) -->
<item>hsb-DE</item> <!-- Upper Sorbian (Germany) -->
<item>hu-HU</item> <!-- Hungarian (Hungary) -->
<item>hy-AM</item> <!-- Armenian (Armenia) -->
- <item>in-ID</item> <!-- Indonesian (Indonesia) -->
<item>ig-NG</item> <!-- Igbo (Nigeria) -->
<item>ii-CN</item> <!-- Sichuan Yi (China) -->
+ <item>in-ID</item> <!-- Indonesian (Indonesia) -->
<item>is-IS</item> <!-- Icelandic (Iceland) -->
<item>it-CH</item> <!-- Italian (Switzerland) -->
<item>it-IT</item> <!-- Italian (Italy) -->
<item>it-SM</item> <!-- Italian (San Marino) -->
+ <item>iw-IL</item> <!-- Hebrew (Israel) -->
<item>ja-JP</item> <!-- Japanese (Japan) -->
<item>jgo-CM</item> <!-- Ngomba (Cameroon) -->
<item>jmc-TZ</item> <!-- Machame (Tanzania) -->
@@ -372,12 +376,12 @@
<item>lg-UG</item> <!-- Ganda (Uganda) -->
<item>lkt-US</item> <!-- Lakota (United States) -->
<item>ln-AO</item> <!-- Lingala (Angola) -->
- <item>ln-CD</item> <!-- Lingala (Congo (DRC)) -->
+ <item>ln-CD</item> <!-- Lingala (Congo - Kinshasa) -->
<item>ln-CF</item> <!-- Lingala (Central African Republic) -->
- <item>ln-CG</item> <!-- Lingala (Congo (Republic)) -->
+ <item>ln-CG</item> <!-- Lingala (Congo - Brazzaville) -->
<item>lo-LA</item> <!-- Lao (Laos) -->
<item>lt-LT</item> <!-- Lithuanian (Lithuania) -->
- <item>lu-CD</item> <!-- Luba-Katanga (Congo (DRC)) -->
+ <item>lu-CD</item> <!-- Luba-Katanga (Congo - Kinshasa) -->
<item>luo-KE</item> <!-- Luo (Kenya) -->
<item>luy-KE</item> <!-- Luyia (Kenya) -->
<item>lv-LV</item> <!-- Latvian (Latvia) -->
@@ -418,11 +422,11 @@
<item>nyn-UG</item> <!-- Nyankole (Uganda) -->
<item>om-ET</item> <!-- Oromo (Ethiopia) -->
<item>om-KE</item> <!-- Oromo (Kenya) -->
- <item>or-IN</item> <!-- Oriya (India) -->
+ <item>or-IN</item> <!-- Odia (India) -->
<item>os-GE</item> <!-- Ossetic (Georgia) -->
<item>os-RU</item> <!-- Ossetic (Russia) -->
- <item>pa-Arab-PK</item> <!-- Punjabi (Arabic,Pakistan) -->
- <item>pa-Guru-IN</item> <!-- Punjabi (Gurmukhi,India) -->
+ <item>pa-Arab-PK</item> <!-- Punjabi (Arabic, Pakistan) -->
+ <item>pa-Guru-IN</item> <!-- Punjabi (Gurmukhi, India) -->
<item>pl-PL</item> <!-- Polish (Poland) -->
<item>ps-AF</item> <!-- Pashto (Afghanistan) -->
<item>pt-AO</item> <!-- Portuguese (Angola) -->
@@ -471,18 +475,18 @@
<item>sq-AL</item> <!-- Albanian (Albania) -->
<item>sq-MK</item> <!-- Albanian (Macedonia (FYROM)) -->
<item>sq-XK</item> <!-- Albanian (Kosovo) -->
- <item>sr-Cyrl-BA</item> <!-- Serbian (Cyrillic,Bosnia & Herzegovina) -->
- <item>sr-Cyrl-ME</item> <!-- Serbian (Cyrillic,Montenegro) -->
- <item>sr-Cyrl-RS</item> <!-- Serbian (Cyrillic,Serbia) -->
- <item>sr-Cyrl-XK</item> <!-- Serbian (Cyrillic,Kosovo) -->
- <item>sr-Latn-BA</item> <!-- Serbian (Latin,Bosnia & Herzegovina) -->
- <item>sr-Latn-ME</item> <!-- Serbian (Latin,Montenegro) -->
- <item>sr-Latn-RS</item> <!-- Serbian (Latin,Serbia) -->
- <item>sr-Latn-XK</item> <!-- Serbian (Latin,Kosovo) -->
+ <item>sr-Cyrl-BA</item> <!-- Serbian (Cyrillic, Bosnia & Herzegovina) -->
+ <item>sr-Cyrl-ME</item> <!-- Serbian (Cyrillic, Montenegro) -->
+ <item>sr-Cyrl-RS</item> <!-- Serbian (Cyrillic, Serbia) -->
+ <item>sr-Cyrl-XK</item> <!-- Serbian (Cyrillic, Kosovo) -->
+ <item>sr-Latn-BA</item> <!-- Serbian (Latin, Bosnia & Herzegovina) -->
+ <item>sr-Latn-ME</item> <!-- Serbian (Latin, Montenegro) -->
+ <item>sr-Latn-RS</item> <!-- Serbian (Latin, Serbia) -->
+ <item>sr-Latn-XK</item> <!-- Serbian (Latin, Kosovo) -->
<item>sv-AX</item> <!-- Swedish (Åland Islands) -->
<item>sv-FI</item> <!-- Swedish (Finland) -->
<item>sv-SE</item> <!-- Swedish (Sweden) -->
- <item>sw-CD</item> <!-- Swahili (Congo (DRC)) -->
+ <item>sw-CD</item> <!-- Swahili (Congo - Kinshasa) -->
<item>sw-KE</item> <!-- Swahili (Kenya) -->
<item>sw-TZ</item> <!-- Swahili (Tanzania) -->
<item>sw-UG</item> <!-- Swahili (Uganda) -->
@@ -502,12 +506,12 @@
<item>ug-CN</item> <!-- Uyghur (China) -->
<item>uk-UA</item> <!-- Ukrainian (Ukraine) -->
<item>ur-IN</item> <!-- Urdu (India) -->
- <item>ur-IN-u-nu-latn</item> <!-- Urdu (India,Western Digits) -->
+ <item>ur-IN-u-nu-latn</item> <!-- Urdu (India, Western Digits) -->
<item>ur-PK</item> <!-- Urdu (Pakistan) -->
- <item>ur-PK-u-nu-arabext</item> <!-- Urdu (Pakistan,Extended Arabic-Indic Digits) -->
- <item>uz-Arab-AF</item> <!-- Uzbek (Arabic,Afghanistan) -->
- <item>uz-Cyrl-UZ</item> <!-- Uzbek (Cyrillic,Uzbekistan) -->
- <item>uz-Latn-UZ</item> <!-- Uzbek (Latin,Uzbekistan) -->
+ <item>ur-PK-u-nu-arabext</item> <!-- Urdu (Pakistan, Extended Arabic-Indic Digits) -->
+ <item>uz-Arab-AF</item> <!-- Uzbek (Arabic, Afghanistan) -->
+ <item>uz-Cyrl-UZ</item> <!-- Uzbek (Cyrillic, Uzbekistan) -->
+ <item>uz-Latn-UZ</item> <!-- Uzbek (Latin, Uzbekistan) -->
<item>vi-VN</item> <!-- Vietnamese (Vietnam) -->
<item>vun-TZ</item> <!-- Vunjo (Tanzania) -->
<item>wae-CH</item> <!-- Walser (Switzerland) -->
@@ -515,15 +519,16 @@
<item>yav-CM</item> <!-- Yangben (Cameroon) -->
<item>yo-BJ</item> <!-- Yoruba (Benin) -->
<item>yo-NG</item> <!-- Yoruba (Nigeria) -->
- <item>yue-HK</item> <!-- Cantonese (Hong Kong) -->
+ <item>yue-Hans-CN</item> <!-- Cantonese (Simplified, China) -->
+ <item>yue-Hant-HK</item> <!-- Cantonese (Traditional, Hong Kong) -->
<item>zgh-MA</item> <!-- Standard Moroccan Tamazight (Morocco) -->
- <item>zh-Hans-CN</item> <!-- Chinese (Simplified Han,China) -->
- <item>zh-Hans-HK</item> <!-- Chinese (Simplified Han,Hong Kong) -->
- <item>zh-Hans-MO</item> <!-- Chinese (Simplified Han,Macau) -->
- <item>zh-Hans-SG</item> <!-- Chinese (Simplified Han,Singapore) -->
- <item>zh-Hant-HK</item> <!-- Chinese (Traditional Han,Hong Kong) -->
- <item>zh-Hant-MO</item> <!-- Chinese (Traditional Han,Macau) -->
- <item>zh-Hant-TW</item> <!-- Chinese (Traditional Han,Taiwan) -->
+ <item>zh-Hans-CN</item> <!-- Chinese (Simplified, China) -->
+ <item>zh-Hans-HK</item> <!-- Chinese (Simplified, Hong Kong) -->
+ <item>zh-Hans-MO</item> <!-- Chinese (Simplified, Macau) -->
+ <item>zh-Hans-SG</item> <!-- Chinese (Simplified, Singapore) -->
+ <item>zh-Hant-HK</item> <!-- Chinese (Traditional, Hong Kong) -->
+ <item>zh-Hant-MO</item> <!-- Chinese (Traditional, Macau) -->
+ <item>zh-Hant-TW</item> <!-- Chinese (Traditional, Taiwan) -->
<item>zu-ZA</item> <!-- Zulu (South Africa) -->
</string-array>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 86879c3..73dae08 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2919,6 +2919,7 @@
<public name="settingsSliceUri" />
<public name="shell" />
<public name="interactiveUiTimeout" />
+ <public name="importantForContentCapture" />
</public-group>
<public-group type="drawable" first-id="0x010800b4">
diff --git a/core/tests/coretests/src/android/app/usage/UsageStatsTest.java b/core/tests/coretests/src/android/app/usage/UsageStatsTest.java
index c6d077d..1f047f9e 100644
--- a/core/tests/coretests/src/android/app/usage/UsageStatsTest.java
+++ b/core/tests/coretests/src/android/app/usage/UsageStatsTest.java
@@ -16,8 +16,19 @@
package android.app.usage;
-import static com.google.common.truth.Truth.assertThat;
+import static android.app.usage.UsageEvents.Event.CONTINUE_PREVIOUS_DAY;
+import static android.app.usage.UsageEvents.Event.CONTINUING_FOREGROUND_SERVICE;
+import static android.app.usage.UsageEvents.Event.END_OF_DAY;
+import static android.app.usage.UsageEvents.Event.FOREGROUND_SERVICE_START;
+import static android.app.usage.UsageEvents.Event.FOREGROUND_SERVICE_STOP;
+import static android.app.usage.UsageEvents.Event.MOVE_TO_BACKGROUND;
+import static android.app.usage.UsageEvents.Event.MOVE_TO_FOREGROUND;
+import static android.app.usage.UsageEvents.Event.ROLLOVER_FOREGROUND_SERVICE;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+
+import android.os.Parcel;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
@@ -46,7 +57,7 @@
left.add(right);
- assertThat(left.getFirstTimeStamp()).isEqualTo(99999);
+ assertEquals(left.getFirstTimeStamp(), 99999);
}
@Test
@@ -58,7 +69,7 @@
left.add(right);
- assertThat(left.getLastTimeStamp()).isEqualTo(100001);
+ assertEquals(left.getLastTimeStamp(), 100001);
}
@Test
@@ -72,7 +83,7 @@
left.add(right);
- assertThat(left.getLastTimeUsed()).isEqualTo(200001);
+ assertEquals(left.getLastTimeUsed(), 200001);
}
@Test
@@ -86,7 +97,7 @@
left.add(right);
- assertThat(left.getLastTimeUsed()).isEqualTo(200000);
+ assertEquals(left.getLastTimeUsed(), 200000);
}
@Test
@@ -100,6 +111,373 @@
left.add(right);
- assertThat(left.getTotalTimeInForeground()).isEqualTo(11);
+ assertEquals(left.getTotalTimeInForeground(), 11);
+ }
+
+ @Test
+ public void testParcelable() {
+ left.mPackageName = "com.test";
+ left.mBeginTimeStamp = 100000;
+ left.mTotalTimeInForeground = 10;
+
+ left.mLastForegroundActivityEventMap.put("com.test.activity1", MOVE_TO_FOREGROUND);
+ left.mLastForegroundActivityEventMap.put("com.test.activity2", MOVE_TO_FOREGROUND);
+ left.mLastForegroundServiceEventMap.put("com.test.service1", FOREGROUND_SERVICE_START);
+ left.mLastForegroundServiceEventMap.put("com.test.service2", FOREGROUND_SERVICE_START);
+
+ Parcel p = Parcel.obtain();
+ left.writeToParcel(p, 0);
+ p.setDataPosition(0);
+ right = UsageStats.CREATOR.createFromParcel(p);
+ compareUsageStats(left, right);
+ }
+
+ @Test
+ public void testForegroundActivity() {
+ left.mPackageName = "com.test";
+ left.mBeginTimeStamp = 100000;
+
+ left.update("com.test.activity1", 200000, MOVE_TO_FOREGROUND);
+ assertEquals(left.mLastTimeUsed, 200000);
+ assertEquals(left.mLastForegroundActivityEventMap.get("com.test.activity1"),
+ new Integer(MOVE_TO_FOREGROUND));
+ assertEquals(left.mLaunchCount, 1);
+
+ left.update("com.test.activity1", 350000, MOVE_TO_BACKGROUND);
+ assertEquals(left.mLastTimeUsed, 350000);
+ assertFalse(left.mLastForegroundActivityEventMap.containsKey("com.test.activity1"));
+ assertEquals(left.mTotalTimeInForeground, 350000 - 200000);
+ }
+
+ @Test
+ public void testEvent_CONTINUE_PREVIOUS_DAY() {
+ left.mPackageName = "com.test";
+ left.mBeginTimeStamp = 100000;
+
+ left.update("com.test.activity1", 100000, CONTINUE_PREVIOUS_DAY);
+ assertEquals(left.mLastTimeUsed, 100000);
+ assertEquals(left.mLastForegroundActivityEventMap.get("com.test.activity1"),
+ new Integer(CONTINUE_PREVIOUS_DAY));
+ assertEquals(left.mLaunchCount, 0);
+
+ left.update("com.test.activity1", 350000, MOVE_TO_BACKGROUND);
+ assertEquals(left.mLastTimeUsed, 350000);
+ assertEquals(left.mLastForegroundActivityEventMap.get("com.test.activity1"), null);
+ assertEquals(left.mTotalTimeInForeground, 350000 - 100000);
+ }
+
+ @Test
+ public void testEvent_END_OF_DAY() {
+ left.mPackageName = "com.test";
+ left.mBeginTimeStamp = 100000;
+
+ left.update("com.test.activity1", 100000, CONTINUE_PREVIOUS_DAY);
+ assertEquals(left.mLastTimeUsed, 100000);
+ assertEquals(left.mLastForegroundActivityEventMap.get("com.test.activity1"),
+ new Integer(CONTINUE_PREVIOUS_DAY));
+ assertEquals(left.mLaunchCount, 0);
+
+ left.update(null, 350000, END_OF_DAY);
+ assertEquals(left.mLastTimeUsed, 350000);
+ assertEquals(left.mLastForegroundActivityEventMap.get("com.test.activity1"),
+ new Integer(END_OF_DAY));
+ assertEquals(left.mTotalTimeInForeground, 350000 - 100000);
+ }
+
+ @Test
+ public void testForegroundActivityEventSequence() {
+ left.mPackageName = "com.test";
+ left.mBeginTimeStamp = 100000;
+
+ left.update("com.test.activity1", 100000, CONTINUE_PREVIOUS_DAY);
+ assertEquals(left.mLastTimeUsed, 100000);
+ assertEquals(left.mLastForegroundActivityEventMap.get("com.test.activity1"),
+ new Integer(CONTINUE_PREVIOUS_DAY));
+ assertEquals(left.mLaunchCount, 0);
+
+ left.update("com.test.activity1", 350000, MOVE_TO_BACKGROUND);
+ assertEquals(left.mLastTimeUsed, 350000);
+ assertEquals(left.mLastForegroundActivityEventMap.get("com.test.activity1"), null);
+ assertEquals(left.mTotalTimeInForeground, 250000 /*350000 - 100000*/);
+
+ left.update("com.test.activity1", 450000, MOVE_TO_FOREGROUND);
+ assertEquals(left.mLastTimeUsed, 450000);
+ assertEquals(left.mLastForegroundActivityEventMap.get("com.test.activity1"),
+ new Integer(MOVE_TO_FOREGROUND));
+ assertEquals(left.mTotalTimeInForeground, 250000);
+
+ left.update("com.test.activity1", 500000, MOVE_TO_BACKGROUND);
+ assertEquals(left.mLastTimeUsed, 500000);
+ assertEquals(left.mLastForegroundActivityEventMap.get("com.test.activity1"), null);
+ assertEquals(left.mTotalTimeInForeground, 250000 + 50000 /*500000 - 450000*/);
+ }
+
+ @Test
+ public void testForegroundActivityEventOutOfSequence() {
+ left.mPackageName = "com.test";
+ left.mBeginTimeStamp = 100000;
+
+ left.update("com.test.activity1", 100000, CONTINUE_PREVIOUS_DAY);
+ assertEquals(left.mLastTimeUsed, 100000);
+ assertEquals(left.mLastForegroundActivityEventMap.get("com.test.activity1"),
+ new Integer(CONTINUE_PREVIOUS_DAY));
+ assertEquals(left.mLaunchCount, 0);
+
+ left.update("com.test.activity1", 150000, MOVE_TO_FOREGROUND);
+ assertEquals(left.mLastTimeUsed, 150000);
+ assertEquals(left.mLastForegroundActivityEventMap.get("com.test.activity1"),
+ new Integer(MOVE_TO_FOREGROUND));
+ assertEquals(left.mLaunchCount, 1);
+ assertEquals(left.mTotalTimeInForeground, 50000 /*150000 - 100000*/);
+
+ left.update("com.test.activity1", 200000, MOVE_TO_FOREGROUND);
+ assertEquals(left.mLastTimeUsed, 200000);
+ assertEquals(left.mLastForegroundActivityEventMap.get("com.test.activity1"),
+ new Integer(MOVE_TO_FOREGROUND));
+ assertEquals(left.mLaunchCount, 2);
+ assertEquals(left.mTotalTimeInForeground, 100000);
+
+ left.update("com.test.activity1", 250000, MOVE_TO_BACKGROUND);
+ assertEquals(left.mLastTimeUsed, 250000);
+ assertEquals(left.mLastForegroundActivityEventMap.get("com.test.activity1"), null);
+ assertEquals(left.mTotalTimeInForeground, 150000);
+
+ left.update("com.test.activity1", 300000, MOVE_TO_BACKGROUND);
+ assertEquals(left.mLastTimeUsed, 250000);
+ assertEquals(left.mLastForegroundActivityEventMap.get("com.test.activity1"), null);
+ assertEquals(left.mTotalTimeInForeground, 150000);
+
+ left.update("com.test.activity1", 350000, MOVE_TO_FOREGROUND);
+ assertEquals(left.mLastTimeUsed, 350000);
+ assertEquals(left.mLastForegroundActivityEventMap.get("com.test.activity1"),
+ new Integer(MOVE_TO_FOREGROUND));
+ assertEquals(left.mTotalTimeInForeground, 150000);
+
+ left.update("com.test.activity1", 400000, END_OF_DAY);
+ assertEquals(left.mLastTimeUsed, 400000);
+ assertEquals(left.mLastForegroundActivityEventMap.get("com.test.activity1"),
+ new Integer(END_OF_DAY));
+ assertEquals(left.mTotalTimeInForeground, 200000);
+ }
+
+ @Test
+ public void testTwoActivityEventSequence() {
+ left.mPackageName = "com.test";
+ left.mBeginTimeStamp = 100000;
+
+ left.update("com.test.activity1", 100000, CONTINUE_PREVIOUS_DAY);
+ left.update("com.test.activity2", 100000, CONTINUE_PREVIOUS_DAY);
+ assertEquals(left.mLastTimeUsed, 100000);
+ assertEquals(left.mLastForegroundActivityEventMap.get("com.test.activity1"),
+ new Integer(CONTINUE_PREVIOUS_DAY));
+ assertEquals(left.mLastForegroundActivityEventMap.get("com.test.activity2"),
+ new Integer(CONTINUE_PREVIOUS_DAY));
+ assertEquals(left.mLaunchCount, 0);
+
+ left.update("com.test.activity1", 350000, MOVE_TO_BACKGROUND);
+ assertEquals(left.mLastTimeUsed, 350000);
+ assertEquals(left.mLastForegroundActivityEventMap.get("com.test.activity1"), null);
+ assertEquals(left.mTotalTimeInForeground, 250000 /*350000 - 100000*/);
+
+ left.update("com.test.activity2", 450000, MOVE_TO_BACKGROUND);
+ assertEquals(left.mLastTimeUsed, 450000);
+ assertEquals(left.mLastForegroundActivityEventMap.get("com.test.activity2"), null);
+ assertEquals(left.mTotalTimeInForeground, 250000 + 100000 /*450000 - 350000*/);
+
+ left.update(null, 500000, END_OF_DAY);
+ assertEquals(left.mLastTimeUsed, 450000);
+ assertEquals(left.mTotalTimeInForeground, 350000);
+ }
+
+ @Test
+ public void testForegroundService() {
+ left.mPackageName = "com.test";
+ left.mBeginTimeStamp = 100000;
+
+ left.update("com.test.service1", 200000, FOREGROUND_SERVICE_START);
+ assertEquals(left.mLastTimeForegroundServiceUsed, 200000);
+ assertEquals(left.mLastForegroundServiceEventMap.get("com.test.service1"),
+ new Integer(FOREGROUND_SERVICE_START));
+ assertEquals(left.mLaunchCount, 0);
+
+ left.update("com.test.service1", 350000, FOREGROUND_SERVICE_STOP);
+ assertEquals(left.mLastTimeForegroundServiceUsed, 350000);
+ assertEquals(left.mLastForegroundServiceEventMap.get("com.test.service1"), null);
+ assertEquals(left.mTotalTimeForegroundServiceUsed, 350000 - 200000);
+ }
+
+ @Test
+ public void testEvent_CONTINUING_FOREGROUND_SERVICE() {
+ left.mPackageName = "com.test";
+ left.mBeginTimeStamp = 100000;
+
+ left.update("com.test.service1", 100000, CONTINUING_FOREGROUND_SERVICE);
+ assertEquals(left.mLastTimeForegroundServiceUsed, 100000);
+ assertEquals(left.mLastForegroundServiceEventMap.get("com.test.service1"),
+ new Integer(CONTINUING_FOREGROUND_SERVICE));
+ assertEquals(left.mLaunchCount, 0);
+
+ left.update("com.test.service1", 350000, FOREGROUND_SERVICE_STOP);
+ assertEquals(left.mLastTimeForegroundServiceUsed, 350000);
+ assertEquals(left.mLastForegroundServiceEventMap.get("com.test.service1"), null);
+ assertEquals(left.mTotalTimeForegroundServiceUsed, 350000 - 100000);
+ }
+
+ @Test
+ public void testEvent_ROLLOVER_FOREGROUND_SERVICE() {
+ left.mPackageName = "com.test";
+ left.mBeginTimeStamp = 100000;
+
+ left.update("com.test.service1", 100000,
+ CONTINUING_FOREGROUND_SERVICE);
+ assertEquals(left.mLastTimeForegroundServiceUsed, 100000);
+ assertEquals(left.mLastForegroundServiceEventMap.get("com.test.service1"),
+ new Integer(CONTINUING_FOREGROUND_SERVICE));
+ assertEquals(left.mLaunchCount, 0);
+
+ left.update(null, 350000, ROLLOVER_FOREGROUND_SERVICE);
+ assertEquals(left.mLastTimeForegroundServiceUsed, 350000);
+ assertEquals(left.mLastForegroundServiceEventMap.get("com.test.service1"),
+ new Integer(ROLLOVER_FOREGROUND_SERVICE));
+ assertEquals(left.mTotalTimeForegroundServiceUsed, 350000 - 100000);
+ }
+
+ @Test
+ public void testForegroundServiceEventSequence() {
+ left.mPackageName = "com.test";
+ left.mBeginTimeStamp = 100000;
+
+ left.update("com.test.service1", 100000,
+ CONTINUING_FOREGROUND_SERVICE);
+ assertEquals(left.mLastTimeForegroundServiceUsed, 100000);
+ assertEquals(left.mLastForegroundServiceEventMap.get("com.test.service1"),
+ new Integer(CONTINUING_FOREGROUND_SERVICE));
+ assertEquals(left.mLaunchCount, 0);
+
+ left.update("com.test.service1", 350000, FOREGROUND_SERVICE_STOP);
+ assertEquals(left.mLastTimeForegroundServiceUsed, 350000);
+ assertEquals(left.mLastForegroundServiceEventMap.get("com.test.service1"), null);
+ assertEquals(left.mTotalTimeForegroundServiceUsed, 250000 /*350000 - 100000*/);
+
+ left.update("com.test.service1", 450000, FOREGROUND_SERVICE_START);
+ assertEquals(left.mLastTimeForegroundServiceUsed, 450000);
+ assertEquals(left.mLastForegroundServiceEventMap.get("com.test.service1"),
+ new Integer(FOREGROUND_SERVICE_START));
+ assertEquals(left.mTotalTimeForegroundServiceUsed, 250000);
+
+ left.update("com.test.service1", 500000, FOREGROUND_SERVICE_STOP);
+ assertEquals(left.mLastTimeForegroundServiceUsed, 500000);
+ assertEquals(left.mLastForegroundServiceEventMap.get("com.test.service1"), null);
+ assertEquals(left.mTotalTimeForegroundServiceUsed, 250000 + 50000 /*500000 - 450000*/);
+ }
+
+ @Test
+ public void testTwoServiceEventSequence() {
+ left.mPackageName = "com.test";
+ left.mBeginTimeStamp = 100000;
+
+ left.update("com.test.service1", 100000,
+ CONTINUING_FOREGROUND_SERVICE);
+ left.update("com.test.service2", 100000,
+ CONTINUING_FOREGROUND_SERVICE);
+ assertEquals(left.mLastTimeForegroundServiceUsed, 100000);
+ assertEquals(left.mLastForegroundServiceEventMap.get("com.test.service1"),
+ new Integer(CONTINUING_FOREGROUND_SERVICE));
+ assertEquals(left.mLastForegroundServiceEventMap.get("com.test.service2"),
+ new Integer(CONTINUING_FOREGROUND_SERVICE));
+ assertEquals(left.mLaunchCount, 0);
+
+ left.update("com.test.service1", 350000, FOREGROUND_SERVICE_STOP);
+ assertEquals(left.mLastTimeForegroundServiceUsed, 350000);
+ assertEquals(left.mLastForegroundServiceEventMap.get("com.test.service1"), null);
+ assertEquals(left.mTotalTimeForegroundServiceUsed, 250000 /*350000 - 100000*/);
+
+ left.update("com.test.service2", 450000, FOREGROUND_SERVICE_STOP);
+ assertEquals(left.mLastTimeForegroundServiceUsed, 450000);
+ assertEquals(left.mLastForegroundServiceEventMap.get("com.test.service2"), null);
+ assertEquals(left.mTotalTimeForegroundServiceUsed, 250000 + 100000 /*450000 - 350000*/);
+
+ left.update(null, 500000, ROLLOVER_FOREGROUND_SERVICE);
+ assertEquals(left.mLastTimeForegroundServiceUsed, 450000);
+ assertEquals(left.mTotalTimeForegroundServiceUsed, 350000);
+ }
+
+ @Test
+ public void testTwoActivityAndTwoServiceEventSequence() {
+ left.mPackageName = "com.test";
+ left.mBeginTimeStamp = 100000;
+
+ left.update("com.test.activity1", 100000, CONTINUE_PREVIOUS_DAY);
+ left.update("com.test.activity2", 100000, CONTINUE_PREVIOUS_DAY);
+ left.update("com.test.service1", 100000,
+ CONTINUING_FOREGROUND_SERVICE);
+ left.update("com.test.service2", 100000,
+ CONTINUING_FOREGROUND_SERVICE);
+ assertEquals(left.mLastTimeUsed, 100000);
+ assertEquals(left.mLastTimeForegroundServiceUsed, 100000);
+ assertEquals(left.mLastForegroundActivityEventMap.get("com.test.activity1"),
+ new Integer(CONTINUE_PREVIOUS_DAY));
+ assertEquals(left.mLastForegroundActivityEventMap.get("com.test.activity2"),
+ new Integer(CONTINUE_PREVIOUS_DAY));
+ assertEquals(left.mLastForegroundServiceEventMap.get("com.test.service1"),
+ new Integer(CONTINUING_FOREGROUND_SERVICE));
+ assertEquals(left.mLastForegroundServiceEventMap.get("com.test.service2"),
+ new Integer(CONTINUING_FOREGROUND_SERVICE));
+ assertEquals(left.mLaunchCount, 0);
+
+ left.update("com.test.activity1", 350000, MOVE_TO_BACKGROUND);
+ assertEquals(left.mLastTimeUsed, 350000);
+ assertEquals(left.mLastForegroundActivityEventMap.get("com.test.activity1"), null);
+ assertEquals(left.mTotalTimeInForeground, 250000 /*350000 - 100000*/);
+
+ left.update("com.test.service1", 400000, FOREGROUND_SERVICE_STOP);
+ assertEquals(left.mLastTimeForegroundServiceUsed, 400000);
+ assertEquals(left.mLastForegroundServiceEventMap.get("com.test.service1"), null);
+ assertEquals(left.mTotalTimeForegroundServiceUsed, 300000 /*400000 - 100000*/);
+
+ left.update("com.test.activity2", 450000, MOVE_TO_BACKGROUND);
+ assertEquals(left.mLastTimeUsed, 450000);
+ assertEquals(left.mLastForegroundActivityEventMap.get("com.test.activity2"), null);
+ assertEquals(left.mTotalTimeInForeground, 250000 + 100000 /*450000 - 350000*/);
+
+ left.update("com.test.service2", 500000, FOREGROUND_SERVICE_STOP);
+ assertEquals(left.mLastTimeForegroundServiceUsed, 500000);
+ assertEquals(left.mLastForegroundServiceEventMap.get("com.test.service2"), null);
+ assertEquals(left.mTotalTimeForegroundServiceUsed, 300000 + 100000 /*500000 - 400000*/);
+
+
+ left.update(null, 550000, END_OF_DAY);
+ assertEquals(left.mLastTimeUsed, 450000);
+ assertEquals(left.mTotalTimeInForeground, 350000);
+ left.update(null, 550000, ROLLOVER_FOREGROUND_SERVICE);
+ assertEquals(left.mLastTimeForegroundServiceUsed, 500000);
+ assertEquals(left.mTotalTimeForegroundServiceUsed, 400000);
+ }
+
+ void compareUsageStats(UsageStats us1, UsageStats us2) {
+ assertEquals(us1.mPackageName, us2.mPackageName);
+ assertEquals(us1.mBeginTimeStamp, us2.mBeginTimeStamp);
+ assertEquals(us1.mLastTimeUsed, us2.mLastTimeUsed);
+ assertEquals(us1.mLastTimeForegroundServiceUsed, us2.mLastTimeForegroundServiceUsed);
+ assertEquals(us1.mTotalTimeInForeground, us2.mTotalTimeInForeground);
+ assertEquals(us1.mTotalTimeForegroundServiceUsed, us2.mTotalTimeForegroundServiceUsed);
+ assertEquals(us1.mAppLaunchCount, us2.mAppLaunchCount);
+ assertEquals(us1.mLastForegroundActivityEventMap.size(),
+ us2.mLastForegroundActivityEventMap.size());
+ for (int i = 0; i < us1.mLastForegroundActivityEventMap.size(); i++) {
+ assertEquals(us1.mLastForegroundActivityEventMap.keyAt(i),
+ us2.mLastForegroundActivityEventMap.keyAt(i));
+ assertEquals(us1.mLastForegroundActivityEventMap.valueAt(i),
+ us2.mLastForegroundActivityEventMap.valueAt(i));
+ }
+ assertEquals(us1.mLastForegroundServiceEventMap.size(),
+ us2.mLastForegroundServiceEventMap.size());
+ for (int i = 0; i < us1.mLastForegroundServiceEventMap.size(); i++) {
+ assertEquals(us1.mLastForegroundServiceEventMap.keyAt(i),
+ us2.mLastForegroundServiceEventMap.keyAt(i));
+ assertEquals(us1.mLastForegroundServiceEventMap.valueAt(i),
+ us2.mLastForegroundServiceEventMap.valueAt(i));
+ }
+ assertEquals(us1.mChooserCounts, us2.mChooserCounts);
}
}
diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
index 78d45d0..727f399 100644
--- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
@@ -261,8 +261,7 @@
Settings.Global.HDMI_SYSTEM_AUDIO_CONTROL_ENABLED,
Settings.Global.HEADS_UP_NOTIFICATIONS_ENABLED,
Settings.Global.HIDDEN_API_ACCESS_LOG_SAMPLING_RATE,
- Settings.Global.HIDDEN_API_POLICY_P_APPS,
- Settings.Global.HIDDEN_API_POLICY_PRE_P_APPS,
+ Settings.Global.HIDDEN_API_POLICY,
Settings.Global.HIDE_ERROR_DIALOGS,
Settings.Global.HTTP_PROXY,
HYBRID_SYSUI_BATTERY_WARNING_FLAGS,
diff --git a/core/tests/coretests/src/android/view/textclassifier/ModelFileManagerTest.java b/core/tests/coretests/src/android/view/textclassifier/ModelFileManagerTest.java
index 51e5aec..f2efabf 100644
--- a/core/tests/coretests/src/android/view/textclassifier/ModelFileManagerTest.java
+++ b/core/tests/coretests/src/android/view/textclassifier/ModelFileManagerTest.java
@@ -43,7 +43,7 @@
@SmallTest
@RunWith(AndroidJUnit4.class)
public class ModelFileManagerTest {
-
+ private static final Locale DEFAULT_LOCALE = Locale.forLanguageTag("en-US");
@Mock
private Supplier<List<ModelFileManager.ModelFile>> mModelFileSupplier;
private ModelFileManager.ModelFileSupplierImpl mModelFileSupplierImpl;
@@ -71,6 +71,8 @@
mRootTestDir.mkdirs();
mFactoryModelDir.mkdirs();
+
+ Locale.setDefault(DEFAULT_LOCALE);
}
@After
@@ -134,7 +136,7 @@
}
@Test
- public void findBestModel_useIndependentWhenNoLanguageModelMatch() {
+ public void findBestModel_noMatchedLanguageModel() {
Locale locale = Locale.forLanguageTag("ja");
ModelFileManager.ModelFile languageIndependentModelFile =
new ModelFileManager.ModelFile(
@@ -157,6 +159,28 @@
}
@Test
+ public void findBestModel_noMatchedLanguageModel_defaultLocaleModelExists() {
+ ModelFileManager.ModelFile languageIndependentModelFile =
+ new ModelFileManager.ModelFile(
+ new File("/path/a"), 1,
+ Collections.emptyList(), true);
+
+ ModelFileManager.ModelFile languageDependentModelFile =
+ new ModelFileManager.ModelFile(
+ new File("/path/b"), 1,
+ Collections.singletonList(DEFAULT_LOCALE), false);
+
+ when(mModelFileSupplier.get())
+ .thenReturn(
+ Arrays.asList(languageIndependentModelFile, languageDependentModelFile));
+
+ ModelFileManager.ModelFile bestModelFile =
+ mModelFileManager.findBestModelFile(
+ LocaleList.forLanguageTags("zh-hk"));
+ assertThat(bestModelFile).isEqualTo(languageIndependentModelFile);
+ }
+
+ @Test
public void findBestModel_languageIsMoreImportantThanVersion() {
ModelFileManager.ModelFile matchButOlderModel =
new ModelFileManager.ModelFile(
diff --git a/core/tests/coretests/src/android/view/textclassifier/TextClassificationManagerTest.java b/core/tests/coretests/src/android/view/textclassifier/TextClassificationManagerTest.java
index 8646c68..3a33d57 100644
--- a/core/tests/coretests/src/android/view/textclassifier/TextClassificationManagerTest.java
+++ b/core/tests/coretests/src/android/view/textclassifier/TextClassificationManagerTest.java
@@ -19,6 +19,7 @@
import static org.hamcrest.CoreMatchers.not;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNotSame;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
@@ -325,6 +326,33 @@
}
@Test
+ public void testSuggestConversationActions_textReplyOnly_maxThree() {
+ if (isTextClassifierDisabled()) return;
+ ConversationActions.Message message =
+ new ConversationActions.Message.Builder().setText("Hello").build();
+ ConversationActions.TypeConfig typeConfig =
+ new ConversationActions.TypeConfig.Builder().includeTypesFromTextClassifier(false)
+ .setIncludedTypes(
+ Collections.singletonList(ConversationActions.TYPE_TEXT_REPLY))
+ .build();
+ ConversationActions.Request request =
+ new ConversationActions.Request.Builder(Collections.singletonList(message))
+ .setMaxSuggestions(1)
+ .setTypeConfig(typeConfig)
+ .build();
+
+ ConversationActions conversationActions = mClassifier.suggestConversationActions(request);
+ assertTrue(conversationActions.getConversationActions().size() <= 1);
+ for (ConversationActions.ConversationAction conversationAction :
+ conversationActions.getConversationActions()) {
+ assertEquals(conversationAction.getType(), ConversationActions.TYPE_TEXT_REPLY);
+ assertNotNull(conversationAction.getTextReply());
+ assertTrue(conversationAction.getConfidenceScore() > 0);
+ assertTrue(conversationAction.getConfidenceScore() <= 1);
+ }
+ }
+
+ @Test
public void testSetTextClassifier() {
TextClassifier classifier = mock(TextClassifier.class);
mTcm.setTextClassifier(classifier);
diff --git a/core/tests/coretests/src/android/view/textclassifier/TextLinksTest.java b/core/tests/coretests/src/android/view/textclassifier/TextLinksTest.java
index fff723f..f6ec0e6 100644
--- a/core/tests/coretests/src/android/view/textclassifier/TextLinksTest.java
+++ b/core/tests/coretests/src/android/view/textclassifier/TextLinksTest.java
@@ -18,6 +18,7 @@
import static org.junit.Assert.assertEquals;
+import android.os.Bundle;
import android.os.LocaleList;
import android.os.Parcel;
import android.support.test.filters.SmallTest;
@@ -38,6 +39,12 @@
@SmallTest
@RunWith(AndroidJUnit4.class)
public class TextLinksTest {
+ private static final String BUNDLE_KEY = "key";
+ private static final String BUNDLE_VALUE = "value";
+ private static final Bundle BUNDLE = new Bundle();
+ static {
+ BUNDLE.putString(BUNDLE_KEY, BUNDLE_VALUE);
+ }
private Map<String, Float> getEntityScores(float address, float phone, float other) {
final Map<String, Float> result = new ArrayMap<>();
@@ -59,6 +66,7 @@
final TextLinks reference = new TextLinks.Builder(fullText)
.addLink(0, 4, getEntityScores(0.f, 0.f, 1.f))
.addLink(5, 12, getEntityScores(.8f, .1f, .5f))
+ .setExtras(BUNDLE)
.build();
// Parcel and unparcel.
@@ -83,6 +91,7 @@
assertEquals(.8f, resultList.get(1).getConfidenceScore(TextClassifier.TYPE_ADDRESS), 1e-7f);
assertEquals(.5f, resultList.get(1).getConfidenceScore(TextClassifier.TYPE_OTHER), 1e-7f);
assertEquals(.1f, resultList.get(1).getConfidenceScore(TextClassifier.TYPE_PHONE), 1e-7f);
+ assertEquals(BUNDLE_VALUE, result.getExtras().getString(BUNDLE_KEY));
}
@Test
@@ -94,6 +103,7 @@
final TextLinks.Request reference = new TextLinks.Request.Builder("text")
.setDefaultLocales(new LocaleList(Locale.US, Locale.GERMANY))
.setEntityConfig(entityConfig)
+ .setExtras(BUNDLE)
.build();
// Parcel and unparcel.
@@ -108,5 +118,6 @@
result.getEntityConfig().getHints().toArray());
assertEquals(new HashSet<String>(Arrays.asList("a", "c")),
result.getEntityConfig().resolveEntityListModifications(Collections.emptyList()));
+ assertEquals(BUNDLE_VALUE, result.getExtras().getString(BUNDLE_KEY));
}
}
diff --git a/core/tests/coretests/src/android/view/textclassifier/TextSelectionTest.java b/core/tests/coretests/src/android/view/textclassifier/TextSelectionTest.java
index 4855dad..7ea5108b 100644
--- a/core/tests/coretests/src/android/view/textclassifier/TextSelectionTest.java
+++ b/core/tests/coretests/src/android/view/textclassifier/TextSelectionTest.java
@@ -18,6 +18,7 @@
import static org.junit.Assert.assertEquals;
+import android.os.Bundle;
import android.os.LocaleList;
import android.os.Parcel;
import android.support.test.filters.SmallTest;
@@ -31,6 +32,12 @@
@SmallTest
@RunWith(AndroidJUnit4.class)
public class TextSelectionTest {
+ private static final String BUNDLE_KEY = "key";
+ private static final String BUNDLE_VALUE = "value";
+ private static final Bundle BUNDLE = new Bundle();
+ static {
+ BUNDLE.putString(BUNDLE_KEY, BUNDLE_VALUE);
+ }
@Test
public void testParcel() {
@@ -42,6 +49,7 @@
.setEntityType(TextClassifier.TYPE_PHONE, 0.7f)
.setEntityType(TextClassifier.TYPE_URL, 0.1f)
.setId(id)
+ .setExtras(BUNDLE)
.build();
// Parcel and unparcel
@@ -61,6 +69,7 @@
assertEquals(0.7f, result.getConfidenceScore(TextClassifier.TYPE_PHONE), 1e-7f);
assertEquals(0.3f, result.getConfidenceScore(TextClassifier.TYPE_ADDRESS), 1e-7f);
assertEquals(0.1f, result.getConfidenceScore(TextClassifier.TYPE_URL), 1e-7f);
+ assertEquals(BUNDLE_VALUE, result.getExtras().getString(BUNDLE_KEY));
}
@Test
@@ -69,6 +78,7 @@
final TextSelection.Request reference =
new TextSelection.Request.Builder(text, 0, text.length())
.setDefaultLocales(new LocaleList(Locale.US, Locale.GERMANY))
+ .setExtras(BUNDLE)
.build();
// Parcel and unparcel.
@@ -81,5 +91,6 @@
assertEquals(0, result.getStartIndex());
assertEquals(text.length(), result.getEndIndex());
assertEquals("en-US,de-DE", result.getDefaultLocales().toLanguageTags());
+ assertEquals(BUNDLE_VALUE, result.getExtras().getString(BUNDLE_KEY));
}
}
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java
index b798042..3cfc644 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java
@@ -38,6 +38,7 @@
BatteryStatsUidTest.class,
BatteryStatsUserLifecycleTests.class,
KernelCpuProcReaderTest.class,
+ KernelCpuProcStringReaderTest.class,
KernelMemoryBandwidthStatsTest.class,
KernelSingleUidTimeReaderTest.class,
KernelUidCpuFreqTimeReaderTest.class,
diff --git a/core/tests/coretests/src/com/android/internal/os/KernelCpuProcStringReaderTest.java b/core/tests/coretests/src/com/android/internal/os/KernelCpuProcStringReaderTest.java
new file mode 100644
index 0000000..dae9eb5
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/os/KernelCpuProcStringReaderTest.java
@@ -0,0 +1,323 @@
+/*
+ * 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.internal.os;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import android.content.Context;
+import android.os.FileUtils;
+import android.os.SystemClock;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.nio.file.Files;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Random;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+import java.util.stream.IntStream;
+
+/**
+ * Test class for {@link KernelCpuProcStringReader}.
+ *
+ * $ atest FrameworksCoreTests:com.android.internal.os.KernelCpuProcStringReaderTest
+ */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class KernelCpuProcStringReaderTest {
+ private File mRoot;
+ private File mTestDir;
+ private File mTestFile;
+ private Random mRand = new Random(12345);
+ private KernelCpuProcStringReader mReader;
+
+ private Context getContext() {
+ return InstrumentationRegistry.getContext();
+ }
+
+ @Before
+ public void setUp() {
+ mTestDir = getContext().getDir("test", Context.MODE_PRIVATE);
+ mRoot = getContext().getFilesDir();
+ mTestFile = new File(mTestDir, "test.file");
+ mReader = new KernelCpuProcStringReader(mTestFile.getAbsolutePath());
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ FileUtils.deleteContents(mTestDir);
+ FileUtils.deleteContents(mRoot);
+ }
+
+
+ /**
+ * Tests that reading will return null if the file does not exist.
+ */
+ @Test
+ public void testReadInvalidFile() throws Exception {
+ assertEquals(null, mReader.open());
+ }
+
+ /**
+ * Tests that reading will always return null after 5 failures.
+ */
+ @Test
+ public void testReadErrorsLimit() throws Exception {
+ for (int i = 0; i < 3; i++) {
+ try (KernelCpuProcStringReader.ProcFileIterator iter = mReader.open()) {
+ assertNull(iter);
+ }
+ SystemClock.sleep(50);
+ }
+ final String data = "018n9x134yrm9sry01298yMF1X980Ym908u98weruwe983^(*)0N)&tu09281my\n";
+ try (BufferedWriter w = Files.newBufferedWriter(mTestFile.toPath())) {
+ w.write(data);
+ }
+ try (KernelCpuProcStringReader.ProcFileIterator iter = mReader.open()) {
+ assertEquals(data.length(), iter.size());
+ assertEquals(data, iter.nextLine().toString() + '\n');
+ }
+ assertTrue(mTestFile.delete());
+ for (int i = 0; i < 3; i++) {
+ try (KernelCpuProcStringReader.ProcFileIterator iter = mReader.open(true)) {
+ assertNull(iter);
+ }
+ SystemClock.sleep(50);
+ }
+ try (BufferedWriter w = Files.newBufferedWriter(mTestFile.toPath())) {
+ w.write(data);
+ }
+ try (KernelCpuProcStringReader.ProcFileIterator iter = mReader.open(true)) {
+ assertNull(iter);
+ }
+ }
+
+ /** Tests nextLine functionality. */
+ @Test
+ public void testReadLine() throws Exception {
+ final String data = "10103: 0 0 0 1 5 3 1 2 0 0 3 0 0 0 0 2 2 330 0 0 0 0 1 0 0 0 0 0 0 0"
+ + " 0 0 0 0 0 0 0 0 0 0 0 13\n"
+ + "50083: 0 0 0 29 0 13 0 4 5 0 0 0 0 0 1 0 0 15 0 0 0 0 0 0 1 0 0 0 0 1 0 1 7 0 "
+ + "0 1 1 1 0 2 0 221\n"
+ + "50227: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 196 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0"
+ + " 0 2 0 0 0 2 721\n"
+ + "10158: 0 0 0 0 19 3 9 1 0 7 4 3 3 3 1 3 10 893 2 0 3 0 0 0 0 0 0 0 0 1 0 2 0 0"
+ + " 1 2 10 0 0 0 1 58\n"
+ + "50138: 0 0 0 8 7 0 0 0 0 0 0 0 0 0 0 0 0 322 0 0 0 3 0 5 0 0 3 0 0 0 0 1 0 0 0"
+ + " 0 0 2 0 0 7 707\n";
+ try (BufferedWriter w = Files.newBufferedWriter(mTestFile.toPath())) {
+ w.write(data);
+ }
+ try (KernelCpuProcStringReader.ProcFileIterator iter = mReader.open()) {
+ assertEquals(
+ "10103: 0 0 0 1 5 3 1 2 0 0 3 0 0 0 0 2 2 330 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0"
+ + " 0 0 0 0 0 0 0 13",
+ iter.nextLine().toString());
+ assertEquals(
+ "50083: 0 0 0 29 0 13 0 4 5 0 0 0 0 0 1 0 0 15 0 0 0 0 0 0 1 0 0 0 0 1 0 1 7 "
+ + "0 0 1 1 1 0 2 0 221",
+ iter.nextLine().toString());
+ long[] actual = new long[43];
+ iter.nextLineAsArray(actual);
+ assertArrayEquals(
+ new long[]{50227, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 196, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 2, 721},
+ actual);
+ assertEquals(
+ "10158: 0 0 0 0 19 3 9 1 0 7 4 3 3 3 1 3 10 893 2 0 3 0 0 0 0 0 0 0 0 1 0 2 0"
+ + " 0 1 2 10 0 0 0 1 58",
+ iter.nextLine().toString());
+ assertEquals(
+ "50138: 0 0 0 8 7 0 0 0 0 0 0 0 0 0 0 0 0 322 0 0 0 3 0 5 0 0 3 0 0 0 0 1 0 0"
+ + " 0 0 0 2 0 0 7 707",
+ iter.nextLine().toString());
+ }
+ }
+
+ /** Stress tests read functionality. */
+ @Test
+ public void testMultipleRead() throws Exception {
+ for (int i = 0; i < 100; i++) {
+ final String data = getTestString(600, 150);
+ try (BufferedWriter w = Files.newBufferedWriter(mTestFile.toPath())) {
+ w.write(data);
+ }
+ String[] lines = data.split("\n");
+ try (KernelCpuProcStringReader.ProcFileIterator iter = mReader.open(true)) {
+ for (String line : lines) {
+ assertEquals(line, iter.nextLine().toString());
+ }
+ }
+ assertTrue(mTestFile.delete());
+ }
+ }
+
+ /** Tests nextLineToArray functionality. */
+ @Test
+ public void testReadLineToArray() throws Exception {
+ final long[][] data = getTestArray(800, 50);
+ try (BufferedWriter w = Files.newBufferedWriter(mTestFile.toPath())) {
+ w.write(arrayToString(data));
+ }
+ long[] actual = new long[50];
+ try (KernelCpuProcStringReader.ProcFileIterator iter = mReader.open()) {
+ for (long[] expected : data) {
+ assertEquals(50, iter.nextLineAsArray(actual));
+ assertArrayEquals(expected, actual);
+ }
+ }
+ }
+
+ /**
+ * Tests that reading a file over the limit (1MB) will return null.
+ */
+ @Test
+ public void testReadOverLimit() throws Exception {
+ final String data = getTestString(1, 1024 * 1024 + 1);
+ try (BufferedWriter w = Files.newBufferedWriter(mTestFile.toPath())) {
+ w.write(data);
+ }
+ try (KernelCpuProcStringReader.ProcFileIterator iter = mReader.open()) {
+ assertNull(iter);
+ }
+ }
+
+ /**
+ * Tests concurrent reading with 5 threads.
+ */
+ @Test
+ public void testConcurrent() throws Exception {
+ final String data = getTestString(200, 150);
+ final String data1 = getTestString(180, 120);
+ final String[] lines = data.split("\n");
+ final String[] lines1 = data1.split("\n");
+ final List<Throwable> errs = Collections.synchronizedList(new ArrayList<>());
+ try (BufferedWriter w = Files.newBufferedWriter(mTestFile.toPath())) {
+ w.write(data);
+ }
+ // An additional thread for modifying the file content.
+ ScheduledExecutorService threadPool = Executors.newScheduledThreadPool(11);
+ final CountDownLatch ready = new CountDownLatch(10);
+ final CountDownLatch start = new CountDownLatch(1);
+ final CountDownLatch modify = new CountDownLatch(1);
+ final CountDownLatch done = new CountDownLatch(10);
+
+ // Schedules 5 threads to be executed together now, and 5 to be executed after file is
+ // modified.
+ for (int i = 0; i < 5; i++) {
+ threadPool.submit(() -> {
+ ready.countDown();
+ try {
+ start.await();
+ try (KernelCpuProcStringReader.ProcFileIterator iter = mReader.open()) {
+ for (String line : lines) {
+ assertEquals(line, iter.nextLine().toString());
+ }
+ }
+ } catch (Throwable e) {
+ errs.add(e);
+ } finally {
+ done.countDown();
+ }
+ });
+ threadPool.submit(() -> {
+ ready.countDown();
+ try {
+ start.await();
+ // Wait for file modification.
+ modify.await();
+ try (KernelCpuProcStringReader.ProcFileIterator iter = mReader.open()) {
+ for (String line : lines1) {
+ assertEquals(line, iter.nextLine().toString());
+ }
+ }
+ } catch (Throwable e) {
+ errs.add(e);
+ } finally {
+ done.countDown();
+ }
+ });
+ }
+
+ assertTrue("Prep timed out", ready.await(100, TimeUnit.MILLISECONDS));
+ start.countDown();
+
+ threadPool.schedule(() -> {
+ assertTrue(mTestFile.delete());
+ try (BufferedWriter w = Files.newBufferedWriter(mTestFile.toPath())) {
+ w.write(data1);
+ modify.countDown();
+ } catch (Throwable e) {
+ errs.add(e);
+ }
+ }, 600, TimeUnit.MILLISECONDS);
+
+ assertTrue("Execution timed out", done.await(3, TimeUnit.SECONDS));
+ threadPool.shutdownNow();
+
+ StringWriter sw = new StringWriter();
+ PrintWriter pw = new PrintWriter(sw);
+ errs.forEach(e -> e.printStackTrace(pw));
+
+ assertTrue("All Exceptions:\n" + sw.toString(), errs.isEmpty());
+ }
+
+ private String getTestString(int lines, int charsPerLine) {
+ StringBuffer sb = new StringBuffer();
+ for (int i = 0; i < lines; i++) {
+ for (int j = 0; j < charsPerLine; j++) {
+ sb.append((char) (mRand.nextInt(93) + 32));
+ }
+ sb.append('\n');
+ }
+ return sb.toString();
+ }
+
+ private long[][] getTestArray(int lines, int numPerLine) {
+ return IntStream.range(0, lines).mapToObj(
+ (i) -> mRand.longs(numPerLine, 0, Long.MAX_VALUE).toArray()).toArray(long[][]::new);
+ }
+
+ private String arrayToString(long[][] array) {
+ StringBuffer sb = new StringBuffer();
+ for (int i = 0; i < array.length; i++) {
+ sb.append(array[i][0]).append(':');
+ for (int j = 1; j < array[0].length; j++) {
+ sb.append(' ').append(array[i][j]);
+ }
+ sb.append('\n');
+ }
+ return sb.toString();
+ }
+}
diff --git a/libs/androidfw/Android.bp b/libs/androidfw/Android.bp
index 92efb6b..98af3eb 100644
--- a/libs/androidfw/Android.bp
+++ b/libs/androidfw/Android.bp
@@ -59,8 +59,6 @@
"ZipFileRO.cpp",
"ZipUtils.cpp",
],
- // Allow implicit fallthroughs in Locale.cpp and ResourceTypes.cpp until they are fixed.
- cflags: ["-Wno-implicit-fallthrough"],
export_include_dirs: ["include"],
export_shared_lib_headers: ["libz"],
target: {
diff --git a/libs/androidfw/Locale.cpp b/libs/androidfw/Locale.cpp
index 2870066..3eedda8 100644
--- a/libs/androidfw/Locale.cpp
+++ b/libs/androidfw/Locale.cpp
@@ -14,6 +14,7 @@
* limitations under the License.
*/
+#include "android-base/macros.h"
#include "androidfw/Locale.h"
#include "androidfw/Util.h"
@@ -162,6 +163,7 @@
set_script(subtags[1].c_str());
break;
}
+ FALLTHROUGH_INTENDED;
case 5:
case 6:
case 7:
diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp
index 388548b..76db18d 100644
--- a/libs/androidfw/ResourceTypes.cpp
+++ b/libs/androidfw/ResourceTypes.cpp
@@ -29,6 +29,7 @@
#include <memory>
#include <type_traits>
+#include <android-base/macros.h>
#include <androidfw/ByteBucketArray.h>
#include <androidfw/ResourceTypes.h>
#include <androidfw/TypeWrappers.h>
@@ -3073,6 +3074,7 @@
}
break;
}
+ FALLTHROUGH_INTENDED;
case 5:
case 6:
case 7:
@@ -7002,7 +7004,7 @@
switch (value->dataType) {
case Res_value::TYPE_ATTRIBUTE:
resolvedType = Res_value::TYPE_ATTRIBUTE;
- // fallthrough
+ FALLTHROUGH_INTENDED;
case Res_value::TYPE_REFERENCE:
// Only resolve non-dynamic references and attributes if the package is loaded as a
// library or if a shared library is attempting to retrieve its own resource
@@ -7015,7 +7017,7 @@
break;
case Res_value::TYPE_DYNAMIC_ATTRIBUTE:
resolvedType = Res_value::TYPE_ATTRIBUTE;
- // fallthrough
+ FALLTHROUGH_INTENDED;
case Res_value::TYPE_DYNAMIC_REFERENCE:
break;
default:
diff --git a/media/java/android/media/MediaDrm.java b/media/java/android/media/MediaDrm.java
index ee12b91..24b7f36 100644
--- a/media/java/android/media/MediaDrm.java
+++ b/media/java/android/media/MediaDrm.java
@@ -979,6 +979,87 @@
throws DeniedByServerException;
/**
+ * The keys in an offline license allow protected content to be played even
+ * if the device is not connected to a network. Offline licenses are stored
+ * on the device after a key request/response exchange when the key request
+ * KeyType is OFFLINE. Normally each app is responsible for keeping track of
+ * the keySetIds it has created. If an app loses the keySetId for any stored
+ * licenses that it created, however, it must be able to recover the stored
+ * keySetIds so those licenses can be removed when they expire or when the
+ * app is uninstalled.
+ * <p>
+ * This method returns a list of the keySetIds for all offline licenses.
+ * The offline license keySetId may be used to query the status of an
+ * offline license with {@link #getOfflineLicenseState} or remove it with
+ * {@link #removeOfflineLicense}.
+ *
+ * @return a list of offline license keySetIds
+ */
+ @NonNull
+ public native List<byte[]> getOfflineLicenseKeySetIds();
+
+ /**
+ * Normally offline licenses are released using a key request/response
+ * exchange using {@link #getKeyRequest} where the key type is
+ * KEY_TYPE_RELEASE, followed by {@link #provideKeyResponse}. This allows
+ * the server to cryptographically confirm that the license has been removed
+ * and then adjust the count of offline licenses allocated to the device.
+ * <p>
+ * In some exceptional situations it may be necessary to directly remove
+ * offline licenses without notifying the server, which may be performed
+ * using this method.
+ *
+ * @param keySetId the id of the offline license to remove
+ * @throws IllegalArgumentException if the keySetId does not refer to an
+ * offline license.
+ */
+ public native void removeOfflineLicense(@NonNull byte[] keySetId);
+
+ /**
+ * Offline license state is unknown, an error occurred while trying
+ * to access it.
+ */
+ public static final int OFFLINE_LICENSE_STATE_UNKNOWN = 0;
+
+ /**
+ * Offline license state is usable, the keys may be used for decryption.
+ */
+ public static final int OFFLINE_LICENSE_USABLE = 1;
+
+ /**
+ * Offline license state is inactive, the keys have been marked for
+ * release using {@link #getKeyRequest} with KEY_TYPE_RELEASE but the
+ * key response has not been received.
+ */
+ public static final int OFFLINE_LICENSE_INACTIVE = 2;
+
+ /** @hide */
+ @IntDef({
+ OFFLINE_LICENSE_STATE_UNKNOWN,
+ OFFLINE_LICENSE_USABLE,
+ OFFLINE_LICENSE_INACTIVE,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface OfflineLicenseState {}
+
+ /**
+ * Request the state of an offline license. An offline license may be usable
+ * or inactive. The keys in a usable offline license are available for
+ * decryption. When the offline license state is inactive, the keys have
+ * been marked for release using {@link #getKeyRequest} with
+ * KEY_TYPE_RELEASE but the key response has not been received. The keys in
+ * an inactive offline license are not usable for decryption.
+ *
+ * @param keySetId selects the offline license
+ * @return the offline license state, one of {@link #OFFLINE_LICENSE_USABLE},
+ * {@link #OFFLINE_LICENSE_INACTIVE} or {@link #OFFLINE_LICENSE_STATE_UNKNOWN}.
+ * @throws IllegalArgumentException if the keySetId does not refer to an
+ * offline license.
+ */
+ @OfflineLicenseState
+ public native int getOfflineLicenseState(@NonNull byte[] keySetId);
+
+ /**
* Secure stops are a way to enforce limits on the number of concurrent
* streams per subscriber across devices. They provide secure monitoring of
* the lifetime of content decryption keys in MediaDrm sessions.
diff --git a/media/java/android/media/MediaPlayer2.java b/media/java/android/media/MediaPlayer2.java
index 0b3c973..4e90162 100644
--- a/media/java/android/media/MediaPlayer2.java
+++ b/media/java/android/media/MediaPlayer2.java
@@ -294,7 +294,7 @@
* reached end of stream and been paused, or never started before,
* playback will start at the beginning.
*
- * @return a token which can be used to cancel the operation later with {@link #cancel}.
+ * @return a token which can be used to cancel the operation later with {@link #cancelCommand}.
*/
// This is an asynchronous call.
public abstract Object play();
@@ -305,21 +305,21 @@
* After setting the datasource and the display surface, you need to
* call prepare().
*
- * @return a token which can be used to cancel the operation later with {@link #cancel}.
+ * @return a token which can be used to cancel the operation later with {@link #cancelCommand}.
*/
// This is an asynchronous call.
public abstract Object prepare();
/**
* Pauses playback. Call play() to resume.
- * @return a token which can be used to cancel the operation later with {@link #cancel}.
+ * @return a token which can be used to cancel the operation later with {@link #cancelCommand}.
*/
// This is an asynchronous call.
public abstract Object pause();
/**
* Tries to play next data source if applicable.
- * @return a token which can be used to cancel the operation later with {@link #cancel}.
+ * @return a token which can be used to cancel the operation later with {@link #cancelCommand}.
*/
// This is an asynchronous call.
public abstract Object skipToNext();
@@ -404,7 +404,7 @@
* You must call this method before {@link #play()} and {@link #pause()} in order
* for the audio attributes to become effective thereafter.
* @param attributes a non-null set of audio attributes
- * @return a token which can be used to cancel the operation later with {@link #cancel}.
+ * @return a token which can be used to cancel the operation later with {@link #cancelCommand}.
*/
// This is an asynchronous call.
public abstract Object setAudioAttributes(@NonNull AudioAttributes attributes);
@@ -419,7 +419,7 @@
* Sets the data source as described by a DataSourceDesc.
*
* @param dsd the descriptor of data source you want to play
- * @return a token which can be used to cancel the operation later with {@link #cancel}.
+ * @return a token which can be used to cancel the operation later with {@link #cancelCommand}.
*/
// This is an asynchronous call.
public abstract Object setDataSource(@NonNull DataSourceDesc dsd);
@@ -429,7 +429,7 @@
* after current data source is finished.
*
* @param dsd the descriptor of data source you want to play after current one
- * @return a token which can be used to cancel the operation later with {@link #cancel}.
+ * @return a token which can be used to cancel the operation later with {@link #cancelCommand}.
*/
// This is an asynchronous call.
public abstract Object setNextDataSource(@NonNull DataSourceDesc dsd);
@@ -438,14 +438,14 @@
* Sets a list of data sources to be played sequentially after current data source is done.
*
* @param dsds the list of data sources you want to play after current one
- * @return a token which can be used to cancel the operation later with {@link #cancel}.
+ * @return a token which can be used to cancel the operation later with {@link #cancelCommand}.
*/
// This is an asynchronous call.
public abstract Object setNextDataSources(@NonNull List<DataSourceDesc> dsds);
/**
* Removes all data sources pending to be played.
- * @return a token which can be used to cancel the operation later with {@link #cancel}.
+ * @return a token which can be used to cancel the operation later with {@link #cancelCommand}.
*/
// This is an asynchronous call.
public abstract Object clearNextDataSources();
@@ -460,7 +460,7 @@
/**
* Configures the player to loop on the current data source.
* @param loop true if the current data source is meant to loop.
- * @return a token which can be used to cancel the operation later with {@link #cancel}.
+ * @return a token which can be used to cancel the operation later with {@link #cancelCommand}.
*/
// This is an asynchronous call.
public abstract Object loopCurrent(boolean loop);
@@ -473,7 +473,7 @@
* A value of 0.0f indicates muting, a value of 1.0f is the nominal unattenuated and unamplified
* gain. See {@link #getMaxPlayerVolume()} for the volume range supported by this player.
* @param volume a value between 0.0f and {@link #getMaxPlayerVolume()}.
- * @return a token which can be used to cancel the operation later with {@link #cancel}.
+ * @return a token which can be used to cancel the operation later with {@link #cancelCommand}.
*/
// This is an asynchronous call.
public abstract Object setPlayerVolume(float volume);
@@ -502,7 +502,7 @@
*
* @param label An application specific Object used to help to identify the completeness
* of a batch of commands.
- * @return a token which can be used to cancel the operation later with {@link #cancel}.
+ * @return a token which can be used to cancel the operation later with {@link #cancelCommand}.
*/
// This is an asynchronous call.
public abstract Object notifyWhenCommandLabelReached(@NonNull Object label);
@@ -518,7 +518,7 @@
* played.
*
* @param sh the SurfaceHolder to use for video display
- * @return a token which can be used to cancel the operation later with {@link #cancel}.
+ * @return a token which can be used to cancel the operation later with {@link #cancelCommand}.
*/
public abstract Object setDisplay(SurfaceHolder sh);
@@ -538,7 +538,7 @@
*
* @param surface The {@link Surface} to be used for the video portion of
* the media.
- * @return a token which can be used to cancel the operation later with {@link #cancel}.
+ * @return a token which can be used to cancel the operation later with {@link #cancelCommand}.
*/
// This is an asynchronous call.
public abstract Object setSurface(Surface surface);
@@ -558,7 +558,7 @@
*
* @param context the Context to use
* @param mode the power/wake mode to set
- * @return a token which can be used to cancel the operation later with {@link #cancel}.
+ * @return a token which can be used to cancel the operation later with {@link #cancelCommand}.
* @see android.os.PowerManager
*/
// This is an asynchronous call.
@@ -572,7 +572,7 @@
* access.
*
* @param screenOn Supply true to keep the screen on, false to allow it to turn off.
- * @return a token which can be used to cancel the operation later with {@link #cancel}.
+ * @return a token which can be used to cancel the operation later with {@link #cancelCommand}.
*/
// This is an asynchronous call.
public abstract Object setScreenOnWhilePlaying(boolean screenOn);
@@ -667,22 +667,11 @@
* available for the media being handled by this instance of MediaPlayer2
* The attributes are descibed in {@link MetricsConstants}.
*
- * Additional vendor-specific fields may also be present in
- * the return value.
+ * Additional vendor-specific fields may also be present in the return value.
*/
public abstract PersistableBundle getMetrics();
/**
- * Checks whether the MediaPlayer2 is playing.
- *
- * @return true if currently playing, false otherwise
- * @throws IllegalStateException if the internal player engine has not been
- * initialized or has been released.
- * @hide
- */
- public abstract boolean isPlaying();
-
- /**
* Gets the current buffering management params used by the source component.
* Calling it only after {@code setDataSource} has been called.
* Each type of data source might have different set of default params.
@@ -690,10 +679,10 @@
* @return the current buffering management params used by the source component.
* @throws IllegalStateException if the internal player engine has not been
* initialized, or {@code setDataSource} has not been called.
- * @hide
*/
+ // TODO: make it public when ready
@NonNull
- public BufferingParams getBufferingParams() {
+ BufferingParams getBufferingParams() {
return new BufferingParams.Builder().build();
}
@@ -705,80 +694,11 @@
* The input is a hint to MediaPlayer2.
*
* @param params the buffering management params.
- * @return a token which can be used to cancel the operation later with {@link #cancel}.
- *
- * @hide
+ * @return a token which can be used to cancel the operation later with {@link #cancelCommand}.
*/
+ // TODO: make it public when ready
// This is an asynchronous call.
- public abstract Object setBufferingParams(@NonNull BufferingParams params);
-
- /**
- * Change playback speed of audio by resampling the audio.
- * <p>
- * Specifies resampling as audio mode for variable rate playback, i.e.,
- * resample the waveform based on the requested playback rate to get
- * a new waveform, and play back the new waveform at the original sampling
- * frequency.
- * When rate is larger than 1.0, pitch becomes higher.
- * When rate is smaller than 1.0, pitch becomes lower.
- *
- * @hide
- */
- public static final int PLAYBACK_RATE_AUDIO_MODE_RESAMPLE = 2;
-
- /**
- * Change playback speed of audio without changing its pitch.
- * <p>
- * Specifies time stretching as audio mode for variable rate playback.
- * Time stretching changes the duration of the audio samples without
- * affecting its pitch.
- * <p>
- * This mode is only supported for a limited range of playback speed factors,
- * e.g. between 1/2x and 2x.
- *
- * @hide
- */
- public static final int PLAYBACK_RATE_AUDIO_MODE_STRETCH = 1;
-
- /**
- * Change playback speed of audio without changing its pitch, and
- * possibly mute audio if time stretching is not supported for the playback
- * speed.
- * <p>
- * Try to keep audio pitch when changing the playback rate, but allow the
- * system to determine how to change audio playback if the rate is out
- * of range.
- *
- * @hide
- */
- public static final int PLAYBACK_RATE_AUDIO_MODE_DEFAULT = 0;
-
- /** @hide */
- @IntDef(flag = false, prefix = "PLAYBACK_RATE_AUDIO_MODE", value = {
- PLAYBACK_RATE_AUDIO_MODE_DEFAULT,
- PLAYBACK_RATE_AUDIO_MODE_STRETCH,
- PLAYBACK_RATE_AUDIO_MODE_RESAMPLE,
- })
- @Retention(RetentionPolicy.SOURCE)
- public @interface PlaybackRateAudioMode {}
-
- /**
- * Sets playback rate and audio mode.
- *
- * @param rate the ratio between desired playback rate and normal one.
- * @param audioMode audio playback mode. Must be one of the supported
- * audio modes.
- *
- * @throws IllegalStateException if the internal player engine has not been
- * initialized.
- * @throws IllegalArgumentException if audioMode is not supported.
- *
- * @hide
- */
- @NonNull
- public PlaybackParams easyPlaybackParams(float rate, @PlaybackRateAudioMode int audioMode) {
- return new PlaybackParams();
- }
+ abstract Object setBufferingParams(@NonNull BufferingParams params);
/**
* Sets playback rate using {@link PlaybackParams}. The object sets its internal
@@ -787,7 +707,7 @@
* the object state.
*
* @param params the playback params.
- * @return a token which can be used to cancel the operation later with {@link #cancel}.
+ * @return a token which can be used to cancel the operation later with {@link #cancelCommand}.
*/
// This is an asynchronous call.
public abstract Object setPlaybackParams(@NonNull PlaybackParams params);
@@ -796,6 +716,7 @@
* Gets the playback params, containing the current playback rate.
*
* @return the playback params.
+ * @throws IllegalStateException if the internal player engine has not been initialized.
*/
@NonNull
public abstract PlaybackParams getPlaybackParams();
@@ -804,7 +725,7 @@
* Sets A/V sync mode.
*
* @param params the A/V sync params to apply
- * @return a token which can be used to cancel the operation later with {@link #cancel}.
+ * @return a token which can be used to cancel the operation later with {@link #cancelCommand}.
*/
// This is an asynchronous call.
public abstract Object setSyncParams(@NonNull SyncParams params);
@@ -813,6 +734,7 @@
* Gets the A/V sync mode.
*
* @return the A/V sync params
+ * @throws IllegalStateException if the internal player engine has not been initialized.
*/
@NonNull
public abstract SyncParams getSyncParams();
@@ -822,7 +744,7 @@
* Same as {@link #seekTo(long, int)} with {@code mode = SEEK_PREVIOUS_SYNC}.
*
* @param msec the offset in milliseconds from the start to seek to
- * @return a token which can be used to cancel the operation later with {@link #cancel}.
+ * @return a token which can be used to cancel the operation later with {@link #cancelCommand}.
*/
// This is an asynchronous call.
public Object seekTo(long msec) {
@@ -882,7 +804,8 @@
/**
* Moves the media to specified time position by considering the given mode.
* <p>
- * When seekTo is finished, the user will be notified via OnSeekComplete supplied by the user.
+ * When seekTo is finished, the user will be notified via
+ * {@link EventCallback#onCallCompleted} with {@link #CALL_COMPLETED_SEEK_TO}.
* There is at most one active seekTo processed at any time. If there is a to-be-completed
* seekTo, new seekTo requests will be queued in such a way that only the last request
* is kept. When current seekTo is completed, the queued request will be processed if
@@ -895,7 +818,7 @@
* If msec is negative, time position zero will be used.
* If msec is larger than duration, duration will be used.
* @param mode the mode indicating where exactly to seek to.
- * @return a token which can be used to cancel the operation later with {@link #cancel}.
+ * @return a token which can be used to cancel the operation later with {@link #cancelCommand}.
*/
// This is an asynchronous call.
public abstract Object seekTo(long msec, @SeekMode int mode);
@@ -953,7 +876,7 @@
* However, it is possible to force this player to be part of an already existing audio session
* by calling this method.
* This method must be called before one of the overloaded <code> setDataSource </code> methods.
- * @return a token which can be used to cancel the operation later with {@link #cancel}.
+ * @return a token which can be used to cancel the operation later with {@link #cancelCommand}.
*/
// This is an asynchronous call.
public abstract Object setAudioSessionId(int sessionId);
@@ -979,7 +902,7 @@
* <p>This method must be called after one of the overloaded <code> setDataSource </code>
* methods.
* @param effectId system wide unique id of the effect to attach
- * @return a token which can be used to cancel the operation later with {@link #cancel}.
+ * @return a token which can be used to cancel the operation later with {@link #cancelCommand}.
*/
// This is an asynchronous call.
public abstract Object attachAuxEffect(int effectId);
@@ -996,7 +919,7 @@
* x == 0 -> level = 0
* 0 < x <= R -> level = 10^(72*(x-R)/20/R)
* @param level send level scalar
- * @return a token which can be used to cancel the operation later with {@link #cancel}.
+ * @return a token which can be used to cancel the operation later with {@link #cancelCommand}.
*/
// This is an asynchronous call.
public abstract Object setAuxEffectSendLevel(float level);
@@ -1122,7 +1045,7 @@
* @param index the index of the track to be selected. The valid range of the index
* is 0..total number of track - 1. The total number of tracks as well as the type of
* each individual track can be found by calling {@link #getTrackInfo()} method.
- * @return a token which can be used to cancel the operation later with {@link #cancel}.
+ * @return a token which can be used to cancel the operation later with {@link #cancelCommand}.
*
* @see MediaPlayer2#getTrackInfo
*/
@@ -1139,7 +1062,7 @@
* @param index the index of the track to be deselected. The valid range of the index
* is 0..total number of tracks - 1. The total number of tracks as well as the type of
* each individual track can be found by calling {@link #getTrackInfo()} method.
- * @return a token which can be used to cancel the operation later with {@link #cancel}.
+ * @return a token which can be used to cancel the operation later with {@link #cancelCommand}.
*
* @see MediaPlayer2#getTrackInfo
*/
@@ -1872,7 +1795,7 @@
* from the source through {@code getDrmInfo} or registering a
* {@link DrmEventCallback#onDrmInfo}.
*
- * @return a token which can be used to cancel the operation later with {@link #cancel}.
+ * @return a token which can be used to cancel the operation later with {@link #cancelCommand}.
*/
// This is an asynchronous call.
public abstract Object prepareDrm(@NonNull UUID uuid);
diff --git a/media/java/android/media/MediaPlayer2Impl.java b/media/java/android/media/MediaPlayer2Impl.java
index 8f475a1..3534636 100644
--- a/media/java/android/media/MediaPlayer2Impl.java
+++ b/media/java/android/media/MediaPlayer2Impl.java
@@ -131,6 +131,13 @@
@GuardedBy("mTaskLock")
private Task mCurrentTask;
+ @GuardedBy("mTaskLock")
+ boolean mIsPreviousCommandSeekTo = false;
+ // |mPreviousSeekPos| and |mPreviousSeekMode| are valid only when |mIsPreviousCommandSeekTo|
+ // is true, and they are accessed on |mHandlerThread| only.
+ long mPreviousSeekPos = -1;
+ int mPreviousSeekMode = SEEK_PREVIOUS_SYNC;
+
@GuardedBy("this")
private boolean mReleased;
@@ -239,15 +246,14 @@
final String msg = "Cannot set AudioAttributes to null";
throw new IllegalArgumentException(msg);
}
- setParameter(KEY_PARAMETER_AUDIO_ATTRIBUTES, attributes);
+ native_setAudioAttributes(attributes);
}
});
}
@Override
public @NonNull AudioAttributes getAudioAttributes() {
- AudioAttributes attributes = (AudioAttributes) getParameter(KEY_PARAMETER_AUDIO_ATTRIBUTES);
- return attributes;
+ return native_getAudioAttributes();
}
@Override
@@ -927,14 +933,11 @@
private native PersistableBundle native_getMetrics();
@Override
- public native boolean isPlaying();
-
- @Override
@NonNull
- public native BufferingParams getBufferingParams();
+ native BufferingParams getBufferingParams();
@Override
- public Object setBufferingParams(@NonNull BufferingParams params) {
+ Object setBufferingParams(@NonNull BufferingParams params) {
return addTask(new Task(CALL_COMPLETED_SET_BUFFERING_PARAMS, false) {
@Override
void process() {
@@ -946,42 +949,6 @@
private native void _setBufferingParams(@NonNull BufferingParams params);
- /**
- * Sets playback rate and audio mode.
- *
- * @param rate the ratio between desired playback rate and normal one.
- * @param audioMode audio playback mode. Must be one of the supported
- * audio modes.
- *
- * @throws IllegalStateException if the internal player engine has not been
- * initialized.
- * @throws IllegalArgumentException if audioMode is not supported.
- *
- * @hide
- */
- @Override
- @NonNull
- public PlaybackParams easyPlaybackParams(float rate, @PlaybackRateAudioMode int audioMode) {
- PlaybackParams params = new PlaybackParams();
- params.allowDefaults();
- switch (audioMode) {
- case PLAYBACK_RATE_AUDIO_MODE_DEFAULT:
- params.setSpeed(rate).setPitch(1.0f);
- break;
- case PLAYBACK_RATE_AUDIO_MODE_STRETCH:
- params.setSpeed(rate).setPitch(1.0f)
- .setAudioFallbackMode(params.AUDIO_FALLBACK_MODE_FAIL);
- break;
- case PLAYBACK_RATE_AUDIO_MODE_RESAMPLE:
- params.setSpeed(rate).setPitch(rate);
- break;
- default:
- final String msg = "Audio playback mode " + audioMode + " is not supported";
- throw new IllegalArgumentException(msg);
- }
- return params;
- }
-
@Override
public Object setPlaybackParams(@NonNull PlaybackParams params) {
return addTask(new Task(CALL_COMPLETED_SET_PLAYBACK_PARAMS, false) {
@@ -995,26 +962,10 @@
private native void _setPlaybackParams(@NonNull PlaybackParams params);
- /**
- * Gets the playback params, containing the current playback rate.
- *
- * @return the playback params.
- * @throws IllegalStateException if the internal player engine has not been
- * initialized.
- */
@Override
@NonNull
public native PlaybackParams getPlaybackParams();
- /**
- * Sets A/V sync mode.
- *
- * @param params the A/V sync params to apply
- *
- * @throws IllegalStateException if the internal player engine has not been
- * initialized.
- * @throws IllegalArgumentException if params are not supported.
- */
@Override
public Object setSyncParams(@NonNull SyncParams params) {
return addTask(new Task(CALL_COMPLETED_SET_SYNC_PARAMS, false) {
@@ -1028,48 +979,10 @@
private native void _setSyncParams(@NonNull SyncParams params);
- /**
- * Gets the A/V sync mode.
- *
- * @return the A/V sync params
- *
- * @throws IllegalStateException if the internal player engine has not been
- * initialized.
- */
@Override
@NonNull
public native SyncParams getSyncParams();
- /**
- * Moves the media to specified time position by considering the given mode.
- * <p>
- * When seekTo is finished, the user will be notified via OnSeekComplete supplied by the user.
- * There is at most one active seekTo processed at any time. If there is a to-be-completed
- * seekTo, new seekTo requests will be queued in such a way that only the last request
- * is kept. When current seekTo is completed, the queued request will be processed if
- * that request is different from just-finished seekTo operation, i.e., the requested
- * position or mode is different.
- *
- * @param msec the offset in milliseconds from the start to seek to.
- * When seeking to the given time position, there is no guarantee that the data source
- * has a frame located at the position. When this happens, a frame nearby will be rendered.
- * If msec is negative, time position zero will be used.
- * If msec is larger than duration, duration will be used.
- * @param mode the mode indicating where exactly to seek to.
- * Use {@link #SEEK_PREVIOUS_SYNC} if one wants to seek to a sync frame
- * that has a timestamp earlier than or the same as msec. Use
- * {@link #SEEK_NEXT_SYNC} if one wants to seek to a sync frame
- * that has a timestamp later than or the same as msec. Use
- * {@link #SEEK_CLOSEST_SYNC} if one wants to seek to a sync frame
- * that has a timestamp closest to or the same as msec. Use
- * {@link #SEEK_CLOSEST} if one wants to seek to a frame that may
- * or may not be a sync frame but is closest to or the same as msec.
- * {@link #SEEK_CLOSEST} often has larger performance overhead compared
- * to the other options if there is no sync frame located at msec.
- * @throws IllegalStateException if the internal player engine has not been
- * initialized
- * @throws IllegalArgumentException if the mode is invalid.
- */
@Override
public Object seekTo(final long msec, @SeekMode int mode) {
return addTask(new Task(CALL_COMPLETED_SEEK_TO, true) {
@@ -1090,7 +1003,23 @@
+ Integer.MIN_VALUE);
posMs = Integer.MIN_VALUE;
}
+
+ synchronized (mTaskLock) {
+ if (mIsPreviousCommandSeekTo
+ && mPreviousSeekPos == posMs
+ && mPreviousSeekMode == mode) {
+ throw new CommandSkippedException(
+ "same as previous seekTo");
+ }
+ }
+
_seekTo(posMs, mode);
+
+ synchronized (mTaskLock) {
+ mIsPreviousCommandSeekTo = true;
+ mPreviousSeekPos = posMs;
+ mPreviousSeekMode = mode;
+ }
}
});
}
@@ -1124,7 +1053,7 @@
return new MediaTimestamp(
getCurrentPosition() * 1000L,
System.nanoTime(),
- isPlaying() ? getPlaybackParams().getSpeed() : 0.f);
+ getState() == PLAYER_STATE_PLAYING ? getPlaybackParams().getSpeed() : 0.f);
} catch (IllegalStateException e) {
return null;
}
@@ -1152,6 +1081,11 @@
mNextSourceState = NEXT_SOURCE_STATE_INIT;
}
+ synchronized (mTaskLock) {
+ mPendingTasks.clear();
+ mIsPreviousCommandSeekTo = false;
+ }
+
stayAwake(false);
_reset();
// make sure none of the listeners get called anymore
@@ -1167,14 +1101,12 @@
// Keep KEY_PARAMETER_* in sync with include/media/mediaplayer2.h
private final static int KEY_PARAMETER_AUDIO_ATTRIBUTES = 1400;
/**
- * Sets the parameter indicated by key.
- * @param key key indicates the parameter to be set.
+ * Sets the audio attributes.
* @param value value of the parameter to be set.
* @return true if the parameter is set successfully, false otherwise
*/
- private native boolean setParameter(int key, Object value);
-
- private native Object getParameter(int key);
+ private native boolean native_setAudioAttributes(AudioAttributes audioAttributes);
+ private native AudioAttributes native_getAudioAttributes();
/**
@@ -1748,6 +1680,12 @@
case MEDIA_SEEK_COMPLETE:
{
synchronized (mTaskLock) {
+ if (!mPendingTasks.isEmpty()
+ && mPendingTasks.get(0).mMediaCallType != CALL_COMPLETED_SEEK_TO
+ && getState() == PLAYER_STATE_PLAYING) {
+ mIsPreviousCommandSeekTo = false;
+ }
+
if (mCurrentTask != null
&& mCurrentTask.mMediaCallType == CALL_COMPLETED_SEEK_TO
&& mCurrentTask.mNeedToWaitForEventToComplete) {
@@ -3150,6 +3088,12 @@
mDSD = mCurrentDSD;
}
+ if (mMediaCallType != CALL_COMPLETED_SEEK_TO) {
+ synchronized (mTaskLock) {
+ mIsPreviousCommandSeekTo = false;
+ }
+ }
+
// TODO: Make native implementations asynchronous and let them send notifications.
if (!mNeedToWaitForEventToComplete || status != CALL_STATUS_NO_ERROR) {
diff --git a/media/java/android/media/MediaScanner.java b/media/java/android/media/MediaScanner.java
index 0cde01e..3a64f43 100644
--- a/media/java/android/media/MediaScanner.java
+++ b/media/java/android/media/MediaScanner.java
@@ -1632,7 +1632,7 @@
selectionArgs = new String[] { path };
c = mMediaProvider.query(mFilesUriNoNotify, FILES_PRESCAN_PROJECTION,
where, selectionArgs, null, null);
- if (c.moveToFirst()) {
+ if (c != null && c.moveToFirst()) {
long rowId = c.getLong(FILES_PRESCAN_ID_COLUMN_INDEX);
long lastModified = c.getLong(FILES_PRESCAN_DATE_MODIFIED_COLUMN_INDEX);
int format = c.getInt(FILES_PRESCAN_FORMAT_COLUMN_INDEX);
diff --git a/media/jni/android_media_MediaDrm.cpp b/media/jni/android_media_MediaDrm.cpp
index 8637ada..be71dad5 100644
--- a/media/jni/android_media_MediaDrm.cpp
+++ b/media/jni/android_media_MediaDrm.cpp
@@ -161,6 +161,12 @@
jint kSecurityLevelHwSecureAll;
} gSecurityLevels;
+struct OfflineLicenseState {
+ jint kOfflineLicenseStateUsable;
+ jint kOfflineLicenseStateInactive;
+ jint kOfflineLicenseStateUnknown;
+} gOfflineLicenseStates;
+
struct fields_t {
jfieldID context;
@@ -740,6 +746,15 @@
GET_STATIC_FIELD_ID(field, clazz, "SECURITY_LEVEL_HW_SECURE_ALL", "I");
gSecurityLevels.kSecurityLevelHwSecureAll = env->GetStaticIntField(clazz, field);
+ GET_STATIC_FIELD_ID(field, clazz, "OFFLINE_LICENSE_USABLE", "I");
+ gOfflineLicenseStates.kOfflineLicenseStateUsable = env->GetStaticIntField(clazz, field);
+ GET_STATIC_FIELD_ID(field, clazz, "OFFLINE_LICENSE_INACTIVE", "I");
+ gOfflineLicenseStates.kOfflineLicenseStateInactive = env->GetStaticIntField(clazz, field);
+ GET_STATIC_FIELD_ID(field, clazz, "OFFLINE_LICENSE_STATE_UNKNOWN", "I");
+ gOfflineLicenseStates.kOfflineLicenseStateUnknown = env->GetStaticIntField(clazz, field);
+
+ GET_STATIC_FIELD_ID(field, clazz, "SECURITY_LEVEL_HW_SECURE_CRYPTO", "I");
+
jmethodID getMaxSecurityLevel;
GET_STATIC_METHOD_ID(getMaxSecurityLevel, clazz, "getMaxSecurityLevel", "()I");
gSecurityLevels.kSecurityLevelMax = env->CallStaticIntMethod(clazz, getMaxSecurityLevel);
@@ -890,9 +905,7 @@
JNIEnv *env, jobject thiz, jint jlevel) {
sp<IDrm> drm = GetDrm(env, thiz);
- if (drm == NULL) {
- jniThrowException(env, "java/lang/IllegalStateException",
- "MediaDrm obj is null");
+ if (!CheckDrm(env, drm)) {
return NULL;
}
@@ -1070,6 +1083,10 @@
JNIEnv *env, jobject thiz, jbyteArray jkeysetId) {
sp<IDrm> drm = GetDrm(env, thiz);
+ if (!CheckDrm(env, drm)) {
+ return;
+ }
+
if (jkeysetId == NULL) {
jniThrowException(env, "java/lang/IllegalArgumentException",
"keySetId is null");
@@ -1231,9 +1248,7 @@
JNIEnv *env, jobject thiz) {
sp<IDrm> drm = GetDrm(env, thiz);
- if (drm == NULL) {
- jniThrowException(env, "java/lang/IllegalStateException",
- "MediaDrm obj is null");
+ if (!CheckDrm(env, drm)) {
return NULL;
}
@@ -1286,9 +1301,7 @@
JNIEnv *env, jobject thiz, jbyteArray ssid) {
sp<IDrm> drm = GetDrm(env, thiz);
- if (drm == NULL) {
- jniThrowException(env, "java/lang/IllegalStateException",
- "MediaDrm obj is null");
+ if (!CheckDrm(env, drm)) {
return;
}
@@ -1437,6 +1450,65 @@
}
}
+static jobject android_media_MediaDrm_getOfflineLicenseKeySetIds(
+ JNIEnv *env, jobject thiz) {
+ sp<IDrm> drm = GetDrm(env, thiz);
+
+ if (!CheckDrm(env, drm)) {
+ return NULL;
+ }
+
+ List<Vector<uint8_t> > keySetIds;
+
+ status_t err = drm->getOfflineLicenseKeySetIds(keySetIds);
+
+ if (throwExceptionAsNecessary(env, err, "Failed to get offline key set Ids")) {
+ return NULL;
+ }
+
+ return ListOfVectorsToArrayListOfByteArray(env, keySetIds);
+}
+
+static void android_media_MediaDrm_removeOfflineLicense(
+ JNIEnv *env, jobject thiz, jbyteArray keySetId) {
+ sp<IDrm> drm = GetDrm(env, thiz);
+
+ if (!CheckDrm(env, drm)) {
+ return;
+ }
+
+ status_t err = drm->removeOfflineLicense(JByteArrayToVector(env, keySetId));
+
+ throwExceptionAsNecessary(env, err, "Failed to remove offline license");
+}
+
+static jint android_media_MediaDrm_getOfflineLicenseState(JNIEnv *env,
+ jobject thiz, jbyteArray jkeySetId) {
+ sp<IDrm> drm = GetDrm(env, thiz);
+
+ if (!CheckDrm(env, drm)) {
+ return gOfflineLicenseStates.kOfflineLicenseStateUnknown;
+ }
+
+ Vector<uint8_t> keySetId(JByteArrayToVector(env, jkeySetId));
+
+ DrmPlugin::OfflineLicenseState state = DrmPlugin::kOfflineLicenseStateUnknown;
+
+ status_t err = drm->getOfflineLicenseState(keySetId, &state);
+
+ if (throwExceptionAsNecessary(env, err, "Failed to get offline license state")) {
+ return gOfflineLicenseStates.kOfflineLicenseStateUnknown;
+ }
+
+ switch(state) {
+ case DrmPlugin::kOfflineLicenseStateUsable:
+ return gOfflineLicenseStates.kOfflineLicenseStateUsable;
+ case DrmPlugin::kOfflineLicenseStateInactive:
+ return gOfflineLicenseStates.kOfflineLicenseStateInactive;
+ default:
+ return gOfflineLicenseStates.kOfflineLicenseStateUnknown;
+ }
+}
static jstring android_media_MediaDrm_getPropertyString(
JNIEnv *env, jobject thiz, jstring jname) {
@@ -1718,9 +1790,8 @@
android_media_MediaDrm_native_getMetrics(JNIEnv *env, jobject thiz)
{
sp<IDrm> drm = GetDrm(env, thiz);
- if (drm == NULL ) {
- jniThrowException(env, "java/lang/IllegalStateException",
- "MediaDrm obj is null");
+
+ if (!CheckDrm(env, drm)) {
return NULL;
}
@@ -1839,6 +1910,15 @@
{ "getSecurityLevel", "([B)I",
(void *)android_media_MediaDrm_getSecurityLevel },
+ { "removeOfflineLicense", "([B)V",
+ (void *)android_media_MediaDrm_removeOfflineLicense },
+
+ { "getOfflineLicenseKeySetIds", "()Ljava/util/List;",
+ (void *)android_media_MediaDrm_getOfflineLicenseKeySetIds },
+
+ { "getOfflineLicenseState", "([B)I",
+ (void *)android_media_MediaDrm_getOfflineLicenseState },
+
{ "getPropertyString", "(Ljava/lang/String;)Ljava/lang/String;",
(void *)android_media_MediaDrm_getPropertyString },
diff --git a/media/jni/android_media_MediaPlayer2.cpp b/media/jni/android_media_MediaPlayer2.cpp
index 520077f..0769e5c 100644
--- a/media/jni/android_media_MediaPlayer2.cpp
+++ b/media/jni/android_media_MediaPlayer2.cpp
@@ -598,20 +598,6 @@
process_media_player_call( env, thiz, mp->pause(), NULL, NULL );
}
-static jboolean
-android_media_MediaPlayer2_isPlaying(JNIEnv *env, jobject thiz)
-{
- sp<MediaPlayer2> mp = getMediaPlayer(env, thiz);
- if (mp == NULL ) {
- jniThrowException(env, "java/lang/IllegalStateException", NULL);
- return JNI_FALSE;
- }
- const jboolean is_playing = mp->isPlaying();
-
- ALOGV("isPlaying: %d", is_playing);
- return is_playing;
-}
-
static void
android_media_MediaPlayer2_setPlaybackParams(JNIEnv *env, jobject thiz, jobject params)
{
@@ -860,56 +846,29 @@
}
static jboolean
-android_media_MediaPlayer2_setParameter(JNIEnv *env, jobject thiz, jint key, jobject)
+android_media_MediaPlayer2_setAudioAttributes(JNIEnv *env, jobject thiz, jobject attributes)
{
- ALOGV("setParameter: key %d", key);
+ ALOGV("setAudioAttributes");
sp<MediaPlayer2> mp = getMediaPlayer(env, thiz);
if (mp == NULL ) {
jniThrowException(env, "java/lang/IllegalStateException", NULL);
return false;
}
-
- return false;
- // TODO: set/getParameter is temporarily disabled to remove android_runtime.so dependency.
- // Once JAudioTrack migration is done, the AudioAttribute jobject
- // should be directly passed to AudioTrack without native parcel conversion.
- /*
- Parcel *request = parcelForJavaObject(env, java_request);
- status_t err = mp->setParameter(key, *request);
- if (err == OK) {
- return true;
- } else {
- return false;
- }
- */
+ status_t err = mp->setAudioAttributes(attributes);
+ return err == OK;
}
static jobject
-android_media_MediaPlayer2_getParameter(JNIEnv *env, jobject thiz, jint key)
+android_media_MediaPlayer2_getAudioAttributes(JNIEnv *env, jobject thiz)
{
- ALOGV("getParameter: key %d", key);
+ ALOGV("getAudioAttributes");
sp<MediaPlayer2> mp = getMediaPlayer(env, thiz);
if (mp == NULL) {
jniThrowException(env, "java/lang/IllegalStateException", NULL);
return NULL;
}
- return NULL;
- // TODO: set/getParameter is temporarily disabled to remove android_runtime.so dependency.
- // Once JAudioTrack migration is done, the AudioAttribute jobject
- // should be directly passed to AudioTrack without native parcel conversion.
- /*
- jobject jParcel = createJavaParcelObject(env);
- if (jParcel != NULL) {
- Parcel* nativeParcel = parcelForJavaObject(env, jParcel);
- status_t err = mp->getParameter(key, nativeParcel);
- if (err != OK) {
- env->DeleteLocalRef(jParcel);
- return NULL;
- }
- }
- return jParcel;
- */
+ return mp->getAudioAttributes();
}
static void
@@ -1442,18 +1401,17 @@
{"native_getState", "()I", (void *)android_media_MediaPlayer2_getState},
{"native_getMetrics", "()Landroid/os/PersistableBundle;", (void *)android_media_MediaPlayer2_native_getMetrics},
{"_setPlaybackParams", "(Landroid/media/PlaybackParams;)V", (void *)android_media_MediaPlayer2_setPlaybackParams},
- {"getPlaybackParams", "()Landroid/media/PlaybackParams;", (void *)android_media_MediaPlayer2_getPlaybackParams},
- {"_setSyncParams", "(Landroid/media/SyncParams;)V", (void *)android_media_MediaPlayer2_setSyncParams},
- {"getSyncParams", "()Landroid/media/SyncParams;", (void *)android_media_MediaPlayer2_getSyncParams},
+ {"getPlaybackParams", "()Landroid/media/PlaybackParams;", (void *)android_media_MediaPlayer2_getPlaybackParams},
+ {"_setSyncParams", "(Landroid/media/SyncParams;)V", (void *)android_media_MediaPlayer2_setSyncParams},
+ {"getSyncParams", "()Landroid/media/SyncParams;", (void *)android_media_MediaPlayer2_getSyncParams},
{"_seekTo", "(JI)V", (void *)android_media_MediaPlayer2_seekTo},
{"_pause", "()V", (void *)android_media_MediaPlayer2_pause},
- {"isPlaying", "()Z", (void *)android_media_MediaPlayer2_isPlaying},
{"getCurrentPosition", "()J", (void *)android_media_MediaPlayer2_getCurrentPosition},
{"getDuration", "()J", (void *)android_media_MediaPlayer2_getDuration},
{"_release", "()V", (void *)android_media_MediaPlayer2_release},
{"_reset", "()V", (void *)android_media_MediaPlayer2_reset},
- {"setParameter", "(ILjava/lang/Object;)Z", (void *)android_media_MediaPlayer2_setParameter},
- {"getParameter", "(I)Ljava/lang/Object;", (void *)android_media_MediaPlayer2_getParameter},
+ {"native_setAudioAttributes", "(Landroid/media/AudioAttributes;)Z", (void *)android_media_MediaPlayer2_setAudioAttributes},
+ {"native_getAudioAttributes", "()Landroid/media/AudioAttributes;", (void *)android_media_MediaPlayer2_getAudioAttributes},
{"setLooping", "(Z)V", (void *)android_media_MediaPlayer2_setLooping},
{"isLooping", "()Z", (void *)android_media_MediaPlayer2_isLooping},
{"native_setVolume", "(F)V", (void *)android_media_MediaPlayer2_setVolume},
diff --git a/native/android/Android.bp b/native/android/Android.bp
index a4306fe..5cfb09b 100644
--- a/native/android/Android.bp
+++ b/native/android/Android.bp
@@ -81,6 +81,8 @@
export_static_lib_headers: ["libarect"],
include_dirs: ["bionic/libc/dns/include"],
+
+ version_script: "libandroid.map.txt",
}
// Network library.
diff --git a/packages/PackageInstaller/res/values-pt-rPT/strings.xml b/packages/PackageInstaller/res/values-pt-rPT/strings.xml
index d45dc1c..e27fed5 100644
--- a/packages/PackageInstaller/res/values-pt-rPT/strings.xml
+++ b/packages/PackageInstaller/res/values-pt-rPT/strings.xml
@@ -41,7 +41,7 @@
<string name="unknown_apps_user_restriction_dlg_text" msgid="151020786933988344">"Este utilizador não pode instalar aplicações desconhecidas."</string>
<string name="install_apps_user_restriction_dlg_text" msgid="2154119597001074022">"Este utilizador não tem autorização para instalar aplicações."</string>
<string name="ok" msgid="7871959885003339302">"OK"</string>
- <string name="manage_applications" msgid="5400164782453975580">"Gerir aplic."</string>
+ <string name="manage_applications" msgid="5400164782453975580">"Gerir app"</string>
<string name="out_of_space_dlg_title" msgid="4156690013884649502">"Sem espaço"</string>
<string name="out_of_space_dlg_text" msgid="8727714096031856231">"Não foi possível instalar a aplicação <xliff:g id="APP_NAME">%1$s</xliff:g>. Liberte algum espaço e tente novamente."</string>
<string name="app_not_found_dlg_title" msgid="5107924008597470285">"Aplicação não encontrada"</string>
@@ -70,7 +70,7 @@
<string name="uninstall_failed_device_policy_manager_of_user" msgid="4813104025494168064">"Não é possível desinstalar a aplicação de administração de dispositivos ativa para <xliff:g id="USERNAME">%1$s</xliff:g>."</string>
<string name="uninstall_all_blocked_profile_owner" msgid="2009393666026751501">"Esta aplicação é necessária para alguns utilizadores ou perfis e foi desinstalada para outros."</string>
<string name="uninstall_blocked_profile_owner" msgid="6373897407002404848">"O perfil necessita desta aplicação e não é possível desinstalá-la."</string>
- <string name="uninstall_blocked_device_owner" msgid="6724602931761073901">"Esta aplic. é exigida pelo administrador do disp. e não pode ser desinstalada."</string>
+ <string name="uninstall_blocked_device_owner" msgid="6724602931761073901">"Esta app é exigida pelo administrador do disp. e não pode ser desinstalada."</string>
<string name="manage_device_administrators" msgid="3092696419363842816">"Gerir aplicações de administração de dispositivos"</string>
<string name="manage_users" msgid="1243995386982560813">"Gerir utilizadores"</string>
<string name="uninstall_failed_msg" msgid="2176744834786696012">"Não foi possível desinstalar a aplicação <xliff:g id="APP_NAME">%1$s</xliff:g>."</string>
diff --git a/packages/SettingsLib/Android.bp b/packages/SettingsLib/Android.bp
index 5161344..d1f140f 100644
--- a/packages/SettingsLib/Android.bp
+++ b/packages/SettingsLib/Android.bp
@@ -9,6 +9,7 @@
"androidx.preference_preference",
"androidx.appcompat_appcompat",
"androidx.lifecycle_lifecycle-runtime",
+ "androidx.mediarouter_mediarouter-nodeps",
"SettingsLibHelpUtils",
"SettingsLibRestrictedLockUtils",
diff --git a/packages/SettingsLib/res/values-af/strings.xml b/packages/SettingsLib/res/values-af/strings.xml
index b77d4e5..d34820c 100644
--- a/packages/SettingsLib/res/values-af/strings.xml
+++ b/packages/SettingsLib/res/values-af/strings.xml
@@ -21,7 +21,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"Kan nie skandeer vir netwerke nie"</string>
- <string name="wifi_security_none" msgid="7985461072596594400">"Geen"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"Gestoor"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"Gedeaktiveer"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP-opstelling het misluk"</string>
diff --git a/packages/SettingsLib/res/values-am/strings.xml b/packages/SettingsLib/res/values-am/strings.xml
index 836ec80..b595e2b 100644
--- a/packages/SettingsLib/res/values-am/strings.xml
+++ b/packages/SettingsLib/res/values-am/strings.xml
@@ -21,7 +21,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"ለአውታረመረቦች መቃኘት አይቻልም"</string>
- <string name="wifi_security_none" msgid="7985461072596594400">"የለም"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"ተቀምጧል"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"ተሰናክሏል"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"የአይ.ፒ. ውቅረት መሰናከል"</string>
diff --git a/packages/SettingsLib/res/values-ar/strings.xml b/packages/SettingsLib/res/values-ar/strings.xml
index 83c4b8e..f8c2ba2 100644
--- a/packages/SettingsLib/res/values-ar/strings.xml
+++ b/packages/SettingsLib/res/values-ar/strings.xml
@@ -21,7 +21,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"لا يمكن فحص الشبكات"</string>
- <string name="wifi_security_none" msgid="7985461072596594400">"بدون"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"تم الحفظ"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"غير مفعّلة"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"تعذّرت تهيئة عنوان IP"</string>
@@ -227,11 +226,11 @@
<string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="6893955536658137179">"اختيار برنامج ترميز LDAC\nلصوت مشغّل البلوتوث: جودة التشغيل"</string>
<string name="bluetooth_select_a2dp_codec_streaming_label" msgid="5347862512596240506">"البث: <xliff:g id="STREAMING_PARAMETER">%1$s</xliff:g>"</string>
<string name="select_private_dns_configuration_title" msgid="3700456559305263922">"نظام أسماء النطاقات الخاص"</string>
- <string name="select_private_dns_configuration_dialog_title" msgid="9221994415765826811">"اختر وضع نظام أسماء النطاقات الخاص"</string>
+ <string name="select_private_dns_configuration_dialog_title" msgid="9221994415765826811">"تحديد وضع \"نظام أسماء النطاقات الخاص\""</string>
<string name="private_dns_mode_off" msgid="8236575187318721684">"غير مفعّل"</string>
<string name="private_dns_mode_opportunistic" msgid="8314986739896927399">"تلقائي"</string>
- <string name="private_dns_mode_provider" msgid="8354935160639360804">"اسم مضيف مزوّد نظام أسماء النطاقات الخاص"</string>
- <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"أدخل اسم مضيف مزوّد نظام أسماء النطاقات"</string>
+ <string name="private_dns_mode_provider" msgid="8354935160639360804">"اسم مضيف مزوّد \"نظام أسماء النطاقات الخاص\""</string>
+ <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"يُرجى إدخال اسم مضيف \"مزوّد نظام أسماء النطاقات\""</string>
<string name="private_dns_mode_provider_failure" msgid="231837290365031223">"تعذّر الاتصال"</string>
<string name="wifi_display_certification_summary" msgid="1155182309166746973">"عرض خيارات شهادة عرض شاشة لاسلكي"</string>
<string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"زيادة مستوى تسجيل Wi-Fi، وعرض لكل SSID RSSI في منتقي Wi-Fi"</string>
@@ -371,10 +370,10 @@
<string name="power_remaining_duration_only_enhanced" msgid="4189311599812296592">"يتبقى <xliff:g id="TIME_REMAINING">%1$s</xliff:g> تقريبًا، بناءً على استخدامك"</string>
<string name="power_discharging_duration_enhanced" msgid="1992003260664804080">"يتبقى <xliff:g id="TIME_REMAINING">%1$s</xliff:g> تقريبًا، بناءً على استخدامك (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_duration_only_short" msgid="3463575350656389957">"الوقت المتبقي: <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
- <string name="power_discharge_by_enhanced" msgid="2095821536747992464">"من المفترض أن يستمر شحن البطارية حتى حوالي الساعة <xliff:g id="TIME">%1$s</xliff:g> حسب استخدامك (<xliff:g id="LEVEL">%2$s</xliff:g>)."</string>
- <string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"من المفترض أن يستمر شحن البطارية حتى حوالي الساعة <xliff:g id="TIME">%1$s</xliff:g> حسب استخدامك."</string>
- <string name="power_discharge_by" msgid="6453537733650125582">"من المفترض أن يستمر شحن البطارية حتى حوالي الساعة <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)."</string>
- <string name="power_discharge_by_only" msgid="107616694963545745">"من المفترض أن يستمر شحن البطارية حتى حوالي الساعة <xliff:g id="TIME">%1$s</xliff:g>."</string>
+ <string name="power_discharge_by_enhanced" msgid="2095821536747992464">"قد تكفي طاقة البطارية حتى حوالي الساعة <xliff:g id="TIME">%1$s</xliff:g> حسب استخدامك (<xliff:g id="LEVEL">%2$s</xliff:g>)."</string>
+ <string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"قد تكفي طاقة البطارية حتى حوالي الساعة <xliff:g id="TIME">%1$s</xliff:g> حسب استخدامك."</string>
+ <string name="power_discharge_by" msgid="6453537733650125582">"قد تكفي طاقة البطارية حتى حوالي الساعة <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)."</string>
+ <string name="power_discharge_by_only" msgid="107616694963545745">"قد تكفي طاقة البطارية حتى حوالي الساعة <xliff:g id="TIME">%1$s</xliff:g>."</string>
<string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"يتبقى أقل من <xliff:g id="THRESHOLD">%1$s</xliff:g>."</string>
<string name="power_remaining_less_than_duration" msgid="5751885147712659423">"يتبقى أقل من <xliff:g id="THRESHOLD">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)."</string>
<string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"يتبقى أكثر من <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)."</string>
diff --git a/packages/SettingsLib/res/values-as/strings.xml b/packages/SettingsLib/res/values-as/strings.xml
index ddd17daf..72122c2 100644
--- a/packages/SettingsLib/res/values-as/strings.xml
+++ b/packages/SettingsLib/res/values-as/strings.xml
@@ -21,7 +21,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"নেটৱৰ্ক বিচাৰি স্কেন কৰিব পৰা নাই"</string>
- <string name="wifi_security_none" msgid="7985461072596594400">"নাই"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"ছেভ কৰি থোৱা নেটৱৰ্কসমূহ"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"নিষ্ক্ৰিয় হৈ আছে"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP কনফিগাৰেশ্বন বিফল হৈছে"</string>
diff --git a/packages/SettingsLib/res/values-az/strings.xml b/packages/SettingsLib/res/values-az/strings.xml
index a6e484f..23008e2 100644
--- a/packages/SettingsLib/res/values-az/strings.xml
+++ b/packages/SettingsLib/res/values-az/strings.xml
@@ -21,7 +21,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"Şəbəkə axtarmaq olmur"</string>
- <string name="wifi_security_none" msgid="7985461072596594400">"Heç biri"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"Yadda saxlanılan"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"Deaktiv"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP Konfiqurasiya Uğursuzluğu"</string>
diff --git a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
index a446845..3ee8589 100644
--- a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
+++ b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
@@ -21,13 +21,12 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"Nije moguće skenirati mreže"</string>
- <string name="wifi_security_none" msgid="7985461072596594400">"Nema"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"Sačuvano"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"Onemogućeno"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP konfiguracija je otkazala"</string>
<string name="wifi_disabled_by_recommendation_provider" msgid="5168315140978066096">"Nije povezano zbog lošeg kvaliteta mreže"</string>
<string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"Wi-Fi veza je otkazala"</string>
- <string name="wifi_disabled_password_failure" msgid="8659805351763133575">"Problem sa potvrdom autentičnosti"</string>
+ <string name="wifi_disabled_password_failure" msgid="8659805351763133575">"Problem sa potvrdom identiteta"</string>
<string name="wifi_cant_connect" msgid="5410016875644565884">"Povezivanje nije uspelo"</string>
<string name="wifi_cant_connect_to_ap" msgid="1222553274052685331">"Povezivanje sa „<xliff:g id="AP_NAME">%1$s</xliff:g>“ nije uspelo"</string>
<string name="wifi_check_password_try_again" msgid="516958988102584767">"Proverite lozinku i probajte ponovo"</string>
@@ -448,5 +447,5 @@
<string name="zen_mode_duration_settings_title" msgid="229547412251222757">"Trajanje"</string>
<string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Uvek pitaj"</string>
<string name="zen_mode_forever" msgid="2704305038191592967">"Dok ne isključite"</string>
- <string name="time_unit_just_now" msgid="6363336622778342422">"Upravo sada"</string>
+ <string name="time_unit_just_now" msgid="6363336622778342422">"Upravo"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-be/strings.xml b/packages/SettingsLib/res/values-be/strings.xml
index d56db13..64253bf 100644
--- a/packages/SettingsLib/res/values-be/strings.xml
+++ b/packages/SettingsLib/res/values-be/strings.xml
@@ -21,7 +21,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"Не атрымлiваецца выканаць сканаванне для сетак"</string>
- <string name="wifi_security_none" msgid="7985461072596594400">"Няма"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"Захавана"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"Адключана"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"Збой канфігурацыі IP"</string>
diff --git a/packages/SettingsLib/res/values-bg/strings.xml b/packages/SettingsLib/res/values-bg/strings.xml
index c1bc31c..668aa2d 100644
--- a/packages/SettingsLib/res/values-bg/strings.xml
+++ b/packages/SettingsLib/res/values-bg/strings.xml
@@ -21,7 +21,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"Не може да се сканира за мрежи"</string>
- <string name="wifi_security_none" msgid="7985461072596594400">"Няма"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"Запазено"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"Деактивирани"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"Неуспешно конфигуриране на IP адреса"</string>
@@ -374,7 +373,7 @@
<string name="power_discharge_by_enhanced" msgid="2095821536747992464">"Следва да издържи приблизително до <xliff:g id="TIME">%1$s</xliff:g> въз основа на използването (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Следва да издържи приблизително до <xliff:g id="TIME">%1$s</xliff:g> въз основа на използването"</string>
<string name="power_discharge_by" msgid="6453537733650125582">"Следва да издържи приблизително до <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
- <string name="power_discharge_by_only" msgid="107616694963545745">"Следва да издържи приблизително до <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="power_discharge_by_only" msgid="107616694963545745">"Следва да издържи до около <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Остава/т по-малко от <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration" msgid="5751885147712659423">"Остава/т по-малко от <xliff:g id="THRESHOLD">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"Остава/т повече от <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
diff --git a/packages/SettingsLib/res/values-bn/strings.xml b/packages/SettingsLib/res/values-bn/strings.xml
index 1550c00..c6eed2c 100644
--- a/packages/SettingsLib/res/values-bn/strings.xml
+++ b/packages/SettingsLib/res/values-bn/strings.xml
@@ -21,7 +21,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"নেটওয়ার্কগুলির জন্য স্ক্যান করা যাবে না"</string>
- <string name="wifi_security_none" msgid="7985461072596594400">"কোনো কিছুই নয়"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"সংরক্ষিত"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"অক্ষম হয়েছে"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP কনফিগারেশনের ব্যর্থতা"</string>
diff --git a/packages/SettingsLib/res/values-bs/strings.xml b/packages/SettingsLib/res/values-bs/strings.xml
index 460e7cf..16179fb 100644
--- a/packages/SettingsLib/res/values-bs/strings.xml
+++ b/packages/SettingsLib/res/values-bs/strings.xml
@@ -21,7 +21,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"Ne može skenirati mreže"</string>
- <string name="wifi_security_none" msgid="7985461072596594400">"Nema"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"Sačuvano"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"Onemogućeno"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"Greška u konfiguraciji IP-a"</string>
diff --git a/packages/SettingsLib/res/values-ca/strings.xml b/packages/SettingsLib/res/values-ca/strings.xml
index 5638947..f906ea4 100644
--- a/packages/SettingsLib/res/values-ca/strings.xml
+++ b/packages/SettingsLib/res/values-ca/strings.xml
@@ -21,7 +21,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"No es poden cercar xarxes"</string>
- <string name="wifi_security_none" msgid="7985461072596594400">"Cap"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"Desat"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"Desactivat"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"Error de configuració d\'IP"</string>
@@ -70,7 +69,7 @@
<string name="bluetooth_active_no_battery_level" msgid="8380223546730241956">"Actiu"</string>
<string name="bluetooth_profile_a2dp" msgid="2031475486179830674">"Àudio multimèdia"</string>
<string name="bluetooth_profile_headset" msgid="7815495680863246034">"Trucades telefòniques"</string>
- <string name="bluetooth_profile_opp" msgid="9168139293654233697">"Transferència del fitxer"</string>
+ <string name="bluetooth_profile_opp" msgid="9168139293654233697">"Transferència de fitxers"</string>
<string name="bluetooth_profile_hid" msgid="3680729023366986480">"Dispositiu d\'entrada"</string>
<string name="bluetooth_profile_pan" msgid="3391606497945147673">"Accés a Internet"</string>
<string name="bluetooth_profile_pbap" msgid="5372051906968576809">"Compartir contactes"</string>
@@ -393,7 +392,7 @@
<string name="battery_info_status_charging_lower" msgid="8689770213898117994">"s\'està carregant"</string>
<string name="battery_info_status_discharging" msgid="310932812698268588">"No s\'està carregant"</string>
<string name="battery_info_status_not_charging" msgid="8523453668342598579">"El dispositiu està endollat però en aquests moments no es pot carregar"</string>
- <string name="battery_info_status_full" msgid="2824614753861462808">"Plena"</string>
+ <string name="battery_info_status_full" msgid="2824614753861462808">"Completa"</string>
<string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"Controlat per l\'administrador"</string>
<string name="disabled" msgid="9206776641295849915">"Desactivat"</string>
<string name="external_source_trusted" msgid="2707996266575928037">"Amb permís"</string>
diff --git a/packages/SettingsLib/res/values-cs/strings.xml b/packages/SettingsLib/res/values-cs/strings.xml
index de02245..f9c8669 100644
--- a/packages/SettingsLib/res/values-cs/strings.xml
+++ b/packages/SettingsLib/res/values-cs/strings.xml
@@ -21,7 +21,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"Nelze hledat sítě"</string>
- <string name="wifi_security_none" msgid="7985461072596594400">"Žádné"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"Uloženo"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"Vypnuto"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"Selhání konfigurace protokolu IP"</string>
diff --git a/packages/SettingsLib/res/values-da/strings.xml b/packages/SettingsLib/res/values-da/strings.xml
index 34608d3..6b2bd98 100644
--- a/packages/SettingsLib/res/values-da/strings.xml
+++ b/packages/SettingsLib/res/values-da/strings.xml
@@ -21,7 +21,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"Der kan ikke søges efter netværk"</string>
- <string name="wifi_security_none" msgid="7985461072596594400">"Ingen"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"Gemt"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"Deaktiveret"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP-konfigurationsfejl"</string>
diff --git a/packages/SettingsLib/res/values-de/strings.xml b/packages/SettingsLib/res/values-de/strings.xml
index 79965d2..ad6f5f2 100644
--- a/packages/SettingsLib/res/values-de/strings.xml
+++ b/packages/SettingsLib/res/values-de/strings.xml
@@ -21,7 +21,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"Netzwerkscan nicht möglich"</string>
- <string name="wifi_security_none" msgid="7985461072596594400">"Keine"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"Gespeichert"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"Deaktiviert"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP-Konfigurationsfehler"</string>
@@ -231,7 +230,7 @@
<string name="private_dns_mode_off" msgid="8236575187318721684">"Aus"</string>
<string name="private_dns_mode_opportunistic" msgid="8314986739896927399">"Automatisch"</string>
<string name="private_dns_mode_provider" msgid="8354935160639360804">"Hostname des privaten DNS-Anbieters"</string>
- <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"Hostnamen des DNS-Anbieters eingeben"</string>
+ <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"Hostname des DNS-Anbieters eingeben"</string>
<string name="private_dns_mode_provider_failure" msgid="231837290365031223">"Verbindung nicht möglich"</string>
<string name="wifi_display_certification_summary" msgid="1155182309166746973">"Optionen zur Zertifizierung für kabellose Übertragung anzeigen"</string>
<string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Level für WLAN-Protokollierung erhöhen, in WiFi Picker pro SSID-RSSI anzeigen"</string>
diff --git a/packages/SettingsLib/res/values-el/strings.xml b/packages/SettingsLib/res/values-el/strings.xml
index b9ea843..5bfbf2c 100644
--- a/packages/SettingsLib/res/values-el/strings.xml
+++ b/packages/SettingsLib/res/values-el/strings.xml
@@ -21,7 +21,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"Δεν είναι δυνατή η σάρωση για δίκτυα"</string>
- <string name="wifi_security_none" msgid="7985461072596594400">"Καμία"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"Αποθηκευμένο"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"Απενεργοποιημένο"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"Αποτυχία διαμόρφωσης διεύθυνσης IP"</string>
@@ -231,7 +230,7 @@
<string name="private_dns_mode_off" msgid="8236575187318721684">"Ανενεργή"</string>
<string name="private_dns_mode_opportunistic" msgid="8314986739896927399">"Αυτόματα"</string>
<string name="private_dns_mode_provider" msgid="8354935160639360804">"Όνομα κεντρικού υπολογιστή παρόχου DNS"</string>
- <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"Εισαγάγετε το όνομα κεντρικού υπολογιστή του παρόχου DNS"</string>
+ <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"Όνομα κεντρικού υπολογιστή του παρόχου DNS"</string>
<string name="private_dns_mode_provider_failure" msgid="231837290365031223">"Δεν ήταν δυνατή η σύνδεση"</string>
<string name="wifi_display_certification_summary" msgid="1155182309166746973">"Εμφάνιση επιλογών για πιστοποίηση ασύρματης οθόνης"</string>
<string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Αύξηση επιπέδου καταγ. Wi-Fi, εμφάνιση ανά SSID RSSI στο εργαλείο επιλογής Wi-Fi"</string>
diff --git a/packages/SettingsLib/res/values-en-rAU/strings.xml b/packages/SettingsLib/res/values-en-rAU/strings.xml
index 1a7d7c5..f0eaeed 100644
--- a/packages/SettingsLib/res/values-en-rAU/strings.xml
+++ b/packages/SettingsLib/res/values-en-rAU/strings.xml
@@ -21,7 +21,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"Can\'t scan for networks"</string>
- <string name="wifi_security_none" msgid="7985461072596594400">"None"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"Saved"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"Disabled"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP Configuration Failure"</string>
diff --git a/packages/SettingsLib/res/values-en-rCA/strings.xml b/packages/SettingsLib/res/values-en-rCA/strings.xml
index 1a7d7c5..f0eaeed 100644
--- a/packages/SettingsLib/res/values-en-rCA/strings.xml
+++ b/packages/SettingsLib/res/values-en-rCA/strings.xml
@@ -21,7 +21,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"Can\'t scan for networks"</string>
- <string name="wifi_security_none" msgid="7985461072596594400">"None"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"Saved"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"Disabled"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP Configuration Failure"</string>
diff --git a/packages/SettingsLib/res/values-en-rGB/strings.xml b/packages/SettingsLib/res/values-en-rGB/strings.xml
index 1a7d7c5..f0eaeed 100644
--- a/packages/SettingsLib/res/values-en-rGB/strings.xml
+++ b/packages/SettingsLib/res/values-en-rGB/strings.xml
@@ -21,7 +21,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"Can\'t scan for networks"</string>
- <string name="wifi_security_none" msgid="7985461072596594400">"None"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"Saved"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"Disabled"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP Configuration Failure"</string>
diff --git a/packages/SettingsLib/res/values-en-rIN/strings.xml b/packages/SettingsLib/res/values-en-rIN/strings.xml
index 1a7d7c5..f0eaeed 100644
--- a/packages/SettingsLib/res/values-en-rIN/strings.xml
+++ b/packages/SettingsLib/res/values-en-rIN/strings.xml
@@ -21,7 +21,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"Can\'t scan for networks"</string>
- <string name="wifi_security_none" msgid="7985461072596594400">"None"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"Saved"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"Disabled"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP Configuration Failure"</string>
diff --git a/packages/SettingsLib/res/values-en-rXC/strings.xml b/packages/SettingsLib/res/values-en-rXC/strings.xml
index 2bfd5b1..6927fda 100644
--- a/packages/SettingsLib/res/values-en-rXC/strings.xml
+++ b/packages/SettingsLib/res/values-en-rXC/strings.xml
@@ -21,7 +21,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"Can\'t scan for networks"</string>
- <string name="wifi_security_none" msgid="7985461072596594400">"None"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"Saved"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"Disabled"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP Configuration Failure"</string>
diff --git a/packages/SettingsLib/res/values-es-rUS/strings.xml b/packages/SettingsLib/res/values-es-rUS/strings.xml
index 790c0d3..675084f 100644
--- a/packages/SettingsLib/res/values-es-rUS/strings.xml
+++ b/packages/SettingsLib/res/values-es-rUS/strings.xml
@@ -21,7 +21,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"No se pueden buscar las redes."</string>
- <string name="wifi_security_none" msgid="7985461072596594400">"Ninguna"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"Guardada"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"Inhabilitada"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"Error de configuración IP"</string>
diff --git a/packages/SettingsLib/res/values-es/strings.xml b/packages/SettingsLib/res/values-es/strings.xml
index 1dec85d..b51b847 100644
--- a/packages/SettingsLib/res/values-es/strings.xml
+++ b/packages/SettingsLib/res/values-es/strings.xml
@@ -21,7 +21,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"No se puede buscar redes."</string>
- <string name="wifi_security_none" msgid="7985461072596594400">"Ninguna"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"Guardado"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"Inhabilitado"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"Error de configuración de IP"</string>
diff --git a/packages/SettingsLib/res/values-et/strings.xml b/packages/SettingsLib/res/values-et/strings.xml
index 406e15d..fb077be 100644
--- a/packages/SettingsLib/res/values-et/strings.xml
+++ b/packages/SettingsLib/res/values-et/strings.xml
@@ -21,7 +21,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"Võrke ei saa kontrollida"</string>
- <string name="wifi_security_none" msgid="7985461072596594400">"Puudub"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"Salvestatud"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"Keelatud"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP seadistamise ebaõnnestumine"</string>
diff --git a/packages/SettingsLib/res/values-eu/strings.xml b/packages/SettingsLib/res/values-eu/strings.xml
index bd4724e..3d3b8c3 100644
--- a/packages/SettingsLib/res/values-eu/strings.xml
+++ b/packages/SettingsLib/res/values-eu/strings.xml
@@ -21,7 +21,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"Ezin dira sareak bilatu"</string>
- <string name="wifi_security_none" msgid="7985461072596594400">"Bat ere ez"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"Gordeta"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"Desgaituta"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"Ezin izan da konfiguratu IP helbidea"</string>
diff --git a/packages/SettingsLib/res/values-fa/strings.xml b/packages/SettingsLib/res/values-fa/strings.xml
index 45b8ac7..0af118e 100644
--- a/packages/SettingsLib/res/values-fa/strings.xml
+++ b/packages/SettingsLib/res/values-fa/strings.xml
@@ -21,7 +21,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"اسکن شبکهها امکانپذیر نیست"</string>
- <string name="wifi_security_none" msgid="7985461072596594400">"هیچکدام"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"ذخیرهشده"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"غیرفعال شد"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"پیکربندی IP انجام نشد"</string>
diff --git a/packages/SettingsLib/res/values-fi/strings.xml b/packages/SettingsLib/res/values-fi/strings.xml
index 23c63b1..dde10d0d 100644
--- a/packages/SettingsLib/res/values-fi/strings.xml
+++ b/packages/SettingsLib/res/values-fi/strings.xml
@@ -21,7 +21,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"Verkkoja ei voi etsiä."</string>
- <string name="wifi_security_none" msgid="7985461072596594400">"Ei mitään"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"Tallennettu"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"Pois käytöstä"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP-kokoonpanovirhe"</string>
diff --git a/packages/SettingsLib/res/values-fr-rCA/strings.xml b/packages/SettingsLib/res/values-fr-rCA/strings.xml
index ad99000..1257ea5 100644
--- a/packages/SettingsLib/res/values-fr-rCA/strings.xml
+++ b/packages/SettingsLib/res/values-fr-rCA/strings.xml
@@ -21,7 +21,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"Impossible de rechercher des réseaux."</string>
- <string name="wifi_security_none" msgid="7985461072596594400">"Aucune"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"Enregistré"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"Désactivés"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"Échec de configuration de l\'adresse IP"</string>
@@ -227,7 +226,7 @@
<string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="6893955536658137179">"Déclencher le codec audio Bluetooth LDAC\nSélection : qualité de lecture"</string>
<string name="bluetooth_select_a2dp_codec_streaming_label" msgid="5347862512596240506">"Diffusion : <xliff:g id="STREAMING_PARAMETER">%1$s</xliff:g>"</string>
<string name="select_private_dns_configuration_title" msgid="3700456559305263922">"DNS privé"</string>
- <string name="select_private_dns_configuration_dialog_title" msgid="9221994415765826811">"Sélectionnez le mode DNS privé"</string>
+ <string name="select_private_dns_configuration_dialog_title" msgid="9221994415765826811">"Sélectionner le mode DNS privé"</string>
<string name="private_dns_mode_off" msgid="8236575187318721684">"Désactivé"</string>
<string name="private_dns_mode_opportunistic" msgid="8314986739896927399">"Automatique"</string>
<string name="private_dns_mode_provider" msgid="8354935160639360804">"Nom d\'hôte du fournisseur DNS privé"</string>
diff --git a/packages/SettingsLib/res/values-fr/strings.xml b/packages/SettingsLib/res/values-fr/strings.xml
index c23db25..444a52e 100644
--- a/packages/SettingsLib/res/values-fr/strings.xml
+++ b/packages/SettingsLib/res/values-fr/strings.xml
@@ -21,7 +21,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"Impossible de rechercher des réseaux."</string>
- <string name="wifi_security_none" msgid="7985461072596594400">"Aucune"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"Enregistré"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"Désactivé"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"Échec de configuration de l\'adresse IP"</string>
@@ -70,7 +69,7 @@
<string name="bluetooth_active_no_battery_level" msgid="8380223546730241956">"Actif"</string>
<string name="bluetooth_profile_a2dp" msgid="2031475486179830674">"Multimédia"</string>
<string name="bluetooth_profile_headset" msgid="7815495680863246034">"Appels téléphoniques"</string>
- <string name="bluetooth_profile_opp" msgid="9168139293654233697">"Transfert de fichier"</string>
+ <string name="bluetooth_profile_opp" msgid="9168139293654233697">"Transfert de fichiers"</string>
<string name="bluetooth_profile_hid" msgid="3680729023366986480">"Périphérique d\'entrée"</string>
<string name="bluetooth_profile_pan" msgid="3391606497945147673">"Accès Internet"</string>
<string name="bluetooth_profile_pbap" msgid="5372051906968576809">"Partage de contacts"</string>
diff --git a/packages/SettingsLib/res/values-gl/strings.xml b/packages/SettingsLib/res/values-gl/strings.xml
index 46532e9..8df2697 100644
--- a/packages/SettingsLib/res/values-gl/strings.xml
+++ b/packages/SettingsLib/res/values-gl/strings.xml
@@ -21,7 +21,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"Non se poden explorar redes"</string>
- <string name="wifi_security_none" msgid="7985461072596594400">"Ningunha"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"Gardada"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"Desactivadas"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"Erro na configuración de IP"</string>
diff --git a/packages/SettingsLib/res/values-gu/strings.xml b/packages/SettingsLib/res/values-gu/strings.xml
index 6a74490..4a92f2a 100644
--- a/packages/SettingsLib/res/values-gu/strings.xml
+++ b/packages/SettingsLib/res/values-gu/strings.xml
@@ -21,7 +21,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"નેટવર્ક્સ માટે સ્કૅન કરી શકતા નથી"</string>
- <string name="wifi_security_none" msgid="7985461072596594400">"કોઈ નહીં"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"સાચવેલા"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"અક્ષમ કર્યો"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP કન્ફિગરેશન નિષ્ફળ"</string>
diff --git a/packages/SettingsLib/res/values-hi/strings.xml b/packages/SettingsLib/res/values-hi/strings.xml
index bd9a6ec..6d88c35 100644
--- a/packages/SettingsLib/res/values-hi/strings.xml
+++ b/packages/SettingsLib/res/values-hi/strings.xml
@@ -21,7 +21,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"नेटवर्क के लिए स्कैन नहीं कर सकता"</string>
- <string name="wifi_security_none" msgid="7985461072596594400">"कोई नहीं"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"सेव किया गया"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"अक्षम"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP कॉन्फ़िगरेशन की विफलता"</string>
diff --git a/packages/SettingsLib/res/values-hr/strings.xml b/packages/SettingsLib/res/values-hr/strings.xml
index 5270531..32c9a62 100644
--- a/packages/SettingsLib/res/values-hr/strings.xml
+++ b/packages/SettingsLib/res/values-hr/strings.xml
@@ -21,7 +21,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"Skeniranje mreža nije moguće"</string>
- <string name="wifi_security_none" msgid="7985461072596594400">"Nema"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"Spremljeno"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"Onemogućeno"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"Konfiguracija IP-a nije uspjela"</string>
diff --git a/packages/SettingsLib/res/values-hu/strings.xml b/packages/SettingsLib/res/values-hu/strings.xml
index ad897f8..ed19267 100644
--- a/packages/SettingsLib/res/values-hu/strings.xml
+++ b/packages/SettingsLib/res/values-hu/strings.xml
@@ -21,7 +21,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"Nem lehet beolvasni a hálózatokat"</string>
- <string name="wifi_security_none" msgid="7985461072596594400">"Nincs"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"Mentve"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"Letiltva"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP-konfigurációs hiba"</string>
diff --git a/packages/SettingsLib/res/values-hy/strings.xml b/packages/SettingsLib/res/values-hy/strings.xml
index 82755f9..6d516ea 100644
--- a/packages/SettingsLib/res/values-hy/strings.xml
+++ b/packages/SettingsLib/res/values-hy/strings.xml
@@ -21,7 +21,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"Հնարավոր չէ սկանավորել ցանցերը"</string>
- <string name="wifi_security_none" msgid="7985461072596594400">"Ոչ մեկը"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"Պահված է"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"Անջատված"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP կարգավորման ձախողում"</string>
diff --git a/packages/SettingsLib/res/values-in/strings.xml b/packages/SettingsLib/res/values-in/strings.xml
index 961f892..a5f2317 100644
--- a/packages/SettingsLib/res/values-in/strings.xml
+++ b/packages/SettingsLib/res/values-in/strings.xml
@@ -21,7 +21,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"Tidak dapat memindai jaringan"</string>
- <string name="wifi_security_none" msgid="7985461072596594400">"Tidak ada"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"Disimpan"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"Nonaktif"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"Kegagalan Konfigurasi IP"</string>
diff --git a/packages/SettingsLib/res/values-is/strings.xml b/packages/SettingsLib/res/values-is/strings.xml
index 759b7ba..6cf09a6 100644
--- a/packages/SettingsLib/res/values-is/strings.xml
+++ b/packages/SettingsLib/res/values-is/strings.xml
@@ -21,7 +21,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"Ekki er hægt að leita að netum"</string>
- <string name="wifi_security_none" msgid="7985461072596594400">"Ekkert"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"Vistað"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"Óvirkt"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP-stillingarvilla"</string>
diff --git a/packages/SettingsLib/res/values-it/strings.xml b/packages/SettingsLib/res/values-it/strings.xml
index bd41e28..a1b9b82 100644
--- a/packages/SettingsLib/res/values-it/strings.xml
+++ b/packages/SettingsLib/res/values-it/strings.xml
@@ -21,7 +21,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"Impossibile cercare reti"</string>
- <string name="wifi_security_none" msgid="7985461072596594400">"Nessuna"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"Salvata"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"Disattivata"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"Errore configurazione IP"</string>
diff --git a/packages/SettingsLib/res/values-iw/strings.xml b/packages/SettingsLib/res/values-iw/strings.xml
index 5da253c..1a50622 100644
--- a/packages/SettingsLib/res/values-iw/strings.xml
+++ b/packages/SettingsLib/res/values-iw/strings.xml
@@ -21,7 +21,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"לא ניתן לסרוק לאיתור רשתות"</string>
- <string name="wifi_security_none" msgid="7985461072596594400">"ללא"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"נשמר"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"מושבת"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"כשל בתצורת IP"</string>
@@ -316,7 +315,7 @@
<string name="app_process_limit_title" msgid="4280600650253107163">"מגבלה של תהליכים ברקע"</string>
<string name="show_all_anrs" msgid="4924885492787069007">"הצגת מקרי ANR ברקע"</string>
<string name="show_all_anrs_summary" msgid="6636514318275139826">"הצגת תיבת דו-שיח של \'אפליקציה לא מגיבה\' עבור אפליקציות שפועלות ברקע"</string>
- <string name="show_notification_channel_warnings" msgid="1399948193466922683">"אזהרות לגבי ערוץ הודעות"</string>
+ <string name="show_notification_channel_warnings" msgid="1399948193466922683">"אזהרות לגבי ערוץ התראות"</string>
<string name="show_notification_channel_warnings_summary" msgid="5536803251863694895">"הצגת אזהרה כשאפליקציה שולחת התראה ללא ערוץ חוקי"</string>
<string name="force_allow_on_external" msgid="3215759785081916381">"אילוץ הרשאת אפליקציות באחסון חיצוני"</string>
<string name="force_allow_on_external_summary" msgid="3640752408258034689">"מאפשר כתיבה של כל אפליקציה באחסון חיצוני, ללא התחשבות בערכי המניפסט"</string>
diff --git a/packages/SettingsLib/res/values-ja/strings.xml b/packages/SettingsLib/res/values-ja/strings.xml
index f6b81ed..4c544af 100644
--- a/packages/SettingsLib/res/values-ja/strings.xml
+++ b/packages/SettingsLib/res/values-ja/strings.xml
@@ -21,7 +21,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"ネットワークをスキャンできません"</string>
- <string name="wifi_security_none" msgid="7985461072596594400">"なし"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"保存済み"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"無効"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP設定エラー"</string>
@@ -436,7 +435,7 @@
<string name="cancel" msgid="6859253417269739139">"キャンセル"</string>
<string name="okay" msgid="1997666393121016642">"OK"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="8287824809739581837">"ON にする"</string>
- <string name="zen_mode_settings_turn_on_dialog_title" msgid="2297134204747331078">"マナーモードを ON にする"</string>
+ <string name="zen_mode_settings_turn_on_dialog_title" msgid="2297134204747331078">"サイレント モードを ON にする"</string>
<string name="zen_mode_settings_summary_off" msgid="6119891445378113334">"なし"</string>
<string name="zen_interruption_level_priority" msgid="2078370238113347720">"優先的な通知のみ"</string>
<string name="zen_mode_and_condition" msgid="4927230238450354412">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>。<xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-ka/strings.xml b/packages/SettingsLib/res/values-ka/strings.xml
index d5f5408..92f3049 100644
--- a/packages/SettingsLib/res/values-ka/strings.xml
+++ b/packages/SettingsLib/res/values-ka/strings.xml
@@ -21,7 +21,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"ქსელების სკანირება არა არის შესაძლებელი"</string>
- <string name="wifi_security_none" msgid="7985461072596594400">"არცერთი"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"დამახსოვრებულია"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"გამორთულია"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP კონფიგურაციის შეფერხება"</string>
diff --git a/packages/SettingsLib/res/values-kk/strings.xml b/packages/SettingsLib/res/values-kk/strings.xml
index c3fbfcd..b56c6fd 100644
--- a/packages/SettingsLib/res/values-kk/strings.xml
+++ b/packages/SettingsLib/res/values-kk/strings.xml
@@ -21,7 +21,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"Желілерді шолу мүмкін емес"</string>
- <string name="wifi_security_none" msgid="7985461072596594400">"Ешқандай"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"Сақталды"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"Өшірілген"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP конфигурациясының қатесі"</string>
diff --git a/packages/SettingsLib/res/values-km/strings.xml b/packages/SettingsLib/res/values-km/strings.xml
index 8e73f81..5860473 100644
--- a/packages/SettingsLib/res/values-km/strings.xml
+++ b/packages/SettingsLib/res/values-km/strings.xml
@@ -21,7 +21,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"មិនអាចវិភាគរកបណ្ដាញ"</string>
- <string name="wifi_security_none" msgid="7985461072596594400">"គ្មាន"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"បានរក្សាទុក"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"បានបិទ"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"ការកំណត់រចនាសម្ព័ន្ធ IP បរាជ័យ"</string>
diff --git a/packages/SettingsLib/res/values-kn/strings.xml b/packages/SettingsLib/res/values-kn/strings.xml
index ce225ea..87e18e6 100644
--- a/packages/SettingsLib/res/values-kn/strings.xml
+++ b/packages/SettingsLib/res/values-kn/strings.xml
@@ -21,7 +21,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"ನೆಟ್ವರ್ಕ್ಗಳಿಗಾಗಿ ಸ್ಕ್ಯಾನ್ ಮಾಡಲು ಸಾಧ್ಯವಿಲ್ಲ"</string>
- <string name="wifi_security_none" msgid="7985461072596594400">"ಯಾವುದೂ ಇಲ್ಲ"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"ಉಳಿಸಲಾಗಿದೆ"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆ"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP ಕಾನ್ಫಿಗರೇಶನ್ ವಿಫಲತೆ"</string>
diff --git a/packages/SettingsLib/res/values-ko/strings.xml b/packages/SettingsLib/res/values-ko/strings.xml
index b47471b..3333c0d 100644
--- a/packages/SettingsLib/res/values-ko/strings.xml
+++ b/packages/SettingsLib/res/values-ko/strings.xml
@@ -21,7 +21,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"네트워크를 검색할 수 없습니다."</string>
- <string name="wifi_security_none" msgid="7985461072596594400">"없음"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"저장됨"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"사용 중지됨"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP 설정 실패"</string>
diff --git a/packages/SettingsLib/res/values-ky/strings.xml b/packages/SettingsLib/res/values-ky/strings.xml
index 9853dac..cbbc47c 100644
--- a/packages/SettingsLib/res/values-ky/strings.xml
+++ b/packages/SettingsLib/res/values-ky/strings.xml
@@ -21,7 +21,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"Түйүндөрдү издөө мүмкүн эмес"</string>
- <string name="wifi_security_none" msgid="7985461072596594400">"Жок"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"Сакталды"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"Өчүрүлгөн"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP конфигурациясы бузулду"</string>
diff --git a/packages/SettingsLib/res/values-lo/strings.xml b/packages/SettingsLib/res/values-lo/strings.xml
index 1816515..21e4679 100644
--- a/packages/SettingsLib/res/values-lo/strings.xml
+++ b/packages/SettingsLib/res/values-lo/strings.xml
@@ -21,7 +21,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"ບໍ່ສາມາດກວດຫາເຄືອຂ່າຍໄດ້"</string>
- <string name="wifi_security_none" msgid="7985461072596594400">"ບໍ່ໃຊ້"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"ບັນທຶກແລ້ວ"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"ປິດການນຳໃຊ້"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"ການຕັ້ງຄ່າ IP ລົ້ມເຫຼວ"</string>
diff --git a/packages/SettingsLib/res/values-lt/strings.xml b/packages/SettingsLib/res/values-lt/strings.xml
index e6a8e1c..cbff9e7 100644
--- a/packages/SettingsLib/res/values-lt/strings.xml
+++ b/packages/SettingsLib/res/values-lt/strings.xml
@@ -21,7 +21,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"Nepavyksta nuskaityti tinklų"</string>
- <string name="wifi_security_none" msgid="7985461072596594400">"Nėra"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"Išsaugotas"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"Neleidžiama"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP konfigūracijos triktis"</string>
diff --git a/packages/SettingsLib/res/values-lv/strings.xml b/packages/SettingsLib/res/values-lv/strings.xml
index b55afc0..d200828 100644
--- a/packages/SettingsLib/res/values-lv/strings.xml
+++ b/packages/SettingsLib/res/values-lv/strings.xml
@@ -21,7 +21,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"Nevar skenēt tīklus"</string>
- <string name="wifi_security_none" msgid="7985461072596594400">"Nav"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"Saglabāts"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"Atspējots"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP konfigurācijas kļūme"</string>
diff --git a/packages/SettingsLib/res/values-mk/strings.xml b/packages/SettingsLib/res/values-mk/strings.xml
index e28c8ee..db16847 100644
--- a/packages/SettingsLib/res/values-mk/strings.xml
+++ b/packages/SettingsLib/res/values-mk/strings.xml
@@ -21,7 +21,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"Не може да скенира за мрежи"</string>
- <string name="wifi_security_none" msgid="7985461072596594400">"Ниедна"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"Зачувано"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"Оневозможено"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"Конфигурирањето ИП не успеа"</string>
diff --git a/packages/SettingsLib/res/values-ml/strings.xml b/packages/SettingsLib/res/values-ml/strings.xml
index 6860398..c3af968 100644
--- a/packages/SettingsLib/res/values-ml/strings.xml
+++ b/packages/SettingsLib/res/values-ml/strings.xml
@@ -21,7 +21,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"നെറ്റ്വർക്കുകൾക്കായി സ്കാൻ ചെയ്യാനായില്ല"</string>
- <string name="wifi_security_none" msgid="7985461072596594400">"ഒന്നുമില്ല"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"സംരക്ഷിച്ചു"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"പ്രവർത്തനരഹിതമാക്കി"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP കോൺഫിഗറേഷൻ പരാജയം"</string>
diff --git a/packages/SettingsLib/res/values-mn/strings.xml b/packages/SettingsLib/res/values-mn/strings.xml
index ba6d601..8627e1b 100644
--- a/packages/SettingsLib/res/values-mn/strings.xml
+++ b/packages/SettingsLib/res/values-mn/strings.xml
@@ -21,7 +21,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"Сүлжээнүүдийг скан хийх боломжгүй"</string>
- <string name="wifi_security_none" msgid="7985461072596594400">"Байхгүй"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"Хадгалагдсан"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"Идэвхгүйжүүлсэн"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP тохируулга амжилтгүй"</string>
diff --git a/packages/SettingsLib/res/values-mr/strings.xml b/packages/SettingsLib/res/values-mr/strings.xml
index a5d230a..cd7f175 100644
--- a/packages/SettingsLib/res/values-mr/strings.xml
+++ b/packages/SettingsLib/res/values-mr/strings.xml
@@ -21,7 +21,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"नेटवर्कसाठी स्कॅन करू शकत नाही"</string>
- <string name="wifi_security_none" msgid="7985461072596594400">"काहीही नाही"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"सेव्ह केले"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"अक्षम"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP कॉन्फिगरेशन अयशस्वी"</string>
diff --git a/packages/SettingsLib/res/values-ms/strings.xml b/packages/SettingsLib/res/values-ms/strings.xml
index a7ac3ef..d0b2e12 100644
--- a/packages/SettingsLib/res/values-ms/strings.xml
+++ b/packages/SettingsLib/res/values-ms/strings.xml
@@ -21,7 +21,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"Tidak boleh mengimbas untuk rangkaian"</string>
- <string name="wifi_security_none" msgid="7985461072596594400">"Tiada"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"Disimpan"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"Dinyahdayakan"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"Kegagalan Konfigurasi IP"</string>
diff --git a/packages/SettingsLib/res/values-my/strings.xml b/packages/SettingsLib/res/values-my/strings.xml
index 6cdab67..5f5957c 100644
--- a/packages/SettingsLib/res/values-my/strings.xml
+++ b/packages/SettingsLib/res/values-my/strings.xml
@@ -21,7 +21,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"ကွန်ယက်များကို စကင်မလုပ်နိုင်ပါ"</string>
- <string name="wifi_security_none" msgid="7985461072596594400">"တစ်ခုမျှ မဟုတ်ပါ"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"သိမ်းဆည်းပြီး"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"ပိတ်ထားသည်"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP ပြုပြင်ခြင်း မအောင်မြင်ပါ"</string>
diff --git a/packages/SettingsLib/res/values-nb/strings.xml b/packages/SettingsLib/res/values-nb/strings.xml
index cf18477..3c240f8 100644
--- a/packages/SettingsLib/res/values-nb/strings.xml
+++ b/packages/SettingsLib/res/values-nb/strings.xml
@@ -21,7 +21,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"Kan ikke søke etter nettverk"</string>
- <string name="wifi_security_none" msgid="7985461072596594400">"Ingen"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"Lagret"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"Slått av"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP-konfigurasjonsfeil"</string>
diff --git a/packages/SettingsLib/res/values-ne/strings.xml b/packages/SettingsLib/res/values-ne/strings.xml
index 8aba0f8..1699870 100644
--- a/packages/SettingsLib/res/values-ne/strings.xml
+++ b/packages/SettingsLib/res/values-ne/strings.xml
@@ -21,7 +21,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"सञ्जालका लागि स्क्यान गर्न सक्दैन"</string>
- <string name="wifi_security_none" msgid="7985461072596594400">"कुनै पनि होइन"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"सुरक्षित गरियो"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"असक्षम पारियो"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP विन्यास असफल"</string>
diff --git a/packages/SettingsLib/res/values-nl/strings.xml b/packages/SettingsLib/res/values-nl/strings.xml
index e3f1aea..738df94 100644
--- a/packages/SettingsLib/res/values-nl/strings.xml
+++ b/packages/SettingsLib/res/values-nl/strings.xml
@@ -21,7 +21,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"Kan niet zoeken naar netwerken"</string>
- <string name="wifi_security_none" msgid="7985461072596594400">"Geen"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"Opgeslagen"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"Uitgeschakeld"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP-configuratie mislukt"</string>
diff --git a/packages/SettingsLib/res/values-or/strings.xml b/packages/SettingsLib/res/values-or/strings.xml
index 8ecac4d..2d39cc6 100644
--- a/packages/SettingsLib/res/values-or/strings.xml
+++ b/packages/SettingsLib/res/values-or/strings.xml
@@ -21,7 +21,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"ନେଟ୍ୱର୍କଗୁଡ଼ିକୁ ଖୋଜିପାରୁନାହିଁ"</string>
- <string name="wifi_security_none" msgid="7985461072596594400">"କିଛି ନାହିଁ"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"ସେଭ୍ ହୋଇଗଲା"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"ଅକ୍ଷମ ହୋଇଛି"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP କନଫିଗରେଶନ ବିଫଳ ହୋଇଛି"</string>
diff --git a/packages/SettingsLib/res/values-pa/strings.xml b/packages/SettingsLib/res/values-pa/strings.xml
index 6f17e13..61f0447 100644
--- a/packages/SettingsLib/res/values-pa/strings.xml
+++ b/packages/SettingsLib/res/values-pa/strings.xml
@@ -21,7 +21,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"ਨੈਟਵਰਕਾਂ ਲਈ ਸਕੈਨ ਨਹੀਂ ਕਰ ਸਕਦਾ"</string>
- <string name="wifi_security_none" msgid="7985461072596594400">"ਕੋਈ ਨਹੀਂ"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"ਰੱਖਿਅਤ ਕੀਤਾ"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"ਅਯੋਗ ਬਣਾਇਆ"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP ਕੌਂਫਿਗਰੇਸ਼ਨ ਅਸਫਲਤਾ"</string>
diff --git a/packages/SettingsLib/res/values-pl/strings.xml b/packages/SettingsLib/res/values-pl/strings.xml
index 3c208e3..519f82c 100644
--- a/packages/SettingsLib/res/values-pl/strings.xml
+++ b/packages/SettingsLib/res/values-pl/strings.xml
@@ -21,7 +21,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"Nie można wyszukać sieci."</string>
- <string name="wifi_security_none" msgid="7985461072596594400">"Brak"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"Zapisana"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"Wyłączona"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"Błąd konfiguracji IP"</string>
diff --git a/packages/SettingsLib/res/values-pt-rBR/strings.xml b/packages/SettingsLib/res/values-pt-rBR/strings.xml
index 5c363a4..16844a2 100644
--- a/packages/SettingsLib/res/values-pt-rBR/strings.xml
+++ b/packages/SettingsLib/res/values-pt-rBR/strings.xml
@@ -21,7 +21,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"Não é possível verificar a existência de redes"</string>
- <string name="wifi_security_none" msgid="7985461072596594400">"Nenhuma"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"Salva"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"Desativado"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"Falha de configuração de IP"</string>
@@ -227,7 +226,7 @@
<string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="6893955536658137179">"Acionar seleção de codec de áudio\nBluetooth LDAC: qualidade de reprodução"</string>
<string name="bluetooth_select_a2dp_codec_streaming_label" msgid="5347862512596240506">"Streaming: <xliff:g id="STREAMING_PARAMETER">%1$s</xliff:g>"</string>
<string name="select_private_dns_configuration_title" msgid="3700456559305263922">"DNS particular"</string>
- <string name="select_private_dns_configuration_dialog_title" msgid="9221994415765826811">"Selecione o modo DNS particular"</string>
+ <string name="select_private_dns_configuration_dialog_title" msgid="9221994415765826811">"Selecione o modo de DNS particular"</string>
<string name="private_dns_mode_off" msgid="8236575187318721684">"Desativado"</string>
<string name="private_dns_mode_opportunistic" msgid="8314986739896927399">"Automático"</string>
<string name="private_dns_mode_provider" msgid="8354935160639360804">"Nome do host do provedor de DNS particular"</string>
diff --git a/packages/SettingsLib/res/values-pt-rPT/strings.xml b/packages/SettingsLib/res/values-pt-rPT/strings.xml
index 66c59ac..f01ddfa 100644
--- a/packages/SettingsLib/res/values-pt-rPT/strings.xml
+++ b/packages/SettingsLib/res/values-pt-rPT/strings.xml
@@ -21,7 +21,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"Não é possível verificar redes"</string>
- <string name="wifi_security_none" msgid="7985461072596594400">"Nenhuma"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"Guardada"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"Desativado"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"Falha de configuração de IP"</string>
@@ -278,7 +277,7 @@
<string name="media_category" msgid="4388305075496848353">"Multimédia"</string>
<string name="debug_monitoring_category" msgid="7640508148375798343">"Monitorização"</string>
<string name="strict_mode" msgid="1938795874357830695">"Modo rigoroso ativado"</string>
- <string name="strict_mode_summary" msgid="142834318897332338">"Piscar ecrã se aplic. fazem oper. prolong. no tópico princ."</string>
+ <string name="strict_mode_summary" msgid="142834318897332338">"Piscar ecrã se app fazem oper. prolong. no tópico princ."</string>
<string name="pointer_location" msgid="6084434787496938001">"Localização do ponteiro"</string>
<string name="pointer_location_summary" msgid="840819275172753713">"Apresentar dados atuais de toque"</string>
<string name="show_touches" msgid="2642976305235070316">"Mostrar toques"</string>
@@ -305,7 +304,7 @@
<string name="show_non_rect_clip" msgid="505954950474595172">"Depurar operações de clipe não retangulares"</string>
<string name="track_frame_time" msgid="6094365083096851167">"Renderiz. HWUI do perfil"</string>
<string name="enable_gpu_debug_layers" msgid="3848838293793255097">"Ativar cam. depuração GPU"</string>
- <string name="enable_gpu_debug_layers_summary" msgid="8009136940671194940">"Permitir carreg. cam. depuração GPU p/ dep. aplic."</string>
+ <string name="enable_gpu_debug_layers_summary" msgid="8009136940671194940">"Permitir carreg. cam. depuração GPU p/ dep. app"</string>
<string name="window_animation_scale_title" msgid="6162587588166114700">"Escala de anim. da janela"</string>
<string name="transition_animation_scale_title" msgid="387527540523595875">"Escala de anim. de trans."</string>
<string name="animator_duration_scale_title" msgid="3406722410819934083">"Esc. de duração do anim."</string>
diff --git a/packages/SettingsLib/res/values-pt/strings.xml b/packages/SettingsLib/res/values-pt/strings.xml
index 5c363a4..16844a2 100644
--- a/packages/SettingsLib/res/values-pt/strings.xml
+++ b/packages/SettingsLib/res/values-pt/strings.xml
@@ -21,7 +21,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"Não é possível verificar a existência de redes"</string>
- <string name="wifi_security_none" msgid="7985461072596594400">"Nenhuma"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"Salva"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"Desativado"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"Falha de configuração de IP"</string>
@@ -227,7 +226,7 @@
<string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title" msgid="6893955536658137179">"Acionar seleção de codec de áudio\nBluetooth LDAC: qualidade de reprodução"</string>
<string name="bluetooth_select_a2dp_codec_streaming_label" msgid="5347862512596240506">"Streaming: <xliff:g id="STREAMING_PARAMETER">%1$s</xliff:g>"</string>
<string name="select_private_dns_configuration_title" msgid="3700456559305263922">"DNS particular"</string>
- <string name="select_private_dns_configuration_dialog_title" msgid="9221994415765826811">"Selecione o modo DNS particular"</string>
+ <string name="select_private_dns_configuration_dialog_title" msgid="9221994415765826811">"Selecione o modo de DNS particular"</string>
<string name="private_dns_mode_off" msgid="8236575187318721684">"Desativado"</string>
<string name="private_dns_mode_opportunistic" msgid="8314986739896927399">"Automático"</string>
<string name="private_dns_mode_provider" msgid="8354935160639360804">"Nome do host do provedor de DNS particular"</string>
diff --git a/packages/SettingsLib/res/values-ro/strings.xml b/packages/SettingsLib/res/values-ro/strings.xml
index 703add6..8a7b440 100644
--- a/packages/SettingsLib/res/values-ro/strings.xml
+++ b/packages/SettingsLib/res/values-ro/strings.xml
@@ -21,7 +21,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"Nu se poate scana pentru rețele"</string>
- <string name="wifi_security_none" msgid="7985461072596594400">"Niciuna"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"Salvată"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"Dezactivată"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"Eroare de configurație IP"</string>
diff --git a/packages/SettingsLib/res/values-ru/strings.xml b/packages/SettingsLib/res/values-ru/strings.xml
index 4eb3ca4..31274d1 100644
--- a/packages/SettingsLib/res/values-ru/strings.xml
+++ b/packages/SettingsLib/res/values-ru/strings.xml
@@ -21,7 +21,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"Не удалось начать поиск сетей."</string>
- <string name="wifi_security_none" msgid="7985461072596594400">"Нет"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"Сохранено"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"Отключено"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"Ошибка IP-конфигурации"</string>
diff --git a/packages/SettingsLib/res/values-si/strings.xml b/packages/SettingsLib/res/values-si/strings.xml
index 1ab5b79..b22e068 100644
--- a/packages/SettingsLib/res/values-si/strings.xml
+++ b/packages/SettingsLib/res/values-si/strings.xml
@@ -21,7 +21,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"ජාල සඳහා පරිලෝකනය කළ නොහැක"</string>
- <string name="wifi_security_none" msgid="7985461072596594400">"කිසිවක් නැත"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"සුරකින ලදි"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"අබලයි"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP වින්යාස කිරීම අසාර්ථකයි"</string>
diff --git a/packages/SettingsLib/res/values-sk/strings.xml b/packages/SettingsLib/res/values-sk/strings.xml
index 5c3920f..43923b8 100644
--- a/packages/SettingsLib/res/values-sk/strings.xml
+++ b/packages/SettingsLib/res/values-sk/strings.xml
@@ -21,7 +21,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"Siete sa nedajú vyhľadávať"</string>
- <string name="wifi_security_none" msgid="7985461072596594400">"Žiadne"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"Uložené"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"Vypnuté"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"Zlyhanie konfigurácie adresy IP"</string>
@@ -438,7 +437,7 @@
<string name="cancel" msgid="6859253417269739139">"Zrušiť"</string>
<string name="okay" msgid="1997666393121016642">"OK"</string>
<string name="zen_mode_enable_dialog_turn_on" msgid="8287824809739581837">"Zapnúť"</string>
- <string name="zen_mode_settings_turn_on_dialog_title" msgid="2297134204747331078">"Zapnite režim Nerušiť"</string>
+ <string name="zen_mode_settings_turn_on_dialog_title" msgid="2297134204747331078">"Zapnite režim bez vyrušení"</string>
<string name="zen_mode_settings_summary_off" msgid="6119891445378113334">"Nikdy"</string>
<string name="zen_interruption_level_priority" msgid="2078370238113347720">"Iba prioritné"</string>
<string name="zen_mode_and_condition" msgid="4927230238450354412">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-sl/strings.xml b/packages/SettingsLib/res/values-sl/strings.xml
index f479405..6d392fe 100644
--- a/packages/SettingsLib/res/values-sl/strings.xml
+++ b/packages/SettingsLib/res/values-sl/strings.xml
@@ -21,7 +21,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"Ni mogoče iskati omrežij"</string>
- <string name="wifi_security_none" msgid="7985461072596594400">"Brez"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"Shranjeno"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"Onemogočeno"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"Konfiguracija IP-ja ni uspela"</string>
diff --git a/packages/SettingsLib/res/values-sq/strings.xml b/packages/SettingsLib/res/values-sq/strings.xml
index 2741985..e05a019 100644
--- a/packages/SettingsLib/res/values-sq/strings.xml
+++ b/packages/SettingsLib/res/values-sq/strings.xml
@@ -21,7 +21,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"Nuk mund të skanojë për rrjete"</string>
- <string name="wifi_security_none" msgid="7985461072596594400">"Asnjë"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"U ruajt"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"Të çaktivizuara"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"Dështim në konfigurimin e IP-së"</string>
diff --git a/packages/SettingsLib/res/values-sr/strings.xml b/packages/SettingsLib/res/values-sr/strings.xml
index 241d9cb..371b909 100644
--- a/packages/SettingsLib/res/values-sr/strings.xml
+++ b/packages/SettingsLib/res/values-sr/strings.xml
@@ -21,13 +21,12 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"Није могуће скенирати мреже"</string>
- <string name="wifi_security_none" msgid="7985461072596594400">"Нема"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"Сачувано"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"Онемогућено"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP конфигурација је отказала"</string>
<string name="wifi_disabled_by_recommendation_provider" msgid="5168315140978066096">"Није повезано због лошег квалитета мреже"</string>
<string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"Wi-Fi веза је отказала"</string>
- <string name="wifi_disabled_password_failure" msgid="8659805351763133575">"Проблем са потврдом аутентичности"</string>
+ <string name="wifi_disabled_password_failure" msgid="8659805351763133575">"Проблем са потврдом идентитета"</string>
<string name="wifi_cant_connect" msgid="5410016875644565884">"Повезивање није успело"</string>
<string name="wifi_cant_connect_to_ap" msgid="1222553274052685331">"Повезивање са „<xliff:g id="AP_NAME">%1$s</xliff:g>“ није успело"</string>
<string name="wifi_check_password_try_again" msgid="516958988102584767">"Проверите лозинку и пробајте поново"</string>
@@ -448,5 +447,5 @@
<string name="zen_mode_duration_settings_title" msgid="229547412251222757">"Трајање"</string>
<string name="zen_mode_duration_always_prompt_title" msgid="6478923750878945501">"Увек питај"</string>
<string name="zen_mode_forever" msgid="2704305038191592967">"Док не искључите"</string>
- <string name="time_unit_just_now" msgid="6363336622778342422">"Управо сада"</string>
+ <string name="time_unit_just_now" msgid="6363336622778342422">"Управо"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-sv/strings.xml b/packages/SettingsLib/res/values-sv/strings.xml
index d338e96..e6872bb 100644
--- a/packages/SettingsLib/res/values-sv/strings.xml
+++ b/packages/SettingsLib/res/values-sv/strings.xml
@@ -21,7 +21,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"Det går inte att söka efter nätverk"</string>
- <string name="wifi_security_none" msgid="7985461072596594400">"Ingen"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"Sparat"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"Inaktiverad"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP-konfigurationsfel"</string>
diff --git a/packages/SettingsLib/res/values-sw/strings.xml b/packages/SettingsLib/res/values-sw/strings.xml
index d693077..23efb91 100644
--- a/packages/SettingsLib/res/values-sw/strings.xml
+++ b/packages/SettingsLib/res/values-sw/strings.xml
@@ -21,7 +21,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"Haiwezi kutambaza mitandao"</string>
- <string name="wifi_security_none" msgid="7985461072596594400">"Hamna"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"Imehifadhiwa"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"Imezimwa"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"Haikuweza Kusanidi IP"</string>
diff --git a/packages/SettingsLib/res/values-ta/strings.xml b/packages/SettingsLib/res/values-ta/strings.xml
index 8f5c7d8..53ba738 100644
--- a/packages/SettingsLib/res/values-ta/strings.xml
+++ b/packages/SettingsLib/res/values-ta/strings.xml
@@ -21,7 +21,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"நெட்வொர்க்குகளுக்கு ஸ்கேன் செய்யப்படவில்லை"</string>
- <string name="wifi_security_none" msgid="7985461072596594400">"ஏதுமில்லை"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"சேமிக்கப்பட்டது"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"முடக்கப்பட்டது"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP உள்ளமைவில் தோல்வி"</string>
diff --git a/packages/SettingsLib/res/values-te/strings.xml b/packages/SettingsLib/res/values-te/strings.xml
index 5f7ab996..c8c6b4c 100644
--- a/packages/SettingsLib/res/values-te/strings.xml
+++ b/packages/SettingsLib/res/values-te/strings.xml
@@ -21,7 +21,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"నెట్వర్క్ల కోసం స్కాన్ చేయడం సాధ్యపడదు"</string>
- <string name="wifi_security_none" msgid="7985461072596594400">"ఏదీ లేదు"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"సేవ్ చేయబడింది"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"నిలిపివేయబడింది"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP కాన్ఫిగరేషన్ వైఫల్యం"</string>
diff --git a/packages/SettingsLib/res/values-th/strings.xml b/packages/SettingsLib/res/values-th/strings.xml
index 020baf0..d293d59 100644
--- a/packages/SettingsLib/res/values-th/strings.xml
+++ b/packages/SettingsLib/res/values-th/strings.xml
@@ -21,7 +21,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"ไม่สามารถสแกนหาเครือข่าย"</string>
- <string name="wifi_security_none" msgid="7985461072596594400">"ไม่มี"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"บันทึกแล้ว"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"ปิดอยู่"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"การกำหนดค่า IP ล้มเหลว"</string>
diff --git a/packages/SettingsLib/res/values-tl/strings.xml b/packages/SettingsLib/res/values-tl/strings.xml
index 2164ade..9da4561 100644
--- a/packages/SettingsLib/res/values-tl/strings.xml
+++ b/packages/SettingsLib/res/values-tl/strings.xml
@@ -21,7 +21,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"Hindi makapag-scan ng mga network"</string>
- <string name="wifi_security_none" msgid="7985461072596594400">"Wala"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"Na-save"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"Naka-disable"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"Pagkabigo ng Configuration ng IP"</string>
@@ -374,7 +373,7 @@
<string name="power_discharge_by_enhanced" msgid="2095821536747992464">"Tatagal dapat nang hanggang humigit-kumulang <xliff:g id="TIME">%1$s</xliff:g> batay sa iyong paggamit (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Tatagal dapat nang hanggang humigit-kumulang <xliff:g id="TIME">%1$s</xliff:g> batay sa iyong paggamit"</string>
<string name="power_discharge_by" msgid="6453537733650125582">"Tatagal dapat nang hanggang humigit-kumulang <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
- <string name="power_discharge_by_only" msgid="107616694963545745">"Tatagal dapat nang hanggang humigit-kumulang <xliff:g id="TIME">%1$s</xliff:g>"</string>
+ <string name="power_discharge_by_only" msgid="107616694963545745">"Tatagal hanggang mga <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Wala nang <xliff:g id="THRESHOLD">%1$s</xliff:g> ang natitira"</string>
<string name="power_remaining_less_than_duration" msgid="5751885147712659423">"Wala nang <xliff:g id="THRESHOLD">%1$s</xliff:g> ang natitira (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
<string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"Mahigit <xliff:g id="TIME_REMAINING">%1$s</xliff:g> pa ang natitira (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
diff --git a/packages/SettingsLib/res/values-tr/strings.xml b/packages/SettingsLib/res/values-tr/strings.xml
index 532927c..2d5cd7f 100644
--- a/packages/SettingsLib/res/values-tr/strings.xml
+++ b/packages/SettingsLib/res/values-tr/strings.xml
@@ -21,7 +21,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"Ağlar taranamıyor"</string>
- <string name="wifi_security_none" msgid="7985461072596594400">"Yok"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"Kaydedildi"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"Devre dışı"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP Yapılandırması Hatası"</string>
diff --git a/packages/SettingsLib/res/values-uk/strings.xml b/packages/SettingsLib/res/values-uk/strings.xml
index cf26d20..512ea86 100644
--- a/packages/SettingsLib/res/values-uk/strings.xml
+++ b/packages/SettingsLib/res/values-uk/strings.xml
@@ -21,7 +21,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"Неможливо здійснити сканування мереж"</string>
- <string name="wifi_security_none" msgid="7985461072596594400">"Немає"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"Збережено"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"Вимкнено"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"Помилка конфігурації IP-адреси"</string>
diff --git a/packages/SettingsLib/res/values-ur/strings.xml b/packages/SettingsLib/res/values-ur/strings.xml
index c158179..84ad3ed 100644
--- a/packages/SettingsLib/res/values-ur/strings.xml
+++ b/packages/SettingsLib/res/values-ur/strings.xml
@@ -21,7 +21,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"نیٹ ورکس کیلئے اسکین نہيں کر سکتے ہیں"</string>
- <string name="wifi_security_none" msgid="7985461072596594400">"کوئی نہیں"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"محفوظ کردیا گیا"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"غیر فعال"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP کنفیگریشن کی ناکامی"</string>
diff --git a/packages/SettingsLib/res/values-uz/strings.xml b/packages/SettingsLib/res/values-uz/strings.xml
index ac8bffe..af2ada5 100644
--- a/packages/SettingsLib/res/values-uz/strings.xml
+++ b/packages/SettingsLib/res/values-uz/strings.xml
@@ -21,7 +21,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"Tarmoqlarni tekshirib chiqishni iloji bo‘lmadi"</string>
- <string name="wifi_security_none" msgid="7985461072596594400">"Hech qanday"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"Saqlandi"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"O‘chiq"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP manzilini sozlab bo‘lmadi"</string>
diff --git a/packages/SettingsLib/res/values-vi/strings.xml b/packages/SettingsLib/res/values-vi/strings.xml
index 78306da..f454127 100644
--- a/packages/SettingsLib/res/values-vi/strings.xml
+++ b/packages/SettingsLib/res/values-vi/strings.xml
@@ -21,7 +21,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"Không thể dò tìm mạng"</string>
- <string name="wifi_security_none" msgid="7985461072596594400">"Không"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"Đã lưu"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"Đã tắt"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"Lỗi cấu hình IP"</string>
diff --git a/packages/SettingsLib/res/values-zh-rCN/strings.xml b/packages/SettingsLib/res/values-zh-rCN/strings.xml
index 4fb26100..10a20be 100644
--- a/packages/SettingsLib/res/values-zh-rCN/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rCN/strings.xml
@@ -21,7 +21,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"无法扫描网络"</string>
- <string name="wifi_security_none" msgid="7985461072596594400">"无"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"已保存"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"已停用"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP 配置失败"</string>
diff --git a/packages/SettingsLib/res/values-zh-rHK/strings.xml b/packages/SettingsLib/res/values-zh-rHK/strings.xml
index c5a8f920..1510545 100644
--- a/packages/SettingsLib/res/values-zh-rHK/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rHK/strings.xml
@@ -21,7 +21,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"無法掃瞄網絡"</string>
- <string name="wifi_security_none" msgid="7985461072596594400">"無"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"已儲存"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"已停用"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP 設定失敗"</string>
diff --git a/packages/SettingsLib/res/values-zh-rTW/strings.xml b/packages/SettingsLib/res/values-zh-rTW/strings.xml
index 0947ccec..90c0d80 100644
--- a/packages/SettingsLib/res/values-zh-rTW/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rTW/strings.xml
@@ -21,7 +21,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"無法掃描網路"</string>
- <string name="wifi_security_none" msgid="7985461072596594400">"無"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"已儲存"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"已停用"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"IP 設定失敗"</string>
diff --git a/packages/SettingsLib/res/values-zu/strings.xml b/packages/SettingsLib/res/values-zu/strings.xml
index cd90d74..e4f9f4d 100644
--- a/packages/SettingsLib/res/values-zu/strings.xml
+++ b/packages/SettingsLib/res/values-zu/strings.xml
@@ -21,7 +21,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="wifi_fail_to_scan" msgid="1265540342578081461">"Ayikwazi ukuhlola amanethiwekhi"</string>
- <string name="wifi_security_none" msgid="7985461072596594400">"Lutho"</string>
<string name="wifi_remembered" msgid="4955746899347821096">"Kulondoloziwe"</string>
<string name="wifi_disabled_generic" msgid="4259794910584943386">"Akusebenzi"</string>
<string name="wifi_disabled_network_failure" msgid="2364951338436007124">"Ukwehluleka kokulungiswa kwe-IP"</string>
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index 738d729..70f9bb6 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -930,6 +930,8 @@
<string name="power_discharge_by">Should last until about <xliff:g id="time">%1$s</xliff:g> (<xliff:g id="level">%2$s</xliff:g>)</string>
<!-- [CHAR_LIMIT=100] Label for estimated time that phone will run out of battery -->
<string name="power_discharge_by_only">Should last until about <xliff:g id="time">%1$s</xliff:g></string>
+ <!-- [CHAR_LIMIT=100] Label for estimated time that phone will run out of battery -->
+ <string name="power_discharge_by_only_short">Until <xliff:g id="time" example="12 PM">%1$s</xliff:g></string>
<!-- [CHAR_LIMIT=60] label for estimated remaining duration of battery when under a certain amount -->
<string name="power_remaining_less_than_duration_only">Less than <xliff:g id="threshold">%1$s</xliff:g> remaining</string>
@@ -1133,4 +1135,7 @@
<!-- UI debug setting: opt in to use updated graphics driver? [CHAR LIMIT=100] -->
<string name="updated_gfx_driver_dev_opt_in_app_summary">Opt in app to use updated graphcis driver in developement</string>
+
+ <!-- Name of the phone device [CHAR LIMIT=NONE] -->
+ <string name="media_transfer_phone_device_name">Phone speaker</string>
</resources>
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/BluetoothMediaDevice.java b/packages/SettingsLib/src/com/android/settingslib/media/BluetoothMediaDevice.java
new file mode 100644
index 0000000..e535348
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/media/BluetoothMediaDevice.java
@@ -0,0 +1,73 @@
+/*
+ * 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 com.android.settingslib.media;
+
+import android.content.Context;
+import android.util.Log;
+
+import com.android.settingslib.R;
+import com.android.settingslib.bluetooth.CachedBluetoothDevice;
+
+/**
+ * BluetoothMediaDevice extends MediaDevice to represents Bluetooth device.
+ */
+public class BluetoothMediaDevice extends MediaDevice {
+
+ private static final String TAG = "BluetoothMediaDevice";
+
+ private CachedBluetoothDevice mCachedDevice;
+
+ BluetoothMediaDevice(Context context, CachedBluetoothDevice device) {
+ super(context, MediaDeviceType.TYPE_BLUETOOTH_DEVICE);
+ mCachedDevice = device;
+ }
+
+ @Override
+ public String getName() {
+ return mCachedDevice.getName();
+ }
+
+ @Override
+ public int getIcon() {
+ //TODO(b/117129183): This is not final icon for bluetooth device, just for demo.
+ return R.drawable.ic_bt_headphones_a2dp;
+ }
+
+ @Override
+ public String getId() {
+ return MediaDeviceUtils.getId(mCachedDevice);
+ }
+
+ @Override
+ public void connect() {
+ //TODO(b/117129183): add callback to notify LocalMediaManager connection state.
+ mIsConnected = mCachedDevice.setActive();
+ Log.d(TAG, "connect() device : " + getName() + ", is selected : " + mIsConnected);
+ }
+
+ @Override
+ public void disconnect() {
+ //TODO(b/117129183): disconnected last select device
+ mIsConnected = false;
+ }
+
+ /**
+ * Get current CachedBluetoothDevice
+ */
+ public CachedBluetoothDevice getCachedDevice() {
+ return mCachedDevice;
+ }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/BluetoothMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/BluetoothMediaManager.java
new file mode 100644
index 0000000..04188e9
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/media/BluetoothMediaManager.java
@@ -0,0 +1,261 @@
+/*
+ * 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 com.android.settingslib.media;
+
+import android.app.Notification;
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+import android.content.Context;
+import android.util.Log;
+
+import com.android.settingslib.bluetooth.A2dpProfile;
+import com.android.settingslib.bluetooth.BluetoothCallback;
+import com.android.settingslib.bluetooth.CachedBluetoothDevice;
+import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager;
+import com.android.settingslib.bluetooth.HearingAidProfile;
+import com.android.settingslib.bluetooth.LocalBluetoothManager;
+import com.android.settingslib.bluetooth.LocalBluetoothProfileManager;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * BluetoothMediaManager provide interface to get Bluetooth device list.
+ */
+public class BluetoothMediaManager extends MediaManager implements BluetoothCallback {
+
+ private static final String TAG = "BluetoothMediaManager";
+
+ private final DeviceAttributeChangeCallback mCachedDeviceCallback =
+ new DeviceAttributeChangeCallback();
+
+ private LocalBluetoothManager mLocalBluetoothManager;
+ private LocalBluetoothProfileManager mProfileManager;
+
+ private MediaDevice mLastAddedDevice;
+ private MediaDevice mLastRemovedDevice;
+
+ BluetoothMediaManager(Context context, LocalBluetoothManager localBluetoothManager,
+ Notification notification) {
+ super(context, notification);
+
+ mLocalBluetoothManager = localBluetoothManager;
+ mProfileManager = mLocalBluetoothManager.getProfileManager();
+ }
+
+ @Override
+ public void startScan() {
+ mMediaDevices.clear();
+ mLocalBluetoothManager.getEventManager().registerCallback(this);
+ buildBluetoothDeviceList();
+ dispatchDeviceListAdded();
+ }
+
+ private void buildBluetoothDeviceList() {
+ addConnectedA2dpDevices();
+ addConnectedHearingAidDevices();
+ }
+
+ private void addConnectedA2dpDevices() {
+ final A2dpProfile a2dpProfile = mProfileManager.getA2dpProfile();
+ if (a2dpProfile == null) {
+ Log.w(TAG, "addConnectedA2dpDevices() a2dp profile is null!");
+ return;
+ }
+ final List<BluetoothDevice> devices = a2dpProfile.getConnectedDevices();
+ final CachedBluetoothDeviceManager cachedBluetoothDeviceManager =
+ mLocalBluetoothManager.getCachedDeviceManager();
+
+ for (BluetoothDevice device : devices) {
+ final CachedBluetoothDevice cachedDevice =
+ cachedBluetoothDeviceManager.findDevice(device);
+
+ if (cachedDevice == null) {
+ Log.w(TAG, "Can't found CachedBluetoothDevice : " + device.getName());
+ continue;
+ }
+
+ Log.d(TAG, "addConnectedA2dpDevices() device : " + cachedDevice.getName()
+ + ", is connected : " + cachedDevice.isConnected());
+
+ if (cachedDevice.isConnected()) {
+ addMediaDevice(cachedDevice);
+ }
+ }
+ }
+
+ private void addConnectedHearingAidDevices() {
+ final HearingAidProfile hapProfile = mProfileManager.getHearingAidProfile();
+ if (hapProfile == null) {
+ Log.w(TAG, "addConnectedA2dpDevices() hap profile is null!");
+ return;
+ }
+ final List<Long> devicesHiSyncIds = new ArrayList<>();
+ final List<BluetoothDevice> devices = hapProfile.getConnectedDevices();
+ final CachedBluetoothDeviceManager cachedBluetoothDeviceManager =
+ mLocalBluetoothManager.getCachedDeviceManager();
+
+ for (BluetoothDevice device : devices) {
+ final CachedBluetoothDevice cachedDevice =
+ cachedBluetoothDeviceManager.findDevice(device);
+
+ if (cachedDevice == null) {
+ Log.w(TAG, "Can't found CachedBluetoothDevice : " + device.getName());
+ continue;
+ }
+
+ Log.d(TAG, "addConnectedHearingAidDevices() device : " + cachedDevice.getName()
+ + ", is connected : " + cachedDevice.isConnected());
+ final long hiSyncId = hapProfile.getHiSyncId(device);
+
+ // device with same hiSyncId should not be shown in the UI.
+ // So do not add it into connectedDevices.
+ if (!devicesHiSyncIds.contains(hiSyncId) && cachedDevice.isConnected()) {
+ devicesHiSyncIds.add(hiSyncId);
+ addMediaDevice(cachedDevice);
+ }
+ }
+ }
+
+ private void addMediaDevice(CachedBluetoothDevice cachedDevice) {
+ MediaDevice mediaDevice = findMediaDevice(MediaDeviceUtils.getId(cachedDevice));
+ if (mediaDevice == null) {
+ mediaDevice = new BluetoothMediaDevice(mContext, cachedDevice);
+ cachedDevice.registerCallback(mCachedDeviceCallback);
+ mLastAddedDevice = mediaDevice;
+ mMediaDevices.add(mediaDevice);
+ }
+ }
+
+ @Override
+ public void stopScan() {
+ mLocalBluetoothManager.getEventManager().unregisterCallback(this);
+ unregisterCachedDeviceCallback();
+ }
+
+ private void unregisterCachedDeviceCallback() {
+ for (MediaDevice device : mMediaDevices) {
+ if (device instanceof BluetoothMediaDevice) {
+ ((BluetoothMediaDevice) device).getCachedDevice()
+ .unregisterCallback(mCachedDeviceCallback);
+ }
+ }
+ }
+
+ @Override
+ public void onBluetoothStateChanged(int bluetoothState) {
+ if (BluetoothAdapter.STATE_ON == bluetoothState) {
+ buildBluetoothDeviceList();
+ dispatchDeviceListAdded();
+ } else if (BluetoothAdapter.STATE_OFF == bluetoothState) {
+ final List<MediaDevice> removeDevicesList = new ArrayList<>();
+ for (MediaDevice device : mMediaDevices) {
+ if (device instanceof BluetoothMediaDevice) {
+ ((BluetoothMediaDevice) device).getCachedDevice()
+ .unregisterCallback(mCachedDeviceCallback);
+ removeDevicesList.add(device);
+ }
+ }
+ mMediaDevices.removeAll(removeDevicesList);
+ dispatchDeviceListRemoved(removeDevicesList);
+ }
+ }
+
+ @Override
+ public void onDeviceAdded(CachedBluetoothDevice cachedDevice) {
+ if (isCachedDeviceConnected(cachedDevice)) {
+ addMediaDevice(cachedDevice);
+ dispatchDeviceAdded(cachedDevice);
+ }
+ }
+
+ private boolean isCachedDeviceConnected(CachedBluetoothDevice cachedDevice) {
+ final boolean isConnectedHearingAidDevice = cachedDevice.isConnectedHearingAidDevice();
+ final boolean isConnectedA2dpDevice = cachedDevice.isConnectedA2dpDevice();
+ Log.d(TAG, "isCachedDeviceConnected() cachedDevice : " + cachedDevice.getName()
+ + ", is hearing aid connected : " + isConnectedHearingAidDevice
+ + ", is a2dp connected : " + isConnectedA2dpDevice);
+
+ return isConnectedHearingAidDevice || isConnectedA2dpDevice;
+ }
+
+ private void dispatchDeviceAdded(CachedBluetoothDevice cachedDevice) {
+ if (mLastAddedDevice != null
+ && MediaDeviceUtils.getId(cachedDevice) == mLastAddedDevice.getId()) {
+ dispatchDeviceAdded(mLastAddedDevice);
+ }
+ }
+
+ @Override
+ public void onDeviceDeleted(CachedBluetoothDevice cachedDevice) {
+ if (!isCachedDeviceConnected(cachedDevice)) {
+ removeMediaDevice(cachedDevice);
+ dispatchDeviceRemoved(cachedDevice);
+ }
+ }
+
+ private void removeMediaDevice(CachedBluetoothDevice cachedDevice) {
+ final MediaDevice mediaDevice = findMediaDevice(MediaDeviceUtils.getId(cachedDevice));
+ if (mediaDevice != null) {
+ cachedDevice.unregisterCallback(mCachedDeviceCallback);
+ mLastRemovedDevice = mediaDevice;
+ mMediaDevices.remove(mediaDevice);
+ }
+ }
+
+ void dispatchDeviceRemoved(CachedBluetoothDevice cachedDevice) {
+ if (mLastRemovedDevice != null
+ && MediaDeviceUtils.getId(cachedDevice) == mLastRemovedDevice.getId()) {
+ dispatchDeviceRemoved(mLastRemovedDevice);
+ }
+ }
+
+ @Override
+ public void onProfileConnectionStateChanged(CachedBluetoothDevice cachedDevice, int state,
+ int bluetoothProfile) {
+ Log.d(TAG, "onProfileConnectionStateChanged() device: " + cachedDevice.getName()
+ + ", state: " + state + ", bluetoothProfile: " + bluetoothProfile);
+
+ if (isCachedDeviceConnected(cachedDevice)) {
+ addMediaDevice(cachedDevice);
+ dispatchDeviceAdded(cachedDevice);
+ } else {
+ removeMediaDevice(cachedDevice);
+ dispatchDeviceRemoved(cachedDevice);
+ }
+ }
+
+ @Override
+ public void onAclConnectionStateChanged(CachedBluetoothDevice cachedDevice, int state) {
+ Log.d(TAG, "onAclConnectionStateChanged() device: " + cachedDevice.getName()
+ + ", state: " + state);
+
+ if (isCachedDeviceConnected(cachedDevice)) {
+ addMediaDevice(cachedDevice);
+ dispatchDeviceAdded(cachedDevice);
+ } else {
+ removeMediaDevice(cachedDevice);
+ dispatchDeviceRemoved(cachedDevice);
+ }
+ }
+
+ class DeviceAttributeChangeCallback implements CachedBluetoothDevice.Callback {
+ @Override
+ public void onDeviceAttributesChanged() {
+ dispatchDeviceAttributesChanged();
+ }
+ }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaDevice.java b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaDevice.java
new file mode 100644
index 0000000..498a0fc
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaDevice.java
@@ -0,0 +1,65 @@
+/*
+ * 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 com.android.settingslib.media;
+
+import android.content.Context;
+
+import androidx.mediarouter.media.MediaRouter;
+
+import com.android.settingslib.R;
+
+/**
+ * InfoMediaDevice extends MediaDevice to represents wifi device.
+ */
+public class InfoMediaDevice extends MediaDevice {
+
+ private static final String TAG = "InfoMediaDevice";
+
+ private MediaRouter.RouteInfo mRouteInfo;
+
+ InfoMediaDevice(Context context, MediaRouter.RouteInfo info) {
+ super(context, MediaDeviceType.TYPE_CAST_DEVICE);
+ mRouteInfo = info;
+ }
+
+ @Override
+ public String getName() {
+ return mRouteInfo.getName();
+ }
+
+ @Override
+ public int getIcon() {
+ //TODO(b/117129183): This is not final icon for cast device, just for demo.
+ return R.drawable.ic_settings_print;
+ }
+
+ @Override
+ public String getId() {
+ return MediaDeviceUtils.getId(mRouteInfo);
+ }
+
+ @Override
+ public void connect() {
+ //TODO(b/117129183): use MediaController2 to transfer media
+ mIsConnected = true;
+ }
+
+ @Override
+ public void disconnect() {
+ //TODO(b/117129183): disconnected last select device
+ mIsConnected = false;
+ }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
new file mode 100644
index 0000000..6907238
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
@@ -0,0 +1,92 @@
+/*
+ * 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 com.android.settingslib.media;
+
+import android.app.Notification;
+import android.content.Context;
+import android.util.Log;
+
+import androidx.mediarouter.media.MediaRouteSelector;
+import androidx.mediarouter.media.MediaRouter;
+
+/**
+ * InfoMediaManager provide interface to get InfoMediaDevice list.
+ */
+public class InfoMediaManager extends MediaManager {
+
+ private static final String TAG = "InfoMediaManager";
+
+ private final MediaRouterCallback mMediaRouterCallback = new MediaRouterCallback();
+
+ private MediaRouter mMediaRouter;
+ private String mPackageName;
+
+ InfoMediaManager(Context context, String packageName, Notification notification) {
+ super(context, notification);
+
+ mMediaRouter = MediaRouter.getInstance(context);
+ mPackageName = packageName;
+ }
+
+ @Override
+ public void startScan() {
+ mMediaDevices.clear();
+ startScanCastDevice();
+ }
+
+ private void startScanCastDevice() {
+ final MediaRouteSelector selector = new MediaRouteSelector.Builder()
+ .addControlCategory(getControlCategoryByPackageName(mPackageName))
+ .build();
+
+ mMediaRouter.addCallback(selector, mMediaRouterCallback,
+ MediaRouter.CALLBACK_FLAG_REQUEST_DISCOVERY);
+ }
+
+ private String getControlCategoryByPackageName(String packageName) {
+ //TODO(b/117129183): Use package name to get ControlCategory.
+ //Since api not ready, return fixed ControlCategory for prototype.
+ return "com.google.android.gms.cast.CATEGORY_CAST/4F8B3483";
+ }
+
+ @Override
+ public void stopScan() {
+ mMediaRouter.removeCallback(mMediaRouterCallback);
+ }
+
+ class MediaRouterCallback extends MediaRouter.Callback {
+ @Override
+ public void onRouteAdded(MediaRouter router, MediaRouter.RouteInfo route) {
+ MediaDevice mediaDevice = findMediaDevice(MediaDeviceUtils.getId(route));
+ if (mediaDevice == null) {
+ mediaDevice = new InfoMediaDevice(mContext, route);
+ Log.d(TAG, "onRouteAdded() route : " + route.getName());
+ mMediaDevices.add(mediaDevice);
+ dispatchDeviceAdded(mediaDevice);
+ }
+ }
+
+ @Override
+ public void onRouteRemoved(MediaRouter router, MediaRouter.RouteInfo route) {
+ final MediaDevice mediaDevice = findMediaDevice(MediaDeviceUtils.getId(route));
+ if (mediaDevice != null) {
+ Log.d(TAG, "onRouteRemoved() route : " + route.getName());
+ mMediaDevices.remove(mediaDevice);
+ dispatchDeviceRemoved(mediaDevice);
+ }
+ }
+ }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java
new file mode 100644
index 0000000..e375ea0
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java
@@ -0,0 +1,234 @@
+/*
+ * 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 com.android.settingslib.media;
+
+import android.app.Notification;
+import android.content.Context;
+import android.util.Log;
+
+import androidx.annotation.IntDef;
+
+import com.android.settingslib.bluetooth.BluetoothCallback;
+import com.android.settingslib.bluetooth.LocalBluetoothManager;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * LocalMediaManager provide interface to get MediaDevice list and transfer media to MediaDevice.
+ */
+public class LocalMediaManager implements BluetoothCallback {
+
+ private static final String TAG = "LocalMediaManager";
+
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({MediaDeviceState.STATE_CONNECTED,
+ MediaDeviceState.STATE_CONNECTING,
+ MediaDeviceState.STATE_DISCONNECTED})
+ public @interface MediaDeviceState {
+ int STATE_CONNECTED = 1;
+ int STATE_CONNECTING = 2;
+ int STATE_DISCONNECTED = 3;
+ }
+
+ private final Collection<DeviceCallback> mCallbacks = new ArrayList<>();
+ private final MediaDeviceCallback mMediaDeviceCallback = new MediaDeviceCallback();
+
+ private Context mContext;
+ private List<MediaDevice> mMediaDevices = new ArrayList<>();
+ private BluetoothMediaManager mBluetoothMediaManager;
+ private InfoMediaManager mInfoMediaManager;
+
+ private LocalBluetoothManager mLocalBluetoothManager;
+ private MediaDevice mLastConnectedDevice;
+ private MediaDevice mPhoneDevice;
+
+ /**
+ * Register to start receiving callbacks for MediaDevice events.
+ */
+ public void registerCallback(DeviceCallback callback) {
+ synchronized (mCallbacks) {
+ mCallbacks.add(callback);
+ }
+ }
+
+ /**
+ * Unregister to stop receiving callbacks for MediaDevice events
+ */
+ public void unregisterCallback(DeviceCallback callback) {
+ synchronized (mCallbacks) {
+ mCallbacks.remove(callback);
+ }
+ }
+
+ public LocalMediaManager(Context context, String packageName, Notification notification) {
+ mContext = context;
+ mLocalBluetoothManager =
+ LocalBluetoothManager.getInstance(context, /* onInitCallback= */ null);
+ if (mLocalBluetoothManager == null) {
+ Log.e(TAG, "Bluetooth is not supported on this device");
+ return;
+ }
+
+ mBluetoothMediaManager =
+ new BluetoothMediaManager(context, mLocalBluetoothManager, notification);
+ mInfoMediaManager = new InfoMediaManager(context, packageName, notification);
+ }
+
+ /**
+ * Connect the MediaDevice to transfer media
+ * @param connectDevice the MediaDevice
+ */
+ public void connectDevice(MediaDevice connectDevice) {
+ if (connectDevice == mLastConnectedDevice) {
+ return;
+ }
+
+ if (mLastConnectedDevice != null) {
+ mLastConnectedDevice.disconnect();
+ }
+
+ connectDevice.connect();
+ if (connectDevice.isConnected()) {
+ mLastConnectedDevice = connectDevice;
+ }
+
+ final int state = connectDevice.isConnected()
+ ? MediaDeviceState.STATE_CONNECTED
+ : MediaDeviceState.STATE_DISCONNECTED;
+ dispatchSelectedDeviceStateChanged(connectDevice, state);
+ }
+
+ void dispatchSelectedDeviceStateChanged(MediaDevice device, @MediaDeviceState int state) {
+ synchronized (mCallbacks) {
+ for (DeviceCallback callback : mCallbacks) {
+ callback.onSelectedDeviceStateChanged(device, state);
+ }
+ }
+ }
+
+ /**
+ * Start scan connected MediaDevice
+ */
+ public void startScan() {
+ mMediaDevices.clear();
+ mBluetoothMediaManager.registerCallback(mMediaDeviceCallback);
+ mInfoMediaManager.registerCallback(mMediaDeviceCallback);
+ mBluetoothMediaManager.startScan();
+ mInfoMediaManager.startScan();
+ }
+
+ private void addPhoneDeviceIfNecessary() {
+ // add phone device to list if there have any Bluetooth device and cast device.
+ if (mMediaDevices.size() > 0 && !mMediaDevices.contains(mPhoneDevice)) {
+ if (mPhoneDevice == null) {
+ mPhoneDevice = new PhoneMediaDevice(mContext, mLocalBluetoothManager);
+ }
+ mMediaDevices.add(mPhoneDevice);
+ }
+ }
+
+ private void removePhoneMediaDeviceIfNecessary() {
+ // if PhoneMediaDevice is the last item in the list, remove it.
+ if (mMediaDevices.size() == 1 && mMediaDevices.contains(mPhoneDevice)) {
+ mMediaDevices.clear();
+ }
+ }
+
+ void dispatchDeviceListUpdate() {
+ synchronized (mCallbacks) {
+ for (DeviceCallback callback : mCallbacks) {
+ callback.onDeviceListUpdate(new ArrayList<>(mMediaDevices));
+ }
+ }
+ }
+
+ /**
+ * Stop scan MediaDevice
+ */
+ public void stopScan() {
+ mBluetoothMediaManager.unregisterCallback(mMediaDeviceCallback);
+ mInfoMediaManager.unregisterCallback(mMediaDeviceCallback);
+ mBluetoothMediaManager.stopScan();
+ mInfoMediaManager.stopScan();
+ }
+
+ class MediaDeviceCallback implements MediaManager.MediaDeviceCallback {
+ @Override
+ public void onDeviceAdded(MediaDevice device) {
+ if (!mMediaDevices.contains(device)) {
+ mMediaDevices.add(device);
+ addPhoneDeviceIfNecessary();
+ dispatchDeviceListUpdate();
+ }
+ }
+
+ @Override
+ public void onDeviceListAdded(List<MediaDevice> devices) {
+ mMediaDevices.addAll(devices);
+ addPhoneDeviceIfNecessary();
+ dispatchDeviceListUpdate();
+ }
+
+ @Override
+ public void onDeviceRemoved(MediaDevice device) {
+ if (mMediaDevices.contains(device)) {
+ mMediaDevices.remove(device);
+ removePhoneMediaDeviceIfNecessary();
+ dispatchDeviceListUpdate();
+ }
+ }
+
+ @Override
+ public void onDeviceListRemoved(List<MediaDevice> devices) {
+ mMediaDevices.removeAll(devices);
+ removePhoneMediaDeviceIfNecessary();
+ dispatchDeviceListUpdate();
+ }
+
+ @Override
+ public void onDeviceAttributesChanged() {
+ dispatchDeviceListUpdate();
+ }
+ }
+
+
+ /**
+ * Callback for notifying device information updating
+ */
+ public interface DeviceCallback {
+ /**
+ * Callback for notifying device list updated.
+ *
+ * @param devices MediaDevice list
+ */
+ void onDeviceListUpdate(List<MediaDevice> devices);
+
+ /**
+ * Callback for notifying the connected device is changed.
+ *
+ * @param device the changed connected MediaDevice
+ * @param state the current MediaDevice state, the possible values are:
+ * {@link MediaDeviceState#STATE_CONNECTED},
+ * {@link MediaDeviceState#STATE_CONNECTING},
+ * {@link MediaDeviceState#STATE_DISCONNECTED}
+ */
+ void onSelectedDeviceStateChanged(MediaDevice device, @MediaDeviceState int state);
+ }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java b/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java
new file mode 100644
index 0000000..6c536f0
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java
@@ -0,0 +1,89 @@
+/*
+ * 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 com.android.settingslib.media;
+
+import android.content.Context;
+
+import androidx.annotation.IntDef;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * MediaDevice represents a media device(such like Bluetooth device, cast device and phone device).
+ */
+public abstract class MediaDevice {
+
+ private static final String TAG = "MediaDevice";
+
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({MediaDeviceType.TYPE_BLUETOOTH_DEVICE,
+ MediaDeviceType.TYPE_CAST_DEVICE,
+ MediaDeviceType.TYPE_PHONE_DEVICE})
+ public @interface MediaDeviceType {
+ int TYPE_BLUETOOTH_DEVICE = 1;
+ int TYPE_CAST_DEVICE = 2;
+ int TYPE_PHONE_DEVICE = 3;
+ }
+
+ protected boolean mIsConnected = false;
+ protected Context mContext;
+ protected int mType;
+
+ MediaDevice(Context context, @MediaDeviceType int type) {
+ mType = type;
+ mContext = context;
+ }
+
+ /**
+ * Check the MediaDevice is be connected to transfer.
+ *
+ * @return true if the MediaDevice is be connected to transfer, false otherwise.
+ */
+ protected boolean isConnected() {
+ return mIsConnected;
+ }
+
+ /**
+ * Get name from MediaDevice.
+ *
+ * @return name of MediaDevice.
+ */
+ public abstract String getName();
+
+ /**
+ * Get resource id of MediaDevice.
+ *
+ * @return resource id of MediaDevice.
+ */
+ public abstract int getIcon();
+
+ /**
+ * Get unique ID that represent MediaDevice
+ * @return unique id of MediaDevice
+ */
+ public abstract String getId();
+
+ /**
+ * Transfer MediaDevice for media
+ */
+ public abstract void connect();
+
+ /**
+ * Stop transfer MediaDevice
+ */
+ public abstract void disconnect();
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/MediaDeviceUtils.java b/packages/SettingsLib/src/com/android/settingslib/media/MediaDeviceUtils.java
new file mode 100644
index 0000000..060e9ad
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/media/MediaDeviceUtils.java
@@ -0,0 +1,46 @@
+/*
+ * 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 com.android.settingslib.media;
+
+import androidx.mediarouter.media.MediaRouter;
+
+import com.android.settingslib.bluetooth.CachedBluetoothDevice;
+
+/**
+ * MediaDeviceUtils provides utility function for MediaDevice
+ */
+public class MediaDeviceUtils {
+
+ /**
+ * Use CachedBluetoothDevice address to represent unique id
+ *
+ * @param cachedDevice the CachedBluetoothDevice
+ * @return CachedBluetoothDevice address
+ */
+ public static String getId(CachedBluetoothDevice cachedDevice) {
+ return cachedDevice.getAddress();
+ }
+
+ /**
+ * Use RouteInfo id to represent unique id
+ *
+ * @param route the RouteInfo
+ * @return RouteInfo id
+ */
+ public static String getId(MediaRouter.RouteInfo route) {
+ return route.getId();
+ }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/MediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/MediaManager.java
new file mode 100644
index 0000000..72b6b09
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/media/MediaManager.java
@@ -0,0 +1,157 @@
+/*
+ * 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 com.android.settingslib.media;
+
+import android.app.Notification;
+import android.content.Context;
+import android.util.Log;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * MediaManager provide interface to get MediaDevice list.
+ */
+public abstract class MediaManager {
+
+ private static final String TAG = "MediaManager";
+
+ protected final Collection<MediaDeviceCallback> mCallbacks = new ArrayList<>();
+ protected final List<MediaDevice> mMediaDevices = new ArrayList<>();
+
+ protected Context mContext;
+ protected Notification mNotification;
+
+ MediaManager(Context context, Notification notification) {
+ mContext = context;
+ mNotification = notification;
+ }
+
+ protected void registerCallback(MediaDeviceCallback callback) {
+ synchronized (mCallbacks) {
+ if (!mCallbacks.contains(callback)) {
+ mCallbacks.add(callback);
+ }
+ }
+ }
+
+ protected void unregisterCallback(MediaDeviceCallback callback) {
+ synchronized (mCallbacks) {
+ if (mCallbacks.contains(callback)) {
+ mCallbacks.remove(callback);
+ }
+ }
+ }
+
+ /**
+ * Start scan connected MediaDevice
+ */
+ public abstract void startScan();
+
+ /**
+ * Stop scan MediaDevice
+ */
+ public abstract void stopScan();
+
+ protected MediaDevice findMediaDevice(String id) {
+ for (MediaDevice mediaDevice : mMediaDevices) {
+ if (mediaDevice.getId().equals(id)) {
+ return mediaDevice;
+ }
+ }
+ Log.e(TAG, "findMediaDevice() can't found device");
+ return null;
+ }
+
+ protected void dispatchDeviceAdded(MediaDevice mediaDevice) {
+ synchronized (mCallbacks) {
+ for (MediaDeviceCallback callback : mCallbacks) {
+ callback.onDeviceAdded(mediaDevice);
+ }
+ }
+ }
+
+ protected void dispatchDeviceRemoved(MediaDevice mediaDevice) {
+ synchronized (mCallbacks) {
+ for (MediaDeviceCallback callback : mCallbacks) {
+ callback.onDeviceRemoved(mediaDevice);
+ }
+ }
+ }
+
+ protected void dispatchDeviceListAdded() {
+ synchronized (mCallbacks) {
+ for (MediaDeviceCallback callback : mCallbacks) {
+ callback.onDeviceListAdded(mMediaDevices);
+ }
+ }
+ }
+
+ protected void dispatchDeviceListRemoved(List<MediaDevice> devices) {
+ synchronized (mCallbacks) {
+ for (MediaDeviceCallback callback : mCallbacks) {
+ callback.onDeviceListRemoved(devices);
+ }
+ }
+ }
+
+ protected void dispatchDeviceAttributesChanged() {
+ synchronized (mCallbacks) {
+ for (MediaDeviceCallback callback : mCallbacks) {
+ callback.onDeviceAttributesChanged();
+ }
+ }
+ }
+
+ /**
+ * Callback for notifying device is added, removed and attributes changed.
+ */
+ public interface MediaDeviceCallback {
+ /**
+ * Callback for notifying MediaDevice is added.
+ *
+ * @param device the MediaDevice
+ */
+ void onDeviceAdded(MediaDevice device);
+
+ /**
+ * Callback for notifying MediaDevice list is added.
+ *
+ * @param devices the MediaDevice list
+ */
+ void onDeviceListAdded(List<MediaDevice> devices);
+
+ /**
+ * Callback for notifying MediaDevice is removed.
+ *
+ * @param device the MediaDevice
+ */
+ void onDeviceRemoved(MediaDevice device);
+
+ /**
+ * Callback for notifying MediaDevice list is removed.
+ *
+ * @param devices the MediaDevice list
+ */
+ void onDeviceListRemoved(List<MediaDevice> devices);
+
+ /**
+ * Callback for notifying MediaDevice attributes is changed.
+ */
+ void onDeviceAttributesChanged();
+ }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/PhoneMediaDevice.java b/packages/SettingsLib/src/com/android/settingslib/media/PhoneMediaDevice.java
new file mode 100644
index 0000000..5e49d6b
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/media/PhoneMediaDevice.java
@@ -0,0 +1,80 @@
+/*
+ * 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 com.android.settingslib.media;
+
+import android.content.Context;
+import android.util.Log;
+
+import com.android.settingslib.R;
+import com.android.settingslib.bluetooth.A2dpProfile;
+import com.android.settingslib.bluetooth.HearingAidProfile;
+import com.android.settingslib.bluetooth.LocalBluetoothManager;
+import com.android.settingslib.bluetooth.LocalBluetoothProfileManager;
+
+/**
+ * PhoneMediaDevice extends MediaDevice to represents Phone device.
+ */
+public class PhoneMediaDevice extends MediaDevice {
+
+ private static final String TAG = "PhoneMediaDevice";
+
+ public static final String ID = "phone_media_device_id_1";
+
+ private LocalBluetoothProfileManager mProfileManager;
+ private LocalBluetoothManager mLocalBluetoothManager;
+
+ PhoneMediaDevice(Context context, LocalBluetoothManager localBluetoothManager) {
+ super(context, MediaDeviceType.TYPE_PHONE_DEVICE);
+
+ mLocalBluetoothManager = localBluetoothManager;
+ mProfileManager = mLocalBluetoothManager.getProfileManager();
+ }
+
+ @Override
+ public String getName() {
+ return mContext
+ .getString(com.android.settingslib.R.string.media_transfer_phone_device_name);
+ }
+
+ @Override
+ public int getIcon() {
+ //TODO(b/117129183): This is not final icon for phone device, just for demo.
+ return R.drawable.ic_bt_cellphone;
+ }
+
+ @Override
+ public String getId() {
+ return ID;
+ }
+
+ @Override
+ public void connect() {
+ final HearingAidProfile hapProfile = mProfileManager.getHearingAidProfile();
+ final A2dpProfile a2dpProfile = mProfileManager.getA2dpProfile();
+
+ if (hapProfile != null && a2dpProfile != null) {
+ mIsConnected =
+ hapProfile.setActiveDevice(null) && a2dpProfile.setActiveDevice(null);
+ }
+ Log.d(TAG, "connect() device : " + getName() + ", is selected : " + mIsConnected);
+ }
+
+ @Override
+ public void disconnect() {
+ //TODO(b/117129183): disconnected last select device
+ mIsConnected = false;
+ }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/utils/PowerUtil.java b/packages/SettingsLib/src/com/android/settingslib/utils/PowerUtil.java
index fa59688..43c97df 100644
--- a/packages/SettingsLib/src/com/android/settingslib/utils/PowerUtil.java
+++ b/packages/SettingsLib/src/com/android/settingslib/utils/PowerUtil.java
@@ -81,6 +81,30 @@
return null;
}
+ /**
+ * Method to produce a shortened string describing the remaining battery. Suitable for Quick
+ * Settings and other areas where space is constrained.
+ *
+ * @param context context to fetch descriptions from
+ * @param drainTimeMs The estimated time remaining before the phone dies in milliseconds.
+ *
+ * @return a properly formatted and localized short string describing how much time remains
+ * before the battery runs out.
+ */
+ @Nullable
+ public static String getBatteryRemainingShortStringFormatted(
+ Context context, long drainTimeMs) {
+ if (drainTimeMs <= 0) {
+ return null;
+ }
+
+ if (drainTimeMs <= ONE_DAY_MILLIS) {
+ return getRegularTimeRemainingShortString(context, drainTimeMs);
+ } else {
+ return getMoreThanOneDayShortString(context, drainTimeMs);
+ }
+ }
+
private static String getShutdownImminentString(Context context, String percentageString) {
return TextUtils.isEmpty(percentageString)
? context.getString(R.string.power_remaining_duration_only_shutdown_imminent)
@@ -120,6 +144,14 @@
}
}
+ private static String getMoreThanOneDayShortString(Context context, long drainTimeMs) {
+ final long roundedTimeMs = roundTimeToNearestThreshold(drainTimeMs, ONE_HOUR_MILLIS);
+ CharSequence timeString = StringUtil.formatElapsedTime(context, roundedTimeMs,
+ false /* withSeconds */);
+
+ return context.getString(R.string.power_remaining_duration_only_short, timeString);
+ }
+
private static String getMoreThanTwoDaysString(Context context, String percentageString) {
final Locale currentLocale = context.getResources().getConfiguration().getLocales().get(0);
final MeasureFormat frmt = MeasureFormat.getInstance(currentLocale, FormatWidth.SHORT);
@@ -162,6 +194,22 @@
}
}
+ private static String getRegularTimeRemainingShortString(Context context, long drainTimeMs) {
+ // Get the time of day we think device will die rounded to the nearest 15 min.
+ final long roundedTimeOfDayMs =
+ roundTimeToNearestThreshold(
+ System.currentTimeMillis() + drainTimeMs,
+ FIFTEEN_MINUTES_MILLIS);
+
+ // convert the time to a properly formatted string.
+ String skeleton = android.text.format.DateFormat.getTimeFormatString(context);
+ DateFormat fmt = DateFormat.getInstanceForSkeleton(skeleton);
+ Date date = Date.from(Instant.ofEpochMilli(roundedTimeOfDayMs));
+ CharSequence timeString = fmt.format(date);
+
+ return context.getString(R.string.power_discharge_by_only_short, timeString);
+ }
+
public static long convertUsToMs(long timeUs) {
return timeUs / 1000;
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
index 523361d..22d5d83 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
@@ -158,11 +158,11 @@
* These values are matched in string arrays -- changes must be kept in sync
*/
public static final int SECURITY_NONE = 0;
- public static final int SECURITY_OWE = 1;
- public static final int SECURITY_WEP = 2;
- public static final int SECURITY_PSK = 3;
- public static final int SECURITY_SAE = 4;
- public static final int SECURITY_EAP = 5;
+ public static final int SECURITY_WEP = 1;
+ public static final int SECURITY_PSK = 2;
+ public static final int SECURITY_EAP = 3;
+ public static final int SECURITY_OWE = 4;
+ public static final int SECURITY_SAE = 5;
public static final int SECURITY_EAP_SUITE_B = 6;
private static final int PSK_UNKNOWN = 0;
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/MetricsFeatureProviderTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/MetricsFeatureProviderTest.java
index 7a7f0d4..603f838 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/MetricsFeatureProviderTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/MetricsFeatureProviderTest.java
@@ -15,11 +15,15 @@
*/
package com.android.settingslib.core.instrumentation;
+import static com.google.common.truth.Truth.assertThat;
+
import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
+import android.app.Activity;
+import android.app.settings.SettingsEnums;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -33,6 +37,7 @@
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.robolectric.Robolectric;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.util.ReflectionHelpers;
@@ -90,4 +95,28 @@
anyString(),
eq(Pair.create(MetricsEvent.FIELD_CONTEXT, MetricsEvent.SETTINGS_GESTURES)));
}
+
+ @Test
+ public void getAttribution_noActivity_shouldReturnUnknown() {
+ assertThat(mProvider.getAttribution(null /* activity */))
+ .isEqualTo(SettingsEnums.PAGE_UNKNOWN);
+ }
+
+ @Test
+ public void getAttribution_notSet_shouldReturnUnknown() {
+ final Activity activity = Robolectric.setupActivity(Activity.class);
+
+ assertThat(mProvider.getAttribution(activity))
+ .isEqualTo(SettingsEnums.PAGE_UNKNOWN);
+ }
+
+ @Test
+ public void getAttribution_set_shouldReturnAttribution() {
+ final Intent intent = new Intent()
+ .putExtra(MetricsFeatureProvider.EXTRA_SOURCE_METRICS_CATEGORY, 100);
+
+ final Activity activity = Robolectric.buildActivity(Activity.class, intent).create().get();
+
+ assertThat(mProvider.getAttribution(activity)).isEqualTo(100);
+ }
}
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index 1371a5c..448a963 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -1648,12 +1648,6 @@
dumpSetting(s, p,
Settings.Secure.TOUCH_EXPLORATION_GRANTED_ACCESSIBILITY_SERVICES,
SecureSettingsProto.Accessibility.TOUCH_EXPLORATION_GRANTED_ACCESSIBILITY_SERVICES);
- dumpSetting(s, p,
- Settings.Secure.ACCESSIBILITY_NON_INTERACTIVE_UI_TIMEOUT_MS,
- SecureSettingsProto.Accessibility.NON_INTERACTIVE_UI_TIMEOUT_MS);
- dumpSetting(s, p,
- Settings.Secure.ACCESSIBILITY_INTERACTIVE_UI_TIMEOUT_MS,
- SecureSettingsProto.Accessibility.INTERACTIVE_UI_TIMEOUT_MS);
p.end(accessibilityToken);
dumpSetting(s, p,
diff --git a/packages/SystemUI/res/layout/ongoing_privacy_chip.xml b/packages/SystemUI/res/layout/ongoing_privacy_chip.xml
index 5e952e3..ddefb6a 100644
--- a/packages/SystemUI/res/layout/ongoing_privacy_chip.xml
+++ b/packages/SystemUI/res/layout/ongoing_privacy_chip.xml
@@ -21,7 +21,8 @@
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_margin="@dimen/ongoing_appops_chip_margin"
- android:gravity="center_vertical|end"
+ android:gravity="center_vertical|center_horizontal"
+ android:layout_gravity="center_vertical|end"
android:orientation="horizontal"
android:paddingStart="@dimen/ongoing_appops_chip_side_padding"
android:paddingEnd="@dimen/ongoing_appops_chip_side_padding"
@@ -32,13 +33,17 @@
android:id="@+id/icons_container"
android:layout_height="match_parent"
android:layout_width="wrap_content"
- android:gravity="center_vertical|start"
+ android:layout_gravity="center_vertical|start"
+ android:gravity="center_vertical"
/>
<TextView
android:id="@+id/app_name"
android:layout_height="match_parent"
android:layout_width="wrap_content"
- android:gravity="center_vertical|end"
+ android:singleLine="true"
+ android:ellipsize="end"
+ android:layout_gravity="center_vertical|end"
+ android:gravity="center_vertical"
/>
</com.android.systemui.privacy.OngoingPrivacyChip>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/quick_qs_status_icons.xml b/packages/SystemUI/res/layout/quick_qs_status_icons.xml
index 94189bb..2000104 100644
--- a/packages/SystemUI/res/layout/quick_qs_status_icons.xml
+++ b/packages/SystemUI/res/layout/quick_qs_status_icons.xml
@@ -42,6 +42,20 @@
android:id="@+id/statusIcons"
android:layout_width="0dp"
android:layout_height="match_parent"
- android:layout_weight="1" />
+ android:layout_weight="1"
+ android:paddingEnd="@dimen/signal_cluster_battery_padding" />
+
+ <com.android.systemui.BatteryMeterView
+ android:id="@+id/batteryRemainingIcon"
+ android:layout_height="match_parent"
+ android:layout_width="wrap_content"
+ android:paddingEnd="2dp" />
+
+ <TextView
+ android:id="@+id/batteryRemainingText"
+ android:textAppearance="@style/TextAppearance.QS.TileLabel"
+ android:layout_height="match_parent"
+ android:layout_width="wrap_content"
+ android:gravity="center_vertical" />
</LinearLayout>
diff --git a/packages/SystemUI/res/layout/quick_status_bar_header_system_icons.xml b/packages/SystemUI/res/layout/quick_status_bar_header_system_icons.xml
index 007070e..e7f2c51 100644
--- a/packages/SystemUI/res/layout/quick_status_bar_header_system_icons.xml
+++ b/packages/SystemUI/res/layout/quick_status_bar_header_system_icons.xml
@@ -27,6 +27,13 @@
android:paddingStart="@dimen/status_bar_padding_start"
android:paddingEnd="@dimen/status_bar_padding_end" >
+ <LinearLayout
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ android:orientation="horizontal"
+ android:gravity="center_vertical|start" >
+
<com.android.systemui.statusbar.policy.Clock
android:id="@+id/clock"
android:layout_width="wrap_content"
@@ -38,13 +45,21 @@
android:singleLine="true"
android:textAppearance="@style/TextAppearance.StatusBar.Clock"
systemui:showDark="false" />
+ </LinearLayout>
<android.widget.Space
android:id="@+id/space"
android:layout_width="0dp"
android:layout_height="match_parent"
+ android:layout_gravity="center_vertical|center_horizontal"
+ android:visibility="gone" />
+
+ <LinearLayout
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
android:layout_weight="1"
- android:gravity="center_vertical|center_horizontal" />
+ android:orientation="horizontal"
+ android:gravity="center_vertical|end">
<include layout="@layout/ongoing_privacy_chip" />
@@ -53,4 +68,5 @@
android:layout_height="match_parent"
android:layout_width="wrap_content"
android:gravity="center_vertical|end" />
+ </LinearLayout>
</LinearLayout>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index f132488..0e41a7f 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -215,6 +215,9 @@
<!-- The width of the view containing navigation buttons -->
<dimen name="navigation_key_width">70dp</dimen>
+ <!-- The width/height of the icon of a navigation button -->
+ <dimen name="navigation_icon_size">32dp</dimen>
+
<dimen name="navigation_key_padding">0dp</dimen>
<!-- The width of the view containing the menu/ime navigation bar icons -->
@@ -861,7 +864,6 @@
<dimen name="nav_content_padding">0dp</dimen>
<dimen name="nav_quick_scrub_track_edge_padding">24dp</dimen>
<dimen name="nav_quick_scrub_track_thickness">10dp</dimen>
- <dimen name="nav_home_back_gesture_drag_limit">40dp</dimen>
<!-- Navigation bar shadow params. -->
<dimen name="nav_key_button_shadow_offset_x">0dp</dimen>
diff --git a/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java b/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java
index f6fec54..053ea67 100644
--- a/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java
+++ b/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java
@@ -19,7 +19,10 @@
import static android.app.StatusBarManager.DISABLE_NONE;
import static android.provider.Settings.System.SHOW_BATTERY_PERCENT;
+import static java.lang.annotation.RetentionPolicy.SOURCE;
+
import android.animation.ArgbEvaluator;
+import android.annotation.IntDef;
import android.app.ActivityManager;
import android.content.Context;
import android.content.res.Resources;
@@ -55,15 +58,23 @@
import com.android.systemui.tuner.TunerService;
import com.android.systemui.tuner.TunerService.Tunable;
import com.android.systemui.util.Utils.DisableStateTracker;
-import com.android.systemui.R;
import java.io.FileDescriptor;
import java.io.PrintWriter;
+import java.lang.annotation.Retention;
import java.text.NumberFormat;
public class BatteryMeterView extends LinearLayout implements
BatteryStateChangeCallback, Tunable, DarkReceiver, ConfigurationListener {
+
+ @Retention(SOURCE)
+ @IntDef({MODE_DEFAULT, MODE_ON, MODE_OFF})
+ public @interface BatteryPercentMode {}
+ public static final int MODE_DEFAULT = 0;
+ public static final int MODE_ON = 1;
+ public static final int MODE_OFF = 2;
+
private final BatteryMeterDrawableBase mDrawable;
private final String mSlotBattery;
private final ImageView mBatteryIconView;
@@ -74,6 +85,7 @@
private SettingObserver mSettingObserver;
private int mTextColor;
private int mLevel;
+ private int mShowPercentMode = MODE_DEFAULT;
private boolean mForceShowPercent;
private boolean mShowPercentAvailable;
@@ -154,7 +166,19 @@
}
public void setForceShowPercent(boolean show) {
- mForceShowPercent = show;
+ setPercentShowMode(show ? MODE_ON : MODE_DEFAULT);
+ }
+
+ /**
+ * Force a particular mode of showing percent
+ *
+ * 0 - No preference
+ * 1 - Force on
+ * 2 - Force off
+ * @param mode desired mode (none, on, off)
+ */
+ public void setPercentShowMode(@BatteryPercentMode int mode) {
+ mShowPercentMode = mode;
updateShowPercent();
}
@@ -273,7 +297,8 @@
.getIntForUser(getContext().getContentResolver(),
SHOW_BATTERY_PERCENT, 0, mUser);
- if ((mShowPercentAvailable && systemSetting) || mForceShowPercent) {
+ if ((mShowPercentAvailable && systemSetting && mShowPercentMode != MODE_OFF)
+ || mShowPercentMode == MODE_ON) {
if (!showing) {
mBatteryPercentView = loadPercentView();
if (mTextColor != 0) mBatteryPercentView.setTextColor(mTextColor);
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeReceiver.java b/packages/SystemUI/src/com/android/systemui/doze/DozeReceiver.java
index 30dfd36..4a2e06c 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeReceiver.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeReceiver.java
@@ -22,11 +22,6 @@
public interface DozeReceiver {
/**
- * If device enters or leaves doze mode
- */
- void setDozing(boolean dozing);
-
- /**
* Invoked every time a minute is elapsed in doze mode
*/
void dozeTimeTick();
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java
index 5d99c57..ce84b84 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java
@@ -31,6 +31,9 @@
import android.provider.Settings;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.systemui.Dependency;
+
+import java.util.concurrent.atomic.AtomicBoolean;
/**
* Controls the screen brightness when dozing.
@@ -63,6 +66,7 @@
* --ei brightness_bucket 1}
*/
private int mDebugBrightnessBucket = -1;
+ private AtomicBoolean mIsDestroyed = new AtomicBoolean();
@VisibleForTesting
public DozeScreenBrightness(Context context, DozeMachine.Service service,
@@ -82,9 +86,13 @@
mSensorToScrimOpacity = sensorToScrimOpacity;
if (mDebuggable) {
- IntentFilter filter = new IntentFilter();
- filter.addAction(ACTION_AOD_BRIGHTNESS);
- mContext.registerReceiverAsUser(this, UserHandle.ALL, filter, null, null);
+ Dependency.get(Dependency.BG_HANDLER).post(()-> {
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(ACTION_AOD_BRIGHTNESS);
+ if (!mIsDestroyed.get()) {
+ mContext.registerReceiverAsUser(this, UserHandle.ALL, filter, null, handler);
+ }
+ });
}
}
@@ -121,6 +129,7 @@
}
private void onDestroy() {
+ mIsDestroyed.set(true);
setLightSensorEnabled(false);
if (mDebuggable) {
mContext.unregisterReceiver(this);
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java
index 82b79ac..e78951a 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java
@@ -25,7 +25,6 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
-import android.graphics.drawable.Icon;
import android.icu.text.DateFormat;
import android.icu.text.DisplayContext;
import android.net.Uri;
@@ -35,6 +34,13 @@
import android.service.notification.ZenModeConfig;
import android.text.TextUtils;
+import androidx.core.graphics.drawable.IconCompat;
+import androidx.slice.Slice;
+import androidx.slice.SliceProvider;
+import androidx.slice.builders.ListBuilder;
+import androidx.slice.builders.ListBuilder.RowBuilder;
+import androidx.slice.builders.SliceAction;
+
import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.R;
import com.android.systemui.statusbar.policy.NextAlarmController;
@@ -46,13 +52,6 @@
import java.util.Locale;
import java.util.concurrent.TimeUnit;
-import androidx.core.graphics.drawable.IconCompat;
-import androidx.slice.Slice;
-import androidx.slice.SliceProvider;
-import androidx.slice.builders.ListBuilder;
-import androidx.slice.builders.ListBuilder.RowBuilder;
-import androidx.slice.builders.SliceAction;
-
/**
* Simple Slice provider that shows the current date.
*/
@@ -226,7 +225,7 @@
private void updateNextAlarm() {
if (withinNHours(mNextAlarmInfo, ALARM_VISIBILITY_HOURS)) {
String pattern = android.text.format.DateFormat.is24HourFormat(getContext(),
- ActivityManager.getCurrentUser()) ? "H:mm" : "h:mm";
+ ActivityManager.getCurrentUser()) ? "HH:mm" : "h:mm";
mNextAlarm = android.text.format.DateFormat.format(pattern,
mNextAlarmInfo.getTriggerTime()).toString();
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyChip.kt b/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyChip.kt
index 3953139d..fc1baef 100644
--- a/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyChip.kt
+++ b/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyChip.kt
@@ -14,21 +14,14 @@
package com.android.systemui.privacy
-import android.app.ActivityManager
-import android.app.AppOpsManager
import android.content.Context
import android.graphics.Color
-import android.os.UserHandle
-import android.os.UserManager
import android.util.AttributeSet
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.LinearLayout
import android.widget.TextView
-import com.android.systemui.Dependency
import com.android.systemui.R
-import com.android.systemui.appops.AppOpItem
-import com.android.systemui.appops.AppOpsController
class OngoingPrivacyChip @JvmOverloads constructor(
context: Context,
@@ -37,37 +30,15 @@
defStyleRes: Int = 0
) : LinearLayout(context, attrs, defStyleAttrs, defStyleRes) {
- companion object {
- val OPS = intArrayOf(AppOpsManager.OP_CAMERA,
- AppOpsManager.OP_RECORD_AUDIO,
- AppOpsManager.OP_COARSE_LOCATION,
- AppOpsManager.OP_FINE_LOCATION)
- }
-
private lateinit var appName: TextView
private lateinit var iconsContainer: LinearLayout
- private var privacyList = emptyList<PrivacyItem>()
- private val appOpsController = Dependency.get(AppOpsController::class.java)
- private val userManager = context.getSystemService(UserManager::class.java)
- private val currentUser = ActivityManager.getCurrentUser()
- private val currentUserIds = userManager.getProfiles(currentUser).map { it.id }
- private var listening = false
-
- var builder = PrivacyDialogBuilder(context, privacyList)
-
- private val callback = object : AppOpsController.Callback {
- override fun onActiveStateChanged(
- code: Int,
- uid: Int,
- packageName: String,
- active: Boolean
- ) {
- val userId = UserHandle.getUserId(uid)
- if (userId in currentUserIds) {
- updatePrivacyList()
- }
+ var builder = PrivacyDialogBuilder(context, emptyList<PrivacyItem>())
+ var privacyList = emptyList<PrivacyItem>()
+ set(value) {
+ field = value
+ builder = PrivacyDialogBuilder(context, value)
+ updateView()
}
- }
override fun onFinishInflate() {
super.onFinishInflate()
@@ -76,36 +47,6 @@
iconsContainer = findViewById(R.id.icons_container)
}
- fun setListening(listen: Boolean) {
- if (listening == listen) return
- listening = listen
- if (listening) {
- appOpsController.addCallback(OPS, callback)
- updatePrivacyList()
- } else {
- appOpsController.removeCallback(OPS, callback)
- }
- }
-
- private fun updatePrivacyList() {
- privacyList = currentUserIds.flatMap { appOpsController.getActiveAppOpsForUser(it) }
- .mapNotNull { toPrivacyItem(it) }
- builder = PrivacyDialogBuilder(context, privacyList)
- updateView()
- }
-
- private fun toPrivacyItem(appOpItem: AppOpItem): PrivacyItem? {
- val type: PrivacyType = when (appOpItem.code) {
- AppOpsManager.OP_CAMERA -> PrivacyType.TYPE_CAMERA
- AppOpsManager.OP_COARSE_LOCATION -> PrivacyType.TYPE_LOCATION
- AppOpsManager.OP_FINE_LOCATION -> PrivacyType.TYPE_LOCATION
- AppOpsManager.OP_RECORD_AUDIO -> PrivacyType.TYPE_MICROPHONE
- else -> return null
- }
- val app = PrivacyApplication(appOpItem.packageName, context)
- return PrivacyItem(type, app, appOpItem.timeStarted)
- }
-
// Should only be called if the builder icons or app changed
private fun updateView() {
fun setIcons(dialogBuilder: PrivacyDialogBuilder, iconsContainer: ViewGroup) {
@@ -121,11 +62,9 @@
}
if (privacyList.isEmpty()) {
- visibility = GONE
return
} else {
generateContentDescription()
- visibility = VISIBLE
setIcons(builder, iconsContainer)
appName.visibility = GONE
builder.app?.let {
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt
new file mode 100644
index 0000000..5141e50
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.privacy
+
+import android.app.ActivityManager
+import android.app.AppOpsManager
+import android.content.Context
+import android.os.Handler
+import android.os.UserHandle
+import android.os.UserManager
+import com.android.systemui.Dependency
+import com.android.systemui.appops.AppOpItem
+import com.android.systemui.appops.AppOpsController
+
+class PrivacyItemController(val context: Context, val callback: Callback) {
+
+ companion object {
+ val OPS = intArrayOf(AppOpsManager.OP_CAMERA,
+ AppOpsManager.OP_RECORD_AUDIO,
+ AppOpsManager.OP_COARSE_LOCATION,
+ AppOpsManager.OP_FINE_LOCATION)
+ }
+
+ private var privacyList = emptyList<PrivacyItem>()
+ private val appOpsController = Dependency.get(AppOpsController::class.java)
+ private val userManager = context.getSystemService(UserManager::class.java)
+ private val currentUser = ActivityManager.getCurrentUser()
+ private val currentUserIds = userManager.getProfiles(currentUser).map { it.id }
+ private val bgHandler = Handler(Dependency.get(Dependency.BG_LOOPER))
+ private val uiHandler = Dependency.get(Dependency.MAIN_HANDLER)
+ private val notifyChanges = Runnable {
+ callback.privacyChanged(privacyList)
+ }
+ private val updateListAndNotifyChanges = Runnable {
+ updatePrivacyList()
+ uiHandler.post(notifyChanges)
+ }
+
+ private var listening = false
+
+ private val cb = object : AppOpsController.Callback {
+ override fun onActiveStateChanged(
+ code: Int,
+ uid: Int,
+ packageName: String,
+ active: Boolean
+ ) {
+ val userId = UserHandle.getUserId(uid)
+ if (userId in currentUserIds) {
+ update()
+ }
+ }
+ }
+
+ private fun update() {
+ bgHandler.post(updateListAndNotifyChanges)
+ }
+
+ fun setListening(listen: Boolean) {
+ if (listening == listen) return
+ listening = listen
+ if (listening) {
+ appOpsController.addCallback(OPS, cb)
+ update()
+ } else {
+ appOpsController.removeCallback(OPS, cb)
+ }
+ }
+
+ private fun updatePrivacyList() {
+ privacyList = currentUserIds.flatMap { appOpsController.getActiveAppOpsForUser(it) }
+ .mapNotNull { toPrivacyItem(it) }
+ }
+
+ private fun toPrivacyItem(appOpItem: AppOpItem): PrivacyItem? {
+ val type: PrivacyType = when (appOpItem.code) {
+ AppOpsManager.OP_CAMERA -> PrivacyType.TYPE_CAMERA
+ AppOpsManager.OP_COARSE_LOCATION -> PrivacyType.TYPE_LOCATION
+ AppOpsManager.OP_FINE_LOCATION -> PrivacyType.TYPE_LOCATION
+ AppOpsManager.OP_RECORD_AUDIO -> PrivacyType.TYPE_MICROPHONE
+ else -> return null
+ }
+ val app = PrivacyApplication(appOpItem.packageName, context)
+ return PrivacyItem(type, app, appOpItem.timeStarted)
+ }
+
+ // Used by containing class to get notified of changes
+ interface Callback {
+ fun privacyChanged(privacyItems: List<PrivacyItem>)
+ }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
index 3ee6195..e3f85d9 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
@@ -15,6 +15,7 @@
package com.android.systemui.qs;
import static android.app.StatusBarManager.DISABLE2_QUICK_SETTINGS;
+import static android.provider.Settings.System.SHOW_BATTERY_PERCENT;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -28,23 +29,29 @@
import android.content.IntentFilter;
import android.content.res.Configuration;
import android.content.res.Resources;
+import android.database.ContentObserver;
import android.graphics.Color;
import android.graphics.Rect;
import android.media.AudioManager;
+import android.net.Uri;
import android.os.Handler;
import android.os.Looper;
import android.provider.AlarmClock;
+import android.provider.Settings;
import android.service.notification.ZenModeConfig;
import android.text.format.DateUtils;
import android.util.AttributeSet;
import android.util.Log;
import android.util.Pair;
+import android.view.DisplayCutout;
import android.view.View;
import android.view.WindowInsets;
import android.view.WindowManager;
import android.widget.FrameLayout;
import android.widget.ImageView;
+import android.widget.LinearLayout;
import android.widget.RelativeLayout;
+import android.widget.Space;
import android.widget.TextView;
import androidx.annotation.VisibleForTesting;
@@ -57,12 +64,15 @@
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.privacy.OngoingPrivacyChip;
import com.android.systemui.privacy.OngoingPrivacyDialog;
+import com.android.systemui.privacy.PrivacyItem;
+import com.android.systemui.privacy.PrivacyItemController;
import com.android.systemui.qs.QSDetail.Callback;
import com.android.systemui.statusbar.phone.PhoneStatusBarView;
import com.android.systemui.statusbar.phone.StatusBarIconController;
import com.android.systemui.statusbar.phone.StatusBarIconController.TintedIconManager;
import com.android.systemui.statusbar.phone.StatusIconContainer;
import com.android.systemui.statusbar.phone.SystemUIDialog;
+import com.android.systemui.statusbar.policy.BatteryController;
import com.android.systemui.statusbar.policy.Clock;
import com.android.systemui.statusbar.policy.DarkIconDispatcher;
import com.android.systemui.statusbar.policy.DarkIconDispatcher.DarkReceiver;
@@ -70,6 +80,7 @@
import com.android.systemui.statusbar.policy.NextAlarmController;
import com.android.systemui.statusbar.policy.ZenModeController;
+import java.util.List;
import java.util.Locale;
import java.util.Objects;
@@ -125,9 +136,14 @@
private Clock mClockView;
private DateView mDateView;
private OngoingPrivacyChip mPrivacyChip;
+ private Space mSpace;
+ private BatteryMeterView mBatteryRemainingIcon;
+ private TextView mBatteryRemainingText;
+ private boolean mShowBatteryPercentAndEstimate;
private NextAlarmController mAlarmController;
private ZenModeController mZenController;
+ private PrivacyItemController mPrivacyItemController;
/** Counts how many times the long press tooltip has been shown to the user. */
private int mShownCount;
@@ -138,16 +154,29 @@
updateStatusText();
}
};
+ private boolean mHasTopCutout = false;
+
+ private final PercentSettingObserver mPercentSettingObserver =
+ new PercentSettingObserver(new Handler(mContext.getMainLooper()));
/**
* Runnable for automatically fading out the long press tooltip (as if it were animating away).
*/
private final Runnable mAutoFadeOutTooltipRunnable = () -> hideLongPressTooltip(false);
+ private PrivacyItemController.Callback mPICCallback = new PrivacyItemController.Callback() {
+ @Override
+ public void privacyChanged(List<PrivacyItem> privacyItems) {
+ mPrivacyChip.setPrivacyList(privacyItems);
+ setChipVisibility(!privacyItems.isEmpty());
+ }
+ };
+
public QuickStatusBarHeader(Context context, AttributeSet attrs) {
super(context, attrs);
mAlarmController = Dependency.get(NextAlarmController.class);
mZenController = Dependency.get(ZenModeController.class);
+ mPrivacyItemController = new PrivacyItemController(context, mPICCallback);
mShownCount = getStoredShownCount();
}
@@ -186,14 +215,28 @@
// Set the correct tint for the status icons so they contrast
mIconManager.setTint(fillColor);
+ mShowBatteryPercentAndEstimate = mContext.getResources().getBoolean(
+ com.android.internal.R.bool.config_battery_percentage_setting_available);
+
mBatteryMeterView = findViewById(R.id.battery);
- mBatteryMeterView.setForceShowPercent(true);
+ mBatteryMeterView.setPercentShowMode(mShowBatteryPercentAndEstimate
+ ? BatteryMeterView.MODE_ON : BatteryMeterView.MODE_OFF);
mBatteryMeterView.setOnClickListener(this);
mClockView = findViewById(R.id.clock);
mClockView.setOnClickListener(this);
mDateView = findViewById(R.id.date);
mPrivacyChip = findViewById(R.id.privacy_chip);
mPrivacyChip.setOnClickListener(this);
+ mSpace = findViewById(R.id.space);
+
+ // Tint for the battery icons are handled in setupHost()
+ mBatteryRemainingIcon = findViewById(R.id.batteryRemainingIcon);
+ mBatteryRemainingIcon.setPercentShowMode(BatteryMeterView.MODE_OFF);
+
+ mBatteryRemainingText = findViewById(R.id.batteryRemainingText);
+ mBatteryRemainingText.setTextColor(fillColor);
+
+ updateShowPercent();
}
private void updateStatusText() {
@@ -208,6 +251,16 @@
}
}
+ private void setChipVisibility(boolean chipVisible) {
+ mBatteryMeterView.setVisibility(View.VISIBLE);
+ if (chipVisible) {
+ mPrivacyChip.setVisibility(View.VISIBLE);
+ if (mHasTopCutout) mBatteryMeterView.setVisibility(View.GONE);
+ } else {
+ mPrivacyChip.setVisibility(View.GONE);
+ }
+ }
+
private boolean updateRingerStatus() {
boolean isOriginalVisible = mRingerModeTextView.getVisibility() == View.VISIBLE;
CharSequence originalRingerText = mRingerModeTextView.getText();
@@ -342,6 +395,14 @@
.build();
}
+ private void updateBatteryRemainingText() {
+ if (!mShowBatteryPercentAndEstimate) {
+ return;
+ }
+ mBatteryRemainingText.setText(
+ Dependency.get(BatteryController.class).getEstimatedTimeRemainingString());
+ }
+
public void setExpanded(boolean expanded) {
if (mExpanded == expanded) return;
mExpanded = expanded;
@@ -407,12 +468,16 @@
super.onAttachedToWindow();
Dependency.get(StatusBarIconController.class).addIconGroup(mIconManager);
requestApplyInsets();
+ mContext.getContentResolver().registerContentObserver(
+ Settings.System.getUriFor(SHOW_BATTERY_PERCENT), false, mPercentSettingObserver,
+ ActivityManager.getCurrentUser());
}
@Override
public WindowInsets onApplyWindowInsets(WindowInsets insets) {
+ DisplayCutout cutout = insets.getDisplayCutout();
Pair<Integer, Integer> padding = PhoneStatusBarView.cornerCutoutMargins(
- insets.getDisplayCutout(), getDisplay());
+ cutout, getDisplay());
if (padding == null) {
mSystemIconsView.setPaddingRelative(
getResources().getDimensionPixelSize(R.dimen.status_bar_padding_start), 0,
@@ -421,6 +486,22 @@
mSystemIconsView.setPadding(padding.first, 0, padding.second, 0);
}
+ LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) mSpace.getLayoutParams();
+ if (cutout != null) {
+ Rect topCutout = cutout.getBoundingRectTop();
+ if (topCutout.isEmpty()) {
+ mHasTopCutout = false;
+ lp.width = 0;
+ mSpace.setVisibility(View.GONE);
+ } else {
+ mHasTopCutout = true;
+ lp.width = topCutout.width();
+ mSpace.setVisibility(View.VISIBLE);
+ }
+ }
+ mSpace.setLayoutParams(lp);
+ // Decide whether to show BatteryMeterView
+ setChipVisibility(mPrivacyChip.getVisibility() == View.VISIBLE);
return super.onApplyWindowInsets(insets);
}
@@ -429,6 +510,7 @@
public void onDetachedFromWindow() {
setListening(false);
Dependency.get(StatusBarIconController.class).removeIconGroup(mIconManager);
+ mContext.getContentResolver().unregisterContentObserver(mPercentSettingObserver);
super.onDetachedFromWindow();
}
@@ -437,7 +519,7 @@
return;
}
mHeaderQsPanel.setListening(listening);
- mPrivacyChip.setListening(listening);
+ mPrivacyItemController.setListening(listening);
mListening = listening;
if (listening) {
@@ -445,6 +527,7 @@
mAlarmController.addCallback(this);
mContext.registerReceiver(mRingerReceiver,
new IntentFilter(AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION));
+ updateBatteryRemainingText();
} else {
mZenController.removeCallback(this);
mAlarmController.removeCallback(this);
@@ -614,6 +697,14 @@
// Use SystemUI context to get battery meter colors, and let it use the default tint (white)
mBatteryMeterView.setColorsFromContext(mHost.getContext());
mBatteryMeterView.onDarkChanged(new Rect(), 0, DarkIconDispatcher.DEFAULT_ICON_TINT);
+
+ Rect tintArea = new Rect(0, 0, 0, 0);
+ int colorForeground = Utils.getColorAttrDefaultColor(getContext(),
+ android.R.attr.colorForeground);
+ float intensity = getColorIntensity(colorForeground);
+ int fillColor = fillColorForIntensity(intensity, getContext());
+ mBatteryRemainingIcon.setColorsFromContext(mHost.getContext());
+ mBatteryRemainingIcon.onDarkChanged(tintArea, intensity, fillColor);
}
public void setCallback(Callback qsPanelCallback) {
@@ -646,4 +737,39 @@
lp.rightMargin = sideMargins;
}
}
+
+ private void updateShowPercent() {
+ final boolean systemSetting = 0 != Settings.System
+ .getIntForUser(getContext().getContentResolver(),
+ SHOW_BATTERY_PERCENT, 0, ActivityManager.getCurrentUser());
+
+ mShowBatteryPercentAndEstimate = systemSetting;
+
+ updateBatteryViews();
+ }
+
+ private void updateBatteryViews() {
+ if (mShowBatteryPercentAndEstimate) {
+ mBatteryMeterView.setPercentShowMode(BatteryMeterView.MODE_ON);
+ mBatteryRemainingIcon.setVisibility(View.VISIBLE);
+ mBatteryRemainingText.setVisibility(View.VISIBLE);
+ updateBatteryRemainingText();
+ } else {
+ mBatteryMeterView.setPercentShowMode(BatteryMeterView.MODE_OFF);
+ mBatteryRemainingIcon.setVisibility(View.GONE);
+ mBatteryRemainingText.setVisibility(View.GONE);
+ }
+ }
+
+ private final class PercentSettingObserver extends ContentObserver {
+ PercentSettingObserver(Handler handler) {
+ super(handler);
+ }
+
+ @Override
+ public void onChange(boolean selfChange, Uri uri) {
+ super.onChange(selfChange, uri);
+ updateShowPercent();
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/BatterySaverTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/BatterySaverTile.java
index 7f3537c..da2828e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/BatterySaverTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/BatterySaverTile.java
@@ -20,6 +20,7 @@
import android.graphics.drawable.Drawable;
import android.service.quicksettings.Tile;
import android.widget.Switch;
+
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.settingslib.graph.BatteryMeterDrawableBase;
import com.android.systemui.Dependency;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateController.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateController.java
index 12c0fcb..65476fd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateController.java
@@ -18,11 +18,17 @@
import static java.lang.annotation.RetentionPolicy.SOURCE;
+import android.animation.ObjectAnimator;
+import android.animation.ValueAnimator;
import android.annotation.IntDef;
-import android.util.ArraySet;
-import android.util.Log;
+import android.util.FloatProperty;
+import android.view.animation.Interpolator;
+
import com.android.internal.annotations.GuardedBy;
+import com.android.systemui.Interpolators;
+import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
import com.android.systemui.statusbar.phone.StatusBar;
+
import java.lang.annotation.Retention;
import java.util.ArrayList;
import java.util.Comparator;
@@ -38,14 +44,51 @@
private static final Comparator <RankedListener> mComparator
= (o1, o2) -> Integer.compare(o1.rank, o2.rank);
+ private static final FloatProperty<StatusBarStateController> SET_DARK_AMOUNT_PROPERTY =
+ new FloatProperty<StatusBarStateController>("mDozeAmount") {
+
+ @Override
+ public void setValue(StatusBarStateController object, float value) {
+ object.setDozeAmountInternal(value);
+ }
+
+ @Override
+ public Float get(StatusBarStateController object) {
+ return object.mDozeAmount;
+ }
+ };
private final ArrayList<RankedListener> mListeners = new ArrayList<>();
- private boolean mIsDozing;
private int mState;
private int mLastState;
private boolean mLeaveOpenOnKeyguardHide;
private boolean mKeyguardRequested;
+ /**
+ * If the device is currently dozing or not.
+ */
+ private boolean mIsDozing;
+
+ /**
+ * Current {@link #mDozeAmount} animator.
+ */
+ private ValueAnimator mDarkAnimator;
+
+ /**
+ * Current doze amount in this frame.
+ */
+ private float mDozeAmount;
+
+ /**
+ * Where the animator will stop.
+ */
+ private float mDozeAmountTarget;
+
+ /**
+ * The type of interpolator that should be used to the doze animation.
+ */
+ private Interpolator mDozeInterpolator = Interpolators.FAST_OUT_SLOW_IN;
+
// TODO: b/115739177 (remove this explicit ordering if we can)
@Retention(SOURCE)
@IntDef({RANK_STATUS_BAR, RANK_STATUS_BAR_WINDOW_CONTROLLER, RANK_STACK_SCROLLER, RANK_SHELF})
@@ -94,6 +137,14 @@
return mIsDozing;
}
+ public float getDozeAmount() {
+ return mDozeAmount;
+ }
+
+ public float getInterpolatedDozeAmount() {
+ return mDozeInterpolator.getInterpolation(mDozeAmount);
+ }
+
/**
* Update the dozing state from {@link StatusBar}'s perspective
* @param isDozing well, are we dozing?
@@ -116,6 +167,51 @@
return true;
}
+ /**
+ * Changes the current doze amount.
+ *
+ * @param dozeAmount New doze/dark amount.
+ * @param animated If change should be animated or not. This will cancel current animations.
+ */
+ public void setDozeAmount(float dozeAmount, boolean animated) {
+ if (mDarkAnimator != null && mDarkAnimator.isRunning()) {
+ if (animated && mDozeAmountTarget == dozeAmount) {
+ return;
+ } else {
+ mDarkAnimator.cancel();
+ }
+ }
+
+ mDozeAmountTarget = dozeAmount;
+ if (animated) {
+ startDozeAnimation();
+ } else {
+ setDozeAmountInternal(dozeAmount);
+ }
+ }
+
+ private void startDozeAnimation() {
+ if (mDozeAmount == 0f || mDozeAmount == 1f) {
+ mDozeInterpolator = mIsDozing
+ ? Interpolators.FAST_OUT_SLOW_IN
+ : Interpolators.TOUCH_RESPONSE_REVERSE;
+ }
+ mDarkAnimator = ObjectAnimator.ofFloat(this, SET_DARK_AMOUNT_PROPERTY, mDozeAmountTarget);
+ mDarkAnimator.setInterpolator(Interpolators.LINEAR);
+ mDarkAnimator.setDuration(StackStateAnimator.ANIMATION_DURATION_WAKEUP);
+ mDarkAnimator.start();
+ }
+
+ private void setDozeAmountInternal(float dozeAmount) {
+ mDozeAmount = dozeAmount;
+ float interpolatedAmount = mDozeInterpolator.getInterpolation(dozeAmount);
+ synchronized (mListeners) {
+ for (RankedListener rl : new ArrayList<>(mListeners)) {
+ rl.listener.onDozeAmountChanged(mDozeAmount, interpolatedAmount);
+ }
+ }
+ }
+
public boolean goingToFullShade() {
return mState == StatusBarState.SHADE && mLeaveOpenOnKeyguardHide;
}
@@ -230,5 +326,12 @@
* @param isDozing {@code true} if dozing according to {@link StatusBar}
*/
public default void onDozingChanged(boolean isDozing) {}
+
+ /**
+ * Callback to be notified when the doze amount changes. Useful for animations.
+ * @param linear A number from 0 to 1, where 1 means that the device is dozing.
+ * @param eased Same as {@code linear} but transformed by an interpolator.
+ */
+ default void onDozeAmountChanged(float linear, float eased) {}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationDozeHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationDozeHelper.java
index fb362c5..ef042ba 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationDozeHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationDozeHelper.java
@@ -26,7 +26,7 @@
import com.android.systemui.Interpolators;
import com.android.systemui.R;
-import com.android.systemui.statusbar.phone.NotificationPanelView;
+import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
import java.util.function.Consumer;
@@ -63,13 +63,14 @@
}
}
+ // TODO: this should be using StatusBarStateController#getDozeAmount
public void startIntensityAnimation(ValueAnimator.AnimatorUpdateListener updateListener,
boolean dark, long delay, Animator.AnimatorListener listener) {
float startIntensity = dark ? 0f : 1f;
float endIntensity = dark ? 1f : 0f;
ValueAnimator animator = ValueAnimator.ofFloat(startIntensity, endIntensity);
animator.addUpdateListener(updateListener);
- animator.setDuration(NotificationPanelView.DOZE_ANIMATION_DURATION);
+ animator.setDuration(StackStateAnimator.ANIMATION_DURATION_WAKEUP);
animator.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN);
animator.setStartDelay(delay);
if (listener != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index 9daba83..3cc17b8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -4399,16 +4399,6 @@
}
@ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
- public long getDarkAnimationDuration(boolean dark) {
- long duration = StackStateAnimator.ANIMATION_DURATION_WAKEUP;
- // Longer animation when sleeping with more than 1 notification
- if (dark && getNotGoneChildCount() > 2) {
- duration *= 1.2f;
- }
- return duration;
- }
-
- @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
private int findDarkAnimationOriginIndex(@Nullable PointF screenLocation) {
if (screenLocation == null || screenLocation.y < mTopPadding) {
return AnimationEvent.DARK_ANIMATION_ORIGIN_INDEX_ABOVE;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java
index d4de8fc..3b13fe9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java
@@ -46,7 +46,8 @@
* and keyguard state. Also manages lifecycle to make sure the views it contains are being
* updated by the StatusBarIconController and DarkIconManager while it is attached.
*/
-public class CollapsedStatusBarFragment extends Fragment implements CommandQueue.Callbacks {
+public class CollapsedStatusBarFragment extends Fragment implements CommandQueue.Callbacks,
+ StatusBarStateController.StateListener {
public static final String TAG = "CollapsedStatusBarFragment";
private static final String EXTRA_PANEL_STATE = "panel_state";
@@ -120,12 +121,14 @@
public void onResume() {
super.onResume();
mCommandQueue.addCallbacks(this);
+ mStatusBarStateController.addListener(this);
}
@Override
public void onPause() {
super.onPause();
mCommandQueue.removeCallbacks(this);
+ mStatusBarStateController.removeListener(this);
}
@Override
@@ -351,4 +354,14 @@
mOperatorNameFrame = stub.inflate();
}
}
+
+ @Override
+ public void onStateChanged(int newState) {
+
+ }
+
+ @Override
+ public void onDozingChanged(boolean isDozing) {
+ disable(mDisabled1, mDisabled1, false /* animate */);
+ }
}
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 ecf6b6a..fa71df2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -22,7 +22,6 @@
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
-import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.app.ActivityManager;
import android.app.Fragment;
@@ -41,7 +40,6 @@
import android.os.PowerManager;
import android.os.SystemProperties;
import android.util.AttributeSet;
-import android.util.FloatProperty;
import android.util.Log;
import android.util.MathUtils;
import android.view.LayoutInflater;
@@ -51,7 +49,6 @@
import android.view.ViewGroup;
import android.view.WindowInsets;
import android.view.accessibility.AccessibilityManager;
-import android.view.animation.Interpolator;
import android.widget.FrameLayout;
import com.android.internal.logging.MetricsLogger;
@@ -104,7 +101,7 @@
View.OnClickListener, NotificationStackScrollLayout.OnOverscrollTopChangedListener,
KeyguardAffordanceHelper.Callback, NotificationStackScrollLayout.OnEmptySpaceClickListener,
OnHeadsUpChangedListener, QS.HeightListener, ZenModeController.Callback,
- ConfigurationController.ConfigurationListener {
+ ConfigurationController.ConfigurationListener, StateListener {
private static final boolean DEBUG = false;
@@ -139,25 +136,9 @@
private static final Rect mDummyDirtyRect = new Rect(0, 0, 1, 1);
- public static final long DOZE_ANIMATION_DURATION = 700;
-
private static final AnimationProperties CLOCK_ANIMATION_PROPERTIES = new AnimationProperties()
.setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
- private static final FloatProperty<NotificationPanelView> SET_DARK_AMOUNT_PROPERTY =
- new FloatProperty<NotificationPanelView>("mInterpolatedDarkAmount") {
- @Override
- public void setValue(NotificationPanelView object, float value) {
- object.setDarkAmount(value, object.mDarkInterpolator.getInterpolation(value));
- }
-
- @Override
- public Float get(NotificationPanelView object) {
- return object.mLinearDarkAmount;
- }
- };
-
- private Interpolator mDarkInterpolator;
private final PowerManager mPowerManager;
private final AccessibilityManager mAccessibilityManager;
@@ -295,11 +276,9 @@
*/
private boolean mSemiAwake;
- private float mDarkAmountTarget;
private boolean mPulsing;
private LockscreenGestureLogger mLockscreenGestureLogger = new LockscreenGestureLogger();
private boolean mNoVisibleNotifications = true;
- private ValueAnimator mDarkAnimator;
private boolean mUserSetupComplete;
private int mQsNotificationTopPadding;
private float mExpandOffset;
@@ -339,7 +318,6 @@
private final NotificationEntryManager mEntryManager =
Dependency.get(NotificationEntryManager.class);
- private final StateListener mListener = this::setBarState;
private final CommandQueue mCommandQueue;
private final NotificationLockscreenUserManager mLockscreenUserManager =
Dependency.get(NotificationLockscreenUserManager.class);
@@ -388,7 +366,7 @@
protected void onAttachedToWindow() {
super.onAttachedToWindow();
FragmentHostManager.get(this).addTagListener(QS.TAG, mFragmentListener);
- Dependency.get(StatusBarStateController.class).addListener(mListener);
+ Dependency.get(StatusBarStateController.class).addListener(this);
Dependency.get(ZenModeController.class).addCallback(this);
Dependency.get(ConfigurationController.class).addCallback(this);
}
@@ -397,7 +375,7 @@
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
FragmentHostManager.get(this).removeTagListener(QS.TAG, mFragmentListener);
- Dependency.get(StatusBarStateController.class).removeListener(mListener);
+ Dependency.get(StatusBarStateController.class).removeListener(this);
Dependency.get(ZenModeController.class).removeCallback(this);
Dependency.get(ConfigurationController.class).removeCallback(this);
}
@@ -475,7 +453,8 @@
mKeyguardBottomArea.initFrom(oldBottomArea);
addView(mKeyguardBottomArea, index);
initBottomArea();
- setDarkAmount(mLinearDarkAmount, mInterpolatedDarkAmount);
+ onDozeAmountChanged(mStatusBarStateController.getDozeAmount(),
+ mStatusBarStateController.getInterpolatedDozeAmount());
if (mKeyguardStatusBar != null) {
mKeyguardStatusBar.onThemeChanged();
@@ -1221,7 +1200,8 @@
}
}
- private void setBarState(int statusBarState) {
+ @Override
+ public void onStateChanged(int statusBarState) {
boolean goingToFullShade = mStatusBarStateController.goingToFullShade();
boolean keyguardFadingAway = mKeyguardMonitor.isKeyguardFadingAway();
int oldState = mBarState;
@@ -2806,24 +2786,10 @@
updateDozingVisibilities(animate);
}
- final float darkAmount = dozing ? 1 : 0;
- if (mDarkAnimator != null && mDarkAnimator.isRunning()) {
- if (animate && mDarkAmountTarget == darkAmount) {
- return;
- } else {
- mDarkAnimator.cancel();
- }
- if (mSemiAwake) {
- setDarkAmount(0, 0);
- }
- }
- mDarkAmountTarget = darkAmount;
- if (!mSemiAwake) {
- if (animate) {
- startDarkAnimation();
- } else {
- setDarkAmount(darkAmount, darkAmount);
- }
+ final float darkAmount = dozing && !mSemiAwake ? 1 : 0;
+ mStatusBarStateController.setDozeAmount(darkAmount, animate);
+ if (animate) {
+ mNotificationStackScroller.notifyDarkAnimationStart(mDozing);
}
}
@@ -2831,21 +2797,8 @@
return mSemiAwake;
}
- private void startDarkAnimation() {
- if (mInterpolatedDarkAmount == 0f || mInterpolatedDarkAmount == 1f) {
- mDarkInterpolator = mDozing
- ? Interpolators.FAST_OUT_SLOW_IN
- : Interpolators.TOUCH_RESPONSE_REVERSE;
- }
- mNotificationStackScroller.notifyDarkAnimationStart(mDozing);
- mDarkAnimator = ObjectAnimator.ofFloat(this, SET_DARK_AMOUNT_PROPERTY, mDozing ? 1 : 0);
- mDarkAnimator.setInterpolator(Interpolators.LINEAR);
- mDarkAnimator.setDuration(
- mNotificationStackScroller.getDarkAnimationDuration(mDozing));
- mDarkAnimator.start();
- }
-
- private void setDarkAmount(float linearAmount, float amount) {
+ @Override
+ public void onDozeAmountChanged(float linearAmount, float amount) {
mInterpolatedDarkAmount = amount;
mLinearDarkAmount = linearAmount;
mKeyguardStatusBar.setDarkAmount(mInterpolatedDarkAmount);
@@ -3047,7 +3000,8 @@
mSemiAwake = false;
mNotificationStackScroller.setDark(false /* dark */, true /* animate */,
null /* touchLocation */);
- startDarkAnimation();
+ mStatusBarStateController.setDozeAmount(0f, true /* animated */);
+ mNotificationStackScroller.notifyDarkAnimationStart(mDozing);
mStatusBar.updateScrimController();
return WAKE_UP_TO_SHADE;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStepController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStepController.java
index c03800e..37c4c58a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStepController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStepController.java
@@ -47,6 +47,7 @@
import com.android.systemui.recents.OverviewProxyService;
import com.android.systemui.SysUiServiceProvider;
import com.android.systemui.shared.recents.IOverviewProxy;
+import com.android.systemui.shared.recents.utilities.Utilities;
import com.android.systemui.shared.system.NavigationBarCompat;
import java.io.PrintWriter;
@@ -54,7 +55,7 @@
/**
* Class to detect gestures on the navigation bar and implement quick scrub.
* Note that the variables in this class horizontal and vertical represents horizontal always
- * aligned with along the navigation bar).
+ * aligned with along the navigation bar.
*/
public class QuickStepController implements GestureHelper {
@@ -65,7 +66,10 @@
private static final long BACK_BUTTON_FADE_IN_ALPHA = 150;
/** When the home-swipe-back gesture is disallowed, make it harder to pull */
- private static final float DISALLOW_GESTURE_DAMPING_FACTOR = 0.16f;
+ private static final float HORIZONTAL_GESTURE_DAMPING = 0.3f;
+ private static final float VERTICAL_GESTURE_DAMPING = 0.15f;
+ private static final float HORIZONTAL_DISABLED_GESTURE_DAMPING = 0.16f;
+ private static final float VERTICAL_DISABLED_GESTURE_DAMPING = 0.06f;
private static final int ACTION_SWIPE_UP_INDEX = 0;
private static final int ACTION_SWIPE_DOWN_INDEX = 1;
@@ -89,13 +93,14 @@
private boolean mIsInScreenPinning;
private boolean mGestureHorizontalDragsButton;
private boolean mGestureVerticalDragsButton;
- private boolean mGestureTrackPositive;
+ private float mMaxDragLimit;
+ private float mMinDragLimit;
+ private float mDragDampeningFactor;
private NavigationGestureAction mCurrentAction;
private NavigationGestureAction[] mGestureActions = new NavigationGestureAction[MAX_GESTURES];
private final OverviewProxyService mOverviewEventSender;
- private final int mHomeBackGestureDragLimit;
private final Context mContext;
private final StatusBar mStatusBar;
private final Matrix mTransformGlobalMatrix = new Matrix();
@@ -106,8 +111,6 @@
mContext = context;
mStatusBar = SysUiServiceProvider.getComponent(context, StatusBar.class);
mOverviewEventSender = Dependency.get(OverviewProxyService.class);
- mHomeBackGestureDragLimit =
- res.getDimensionPixelSize(R.dimen.nav_home_back_gesture_drag_limit);
}
public void setComponents(NavigationBarView navigationBarView) {
@@ -335,21 +338,9 @@
mDragBtnAnimator = null;
}
- int diff = position - touchDown;
- // If dragging the incorrect direction after starting gesture or unable to
- // execute tried action, then move the button but dampen its distance
- if (mCurrentAction == null || (mGestureTrackPositive ? diff < 0 : diff > 0)) {
- diff *= DISALLOW_GESTURE_DAMPING_FACTOR;
- } else if (Math.abs(diff) > mHomeBackGestureDragLimit) {
- // Once the user drags the button past a certain limit, the distance will
- // lessen as the button dampens that it was pulled too far
- float distanceAfterDragLimit = (Math.abs(diff) - mHomeBackGestureDragLimit)
- * DISALLOW_GESTURE_DAMPING_FACTOR;
- diff = (int) (distanceAfterDragLimit + mHomeBackGestureDragLimit);
- if (!mGestureTrackPositive) {
- diff *= -1;
- }
- }
+ // Clamp drag to the bounding box of the navigation bar
+ float diff = (position - touchDown) * mDragDampeningFactor;
+ diff = Utilities.clamp(diff, mMinDragLimit, mMaxDragLimit);
if (mGestureVerticalDragsButton ^ isNavBarVertical()) {
button.setTranslationY(diff);
} else {
@@ -480,22 +471,44 @@
event.transform(mTransformGlobalMatrix);
action.startGesture(event);
event.transform(mTransformLocalMatrix);
+
+ // Calculate the bounding limits of drag to avoid dragging off nav bar's window
+ if (action.requiresDragWithHitTarget() && mHitTarget != null) {
+ final int[] buttonCenter = new int[2];
+ View button = mHitTarget.getCurrentView();
+ button.getLocationInWindow(buttonCenter);
+ buttonCenter[0] += button.getWidth() / 2;
+ buttonCenter[1] += button.getHeight() / 2;
+ final int x = isNavBarVertical() ? buttonCenter[1] : buttonCenter[0];
+ final int y = isNavBarVertical() ? buttonCenter[0] : buttonCenter[1];
+ final int iconHalfSize = mContext.getResources()
+ .getDimensionPixelSize(R.dimen.navigation_icon_size) / 2;
+
+ if (alignedWithNavBar) {
+ mMinDragLimit = iconHalfSize - x;
+ mMaxDragLimit = -x - iconHalfSize + (isNavBarVertical()
+ ? mNavigationBarView.getHeight() : mNavigationBarView.getWidth());
+ } else {
+ mMinDragLimit = iconHalfSize - y;
+ mMaxDragLimit = -y - iconHalfSize + (isNavBarVertical()
+ ? mNavigationBarView.getWidth() : mNavigationBarView.getHeight());
+ }
+ }
}
// Handle direction of the hit target drag from the axis that started the gesture
+ // Also calculate the dampening factor, weaker dampening if there is an active action
if (action.requiresDragWithHitTarget()) {
if (alignedWithNavBar) {
mGestureHorizontalDragsButton = true;
mGestureVerticalDragsButton = false;
- if (positiveDirection) {
- mGestureTrackPositive = mDragHPositive;
- }
+ mDragDampeningFactor = action.isActive()
+ ? HORIZONTAL_GESTURE_DAMPING : HORIZONTAL_DISABLED_GESTURE_DAMPING;
} else {
mGestureVerticalDragsButton = true;
mGestureHorizontalDragsButton = false;
- if (positiveDirection) {
- mGestureTrackPositive = mDragVPositive;
- }
+ mDragDampeningFactor = action.isActive()
+ ? VERTICAL_GESTURE_DAMPING : VERTICAL_DISABLED_GESTURE_DAMPING;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java
index 6f4026d..f65f826 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java
@@ -55,4 +55,11 @@
default void onBatteryLevelChanged(int level, boolean pluggedIn, boolean charging) {}
default void onPowerSaveChanged(boolean isPowerSave) {}
}
+
+ /**
+ * If available, get the estimated battery time remaining as a string
+ */
+ default String getEstimatedTimeRemainingString() {
+ return null;
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java
index 7221efa..ddcfbf6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java
@@ -29,9 +29,14 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.settingslib.fuelgauge.BatterySaverUtils;
+import com.android.settingslib.utils.PowerUtil;
+import com.android.systemui.Dependency;
+import com.android.systemui.power.EnhancedEstimates;
+import com.android.systemui.power.Estimate;
import java.io.FileDescriptor;
import java.io.PrintWriter;
+import java.text.NumberFormat;
import java.util.ArrayList;
/**
@@ -44,7 +49,9 @@
public static final String ACTION_LEVEL_TEST = "com.android.systemui.BATTERY_LEVEL_TEST";
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+ private static final int UPDATE_GRANULARITY_MSEC = 1000 * 60;
+ private final EnhancedEstimates mEstimates = Dependency.get(EnhancedEstimates.class);
private final ArrayList<BatteryController.BatteryStateChangeCallback> mChangeCallbacks = new ArrayList<>();
private final PowerManager mPowerManager;
private final Handler mHandler;
@@ -58,6 +65,8 @@
protected boolean mAodPowerSave;
private boolean mTestmode = false;
private boolean mHasReceivedBattery = false;
+ private Estimate mEstimate;
+ private long mLastEstimateTimestamp = -1;
public BatteryControllerImpl(Context context) {
this(context, context.getSystemService(PowerManager.class));
@@ -71,6 +80,7 @@
registerReceiver();
updatePowerSave();
+ updateEstimate();
}
private void registerReceiver() {
@@ -180,6 +190,26 @@
return mAodPowerSave;
}
+ @Override
+ public String getEstimatedTimeRemainingString() {
+ if (mEstimate == null
+ || System.currentTimeMillis() > mLastEstimateTimestamp + UPDATE_GRANULARITY_MSEC) {
+ updateEstimate();
+ }
+ // Estimates may not exist yet even if we've checked
+ if (mEstimate == null) {
+ return null;
+ }
+ final String percentage = NumberFormat.getPercentInstance().format((double) mLevel / 100.0);
+ return PowerUtil.getBatteryRemainingShortStringFormatted(
+ mContext, mEstimate.estimateMillis);
+ }
+
+ private void updateEstimate() {
+ mEstimate = mEstimates.getEstimate();
+ mLastEstimateTimestamp = System.currentTimeMillis();
+ }
+
private void updatePowerSave() {
setPowerSave(mPowerManager.isPowerSaveMode());
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerTest.kt
new file mode 100644
index 0000000..48491d7
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerTest.kt
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.privacy
+
+import android.app.AppOpsManager
+import android.os.Handler
+import android.support.test.filters.SmallTest
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
+import android.testing.TestableLooper.RunWithLooper
+import com.android.systemui.Dependency
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.appops.AppOpItem
+import com.android.systemui.appops.AppOpsController
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.any
+import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.ArgumentMatchers.anyList
+import org.mockito.ArgumentMatchers.eq
+import org.mockito.Mock
+import org.mockito.Mockito.doReturn
+import org.mockito.Mockito.verify
+
+import org.mockito.MockitoAnnotations
+
+@RunWith(AndroidTestingRunner::class)
+@SmallTest
+@RunWithLooper
+class PrivacyItemControllerTest : SysuiTestCase() {
+
+ @Mock
+ private lateinit var appOpsController: AppOpsController
+ @Mock
+ private lateinit var callback: PrivacyItemController.Callback
+
+ private lateinit var testableLooper: TestableLooper
+ private lateinit var privacyItemController: PrivacyItemController
+
+ @Before
+ fun setup() {
+ MockitoAnnotations.initMocks(this)
+ testableLooper = TestableLooper.get(this)
+
+ appOpsController = mDependency.injectMockDependency(AppOpsController:: class.java)
+ mDependency.injectTestDependency(Dependency.BG_LOOPER, testableLooper.looper)
+ mDependency.injectTestDependency(Dependency.MAIN_HANDLER, Handler(testableLooper.looper))
+
+ doReturn(listOf(AppOpItem(AppOpsManager.OP_CAMERA, 0, "", 0)))
+ .`when`(appOpsController).getActiveAppOpsForUser(anyInt())
+
+ privacyItemController = PrivacyItemController(mContext, callback)
+ }
+ @Test
+ fun testSetListeningTrue() {
+ privacyItemController.setListening(true)
+ verify(appOpsController).addCallback(eq(PrivacyItemController.OPS),
+ any(AppOpsController.Callback::class.java))
+ testableLooper.processAllMessages()
+ verify(callback).privacyChanged(anyList())
+ }
+
+ @Test
+ fun testSetListeningFalse() {
+ privacyItemController.setListening(true)
+ privacyItemController.setListening(false)
+ verify(appOpsController).removeCallback(eq(PrivacyItemController.OPS),
+ any(AppOpsController.Callback:: class.java))
+ }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelperTest.java
index 098fa62..dcd5946 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelperTest.java
@@ -44,6 +44,7 @@
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import org.junit.Before;
+import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -55,6 +56,7 @@
* Tests for {@link NotificationSwipeHelper}.
*/
@SmallTest
+@Ignore
@RunWith(AndroidJUnit4.class)
@UiThreadTest
public class NotificationSwipeHelperTest extends SysuiTestCase {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentTest.java
index 231cdf5..93d8aad 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentTest.java
@@ -18,24 +18,25 @@
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.atLeast;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.when;
import android.app.StatusBarManager;
import android.support.test.filters.SmallTest;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper.RunWithLooper;
import android.view.View;
import android.view.ViewPropertyAnimator;
import com.android.systemui.R;
-import android.testing.AndroidTestingRunner;
-
import com.android.systemui.SysuiBaseFragmentTest;
import com.android.systemui.statusbar.CommandQueue;
+import com.android.systemui.statusbar.StatusBarStateController;
import com.android.systemui.tuner.TunerService;
-import android.testing.TestableLooper.RunWithLooper;
import org.junit.Before;
-import org.junit.runner.RunWith;
import org.junit.Test;
+import org.junit.runner.RunWith;
import org.mockito.Mockito;
@RunWith(AndroidTestingRunner.class)
@@ -45,6 +46,7 @@
private NotificationIconAreaController mMockNotificiationAreaController;
private View mNotificationAreaInner;
+ private StatusBarStateController mStatusBarStateController;
public CollapsedStatusBarFragmentTest() {
super(CollapsedStatusBarFragment.class);
@@ -55,6 +57,8 @@
mSysuiContext.putComponent(CommandQueue.class, mock(CommandQueue.class));
mSysuiContext.putComponent(StatusBar.class, mock(StatusBar.class));
mSysuiContext.putComponent(TunerService.class, mock(TunerService.class));
+ mStatusBarStateController = mDependency
+ .injectMockDependency(StatusBarStateController.class);
injectLeakCheckedDependencies(ALL_SUPPORTED_CLASSES);
mMockNotificiationAreaController = mock(NotificationIconAreaController.class);
mNotificationAreaInner = mock(View.class);
@@ -127,4 +131,23 @@
assertEquals(View.VISIBLE, mFragment.getView().findViewById(R.id.clock).getVisibility());
}
+
+ @Test
+ public void testOnDozingChanged() throws Exception {
+ mFragments.dispatchResume();
+ processAllMessages();
+
+ CollapsedStatusBarFragment fragment = (CollapsedStatusBarFragment) mFragment;
+ fragment.initNotificationIconArea(mMockNotificiationAreaController);
+ fragment.disable(StatusBarManager.DISABLE_NOTIFICATION_ICONS, 0, false);
+
+ Mockito.verify(mNotificationAreaInner).setVisibility(eq(View.INVISIBLE));
+
+ reset(mStatusBarStateController);
+ when(mStatusBarStateController.isDozing()).thenReturn(true);
+ fragment.onDozingChanged(true);
+
+ Mockito.verify(mStatusBarStateController).isDozing();
+ Mockito.verify(mNotificationAreaInner, atLeast(1)).setVisibility(eq(View.VISIBLE));
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/QuickStepControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/QuickStepControllerTest.java
index 0781602..4177cd1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/QuickStepControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/QuickStepControllerTest.java
@@ -38,6 +38,7 @@
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
+import android.content.Context;
import com.android.systemui.R;
import com.android.systemui.recents.OverviewProxyService;
import com.android.systemui.shared.recents.IOverviewProxy;
@@ -472,8 +473,11 @@
@Test
public void testHitTargetDragged() throws Exception {
+ final int navbarWidth = 1000;
+ final int navbarHeight = 1000;
ButtonDispatcher button = mock(ButtonDispatcher.class);
- View buttonView = spy(new View(mContext));
+ FakeLocationView buttonView = spy(new FakeLocationView(mContext, navbarWidth / 2,
+ navbarHeight / 2));
doReturn(buttonView).when(button).getCurrentView();
NavigationGestureAction action = mockAction(true);
@@ -484,6 +488,8 @@
doReturn(true).when(action).requiresDragWithHitTarget();
doReturn(HIT_TARGET_HOME).when(mNavigationBarView).getDownHitTarget();
doReturn(button).when(mNavigationBarView).getHomeButton();
+ doReturn(navbarWidth).when(mNavigationBarView).getWidth();
+ doReturn(navbarHeight).when(mNavigationBarView).getHeight();
// Portrait
assertGestureDragsHitTargetAllDirections(buttonView, false /* isRTL */, NAV_BAR_BOTTOM);
@@ -615,4 +621,21 @@
assertNull(mController.getCurrentAction());
verify(action, times(1)).endGesture();
}
+
+ static class FakeLocationView extends View {
+ private final int mX;
+ private final int mY;
+
+ public FakeLocationView(Context context, int x, int y) {
+ super(context);
+ mX = x;
+ mY = y;
+ }
+
+ @Override
+ public void getLocationInWindow(int[] outLocation) {
+ outLocation[0] = mX;
+ outLocation[1] = mY;
+ }
+ }
}
diff --git a/proto/src/metrics_constants/metrics_constants.proto b/proto/src/metrics_constants/metrics_constants.proto
index a947ea1..8ae5872 100644
--- a/proto/src/metrics_constants/metrics_constants.proto
+++ b/proto/src/metrics_constants/metrics_constants.proto
@@ -6584,6 +6584,18 @@
// OS: Q
TOP_LEVEL_PRIVACY = 1587;
+ // OPEN: Settings > Sound & notification > Do Not Disturb > See all exceptions >
+ // Allow apps to override
+ // CATEGORY: SETTINGS
+ // OS: Q
+ NOTIFICATION_ZEN_MODE_OVERRIDING_APPS = 1588;
+
+ // OPEN: Settings > Sound & notification > Do Not Disturb > See all exceptions >
+ // Allow apps to override > Choose app
+ // CATEGORY: SETTINGS
+ // OS: Q
+ NOTIFICATION_ZEN_MODE_OVERRIDING_APP = 1589;
+
// ---- End Q Constants, all Q constants go above this line ----
// Add new aosp constants above this line.
diff --git a/services/autofill/java/com/android/server/autofill/ui/FillUi.java b/services/autofill/java/com/android/server/autofill/ui/FillUi.java
index d1fe970c..7a65a53 100644
--- a/services/autofill/java/com/android/server/autofill/ui/FillUi.java
+++ b/services/autofill/java/com/android/server/autofill/ui/FillUi.java
@@ -218,8 +218,8 @@
ViewGroup container = decor.findViewById(R.id.autofill_dataset_picker);
final View content;
try {
- response.getPresentation().setApplyTheme(mThemeId);
- content = response.getPresentation().apply(mContext, decor, interceptionHandler);
+ content = response.getPresentation().applyWithTheme(
+ mContext, decor, interceptionHandler, mThemeId);
container.addView(content);
} catch (RuntimeException e) {
callback.onCanceled();
@@ -259,8 +259,7 @@
RemoteViews.OnClickHandler clickBlocker = null;
if (headerPresentation != null) {
clickBlocker = newClickBlocker();
- headerPresentation.setApplyTheme(mThemeId);
- mHeader = headerPresentation.apply(mContext, null, clickBlocker);
+ mHeader = headerPresentation.applyWithTheme(mContext, null, clickBlocker, mThemeId);
final LinearLayout headerContainer =
decor.findViewById(R.id.autofill_dataset_header);
if (sVerbose) Slog.v(TAG, "adding header");
@@ -277,8 +276,8 @@
if (clickBlocker == null) { // already set for header
clickBlocker = newClickBlocker();
}
- footerPresentation.setApplyTheme(mThemeId);
- mFooter = footerPresentation.apply(mContext, null, clickBlocker);
+ mFooter = footerPresentation.applyWithTheme(
+ mContext, null, clickBlocker, mThemeId);
// Footer not supported on some platform e.g. TV
if (sVerbose) Slog.v(TAG, "adding footer");
footerContainer.addView(mFooter);
@@ -304,8 +303,8 @@
final View view;
try {
if (sVerbose) Slog.v(TAG, "setting remote view for " + focusedViewId);
- presentation.setApplyTheme(mThemeId);
- view = presentation.apply(mContext, null, interceptionHandler);
+ view = presentation.applyWithTheme(
+ mContext, null, interceptionHandler, mThemeId);
} catch (RuntimeException e) {
Slog.e(TAG, "Error inflating remote views", e);
continue;
diff --git a/services/autofill/java/com/android/server/autofill/ui/SaveUi.java b/services/autofill/java/com/android/server/autofill/ui/SaveUi.java
index 1e30c8a..843aa74 100644
--- a/services/autofill/java/com/android/server/autofill/ui/SaveUi.java
+++ b/services/autofill/java/com/android/server/autofill/ui/SaveUi.java
@@ -339,8 +339,8 @@
try {
// Create the remote view peer.
- template.setApplyTheme(mThemeId);
- final View customSubtitleView = template.apply(context, null, handler);
+ final View customSubtitleView = template.applyWithTheme(
+ context, null, handler, mThemeId);
// Apply batch updates (if any).
final ArrayList<Pair<InternalValidator, BatchUpdates>> updates =
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index 54c7d17..5b3ab85 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -38,6 +38,7 @@
import android.Manifest;
import android.annotation.Nullable;
import android.app.ActivityManager;
+import android.app.ActivityManagerInternal;
import android.app.AppOpsManager;
import android.app.IActivityManager;
import android.app.KeyguardManager;
@@ -450,6 +451,7 @@
private PackageManagerInternal mPmInternal;
private UserManagerInternal mUmInternal;
+ private ActivityManagerInternal mAmInternal;
private final Callbacks mCallbacks;
private final LockPatternUtils mLockPatternUtils;
@@ -1439,6 +1441,7 @@
mPmInternal = LocalServices.getService(PackageManagerInternal.class);
mUmInternal = LocalServices.getService(UserManagerInternal.class);
+ mAmInternal = LocalServices.getService(ActivityManagerInternal.class);
HandlerThread hthread = new HandlerThread(TAG);
hthread.start();
@@ -2097,6 +2100,26 @@
Binder.restoreCallingIdentity(token);
}
}
+
+ if ((mask & StorageManager.DEBUG_ISOLATED_STORAGE) != 0) {
+ final boolean enabled = (flags & StorageManager.DEBUG_ISOLATED_STORAGE) != 0;
+
+ final long token = Binder.clearCallingIdentity();
+ try {
+ SystemProperties.set(StorageManager.PROP_ISOLATED_STORAGE,
+ Boolean.toString(enabled));
+
+ // Some of the storage related permissions get fiddled with during
+ // package scanning. So, delete the package cache to force PackageManagerService
+ // to do package scanning.
+ FileUtils.deleteContents(Environment.getPackageCacheDirectory());
+
+ // Perform hard reboot to kick policy into place
+ mContext.getSystemService(PowerManager.class).reboot(null);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
}
@Override
@@ -3040,25 +3063,25 @@
"(?i)^(/storage/[^/]+/(?:[0-9]+/)?)(.*)");
@Override
- public String translateAppToSystem(String path, String packageName, int userId) {
- return translateInternal(path, packageName, userId, true);
+ public String translateAppToSystem(String path, int pid, int uid) {
+ return translateInternal(path, pid, uid, true);
}
@Override
- public String translateSystemToApp(String path, String packageName, int userId) {
- return translateInternal(path, packageName, userId, false);
+ public String translateSystemToApp(String path, int pid, int uid) {
+ return translateInternal(path, pid, uid, false);
}
- private String translateInternal(String path, String packageName, int userId,
- boolean toSystem) {
+ private String translateInternal(String path, int pid, int uid, boolean toSystem) {
if (!ENABLE_ISOLATED_STORAGE) return path;
if (path.contains("/../")) {
throw new SecurityException("Shady looking path " + path);
}
- final String sharedUserId = mPmInternal.getSharedUserIdForPackage(packageName);
- final String sandboxId = getSandboxId(packageName, sharedUserId);
+ if (!mAmInternal.isAppStorageSandboxed(pid, uid)) {
+ return path;
+ }
final Matcher m = PATTERN_TRANSLATE.matcher(path);
if (m.matches()) {
@@ -3067,9 +3090,7 @@
// Does path belong to any packages belonging to this UID? If so,
// they get to go straight through to legacy paths.
- final String[] pkgs = (sharedUserId == null)
- ? new String[] {packageName}
- : mPmInternal.getPackagesForSharedUserId(sharedUserId, userId);
+ final String[] pkgs = mContext.getPackageManager().getPackagesForUid(uid);
for (String pkg : pkgs) {
if (devicePath.startsWith("Android/data/" + pkg + "/") ||
devicePath.startsWith("Android/media/" + pkg + "/") ||
@@ -3078,6 +3099,9 @@
}
}
+ final String sharedUserId = mPmInternal.getSharedUserIdForPackage(pkgs[0]);
+ final String sandboxId = getSandboxId(pkgs[0], sharedUserId);
+
if (toSystem) {
// Everything else goes into sandbox.
return device + "Android/sandbox/" + sandboxId + "/" + devicePath;
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 2d3912b..a33ac70 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -17,40 +17,69 @@
package com.android.server.am;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
-import static com.android.server.am.ActivityManagerDebugConfig.*;
-import java.io.FileDescriptor;
-import java.io.IOException;
-import java.io.PrintWriter;
-import java.io.StringWriter;
-import java.util.ArrayList;
-import java.util.Comparator;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Set;
-import java.util.function.Predicate;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_BACKGROUND_CHECK;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_FOREGROUND_SERVICE;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_MU;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PERMISSIONS_REVIEW;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_SERVICE;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_SERVICE_EXECUTING;
+import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_MU;
+import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_SERVICE;
+import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_SERVICE_EXECUTING;
+import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
+import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
+import android.app.ActivityManager;
import android.app.ActivityManagerInternal;
import android.app.ActivityThread;
+import android.app.AppGlobals;
import android.app.AppOpsManager;
+import android.app.IApplicationThread;
+import android.app.IServiceConnection;
+import android.app.Notification;
import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.app.Service;
import android.app.ServiceStartArgs;
+import android.content.ComponentName;
import android.content.ComponentName.WithComponentName;
+import android.content.Context;
import android.content.IIntentSender;
+import android.content.Intent;
import android.content.IntentSender;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
import android.content.pm.ParceledListSlice;
+import android.content.pm.ResolveInfo;
+import android.content.pm.ServiceInfo;
import android.net.Uri;
+import android.os.Binder;
import android.os.Build;
import android.os.Bundle;
import android.os.DeadObjectException;
import android.os.Handler;
+import android.os.IBinder;
import android.os.Looper;
+import android.os.Message;
+import android.os.Process;
import android.os.RemoteCallback;
+import android.os.RemoteException;
+import android.os.SystemClock;
import android.os.SystemProperties;
import android.os.TransactionTooLargeException;
+import android.os.UserHandle;
import android.provider.Settings;
import android.util.ArrayMap;
import android.util.ArraySet;
+import android.util.EventLog;
+import android.util.PrintWriterPrinter;
+import android.util.Slog;
+import android.util.SparseArray;
+import android.util.StatsLog;
+import android.util.TimeUtils;
+import android.util.proto.ProtoOutputStream;
+import android.webkit.WebViewZygote;
import com.android.internal.R;
import com.android.internal.app.procstats.ServiceState;
@@ -64,39 +93,20 @@
import com.android.server.LocalServices;
import com.android.server.SystemService;
import com.android.server.am.ActivityManagerService.ItemMatcher;
-
-import android.app.ActivityManager;
-import android.app.AppGlobals;
-import android.app.IApplicationThread;
-import android.app.IServiceConnection;
-import android.app.Notification;
-import android.app.PendingIntent;
-import android.app.Service;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
-import android.content.pm.ServiceInfo;
-import android.os.Binder;
-import android.os.IBinder;
-import android.os.Message;
-import android.os.Process;
-import android.os.RemoteException;
-import android.os.SystemClock;
-import android.os.UserHandle;
-import android.util.EventLog;
-import android.util.PrintWriterPrinter;
-import android.util.Slog;
-import android.util.StatsLog;
-import android.util.SparseArray;
-import android.util.TimeUtils;
-import android.util.proto.ProtoOutputStream;
-import android.webkit.WebViewZygote;
import com.android.server.uri.NeededUriGrants;
import com.android.server.wm.ActivityServiceConnectionsHolder;
+import java.io.FileDescriptor;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+import java.util.function.Predicate;
+
public final class ActiveServices {
private static final String TAG = TAG_WITH_CLASS_NAME ? "ActiveServices" : TAG_AM;
private static final String TAG_MU = TAG + POSTFIX_MU;
@@ -1285,6 +1295,7 @@
StatsLog.write(StatsLog.FOREGROUND_SERVICE_STATE_CHANGED,
r.appInfo.uid, r.shortName,
StatsLog.FOREGROUND_SERVICE_STATE_CHANGED__STATE__ENTER);
+ mAm.updateForegroundServiceUsageStats(r.name, r.userId, true);
}
r.postNotification();
if (r.app != null) {
@@ -1334,6 +1345,7 @@
StatsLog.write(StatsLog.FOREGROUND_SERVICE_STATE_CHANGED,
r.appInfo.uid, r.shortName,
StatsLog.FOREGROUND_SERVICE_STATE_CHANGED__STATE__EXIT);
+ mAm.updateForegroundServiceUsageStats(r.name, r.userId, false);
if (r.app != null) {
mAm.updateLruProcessLocked(r.app, false, null);
updateServiceForegroundLocked(r.app, true);
@@ -2797,6 +2809,7 @@
AppOpsManager.OP_START_FOREGROUND, r.appInfo.uid, r.packageName);
StatsLog.write(StatsLog.FOREGROUND_SERVICE_STATE_CHANGED, r.appInfo.uid, r.shortName,
StatsLog.FOREGROUND_SERVICE_STATE_CHANGED__STATE__EXIT);
+ mAm.updateForegroundServiceUsageStats(r.name, r.userId, false);
}
r.isForeground = false;
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index e90d42f..0bae1f3 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -68,9 +68,7 @@
import static android.os.Process.THREAD_GROUP_RESTRICTED;
import static android.os.Process.THREAD_GROUP_TOP_APP;
import static android.os.Process.THREAD_PRIORITY_FOREGROUND;
-import static android.os.Process.getPidsForCommands;
import static android.os.Process.getTotalMemory;
-import static android.os.Process.getUidForPid;
import static android.os.Process.isThreadInProcess;
import static android.os.Process.killProcess;
import static android.os.Process.killProcessQuiet;
@@ -127,11 +125,8 @@
import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_UID_OBSERVERS;
import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
-import static com.android.server.am.MemoryStatUtil.MEMORY_STAT_INTERESTING_NATIVE_PROCESSES;
import static com.android.server.am.MemoryStatUtil.hasMemcg;
-import static com.android.server.am.MemoryStatUtil.readCmdlineFromProcfs;
import static com.android.server.am.MemoryStatUtil.readMemoryStatFromFilesystem;
-import static com.android.server.am.MemoryStatUtil.readMemoryStatFromProcfs;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_CLEANUP;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_CONFIGURATION;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_SWITCH;
@@ -318,6 +313,7 @@
import com.android.internal.os.IResultReceiver;
import com.android.internal.os.ProcessCpuTracker;
import com.android.internal.os.TransferPipe;
+import com.android.internal.os.Zygote;
import com.android.internal.telephony.TelephonyIntents;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.DumpUtils;
@@ -2059,8 +2055,7 @@
private String mExemptionsStr;
private List<String> mExemptions = Collections.emptyList();
private int mLogSampleRate = -1;
- @HiddenApiEnforcementPolicy private int mPolicyPreP = HIDDEN_API_ENFORCEMENT_DEFAULT;
- @HiddenApiEnforcementPolicy private int mPolicyP = HIDDEN_API_ENFORCEMENT_DEFAULT;
+ @HiddenApiEnforcementPolicy private int mPolicy = HIDDEN_API_ENFORCEMENT_DEFAULT;
public HiddenApiSettings(Handler handler, Context context) {
super(handler);
@@ -2077,11 +2072,7 @@
false,
this);
mContext.getContentResolver().registerContentObserver(
- Settings.Global.getUriFor(Settings.Global.HIDDEN_API_POLICY_PRE_P_APPS),
- false,
- this);
- mContext.getContentResolver().registerContentObserver(
- Settings.Global.getUriFor(Settings.Global.HIDDEN_API_POLICY_P_APPS),
+ Settings.Global.getUriFor(Settings.Global.HIDDEN_API_POLICY),
false,
this);
update();
@@ -2116,8 +2107,7 @@
mLogSampleRate = logSampleRate;
zygoteProcess.setHiddenApiAccessLogSampleRate(mLogSampleRate);
}
- mPolicyPreP = getValidEnforcementPolicy(Settings.Global.HIDDEN_API_POLICY_PRE_P_APPS);
- mPolicyP = getValidEnforcementPolicy(Settings.Global.HIDDEN_API_POLICY_P_APPS);
+ mPolicy = getValidEnforcementPolicy(Settings.Global.HIDDEN_API_POLICY);
}
private @HiddenApiEnforcementPolicy int getValidEnforcementPolicy(String settingsKey) {
@@ -2134,12 +2124,8 @@
return mBlacklistDisabled;
}
- @HiddenApiEnforcementPolicy int getPolicyForPrePApps() {
- return mPolicyPreP;
- }
-
- @HiddenApiEnforcementPolicy int getPolicyForPApps() {
- return mPolicyP;
+ @HiddenApiEnforcementPolicy int getPolicy() {
+ return mPolicy;
}
public void onChange(boolean selfChange) {
@@ -2690,8 +2676,10 @@
}
void updateUsageStats(ComponentName activity, int uid, int userId, boolean resumed) {
- if (DEBUG_SWITCH) Slog.d(TAG_SWITCH,
- "updateUsageStats: comp=" + activity + "res=" + resumed);
+ if (DEBUG_SWITCH) {
+ Slog.d(TAG_SWITCH,
+ "updateUsageStats: comp=" + activity + "res=" + resumed);
+ }
final BatteryStatsImpl stats = mBatteryStatsService.getActiveStatistics();
StatsLog.write(StatsLog.ACTIVITY_FOREGROUND_STATE_CHANGED,
uid, activity.getPackageName(),
@@ -2718,6 +2706,20 @@
}
}
+ void updateForegroundServiceUsageStats(ComponentName service, int userId, boolean started) {
+ if (DEBUG_SWITCH) {
+ Slog.d(TAG_SWITCH, "updateForegroundServiceUsageStats: comp="
+ + service + "started=" + started);
+ }
+ synchronized (this) {
+ if (mUsageStatsService != null) {
+ mUsageStatsService.reportEvent(service, userId,
+ started ? UsageEvents.Event.FOREGROUND_SERVICE_START
+ : UsageEvents.Event.FOREGROUND_SERVICE_STOP);
+ }
+ }
+ }
+
CompatibilityInfo compatibilityInfoForPackage(ApplicationInfo ai) {
return mAtmInternal.compatibilityInfoForPackage(ai);
}
@@ -17161,6 +17163,179 @@
}
@GuardedBy("this")
+ final boolean updateLowMemStateLocked(int numCached, int numEmpty, int numTrimming) {
+ final int N = mProcessList.getLruSizeLocked();
+ final long now = SystemClock.uptimeMillis();
+ // Now determine the memory trimming level of background processes.
+ // Unfortunately we need to start at the back of the list to do this
+ // properly. We only do this if the number of background apps we
+ // are managing to keep around is less than half the maximum we desire;
+ // if we are keeping a good number around, we'll let them use whatever
+ // memory they want.
+ final int numCachedAndEmpty = numCached + numEmpty;
+ int memFactor;
+ if (numCached <= mConstants.CUR_TRIM_CACHED_PROCESSES
+ && numEmpty <= mConstants.CUR_TRIM_EMPTY_PROCESSES) {
+ if (numCachedAndEmpty <= ProcessList.TRIM_CRITICAL_THRESHOLD) {
+ memFactor = ProcessStats.ADJ_MEM_FACTOR_CRITICAL;
+ } else if (numCachedAndEmpty <= ProcessList.TRIM_LOW_THRESHOLD) {
+ memFactor = ProcessStats.ADJ_MEM_FACTOR_LOW;
+ } else {
+ memFactor = ProcessStats.ADJ_MEM_FACTOR_MODERATE;
+ }
+ } else {
+ memFactor = ProcessStats.ADJ_MEM_FACTOR_NORMAL;
+ }
+ // We always allow the memory level to go up (better). We only allow it to go
+ // down if we are in a state where that is allowed, *and* the total number of processes
+ // has gone down since last time.
+ if (DEBUG_OOM_ADJ) Slog.d(TAG_OOM_ADJ, "oom: memFactor=" + memFactor
+ + " last=" + mLastMemoryLevel + " allowLow=" + mAllowLowerMemLevel
+ + " numProcs=" + mProcessList.getLruSizeLocked() + " last=" + mLastNumProcesses);
+ if (memFactor > mLastMemoryLevel) {
+ if (!mAllowLowerMemLevel || mProcessList.getLruSizeLocked() >= mLastNumProcesses) {
+ memFactor = mLastMemoryLevel;
+ if (DEBUG_OOM_ADJ) Slog.d(TAG_OOM_ADJ, "Keeping last mem factor!");
+ }
+ }
+ if (memFactor != mLastMemoryLevel) {
+ EventLogTags.writeAmMemFactor(memFactor, mLastMemoryLevel);
+ StatsLog.write(StatsLog.MEMORY_FACTOR_STATE_CHANGED, memFactor);
+ }
+ mLastMemoryLevel = memFactor;
+ mLastNumProcesses = mProcessList.getLruSizeLocked();
+ boolean allChanged = mProcessStats.setMemFactorLocked(
+ memFactor, mAtmInternal != null ? !mAtmInternal.isSleeping() : true, now);
+ final int trackerMemFactor = mProcessStats.getMemFactorLocked();
+ if (memFactor != ProcessStats.ADJ_MEM_FACTOR_NORMAL) {
+ if (mLowRamStartTime == 0) {
+ mLowRamStartTime = now;
+ }
+ int step = 0;
+ int fgTrimLevel;
+ switch (memFactor) {
+ case ProcessStats.ADJ_MEM_FACTOR_CRITICAL:
+ fgTrimLevel = ComponentCallbacks2.TRIM_MEMORY_RUNNING_CRITICAL;
+ break;
+ case ProcessStats.ADJ_MEM_FACTOR_LOW:
+ fgTrimLevel = ComponentCallbacks2.TRIM_MEMORY_RUNNING_LOW;
+ break;
+ default:
+ fgTrimLevel = ComponentCallbacks2.TRIM_MEMORY_RUNNING_MODERATE;
+ break;
+ }
+ int factor = numTrimming/3;
+ int minFactor = 2;
+ if (mAtmInternal.getHomeProcess() != null) minFactor++;
+ if (mAtmInternal.getPreviousProcess() != null) minFactor++;
+ if (factor < minFactor) factor = minFactor;
+ int curLevel = ComponentCallbacks2.TRIM_MEMORY_COMPLETE;
+ for (int i=N-1; i>=0; i--) {
+ ProcessRecord app = mProcessList.mLruProcesses.get(i);
+ if (allChanged || app.procStateChanged) {
+ setProcessTrackerStateLocked(app, trackerMemFactor, now);
+ app.procStateChanged = false;
+ }
+ if (app.getCurProcState() >= ActivityManager.PROCESS_STATE_HOME
+ && !app.killedByAm) {
+ if (app.trimMemoryLevel < curLevel && app.thread != null) {
+ try {
+ if (DEBUG_SWITCH || DEBUG_OOM_ADJ) Slog.v(TAG_OOM_ADJ,
+ "Trimming memory of " + app.processName + " to " + curLevel);
+ app.thread.scheduleTrimMemory(curLevel);
+ } catch (RemoteException e) {
+ }
+ }
+ app.trimMemoryLevel = curLevel;
+ step++;
+ if (step >= factor) {
+ step = 0;
+ switch (curLevel) {
+ case ComponentCallbacks2.TRIM_MEMORY_COMPLETE:
+ curLevel = ComponentCallbacks2.TRIM_MEMORY_MODERATE;
+ break;
+ case ComponentCallbacks2.TRIM_MEMORY_MODERATE:
+ curLevel = ComponentCallbacks2.TRIM_MEMORY_BACKGROUND;
+ break;
+ }
+ }
+ } else if (app.getCurProcState() == ActivityManager.PROCESS_STATE_HEAVY_WEIGHT
+ && !app.killedByAm) {
+ if (app.trimMemoryLevel < ComponentCallbacks2.TRIM_MEMORY_BACKGROUND
+ && app.thread != null) {
+ try {
+ if (DEBUG_SWITCH || DEBUG_OOM_ADJ) Slog.v(TAG_OOM_ADJ,
+ "Trimming memory of heavy-weight " + app.processName
+ + " to " + ComponentCallbacks2.TRIM_MEMORY_BACKGROUND);
+ app.thread.scheduleTrimMemory(
+ ComponentCallbacks2.TRIM_MEMORY_BACKGROUND);
+ } catch (RemoteException e) {
+ }
+ }
+ app.trimMemoryLevel = ComponentCallbacks2.TRIM_MEMORY_BACKGROUND;
+ } else {
+ if ((app.getCurProcState() >= ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND
+ || app.systemNoUi) && app.hasPendingUiClean()) {
+ // If this application is now in the background and it
+ // had done UI, then give it the special trim level to
+ // have it free UI resources.
+ final int level = ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN;
+ if (app.trimMemoryLevel < level && app.thread != null) {
+ try {
+ if (DEBUG_SWITCH || DEBUG_OOM_ADJ) Slog.v(TAG_OOM_ADJ,
+ "Trimming memory of bg-ui " + app.processName
+ + " to " + level);
+ app.thread.scheduleTrimMemory(level);
+ } catch (RemoteException e) {
+ }
+ }
+ app.setPendingUiClean(false);
+ }
+ if (app.trimMemoryLevel < fgTrimLevel && app.thread != null) {
+ try {
+ if (DEBUG_SWITCH || DEBUG_OOM_ADJ) Slog.v(TAG_OOM_ADJ,
+ "Trimming memory of fg " + app.processName
+ + " to " + fgTrimLevel);
+ app.thread.scheduleTrimMemory(fgTrimLevel);
+ } catch (RemoteException e) {
+ }
+ }
+ app.trimMemoryLevel = fgTrimLevel;
+ }
+ }
+ } else {
+ if (mLowRamStartTime != 0) {
+ mLowRamTimeSinceLastIdle += now - mLowRamStartTime;
+ mLowRamStartTime = 0;
+ }
+ for (int i=N-1; i>=0; i--) {
+ ProcessRecord app = mProcessList.mLruProcesses.get(i);
+ if (allChanged || app.procStateChanged) {
+ setProcessTrackerStateLocked(app, trackerMemFactor, now);
+ app.procStateChanged = false;
+ }
+ if ((app.getCurProcState() >= ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND
+ || app.systemNoUi) && app.hasPendingUiClean()) {
+ if (app.trimMemoryLevel < ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN
+ && app.thread != null) {
+ try {
+ if (DEBUG_SWITCH || DEBUG_OOM_ADJ) Slog.v(TAG_OOM_ADJ,
+ "Trimming memory of ui hidden " + app.processName
+ + " to " + ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN);
+ app.thread.scheduleTrimMemory(
+ ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN);
+ } catch (RemoteException e) {
+ }
+ }
+ app.setPendingUiClean(false);
+ }
+ app.trimMemoryLevel = 0;
+ }
+ }
+ return allChanged;
+ }
+
+ @GuardedBy("this")
final void updateOomAdjLocked() {
mOomAdjProfiler.oomAdjStarted();
final ProcessRecord TOP_APP = getTopAppLocked();
@@ -17392,172 +17567,7 @@
mNumServiceProcs = mNewNumServiceProcs;
- // Now determine the memory trimming level of background processes.
- // Unfortunately we need to start at the back of the list to do this
- // properly. We only do this if the number of background apps we
- // are managing to keep around is less than half the maximum we desire;
- // if we are keeping a good number around, we'll let them use whatever
- // memory they want.
- final int numCachedAndEmpty = numCached + numEmpty;
- int memFactor;
- if (numCached <= mConstants.CUR_TRIM_CACHED_PROCESSES
- && numEmpty <= mConstants.CUR_TRIM_EMPTY_PROCESSES) {
- if (numCachedAndEmpty <= ProcessList.TRIM_CRITICAL_THRESHOLD) {
- memFactor = ProcessStats.ADJ_MEM_FACTOR_CRITICAL;
- } else if (numCachedAndEmpty <= ProcessList.TRIM_LOW_THRESHOLD) {
- memFactor = ProcessStats.ADJ_MEM_FACTOR_LOW;
- } else {
- memFactor = ProcessStats.ADJ_MEM_FACTOR_MODERATE;
- }
- } else {
- memFactor = ProcessStats.ADJ_MEM_FACTOR_NORMAL;
- }
- // We always allow the memory level to go up (better). We only allow it to go
- // down if we are in a state where that is allowed, *and* the total number of processes
- // has gone down since last time.
- if (DEBUG_OOM_ADJ) Slog.d(TAG_OOM_ADJ, "oom: memFactor=" + memFactor
- + " last=" + mLastMemoryLevel + " allowLow=" + mAllowLowerMemLevel
- + " numProcs=" + mProcessList.getLruSizeLocked() + " last=" + mLastNumProcesses);
- if (memFactor > mLastMemoryLevel) {
- if (!mAllowLowerMemLevel || mProcessList.getLruSizeLocked() >= mLastNumProcesses) {
- memFactor = mLastMemoryLevel;
- if (DEBUG_OOM_ADJ) Slog.d(TAG_OOM_ADJ, "Keeping last mem factor!");
- }
- }
- if (memFactor != mLastMemoryLevel) {
- EventLogTags.writeAmMemFactor(memFactor, mLastMemoryLevel);
- StatsLog.write(StatsLog.MEMORY_FACTOR_STATE_CHANGED, memFactor);
- }
- mLastMemoryLevel = memFactor;
- mLastNumProcesses = mProcessList.getLruSizeLocked();
- boolean allChanged = mProcessStats.setMemFactorLocked(
- memFactor, mAtmInternal != null ? !mAtmInternal.isSleeping() : true, now);
- final int trackerMemFactor = mProcessStats.getMemFactorLocked();
- if (memFactor != ProcessStats.ADJ_MEM_FACTOR_NORMAL) {
- if (mLowRamStartTime == 0) {
- mLowRamStartTime = now;
- }
- int step = 0;
- int fgTrimLevel;
- switch (memFactor) {
- case ProcessStats.ADJ_MEM_FACTOR_CRITICAL:
- fgTrimLevel = ComponentCallbacks2.TRIM_MEMORY_RUNNING_CRITICAL;
- break;
- case ProcessStats.ADJ_MEM_FACTOR_LOW:
- fgTrimLevel = ComponentCallbacks2.TRIM_MEMORY_RUNNING_LOW;
- break;
- default:
- fgTrimLevel = ComponentCallbacks2.TRIM_MEMORY_RUNNING_MODERATE;
- break;
- }
- int factor = numTrimming/3;
- int minFactor = 2;
- if (mAtmInternal.getHomeProcess() != null) minFactor++;
- if (mAtmInternal.getPreviousProcess() != null) minFactor++;
- if (factor < minFactor) factor = minFactor;
- int curLevel = ComponentCallbacks2.TRIM_MEMORY_COMPLETE;
- for (int i=N-1; i>=0; i--) {
- ProcessRecord app = mProcessList.mLruProcesses.get(i);
- if (allChanged || app.procStateChanged) {
- setProcessTrackerStateLocked(app, trackerMemFactor, now);
- app.procStateChanged = false;
- }
- if (app.getCurProcState() >= ActivityManager.PROCESS_STATE_HOME
- && !app.killedByAm) {
- if (app.trimMemoryLevel < curLevel && app.thread != null) {
- try {
- if (DEBUG_SWITCH || DEBUG_OOM_ADJ) Slog.v(TAG_OOM_ADJ,
- "Trimming memory of " + app.processName + " to " + curLevel);
- app.thread.scheduleTrimMemory(curLevel);
- } catch (RemoteException e) {
- }
- }
- app.trimMemoryLevel = curLevel;
- step++;
- if (step >= factor) {
- step = 0;
- switch (curLevel) {
- case ComponentCallbacks2.TRIM_MEMORY_COMPLETE:
- curLevel = ComponentCallbacks2.TRIM_MEMORY_MODERATE;
- break;
- case ComponentCallbacks2.TRIM_MEMORY_MODERATE:
- curLevel = ComponentCallbacks2.TRIM_MEMORY_BACKGROUND;
- break;
- }
- }
- } else if (app.getCurProcState() == ActivityManager.PROCESS_STATE_HEAVY_WEIGHT
- && !app.killedByAm) {
- if (app.trimMemoryLevel < ComponentCallbacks2.TRIM_MEMORY_BACKGROUND
- && app.thread != null) {
- try {
- if (DEBUG_SWITCH || DEBUG_OOM_ADJ) Slog.v(TAG_OOM_ADJ,
- "Trimming memory of heavy-weight " + app.processName
- + " to " + ComponentCallbacks2.TRIM_MEMORY_BACKGROUND);
- app.thread.scheduleTrimMemory(
- ComponentCallbacks2.TRIM_MEMORY_BACKGROUND);
- } catch (RemoteException e) {
- }
- }
- app.trimMemoryLevel = ComponentCallbacks2.TRIM_MEMORY_BACKGROUND;
- } else {
- if ((app.getCurProcState() >= ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND
- || app.systemNoUi) && app.hasPendingUiClean()) {
- // If this application is now in the background and it
- // had done UI, then give it the special trim level to
- // have it free UI resources.
- final int level = ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN;
- if (app.trimMemoryLevel < level && app.thread != null) {
- try {
- if (DEBUG_SWITCH || DEBUG_OOM_ADJ) Slog.v(TAG_OOM_ADJ,
- "Trimming memory of bg-ui " + app.processName
- + " to " + level);
- app.thread.scheduleTrimMemory(level);
- } catch (RemoteException e) {
- }
- }
- app.setPendingUiClean(false);
- }
- if (app.trimMemoryLevel < fgTrimLevel && app.thread != null) {
- try {
- if (DEBUG_SWITCH || DEBUG_OOM_ADJ) Slog.v(TAG_OOM_ADJ,
- "Trimming memory of fg " + app.processName
- + " to " + fgTrimLevel);
- app.thread.scheduleTrimMemory(fgTrimLevel);
- } catch (RemoteException e) {
- }
- }
- app.trimMemoryLevel = fgTrimLevel;
- }
- }
- } else {
- if (mLowRamStartTime != 0) {
- mLowRamTimeSinceLastIdle += now - mLowRamStartTime;
- mLowRamStartTime = 0;
- }
- for (int i=N-1; i>=0; i--) {
- ProcessRecord app = mProcessList.mLruProcesses.get(i);
- if (allChanged || app.procStateChanged) {
- setProcessTrackerStateLocked(app, trackerMemFactor, now);
- app.procStateChanged = false;
- }
- if ((app.getCurProcState() >= ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND
- || app.systemNoUi) && app.hasPendingUiClean()) {
- if (app.trimMemoryLevel < ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN
- && app.thread != null) {
- try {
- if (DEBUG_SWITCH || DEBUG_OOM_ADJ) Slog.v(TAG_OOM_ADJ,
- "Trimming memory of ui hidden " + app.processName
- + " to " + ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN);
- app.thread.scheduleTrimMemory(
- ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN);
- } catch (RemoteException e) {
- }
- }
- app.setPendingUiClean(false);
- }
- app.trimMemoryLevel = 0;
- }
- }
+ boolean allChanged = updateLowMemStateLocked(numCached, numEmpty, numTrimming);
if (mAlwaysFinishActivities) {
// Need to do this on its own message because the stack may not
@@ -18758,28 +18768,6 @@
}
@Override
- public List<ProcessMemoryState> getMemoryStateForNativeProcesses() {
- List<ProcessMemoryState> processMemoryStates = new ArrayList<>();
- int[] pids = getPidsForCommands(MEMORY_STAT_INTERESTING_NATIVE_PROCESSES);
- for (int i = 0; i < pids.length; i++) {
- int pid = pids[i];
- MemoryStat memoryStat = readMemoryStatFromProcfs(pid);
- if (memoryStat == null) {
- continue;
- }
- int uid = getUidForPid(pid);
- String processName = readCmdlineFromProcfs(pid);
- int oomScore = -1; // Unused, not included in the NativeProcessMemoryState atom.
- ProcessMemoryState processMemoryState = new ProcessMemoryState(uid, processName,
- oomScore, memoryStat.pgfault, memoryStat.pgmajfault,
- memoryStat.rssInBytes, memoryStat.cacheInBytes, memoryStat.swapInBytes,
- memoryStat.rssHighWatermarkInBytes, memoryStat.startTimeNanos);
- processMemoryStates.add(processMemoryState);
- }
- return processMemoryStates;
- }
-
- @Override
public int handleIncomingUser(int callingPid, int callingUid, int userId,
boolean allowAll, int allowMode, String name, String callerPackage) {
return mUserController.handleIncomingUser(callingPid, callingUid, userId, allowAll,
@@ -19176,6 +19164,17 @@
}
}
}
+
+ @Override
+ public boolean isAppStorageSandboxed(int pid, int uid) {
+ if (!SystemProperties.getBoolean(StorageManager.PROP_ISOLATED_STORAGE, false)) {
+ return false;
+ }
+ synchronized (mPidsSelfLocked) {
+ final ProcessRecord pr = mPidsSelfLocked.get(pid);
+ return pr == null || pr.mountMode != Zygote.MOUNT_EXTERNAL_FULL;
+ }
+ }
}
long inputDispatchingTimedOut(int pid, final boolean aboveSystem, String reason) {
diff --git a/services/core/java/com/android/server/am/MemoryStatUtil.java b/services/core/java/com/android/server/am/MemoryStatUtil.java
index c978c13..80b4f77 100644
--- a/services/core/java/com/android/server/am/MemoryStatUtil.java
+++ b/services/core/java/com/android/server/am/MemoryStatUtil.java
@@ -16,9 +16,9 @@
package com.android.server.am;
-import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_METRICS;
import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_METRICS;
import android.annotation.Nullable;
import android.os.FileUtils;
@@ -45,7 +45,7 @@
* <p>Processes are matched by their cmdline in procfs. Example: cat /proc/pid/cmdline returns
* /system/bin/statsd for the stats daemon.
*/
- static final String[] MEMORY_STAT_INTERESTING_NATIVE_PROCESSES = new String[]{
+ public static final String[] MEMORY_STAT_INTERESTING_NATIVE_PROCESSES = new String[]{
"/system/bin/statsd", // Stats daemon.
"/system/bin/surfaceflinger",
"/system/bin/apexd", // APEX daemon.
@@ -146,7 +146,7 @@
* Returns null if file is not found in procfs or if file has unrecognized contents.
*/
@Nullable
- static MemoryStat readMemoryStatFromProcfs(int pid) {
+ public static MemoryStat readMemoryStatFromProcfs(int pid) {
final String statPath = String.format(Locale.US, PROC_STAT_FILE_FMT, pid);
MemoryStat stat = parseMemoryStatFromProcfs(readFileContents(statPath));
if (stat == null) {
@@ -163,7 +163,7 @@
* Returns content of /proc/pid/cmdline (e.g. /system/bin/statsd) or an empty string
* if the file is not available.
*/
- static String readCmdlineFromProcfs(int pid) {
+ public static String readCmdlineFromProcfs(int pid) {
String path = String.format(Locale.US, PROC_CMDLINE_FILE_FMT, pid);
String cmdline = readFileContents(path);
return cmdline != null ? cmdline : "";
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index 93c8391..84b364b 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -97,18 +97,18 @@
import dalvik.system.VMRuntime;
+import libcore.io.IoUtils;
+
import java.io.File;
import java.io.IOException;
-import java.io.OutputStream;
import java.io.InputStream;
+import java.io.OutputStream;
import java.io.PrintWriter;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
-import libcore.io.IoUtils;
-
/**
* Activity manager code dealing with processes.
*
@@ -1311,6 +1311,7 @@
if (gids[0] == UserHandle.ERR_GID) gids[0] = gids[2];
if (gids[1] == UserHandle.ERR_GID) gids[1] = gids[2];
}
+ app.mountMode = mountExternal;
checkSlow(startTime, "startProcess: building args");
if (mService.mAtmInternal.isFactoryTestProcess(app.getWindowProcessController())) {
uid = 0;
@@ -1362,8 +1363,7 @@
if (!disableHiddenApiChecks && !mService.mHiddenApiBlacklist.isDisabled()) {
app.info.maybeUpdateHiddenApiEnforcementPolicy(
- mService.mHiddenApiBlacklist.getPolicyForPrePApps(),
- mService.mHiddenApiBlacklist.getPolicyForPApps());
+ mService.mHiddenApiBlacklist.getPolicy());
@ApplicationInfo.HiddenApiEnforcementPolicy int policy =
app.info.getHiddenApiEnforcementPolicy();
int policyBits = (policy << Zygote.API_ENFORCEMENT_POLICY_SHIFT);
diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java
index 8a3f139..745c1263 100644
--- a/services/core/java/com/android/server/am/ProcessRecord.java
+++ b/services/core/java/com/android/server/am/ProcessRecord.java
@@ -28,7 +28,6 @@
import android.app.ApplicationErrorReport;
import android.app.Dialog;
import android.app.IApplicationThread;
-import android.app.ProfilerInfo;
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.ApplicationInfo;
@@ -57,11 +56,11 @@
import com.android.internal.app.procstats.ProcessStats;
import com.android.internal.os.BatteryStatsImpl;
import com.android.internal.os.ProcessCpuTracker;
+import com.android.internal.os.Zygote;
import com.android.server.wm.WindowProcessController;
import com.android.server.wm.WindowProcessListener;
import java.io.File;
-import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
@@ -263,6 +262,7 @@
boolean pendingStart; // Process start is pending.
long startSeq; // Seq no. indicating the latest process start associated with
// this process record.
+ int mountMode; // Indicates how the external storage was mounted for this process.
// These reports are generated & stored when an app gets into an error condition.
// They will be "null" when all is OK.
@@ -443,6 +443,8 @@
pw.print(prefix); pw.print("pendingStart="); pw.println(pendingStart);
}
pw.print(prefix); pw.print("startSeq="); pw.println(startSeq);
+ pw.print(prefix); pw.print("mountMode="); pw.println(
+ DebugUtils.valueToString(Zygote.class, "MOUNT_EXTERNAL_", mountMode));
if (setProcState > ActivityManager.PROCESS_STATE_SERVICE) {
pw.print(prefix); pw.print("lastCpuTime="); pw.print(lastCpuTime);
if (lastCpuTime > 0) {
diff --git a/services/core/java/com/android/server/connectivity/LingerMonitor.java b/services/core/java/com/android/server/connectivity/LingerMonitor.java
index 635db19..0e727c5 100644
--- a/services/core/java/com/android/server/connectivity/LingerMonitor.java
+++ b/services/core/java/com/android/server/connectivity/LingerMonitor.java
@@ -90,6 +90,8 @@
mNotifier = notifier;
mDailyLimit = dailyLimit;
mRateLimitMillis = rateLimitMillis;
+ // Ensure that (now - mFirstNotificationMillis) >= rateLimitMillis at first
+ mFirstNotificationMillis = -rateLimitMillis;
}
private static HashMap<String, Integer> makeTransportToNameMap() {
diff --git a/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java
new file mode 100644
index 0000000..2b67fe7
--- /dev/null
+++ b/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java
@@ -0,0 +1,1630 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.inputmethod;
+
+import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
+
+import static java.lang.annotation.RetentionPolicy.SOURCE;
+
+import android.annotation.AnyThread;
+import android.annotation.BinderThread;
+import android.annotation.IntDef;
+import android.annotation.MainThread;
+import android.annotation.Nullable;
+import android.annotation.UserIdInt;
+import android.annotation.WorkerThread;
+import android.app.AppOpsManager;
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.ServiceConnection;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.pm.ServiceInfo;
+import android.inputmethodservice.MultiClientInputMethodServiceDelegate;
+import android.net.Uri;
+import android.os.Binder;
+import android.os.Build;
+import android.os.Debug;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.ResultReceiver;
+import android.os.ShellCallback;
+import android.os.SystemProperties;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.text.TextUtils;
+import android.text.style.SuggestionSpan;
+import android.util.ArrayMap;
+import android.util.ArraySet;
+import android.util.Slog;
+import android.util.SparseArray;
+import android.view.InputChannel;
+import android.view.WindowManager.LayoutParams.SoftInputModeFlags;
+import android.view.inputmethod.EditorInfo;
+import android.view.inputmethod.InputConnectionInspector.MissingMethodFlags;
+import android.view.inputmethod.InputMethodInfo;
+import android.view.inputmethod.InputMethodSubtype;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.inputmethod.IMultiClientInputMethod;
+import com.android.internal.inputmethod.IMultiClientInputMethodPrivilegedOperations;
+import com.android.internal.inputmethod.IMultiClientInputMethodSession;
+import com.android.internal.inputmethod.StartInputFlags;
+import com.android.internal.inputmethod.StartInputReason;
+import com.android.internal.inputmethod.UnbindReason;
+import com.android.internal.util.function.pooled.PooledLambda;
+import com.android.internal.view.IInputContext;
+import com.android.internal.view.IInputMethodClient;
+import com.android.internal.view.IInputMethodManager;
+import com.android.internal.view.IInputMethodSession;
+import com.android.internal.view.InputBindResult;
+import com.android.server.LocalServices;
+import com.android.server.SystemService;
+import com.android.server.wm.WindowManagerInternal;
+
+import java.io.FileDescriptor;
+import java.lang.annotation.Retention;
+import java.util.Collections;
+import java.util.List;
+import java.util.WeakHashMap;
+
+/**
+ * Actual implementation of multi-client InputMethodManagerService.
+ *
+ * <p>This system service is intentionally compatible with {@link InputMethodManagerService} so that
+ * we can switch the implementation at the boot time.</p>
+ */
+public final class MultiClientInputMethodManagerService {
+ static final String TAG = "MultiClientInputMethodManagerService";
+ static final boolean DEBUG = false;
+
+ /**
+ * System property key for the production use. The value must be either empty or a valid
+ * (flattened) component name of the multi-client IME.
+ */
+ private static final String PROP_PROD_MULTI_CLIENT_IME = "ro.sys.multi_client_ime";
+
+ /**
+ * System property key for debugging purpose. The value must be either empty or a valid
+ * (flattened) component name of the multi-client IME.
+ *
+ * <p>This value will be ignored when {@link Build#IS_DEBUGGABLE} returns {@code false}</p>
+ */
+ private static final String PROP_DEBUG_MULTI_CLIENT_IME = "persist.debug.multi_client_ime";
+
+ private static final long RECONNECT_DELAY_MSEC = 1000;
+
+ /**
+ * Unlike {@link InputMethodManagerService}, {@link MultiClientInputMethodManagerService}
+ * always binds to the IME with {@link Context#BIND_FOREGROUND_SERVICE} for now for simplicity.
+ */
+ private static final int IME_CONNECTION_UNIFIED_BIND_FLAGS =
+ Context.BIND_AUTO_CREATE
+ | Context.BIND_NOT_VISIBLE
+ | Context.BIND_NOT_FOREGROUND
+ | Context.BIND_FOREGROUND_SERVICE;
+
+ /**
+ * Inner class to read system property on demand, not when
+ * {@link MultiClientInputMethodManagerService} class is accessed.
+ */
+ private static final class ImeComponentName {
+ private static ComponentName evaluate() {
+ if (Build.IS_DEBUGGABLE) {
+ // If debuggable, allow developers to override the multi-client IME component name
+ // with a different (writable) key.
+ final ComponentName debugIme = ComponentName.unflattenFromString(
+ SystemProperties.get(PROP_DEBUG_MULTI_CLIENT_IME, ""));
+ if (debugIme != null) {
+ return debugIme;
+ }
+ }
+ return ComponentName.unflattenFromString(
+ SystemProperties.get(PROP_PROD_MULTI_CLIENT_IME, ""));
+ }
+
+ /**
+ * {@link ComponentName} of the multi-client IME. {@code null} when the system is not
+ * configured to use multi-client IME.
+ */
+ @Nullable
+ static final ComponentName sValue = evaluate();
+ }
+
+ public static boolean isConfiguredToUse() {
+ return ImeComponentName.sValue != null;
+ }
+
+ private static void reportNotSupported() {
+ if (DEBUG) {
+ Slog.d(TAG, "non-supported operation. callers=" + Debug.getCallers(3));
+ }
+ }
+
+ /**
+ * {@link MultiClientInputMethodManagerService} is not intended to be instantiated.
+ */
+ private MultiClientInputMethodManagerService() {
+ }
+
+ /**
+ * The implementation of {@link SystemService} for multi-client IME.
+ */
+ public static final class Lifecycle extends SystemService {
+ private final ApiCallbacks mApiCallbacks;
+ private final OnWorkerThreadCallback mOnWorkerThreadCallback;
+
+ @MainThread
+ public Lifecycle(Context context) {
+ super(context);
+
+ final UserToInputMethodInfoMap userIdToInputMethodInfoMapper =
+ new UserToInputMethodInfoMap();
+ final UserDataMap userDataMap = new UserDataMap();
+ final HandlerThread workerThread = new HandlerThread(TAG);
+ workerThread.start();
+ mApiCallbacks = new ApiCallbacks(context, userDataMap, userIdToInputMethodInfoMapper);
+ mOnWorkerThreadCallback = new OnWorkerThreadCallback(
+ context, userDataMap, userIdToInputMethodInfoMapper,
+ new Handler(workerThread.getLooper(), msg -> false, true));
+
+ LocalServices.addService(InputMethodManagerInternal.class,
+ new InputMethodManagerInternal() {
+ @Override
+ public void setInteractive(boolean interactive) {
+ reportNotSupported();
+ }
+
+ @Override
+ public void hideCurrentInputMethod() {
+ reportNotSupported();
+ }
+
+ @Override
+ public void startVrInputMethodNoCheck(ComponentName componentName) {
+ reportNotSupported();
+ }
+ });
+ }
+
+ @MainThread
+ @Override
+ public void onBootPhase(int phase) {
+ mOnWorkerThreadCallback.getHandler().sendMessage(PooledLambda.obtainMessage(
+ OnWorkerThreadCallback::onBootPhase, mOnWorkerThreadCallback, phase));
+ }
+
+ @MainThread
+ @Override
+ public void onStart() {
+ publishBinderService(Context.INPUT_METHOD_SERVICE, mApiCallbacks);
+ }
+
+ @MainThread
+ @Override
+ public void onStartUser(@UserIdInt int userId) {
+ mOnWorkerThreadCallback.getHandler().sendMessage(PooledLambda.obtainMessage(
+ OnWorkerThreadCallback::onStartUser, mOnWorkerThreadCallback, userId));
+ }
+
+ @MainThread
+ @Override
+ public void onUnlockUser(@UserIdInt int userId) {
+ mOnWorkerThreadCallback.getHandler().sendMessage(PooledLambda.obtainMessage(
+ OnWorkerThreadCallback::onUnlockUser, mOnWorkerThreadCallback, userId));
+ }
+
+ @MainThread
+ @Override
+ public void onStopUser(@UserIdInt int userId) {
+ mOnWorkerThreadCallback.getHandler().sendMessage(PooledLambda.obtainMessage(
+ OnWorkerThreadCallback::onStopUser, mOnWorkerThreadCallback, userId));
+ }
+ }
+
+ private static final class OnWorkerThreadCallback {
+ private final Context mContext;
+ private final UserDataMap mUserDataMap;
+ private final UserToInputMethodInfoMap mInputMethodInfoMap;
+ private final Handler mHandler;
+
+ OnWorkerThreadCallback(Context context, UserDataMap userDataMap,
+ UserToInputMethodInfoMap inputMethodInfoMap, Handler handler) {
+ mContext = context;
+ mUserDataMap = userDataMap;
+ mInputMethodInfoMap = inputMethodInfoMap;
+ mHandler = handler;
+ }
+
+ @AnyThread
+ Handler getHandler() {
+ return mHandler;
+ }
+
+ @WorkerThread
+ private void tryBindInputMethodService(@UserIdInt int userId) {
+ final PerUserData data = mUserDataMap.get(userId);
+ if (data == null) {
+ Slog.i(TAG, "tryBindInputMethodService is called for an unknown user=" + userId);
+ return;
+ }
+
+ final InputMethodInfo imi = queryInputMethod(mContext, userId, ImeComponentName.sValue);
+ if (imi == null) {
+ Slog.w(TAG, "Multi-client InputMethod is not found. component="
+ + ImeComponentName.sValue);
+ synchronized (data.mLock) {
+ switch (data.mState) {
+ case PerUserState.USER_LOCKED:
+ case PerUserState.SERVICE_NOT_QUERIED:
+ case PerUserState.SERVICE_RECOGNIZED:
+ case PerUserState.UNBIND_CALLED:
+ // Safe to clean up.
+ mInputMethodInfoMap.remove(userId);
+ break;
+ }
+ }
+ return;
+ }
+
+ synchronized (data.mLock) {
+ switch (data.mState) {
+ case PerUserState.USER_LOCKED:
+ // If the user is still locked, we currently do not try to start IME.
+ return;
+ case PerUserState.SERVICE_NOT_QUERIED:
+ case PerUserState.SERVICE_RECOGNIZED:
+ case PerUserState.UNBIND_CALLED:
+ break;
+ case PerUserState.WAITING_SERVICE_CONNECTED:
+ case PerUserState.SERVICE_CONNECTED:
+ // OK, nothing to do.
+ return;
+ default:
+ Slog.wtf(TAG, "Unknown state=" + data.mState);
+ return;
+ }
+ data.mState = PerUserState.SERVICE_RECOGNIZED;
+ data.mCurrentInputMethodInfo = imi;
+ mInputMethodInfoMap.put(userId, imi);
+ final boolean bindResult = data.bindServiceLocked(mContext, userId);
+ if (!bindResult) {
+ Slog.e(TAG, "Failed to bind Multi-client InputMethod.");
+ return;
+ }
+ data.mState = PerUserState.WAITING_SERVICE_CONNECTED;
+ }
+ }
+
+ @WorkerThread
+ void onStartUser(@UserIdInt int userId) {
+ if (DEBUG) {
+ Slog.v(TAG, "onStartUser userId=" + userId);
+ }
+ final PerUserData data = new PerUserData(userId, null, PerUserState.USER_LOCKED, this);
+ mUserDataMap.put(userId, data);
+ }
+
+ @WorkerThread
+ void onUnlockUser(@UserIdInt int userId) {
+ if (DEBUG) {
+ Slog.v(TAG, "onUnlockUser() userId=" + userId);
+ }
+ final PerUserData data = mUserDataMap.get(userId);
+ if (data == null) {
+ Slog.i(TAG, "onUnlockUser is called for an unknown user=" + userId);
+ return;
+ }
+ synchronized (data.mLock) {
+ switch (data.mState) {
+ case PerUserState.USER_LOCKED:
+ data.mState = PerUserState.SERVICE_NOT_QUERIED;
+ tryBindInputMethodService(userId);
+ break;
+ default:
+ Slog.wtf(TAG, "Unknown state=" + data.mState);
+ break;
+ }
+ }
+ }
+
+ @WorkerThread
+ void onStopUser(@UserIdInt int userId) {
+ if (DEBUG) {
+ Slog.v(TAG, "onStopUser() userId=" + userId);
+ }
+ mInputMethodInfoMap.remove(userId);
+ final PerUserData data = mUserDataMap.removeReturnOld(userId);
+ if (data == null) {
+ Slog.i(TAG, "onStopUser is called for an unknown user=" + userId);
+ return;
+ }
+ synchronized (data.mLock) {
+ switch (data.mState) {
+ case PerUserState.USER_LOCKED:
+ case PerUserState.SERVICE_RECOGNIZED:
+ case PerUserState.UNBIND_CALLED:
+ // OK, nothing to do.
+ return;
+ case PerUserState.SERVICE_CONNECTED:
+ case PerUserState.WAITING_SERVICE_CONNECTED:
+ break;
+ default:
+ Slog.wtf(TAG, "Unknown state=" + data.mState);
+ break;
+ }
+ data.unbindServiceLocked(mContext);
+ data.mState = PerUserState.UNBIND_CALLED;
+ data.mCurrentInputMethod = null;
+
+ // When a Service is explicitly unbound with Context.unbindService(),
+ // onServiceDisconnected() will not be triggered. Hence here we explicitly call
+ // onInputMethodDisconnectedLocked() as if the Service is already gone.
+ data.onInputMethodDisconnectedLocked();
+ }
+ }
+
+ @WorkerThread
+ void onServiceConnected(PerUserData data, IMultiClientInputMethod service) {
+ if (DEBUG) {
+ Slog.v(TAG, "onServiceConnected() data.mUserId=" + data.mUserId);
+ }
+ synchronized (data.mLock) {
+ switch (data.mState) {
+ case PerUserState.UNBIND_CALLED:
+ // We should ignore this callback.
+ return;
+ case PerUserState.WAITING_SERVICE_CONNECTED:
+ // OK.
+ data.mState = PerUserState.SERVICE_CONNECTED;
+ data.mCurrentInputMethod = service;
+ try {
+ data.mCurrentInputMethod.initialize(new ImeCallbacks(data));
+ } catch (RemoteException e) {
+ }
+ data.onInputMethodConnectedLocked();
+ break;
+ default:
+ Slog.wtf(TAG, "Unknown state=" + data.mState);
+ return;
+ }
+ }
+ }
+
+ @WorkerThread
+ void onServiceDisconnected(PerUserData data) {
+ if (DEBUG) {
+ Slog.v(TAG, "onServiceDisconnected() data.mUserId=" + data.mUserId);
+ }
+ final WindowManagerInternal windowManagerInternal =
+ LocalServices.getService(WindowManagerInternal.class);
+ synchronized (data.mLock) {
+ // We assume the number of tokens would not be that large (up to 10 or so) hence
+ // linear search should be acceptable.
+ final int numTokens = data.mDisplayIdToImeWindowTokenMap.size();
+ for (int i = 0; i < numTokens; ++i) {
+ final TokenInfo info = data.mDisplayIdToImeWindowTokenMap.valueAt(i);
+ windowManagerInternal.removeWindowToken(info.mToken, false, info.mDisplayId);
+ }
+ data.mDisplayIdToImeWindowTokenMap.clear();
+ switch (data.mState) {
+ case PerUserState.UNBIND_CALLED:
+ // We should ignore this callback.
+ return;
+ case PerUserState.WAITING_SERVICE_CONNECTED:
+ case PerUserState.SERVICE_CONNECTED:
+ // onServiceDisconnected() means the biding is still alive.
+ data.mState = PerUserState.WAITING_SERVICE_CONNECTED;
+ data.mCurrentInputMethod = null;
+ data.onInputMethodDisconnectedLocked();
+ break;
+ default:
+ Slog.wtf(TAG, "Unknown state=" + data.mState);
+ return;
+ }
+ }
+ }
+
+ @WorkerThread
+ void onBindingDied(PerUserData data) {
+ if (DEBUG) {
+ Slog.v(TAG, "onBindingDied() data.mUserId=" + data.mUserId);
+ }
+ final WindowManagerInternal windowManagerInternal =
+ LocalServices.getService(WindowManagerInternal.class);
+ synchronized (data.mLock) {
+ // We assume the number of tokens would not be that large (up to 10 or so) hence
+ // linear search should be acceptable.
+ final int numTokens = data.mDisplayIdToImeWindowTokenMap.size();
+ for (int i = 0; i < numTokens; ++i) {
+ final TokenInfo info = data.mDisplayIdToImeWindowTokenMap.valueAt(i);
+ windowManagerInternal.removeWindowToken(info.mToken, false, info.mDisplayId);
+ }
+ data.mDisplayIdToImeWindowTokenMap.clear();
+ switch (data.mState) {
+ case PerUserState.UNBIND_CALLED:
+ // We should ignore this callback.
+ return;
+ case PerUserState.WAITING_SERVICE_CONNECTED:
+ case PerUserState.SERVICE_CONNECTED: {
+ // onBindingDied() means the biding is dead.
+ data.mState = PerUserState.UNBIND_CALLED;
+ data.mCurrentInputMethod = null;
+ data.onInputMethodDisconnectedLocked();
+ // Schedule a retry
+ mHandler.sendMessageDelayed(PooledLambda.obtainMessage(
+ OnWorkerThreadCallback::tryBindInputMethodService,
+ this, data.mUserId), RECONNECT_DELAY_MSEC);
+ break;
+ }
+ default:
+ Slog.wtf(TAG, "Unknown state=" + data.mState);
+ return;
+ }
+ }
+ }
+
+ @WorkerThread
+ void onBootPhase(int phase) {
+ if (DEBUG) {
+ Slog.v(TAG, "onBootPhase() phase=" + phase);
+ }
+ switch (phase) {
+ case SystemService.PHASE_ACTIVITY_MANAGER_READY: {
+ final IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
+ filter.addDataScheme("package");
+ mContext.registerReceiver(new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ onPackageAdded(intent);
+ }
+ }, filter, null, mHandler);
+ }
+ break;
+ }
+ }
+
+ @WorkerThread
+ void onPackageAdded(Intent intent) {
+ if (DEBUG) {
+ Slog.v(TAG, "onPackageAdded() intent=" + intent);
+ }
+ final Uri uri = intent.getData();
+ if (uri == null) {
+ return;
+ }
+ if (!intent.hasExtra(Intent.EXTRA_UID)) {
+ return;
+ }
+ final String packageName = uri.getSchemeSpecificPart();
+ if (ImeComponentName.sValue == null
+ || packageName == null
+ || !TextUtils.equals(ImeComponentName.sValue.getPackageName(), packageName)) {
+ return;
+ }
+ final int userId = UserHandle.getUserId(intent.getIntExtra(Intent.EXTRA_UID, 0));
+ tryBindInputMethodService(userId);
+ }
+ }
+
+ private static final class WindowInfo {
+ final IBinder mWindowToken;
+ final int mWindowHandle;
+
+ WindowInfo(IBinder windowToken, int windowCookie) {
+ mWindowToken = windowToken;
+ mWindowHandle = windowCookie;
+ }
+ }
+
+ /**
+ * Describes the state of each IME client.
+ */
+ @Retention(SOURCE)
+ @IntDef({InputMethodClientState.REGISTERED,
+ InputMethodClientState.WAITING_FOR_IME_SESSION,
+ InputMethodClientState.READY_TO_SEND_FIRST_BIND_RESULT,
+ InputMethodClientState.ALREADY_SENT_BIND_RESULT,
+ InputMethodClientState.UNREGISTERED})
+ private @interface InputMethodClientState {
+ /**
+ * {@link IInputMethodManager#addClient(IInputMethodClient, IInputContext, int)} is called
+ * and this client is now recognized by the system. When the system lost the connection to
+ * the current IME, all the clients need to be re-initialized from this state.
+ */
+ int REGISTERED = 1;
+ /**
+ * This client is notified to the current IME with {@link
+ * IMultiClientInputMethod#addClient(int, int, int, int)} but the IME is not yet responded
+ * with {@link IMultiClientInputMethodPrivilegedOperations#acceptClient(int,
+ * IInputMethodSession, IMultiClientInputMethodSession, InputChannel)}.
+ */
+ int WAITING_FOR_IME_SESSION = 2;
+ /**
+ * This client is already accepted by the IME but a valid {@link InputBindResult} has not
+ * been returned to the client yet.
+ */
+ int READY_TO_SEND_FIRST_BIND_RESULT = 3;
+ /**
+ * This client has already received a valid {@link InputBindResult} at least once. This
+ * means that the client can directly call {@link IInputMethodSession} IPCs and key events
+ * via {@link InputChannel}. When the current IME is unbound, these client end points also
+ * need to be cleared.
+ */
+ int ALREADY_SENT_BIND_RESULT = 4;
+ /**
+ * The client process is dying.
+ */
+ int UNREGISTERED = 5;
+ }
+
+ private static final class InputMethodClientIdSource {
+ @GuardedBy("InputMethodClientIdSource.class")
+ private static int sNextValue = 0;
+
+ private InputMethodClientIdSource() {
+ }
+
+ static synchronized int getNext() {
+ final int result = sNextValue;
+ sNextValue++;
+ if (sNextValue < 0) {
+ sNextValue = 0;
+ }
+ return result;
+ }
+ }
+
+ private static final class WindowHandleSource {
+ @GuardedBy("WindowHandleSource.class")
+ private static int sNextValue = 0;
+
+ private WindowHandleSource() {
+ }
+
+ static synchronized int getNext() {
+ final int result = sNextValue;
+ sNextValue++;
+ if (sNextValue < 0) {
+ sNextValue = 0;
+ }
+ return result;
+ }
+ }
+
+ private static final class InputMethodClientInfo {
+ final IInputMethodClient mClient;
+ final int mUid;
+ final int mPid;
+ final int mSelfReportedDisplayId;
+ final int mClientId;
+
+ @GuardedBy("PerUserData.mLock")
+ @InputMethodClientState
+ int mState;
+ @GuardedBy("PerUserData.mLock")
+ int mBindingSequence;
+ @GuardedBy("PerUserData.mLock")
+ InputChannel mWriteChannel;
+ @GuardedBy("PerUserData.mLock")
+ IInputMethodSession mInputMethodSession;
+ @GuardedBy("PerUserData.mLock")
+ IMultiClientInputMethodSession mMSInputMethodSession;
+ @GuardedBy("PerUserData.mLock")
+ final WeakHashMap<IBinder, WindowInfo> mWindowMap = new WeakHashMap<>();
+
+ InputMethodClientInfo(IInputMethodClient client, int uid, int pid,
+ int selfReportedDisplayId) {
+ mClient = client;
+ mUid = uid;
+ mPid = pid;
+ mSelfReportedDisplayId = selfReportedDisplayId;
+ mClientId = InputMethodClientIdSource.getNext();
+ }
+ }
+
+ private static final class UserDataMap {
+ @GuardedBy("mMap")
+ private final SparseArray<PerUserData> mMap = new SparseArray<>();
+
+ @AnyThread
+ @Nullable
+ PerUserData get(@UserIdInt int userId) {
+ synchronized (mMap) {
+ return mMap.get(userId);
+ }
+ }
+
+ @AnyThread
+ void put(@UserIdInt int userId, PerUserData data) {
+ synchronized (mMap) {
+ mMap.put(userId, data);
+ }
+ }
+
+ @AnyThread
+ @Nullable
+ PerUserData removeReturnOld(@UserIdInt int userId) {
+ synchronized (mMap) {
+ return mMap.removeReturnOld(userId);
+ }
+ }
+ }
+
+ private static final class TokenInfo {
+ final Binder mToken;
+ final int mDisplayId;
+ TokenInfo(Binder token, int displayId) {
+ mToken = token;
+ mDisplayId = displayId;
+ }
+ }
+
+ @Retention(SOURCE)
+ @IntDef({
+ PerUserState.USER_LOCKED,
+ PerUserState.SERVICE_NOT_QUERIED,
+ PerUserState.SERVICE_RECOGNIZED,
+ PerUserState.WAITING_SERVICE_CONNECTED,
+ PerUserState.SERVICE_CONNECTED,
+ PerUserState.UNBIND_CALLED})
+ private @interface PerUserState {
+ /**
+ * The user is still locked.
+ */
+ int USER_LOCKED = 1;
+ /**
+ * The system has not queried whether there is a multi-client IME or not.
+ */
+ int SERVICE_NOT_QUERIED = 2;
+ /**
+ * A multi-client IME specified in {@link #PROP_DEBUG_MULTI_CLIENT_IME} is found in the
+ * system, but not bound yet.
+ */
+ int SERVICE_RECOGNIZED = 3;
+ /**
+ * {@link Context#bindServiceAsUser(Intent, ServiceConnection, int, Handler, UserHandle)} is
+ * already called for the IME but
+ * {@link ServiceConnection#onServiceConnected(ComponentName, IBinder)} is not yet called
+ * back. This includes once the IME is bound but temporarily disconnected as notified with
+ * {@link ServiceConnection#onServiceDisconnected(ComponentName)}.
+ */
+ int WAITING_SERVICE_CONNECTED = 4;
+ /**
+ * {@link ServiceConnection#onServiceConnected(ComponentName, IBinder)} is already called
+ * back. The IME is ready to be used.
+ */
+ int SERVICE_CONNECTED = 5;
+ /**
+ * The binding is gone. Either {@link Context#unbindService(ServiceConnection)} is
+ * explicitly called or the system decided to destroy the binding as notified with
+ * {@link ServiceConnection#onBindingDied(ComponentName)}.
+ */
+ int UNBIND_CALLED = 6;
+ }
+
+ /**
+ * Takes care of per-user state separation.
+ */
+ private static final class PerUserData {
+ final Object mLock = new Object();
+
+ /**
+ * User ID (not UID) that is associated with this data.
+ */
+ @UserIdInt
+ private final int mUserId;
+
+ /**
+ * {@link IMultiClientInputMethod} of the currently connected multi-client IME. This
+ * must be non-{@code null} only while {@link #mState} is
+ * {@link PerUserState#SERVICE_CONNECTED}.
+ */
+ @Nullable
+ @GuardedBy("mLock")
+ IMultiClientInputMethod mCurrentInputMethod;
+
+ /**
+ * {@link InputMethodInfo} of the currently selected multi-client IME. This must be
+ * non-{@code null} unless {@link #mState} is {@link PerUserState#SERVICE_NOT_QUERIED}.
+ */
+ @GuardedBy("mLock")
+ @Nullable
+ InputMethodInfo mCurrentInputMethodInfo;
+
+ /**
+ * Describes the current service state.
+ */
+ @GuardedBy("mLock")
+ @PerUserState
+ int mState;
+
+ /**
+ * A {@link SparseArray} that maps display ID to IME Window token that is already issued to
+ * the IME.
+ */
+ @GuardedBy("mLock")
+ final ArraySet<TokenInfo> mDisplayIdToImeWindowTokenMap = new ArraySet<>();
+
+ @GuardedBy("mLock")
+ private final ArrayMap<IBinder, InputMethodClientInfo> mClientMap = new ArrayMap<>();
+
+ @GuardedBy("mLock")
+ private SparseArray<InputMethodClientInfo> mClientIdToClientMap = new SparseArray<>();
+
+ private final OnWorkerThreadServiceConnection mOnWorkerThreadServiceConnection;
+
+ /**
+ * A {@link ServiceConnection} that is designed to run on a certain worker thread with
+ * which {@link OnWorkerThreadCallback} is associated.
+ *
+ * @see Context#bindServiceAsUser(Intent, ServiceConnection, int, Handler, UserHandle).
+ */
+ private static final class OnWorkerThreadServiceConnection implements ServiceConnection {
+ private final PerUserData mData;
+ private final OnWorkerThreadCallback mCallback;
+
+ OnWorkerThreadServiceConnection(PerUserData data, OnWorkerThreadCallback callback) {
+ mData = data;
+ mCallback = callback;
+ }
+
+ @WorkerThread
+ @Override
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ mCallback.onServiceConnected(mData,
+ IMultiClientInputMethod.Stub.asInterface(service));
+ }
+
+ @WorkerThread
+ @Override
+ public void onServiceDisconnected(ComponentName name) {
+ mCallback.onServiceDisconnected(mData);
+ }
+
+ @WorkerThread
+ @Override
+ public void onBindingDied(ComponentName name) {
+ mCallback.onBindingDied(mData);
+ }
+
+ Handler getHandler() {
+ return mCallback.getHandler();
+ }
+ }
+
+ PerUserData(@UserIdInt int userId, @Nullable InputMethodInfo inputMethodInfo,
+ @PerUserState int initialState, OnWorkerThreadCallback callback) {
+ mUserId = userId;
+ mCurrentInputMethodInfo = inputMethodInfo;
+ mState = initialState;
+ mOnWorkerThreadServiceConnection =
+ new OnWorkerThreadServiceConnection(this, callback);
+ }
+
+ @GuardedBy("mLock")
+ boolean bindServiceLocked(Context context, @UserIdInt int userId) {
+ final Intent intent =
+ new Intent(MultiClientInputMethodServiceDelegate.SERVICE_INTERFACE)
+ .setComponent(mCurrentInputMethodInfo.getComponent())
+ .putExtra(Intent.EXTRA_CLIENT_LABEL,
+ com.android.internal.R.string.input_method_binding_label)
+ .putExtra(Intent.EXTRA_CLIENT_INTENT, PendingIntent.getActivity(
+ context, 0,
+ new Intent(Settings.ACTION_INPUT_METHOD_SETTINGS), 0));
+
+ // Note: Instead of re-dispatching callback from the main thread to the worker thread
+ // where OnWorkerThreadCallback is running, we pass the Handler object here so that
+ // the callbacks will be directly dispatched to the worker thread.
+ return context.bindServiceAsUser(intent, mOnWorkerThreadServiceConnection,
+ IME_CONNECTION_UNIFIED_BIND_FLAGS,
+ mOnWorkerThreadServiceConnection.getHandler(), UserHandle.of(userId));
+ }
+
+ @GuardedBy("mLock")
+ void unbindServiceLocked(Context context) {
+ context.unbindService(mOnWorkerThreadServiceConnection);
+ }
+
+ @GuardedBy("mLock")
+ @Nullable
+ InputMethodClientInfo getClientLocked(IInputMethodClient client) {
+ return mClientMap.get(client.asBinder());
+ }
+
+ @GuardedBy("mLock")
+ @Nullable
+ InputMethodClientInfo getClientFromIdLocked(int clientId) {
+ return mClientIdToClientMap.get(clientId);
+ }
+
+ @GuardedBy("mLock")
+ @Nullable
+ InputMethodClientInfo removeClientLocked(IInputMethodClient client) {
+ final InputMethodClientInfo info = mClientMap.remove(client.asBinder());
+ if (info != null) {
+ mClientIdToClientMap.remove(info.mClientId);
+ }
+ return info;
+ }
+
+ @GuardedBy("mLock")
+ void addClientLocked(int uid, int pid, IInputMethodClient client,
+ int selfReportedDisplayId) {
+ if (getClientLocked(client) != null) {
+ Slog.wtf(TAG, "The same client is added multiple times");
+ return;
+ }
+ final ClientDeathRecipient deathRecipient = new ClientDeathRecipient(this, client);
+ try {
+ client.asBinder().linkToDeath(deathRecipient, 0);
+ } catch (RemoteException e) {
+ throw new IllegalStateException(e);
+ }
+ final InputMethodClientInfo clientInfo =
+ new InputMethodClientInfo(client, uid, pid, selfReportedDisplayId);
+ clientInfo.mState = InputMethodClientState.REGISTERED;
+ mClientMap.put(client.asBinder(), clientInfo);
+ mClientIdToClientMap.put(clientInfo.mClientId, clientInfo);
+ switch (mState) {
+ case PerUserState.SERVICE_CONNECTED:
+ try {
+ mCurrentInputMethod.addClient(
+ clientInfo.mClientId, clientInfo.mPid, clientInfo.mUid,
+ clientInfo.mSelfReportedDisplayId);
+ clientInfo.mState = InputMethodClientState.WAITING_FOR_IME_SESSION;
+ } catch (RemoteException e) {
+ // TODO(yukawa): Need logging and expected behavior
+ }
+ break;
+ }
+ }
+
+ @GuardedBy("mLock")
+ void onInputMethodConnectedLocked() {
+ final int numClients = mClientMap.size();
+ for (int i = 0; i < numClients; ++i) {
+ final InputMethodClientInfo clientInfo = mClientMap.valueAt(i);
+ switch (clientInfo.mState) {
+ case InputMethodClientState.REGISTERED:
+ // OK
+ break;
+ default:
+ Slog.e(TAG, "Unexpected state=" + clientInfo.mState);
+ return;
+ }
+ try {
+ mCurrentInputMethod.addClient(
+ clientInfo.mClientId, clientInfo.mUid, clientInfo.mPid,
+ clientInfo.mSelfReportedDisplayId);
+ clientInfo.mState = InputMethodClientState.WAITING_FOR_IME_SESSION;
+ } catch (RemoteException e) {
+ }
+ }
+ }
+
+ @GuardedBy("mLock")
+ void onInputMethodDisconnectedLocked() {
+ final int numClients = mClientMap.size();
+ for (int i = 0; i < numClients; ++i) {
+ final InputMethodClientInfo clientInfo = mClientMap.valueAt(i);
+ switch (clientInfo.mState) {
+ case InputMethodClientState.REGISTERED:
+ // Disconnected before onInputMethodConnectedLocked().
+ break;
+ case InputMethodClientState.WAITING_FOR_IME_SESSION:
+ // Disconnected between addClient() and acceptClient().
+ clientInfo.mState = InputMethodClientState.REGISTERED;
+ break;
+ case InputMethodClientState.READY_TO_SEND_FIRST_BIND_RESULT:
+ clientInfo.mState = InputMethodClientState.REGISTERED;
+ clientInfo.mInputMethodSession = null;
+ clientInfo.mMSInputMethodSession = null;
+ if (clientInfo.mWriteChannel != null) {
+ clientInfo.mWriteChannel.dispose();
+ clientInfo.mWriteChannel = null;
+ }
+ break;
+ case InputMethodClientState.ALREADY_SENT_BIND_RESULT:
+ try {
+ clientInfo.mClient.onUnbindMethod(clientInfo.mBindingSequence,
+ UnbindReason.DISCONNECT_IME);
+ } catch (RemoteException e) {
+ }
+ clientInfo.mState = InputMethodClientState.REGISTERED;
+ clientInfo.mInputMethodSession = null;
+ clientInfo.mMSInputMethodSession = null;
+ if (clientInfo.mWriteChannel != null) {
+ clientInfo.mWriteChannel.dispose();
+ clientInfo.mWriteChannel = null;
+ }
+ break;
+ }
+ }
+ }
+
+ private static final class ClientDeathRecipient implements IBinder.DeathRecipient {
+ private final PerUserData mPerUserData;
+ private final IInputMethodClient mClient;
+
+ ClientDeathRecipient(PerUserData perUserData, IInputMethodClient client) {
+ mPerUserData = perUserData;
+ mClient = client;
+ }
+
+ @BinderThread
+ @Override
+ public void binderDied() {
+ synchronized (mPerUserData.mLock) {
+ mClient.asBinder().unlinkToDeath(this, 0);
+
+ final InputMethodClientInfo clientInfo =
+ mPerUserData.removeClientLocked(mClient);
+ if (clientInfo == null) {
+ return;
+ }
+
+ if (clientInfo.mWriteChannel != null) {
+ clientInfo.mWriteChannel.dispose();
+ clientInfo.mWriteChannel = null;
+ }
+ if (clientInfo.mInputMethodSession != null) {
+ try {
+ clientInfo.mInputMethodSession.finishSession();
+ } catch (RemoteException e) {
+ }
+ clientInfo.mInputMethodSession = null;
+ }
+ clientInfo.mMSInputMethodSession = null;
+ clientInfo.mState = InputMethodClientState.UNREGISTERED;
+ switch (mPerUserData.mState) {
+ case PerUserState.SERVICE_CONNECTED:
+ try {
+ mPerUserData.mCurrentInputMethod.removeClient(clientInfo.mClientId);
+ } catch (RemoteException e) {
+ // TODO(yukawa): Need logging and expected behavior
+ }
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Queries for multi-client IME specified with {@code componentName}.
+ *
+ * @param context {@link Context} to be used to query component.
+ * @param userId User ID for which the multi-client IME is queried.
+ * @param componentName {@link ComponentName} to be queried.
+ * @return {@link InputMethodInfo} when multi-client IME is found. Otherwise {@code null}.
+ */
+ @Nullable
+ private static InputMethodInfo queryInputMethod(Context context, @UserIdInt int userId,
+ @Nullable ComponentName componentName) {
+ if (componentName == null) {
+ return null;
+ }
+
+ // Use for queryIntentServicesAsUser
+ final PackageManager pm = context.getPackageManager();
+ final List<ResolveInfo> services = pm.queryIntentServicesAsUser(
+ new Intent(MultiClientInputMethodServiceDelegate.SERVICE_INTERFACE)
+ .setComponent(componentName),
+ PackageManager.GET_META_DATA, userId);
+
+ if (services.isEmpty()) {
+ Slog.e(TAG, "No IME found");
+ return null;
+ }
+
+ if (services.size() > 1) {
+ Slog.e(TAG, "Only one IME service is supported.");
+ return null;
+ }
+
+ final ResolveInfo ri = services.get(0);
+ ServiceInfo si = ri.serviceInfo;
+ final String imeId = InputMethodInfo.computeId(ri);
+ if (!android.Manifest.permission.BIND_INPUT_METHOD.equals(si.permission)) {
+ Slog.e(TAG, imeId + " must have required"
+ + android.Manifest.permission.BIND_INPUT_METHOD);
+ return null;
+ }
+
+ if (!Build.IS_DEBUGGABLE && (si.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) == 0) {
+ Slog.e(TAG, imeId + " must be pre-installed when Build.IS_DEBUGGABLE is false");
+ return null;
+ }
+
+ try {
+ return new InputMethodInfo(context, ri);
+ } catch (Exception e) {
+ Slog.wtf(TAG, "Unable to load input method " + imeId, e);
+ }
+ return null;
+ }
+
+ /**
+ * Manages the mapping rule from user ID to {@link InputMethodInfo}.
+ */
+ private static final class UserToInputMethodInfoMap {
+ @GuardedBy("mArray")
+ private final SparseArray<InputMethodInfo> mArray = new SparseArray<>();
+
+ @AnyThread
+ void put(@UserIdInt int userId, InputMethodInfo imi) {
+ synchronized (mArray) {
+ mArray.put(userId, imi);
+ }
+ }
+
+ @AnyThread
+ void remove(@UserIdInt int userId) {
+ synchronized (mArray) {
+ mArray.remove(userId);
+ }
+ }
+
+ @AnyThread
+ @Nullable
+ InputMethodInfo get(@UserIdInt int userId) {
+ synchronized (mArray) {
+ return mArray.get(userId);
+ }
+ }
+
+ @AnyThread
+ List<InputMethodInfo> getAsList(@UserIdInt int userId) {
+ final InputMethodInfo info = get(userId);
+ if (info == null) {
+ return Collections.emptyList();
+ }
+ return Collections.singletonList(info);
+ }
+ }
+
+ /**
+ * Takes care of IPCs exposed to the multi-client IME.
+ */
+ private static final class ImeCallbacks
+ extends IMultiClientInputMethodPrivilegedOperations.Stub {
+ private final PerUserData mPerUserData;
+ private final WindowManagerInternal mIWindowManagerInternal;
+
+ ImeCallbacks(PerUserData perUserData) {
+ mPerUserData = perUserData;
+ mIWindowManagerInternal = LocalServices.getService(WindowManagerInternal.class);
+ }
+
+ @BinderThread
+ @Override
+ public IBinder createInputMethodWindowToken(int displayId) {
+ synchronized (mPerUserData.mLock) {
+ // We assume the number of tokens would not be that large (up to 10 or so) hence
+ // linear search should be acceptable.
+ final int numTokens = mPerUserData.mDisplayIdToImeWindowTokenMap.size();
+ for (int i = 0; i < numTokens; ++i) {
+ final TokenInfo tokenInfo =
+ mPerUserData.mDisplayIdToImeWindowTokenMap.valueAt(i);
+ // Currently we issue up to one window token per display.
+ if (tokenInfo.mDisplayId == displayId) {
+ return tokenInfo.mToken;
+ }
+ }
+
+ final Binder token = new Binder();
+ Binder.withCleanCallingIdentity(
+ PooledLambda.obtainRunnable(WindowManagerInternal::addWindowToken,
+ mIWindowManagerInternal, token, TYPE_INPUT_METHOD, displayId));
+ mPerUserData.mDisplayIdToImeWindowTokenMap.add(new TokenInfo(token, displayId));
+ return token;
+ }
+ }
+
+ @BinderThread
+ @Override
+ public void deleteInputMethodWindowToken(IBinder token) {
+ synchronized (mPerUserData.mLock) {
+ // We assume the number of tokens would not be that large (up to 10 or so) hence
+ // linear search should be acceptable.
+ final int numTokens = mPerUserData.mDisplayIdToImeWindowTokenMap.size();
+ for (int i = 0; i < numTokens; ++i) {
+ final TokenInfo tokenInfo =
+ mPerUserData.mDisplayIdToImeWindowTokenMap.valueAt(i);
+ if (tokenInfo.mToken == token) {
+ mPerUserData.mDisplayIdToImeWindowTokenMap.remove(tokenInfo);
+ break;
+ }
+ }
+ }
+ }
+
+ @BinderThread
+ @Override
+ public void acceptClient(int clientId, IInputMethodSession inputMethodSession,
+ IMultiClientInputMethodSession multiSessionInputMethodSession,
+ InputChannel writeChannel) {
+ synchronized (mPerUserData.mLock) {
+ final InputMethodClientInfo clientInfo =
+ mPerUserData.getClientFromIdLocked(clientId);
+ if (clientInfo == null) {
+ Slog.e(TAG, "Unknown clientId=" + clientId);
+ return;
+ }
+ switch (clientInfo.mState) {
+ case InputMethodClientState.WAITING_FOR_IME_SESSION:
+ try {
+ clientInfo.mClient.setActive(true, false);
+ } catch (RemoteException e) {
+ // TODO(yukawa): Remove this client.
+ return;
+ }
+ clientInfo.mState = InputMethodClientState.READY_TO_SEND_FIRST_BIND_RESULT;
+ clientInfo.mWriteChannel = writeChannel;
+ clientInfo.mInputMethodSession = inputMethodSession;
+ clientInfo.mMSInputMethodSession = multiSessionInputMethodSession;
+ break;
+ default:
+ Slog.e(TAG, "Unexpected state=" + clientInfo.mState);
+ break;
+ }
+ }
+ }
+
+ @BinderThread
+ @Override
+ public void reportImeWindowTarget(int clientId, int targetWindowHandle,
+ IBinder imeWindowToken) {
+ synchronized (mPerUserData.mLock) {
+ final InputMethodClientInfo clientInfo =
+ mPerUserData.getClientFromIdLocked(clientId);
+ if (clientInfo == null) {
+ Slog.e(TAG, "Unknown clientId=" + clientId);
+ return;
+ }
+ for (WindowInfo windowInfo : clientInfo.mWindowMap.values()) {
+ if (windowInfo.mWindowHandle == targetWindowHandle) {
+ final IBinder targetWindowToken = windowInfo.mWindowToken;
+ // TODO(yukawa): Report targetWindowToken and targetWindowToken to WMS.
+ if (DEBUG) {
+ Slog.v(TAG, "reportImeWindowTarget"
+ + " clientId=" + clientId
+ + " imeWindowToken=" + imeWindowToken
+ + " targetWindowToken=" + targetWindowToken);
+ }
+ }
+ }
+ // not found.
+ }
+ }
+
+ @BinderThread
+ @Override
+ public boolean isUidAllowedOnDisplay(int displayId, int uid) {
+ return mIWindowManagerInternal.isUidAllowedOnDisplay(displayId, uid);
+ }
+ }
+
+ /**
+ * Takes care of IPCs exposed to the IME client.
+ */
+ private static final class ApiCallbacks extends IInputMethodManager.Stub {
+ private final UserDataMap mUserDataMap;
+ private final UserToInputMethodInfoMap mInputMethodInfoMap;
+ private final AppOpsManager mAppOpsManager;
+ private final WindowManagerInternal mWindowManagerInternal;
+
+ ApiCallbacks(Context context, UserDataMap userDataMap,
+ UserToInputMethodInfoMap inputMethodInfoMap) {
+ mUserDataMap = userDataMap;
+ mInputMethodInfoMap = inputMethodInfoMap;
+ mAppOpsManager = context.getSystemService(AppOpsManager.class);
+ mWindowManagerInternal = LocalServices.getService(WindowManagerInternal.class);
+ }
+
+ @AnyThread
+ private boolean checkFocus(int uid, int pid, int displayId) {
+ return mWindowManagerInternal.isInputMethodClientFocus(uid, pid, displayId);
+ }
+
+ @BinderThread
+ @Override
+ public void addClient(IInputMethodClient client, IInputContext inputContext,
+ int selfReportedDisplayId) {
+ final int callingUid = Binder.getCallingUid();
+ final int callingPid = Binder.getCallingPid();
+ final int userId = UserHandle.getUserId(callingUid);
+ final PerUserData data = mUserDataMap.get(userId);
+ if (data == null) {
+ Slog.e(TAG, "addClient() from unknown userId=" + userId
+ + " uid=" + callingUid + " pid=" + callingPid);
+ return;
+ }
+ synchronized (data.mLock) {
+ data.addClientLocked(callingUid, callingPid, client, selfReportedDisplayId);
+ }
+ }
+
+ @BinderThread
+ @Override
+ public List<InputMethodInfo> getInputMethodList() {
+ return mInputMethodInfoMap.getAsList(UserHandle.getUserId(Binder.getCallingUid()));
+ }
+
+ @BinderThread
+ @Override
+ public List<InputMethodInfo> getVrInputMethodList() {
+ reportNotSupported();
+ return Collections.emptyList();
+ }
+
+ @BinderThread
+ @Override
+ public List<InputMethodInfo> getEnabledInputMethodList() {
+ return mInputMethodInfoMap.getAsList(UserHandle.getUserId(Binder.getCallingUid()));
+ }
+
+ @BinderThread
+ @Override
+ public List<InputMethodSubtype> getEnabledInputMethodSubtypeList(String imiId,
+ boolean allowsImplicitlySelectedSubtypes) {
+ reportNotSupported();
+ return Collections.emptyList();
+ }
+
+ @BinderThread
+ @Override
+ public InputMethodSubtype getLastInputMethodSubtype() {
+ reportNotSupported();
+ return null;
+ }
+
+ @BinderThread
+ @Override
+ public List getShortcutInputMethodsAndSubtypes() {
+ reportNotSupported();
+ return null;
+ }
+
+ @BinderThread
+ @Override
+ public boolean showSoftInput(
+ IInputMethodClient client, int flags, ResultReceiver resultReceiver) {
+ final int callingUid = Binder.getCallingUid();
+ final int callingPid = Binder.getCallingPid();
+ final int userId = UserHandle.getUserId(callingUid);
+ final PerUserData data = mUserDataMap.get(userId);
+ if (data == null) {
+ Slog.e(TAG, "showSoftInput() from unknown userId=" + userId
+ + " uid=" + callingUid + " pid=" + callingPid);
+ return false;
+ }
+ synchronized (data.mLock) {
+ final InputMethodClientInfo clientInfo = data.getClientLocked(client);
+ if (clientInfo == null) {
+ Slog.e(TAG, "showSoftInput. client not found. ignoring.");
+ return false;
+ }
+ if (clientInfo.mUid != callingUid) {
+ Slog.e(TAG, "Expected calling UID=" + clientInfo.mUid
+ + " actual=" + callingUid);
+ return false;
+ }
+ switch (clientInfo.mState) {
+ case InputMethodClientState.READY_TO_SEND_FIRST_BIND_RESULT:
+ case InputMethodClientState.ALREADY_SENT_BIND_RESULT:
+ try {
+ clientInfo.mMSInputMethodSession.showSoftInput(flags, resultReceiver);
+ } catch (RemoteException e) {
+ }
+ break;
+ default:
+ if (DEBUG) {
+ Slog.e(TAG, "Ignoring showSoftInput(). clientState="
+ + clientInfo.mState);
+ }
+ break;
+ }
+ return true;
+ }
+ }
+
+ @BinderThread
+ @Override
+ public boolean hideSoftInput(
+ IInputMethodClient client, int flags, ResultReceiver resultReceiver) {
+ final int callingUid = Binder.getCallingUid();
+ final int callingPid = Binder.getCallingPid();
+ final int userId = UserHandle.getUserId(callingUid);
+ final PerUserData data = mUserDataMap.get(userId);
+ if (data == null) {
+ Slog.e(TAG, "hideSoftInput() from unknown userId=" + userId
+ + " uid=" + callingUid + " pid=" + callingPid);
+ return false;
+ }
+ synchronized (data.mLock) {
+ final InputMethodClientInfo clientInfo = data.getClientLocked(client);
+ if (clientInfo == null) {
+ return false;
+ }
+ if (clientInfo.mUid != callingUid) {
+ Slog.e(TAG, "Expected calling UID=" + clientInfo.mUid
+ + " actual=" + callingUid);
+ return false;
+ }
+ switch (clientInfo.mState) {
+ case InputMethodClientState.READY_TO_SEND_FIRST_BIND_RESULT:
+ case InputMethodClientState.ALREADY_SENT_BIND_RESULT:
+ try {
+ clientInfo.mMSInputMethodSession.hideSoftInput(flags, resultReceiver);
+ } catch (RemoteException e) {
+ }
+ break;
+ default:
+ if (DEBUG) {
+ Slog.e(TAG, "Ignoring hideSoftInput(). clientState="
+ + clientInfo.mState);
+ }
+ break;
+ }
+ return true;
+ }
+ }
+
+ @BinderThread
+ @Override
+ public InputBindResult startInputOrWindowGainedFocus(
+ @StartInputReason int startInputReason,
+ @Nullable IInputMethodClient client,
+ @Nullable IBinder windowToken,
+ @StartInputFlags int startInputFlags,
+ @SoftInputModeFlags int softInputMode,
+ int windowFlags,
+ @Nullable EditorInfo editorInfo,
+ @Nullable IInputContext inputContext,
+ @MissingMethodFlags int missingMethods,
+ int unverifiedTargetSdkVersion) {
+ final int callingUid = Binder.getCallingUid();
+ final int callingPid = Binder.getCallingPid();
+ final int userId = UserHandle.getUserId(callingUid);
+
+ if (client == null) {
+ return InputBindResult.INVALID_CLIENT;
+ }
+
+ final boolean packageNameVerified =
+ editorInfo != null && InputMethodUtils.checkIfPackageBelongsToUid(
+ mAppOpsManager, callingUid, editorInfo.packageName);
+ if (editorInfo != null && !packageNameVerified) {
+ Slog.e(TAG, "Rejecting this client as it reported an invalid package name."
+ + " uid=" + callingUid + " package=" + editorInfo.packageName);
+ return InputBindResult.INVALID_PACKAGE_NAME;
+ }
+
+ final PerUserData data = mUserDataMap.get(userId);
+ if (data == null) {
+ Slog.e(TAG, "startInputOrWindowGainedFocus() from unknown userId=" + userId
+ + " uid=" + callingUid + " pid=" + callingPid);
+ return InputBindResult.INVALID_USER;
+ }
+
+ synchronized (data.mLock) {
+ final InputMethodClientInfo clientInfo = data.getClientLocked(client);
+ if (clientInfo == null) {
+ return InputBindResult.INVALID_CLIENT;
+ }
+ if (clientInfo.mUid != callingUid) {
+ Slog.e(TAG, "Expected calling UID=" + clientInfo.mUid
+ + " actual=" + callingUid);
+ return InputBindResult.INVALID_CLIENT;
+ }
+
+ switch (data.mState) {
+ case PerUserState.USER_LOCKED:
+ case PerUserState.SERVICE_NOT_QUERIED:
+ case PerUserState.SERVICE_RECOGNIZED:
+ case PerUserState.WAITING_SERVICE_CONNECTED:
+ case PerUserState.UNBIND_CALLED:
+ return InputBindResult.IME_NOT_CONNECTED;
+ case PerUserState.SERVICE_CONNECTED:
+ // OK
+ break;
+ default:
+ Slog.wtf(TAG, "Unexpected state=" + data.mState);
+ return InputBindResult.IME_NOT_CONNECTED;
+ }
+
+ WindowInfo windowInfo = null;
+ if (windowToken != null) {
+ windowInfo = clientInfo.mWindowMap.get(windowToken);
+ if (windowInfo == null) {
+ windowInfo = new WindowInfo(windowToken, WindowHandleSource.getNext());
+ clientInfo.mWindowMap.put(windowToken, windowInfo);
+ }
+ }
+
+ if (!checkFocus(clientInfo.mUid, clientInfo.mPid,
+ clientInfo.mSelfReportedDisplayId)) {
+ return InputBindResult.NOT_IME_TARGET_WINDOW;
+ }
+
+ if (editorInfo == null) {
+ // So-called dummy InputConnection scenario. For app compatibility, we still
+ // notify this to the IME.
+ switch (clientInfo.mState) {
+ case InputMethodClientState.READY_TO_SEND_FIRST_BIND_RESULT:
+ case InputMethodClientState.ALREADY_SENT_BIND_RESULT:
+ final int windowHandle = windowInfo != null
+ ? windowInfo.mWindowHandle
+ : MultiClientInputMethodServiceDelegate.INVALID_WINDOW_HANDLE;
+ try {
+ clientInfo.mMSInputMethodSession.startInputOrWindowGainedFocus(
+ inputContext, missingMethods, editorInfo, startInputFlags,
+ softInputMode, windowHandle);
+ } catch (RemoteException e) {
+ }
+ break;
+ }
+ return InputBindResult.NULL_EDITOR_INFO;
+ }
+
+ switch (clientInfo.mState) {
+ case InputMethodClientState.REGISTERED:
+ case InputMethodClientState.WAITING_FOR_IME_SESSION:
+ clientInfo.mBindingSequence++;
+ if (clientInfo.mBindingSequence < 0) {
+ clientInfo.mBindingSequence = 0;
+ }
+ return new InputBindResult(
+ InputBindResult.ResultCode.SUCCESS_WAITING_IME_SESSION,
+ null, null, data.mCurrentInputMethodInfo.getId(),
+ clientInfo.mBindingSequence);
+ case InputMethodClientState.READY_TO_SEND_FIRST_BIND_RESULT:
+ case InputMethodClientState.ALREADY_SENT_BIND_RESULT:
+ clientInfo.mBindingSequence++;
+ if (clientInfo.mBindingSequence < 0) {
+ clientInfo.mBindingSequence = 0;
+ }
+ // Successful start input.
+ final int windowHandle = windowInfo != null
+ ? windowInfo.mWindowHandle
+ : MultiClientInputMethodServiceDelegate.INVALID_WINDOW_HANDLE;
+ try {
+ clientInfo.mMSInputMethodSession.startInputOrWindowGainedFocus(
+ inputContext, missingMethods, editorInfo, startInputFlags,
+ softInputMode, windowHandle);
+ } catch (RemoteException e) {
+ }
+ clientInfo.mState = InputMethodClientState.ALREADY_SENT_BIND_RESULT;
+ return new InputBindResult(
+ InputBindResult.ResultCode.SUCCESS_WITH_IME_SESSION,
+ clientInfo.mInputMethodSession,
+ clientInfo.mWriteChannel.dup(),
+ data.mCurrentInputMethodInfo.getId(),
+ clientInfo.mBindingSequence);
+ case InputMethodClientState.UNREGISTERED:
+ Slog.e(TAG, "The client is already unregistered.");
+ return InputBindResult.INVALID_CLIENT;
+ }
+ }
+ return null;
+ }
+
+ @BinderThread
+ @Override
+ public void showInputMethodPickerFromClient(
+ IInputMethodClient client, int auxiliarySubtypeMode) {
+ reportNotSupported();
+ }
+
+ @BinderThread
+ @Override
+ public void showInputMethodAndSubtypeEnablerFromClient(
+ IInputMethodClient client, String inputMethodId) {
+ reportNotSupported();
+ }
+
+ @BinderThread
+ @Override
+ public boolean isInputMethodPickerShownForTest() {
+ reportNotSupported();
+ return false;
+ }
+
+ @BinderThread
+ @Override
+ public void setInputMethod(IBinder token, String id) {
+ reportNotSupported();
+ }
+
+ @BinderThread
+ @Override
+ public void setInputMethodAndSubtype(IBinder token, String id, InputMethodSubtype subtype) {
+ reportNotSupported();
+ }
+
+ @BinderThread
+ @Override
+ public void registerSuggestionSpansForNotification(SuggestionSpan[] suggestionSpans) {
+ reportNotSupported();
+ }
+
+ @BinderThread
+ @Override
+ public boolean notifySuggestionPicked(
+ SuggestionSpan span, String originalString, int index) {
+ reportNotSupported();
+ return false;
+ }
+
+ @BinderThread
+ @Override
+ public InputMethodSubtype getCurrentInputMethodSubtype() {
+ reportNotSupported();
+ return null;
+ }
+
+ @BinderThread
+ @Override
+ public boolean setCurrentInputMethodSubtype(InputMethodSubtype subtype) {
+ reportNotSupported();
+ return false;
+ }
+
+ @BinderThread
+ @Override
+ public boolean switchToPreviousInputMethod(IBinder token) {
+ reportNotSupported();
+ return false;
+ }
+
+ @BinderThread
+ @Override
+ public boolean switchToNextInputMethod(IBinder token, boolean onlyCurrentIme) {
+ reportNotSupported();
+ return false;
+ }
+
+ @BinderThread
+ @Override
+ public void setAdditionalInputMethodSubtypes(String imiId, InputMethodSubtype[] subtypes) {
+ reportNotSupported();
+ }
+
+ @BinderThread
+ @Override
+ public int getInputMethodWindowVisibleHeight() {
+ reportNotSupported();
+ return 0;
+ }
+
+ @BinderThread
+ @Override
+ public void onShellCommand(@Nullable FileDescriptor in, @Nullable FileDescriptor out,
+ @Nullable FileDescriptor err, String[] args, @Nullable ShellCallback callback,
+ ResultReceiver resultReceiver) {
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/inputmethod/multi-client-ime.md b/services/core/java/com/android/server/inputmethod/multi-client-ime.md
new file mode 100644
index 0000000..3021d2f
--- /dev/null
+++ b/services/core/java/com/android/server/inputmethod/multi-client-ime.md
@@ -0,0 +1,192 @@
+<!-- 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.
+-->
+
+# Multi Client Input Method Editors
+
+## History of Multi Client Input Method Editors (Multi Client IMEs)
+
+An advanced multi-display support is requested for certain Android form-factors so that user(s) can type text on each display at the same time without losing software keyboard focus in other displays (hereafter called "multi-client scenario"). This is not possible in Android IMEs built on top of `InputMethodService` class. The assumption that a single IME client can be focused at the same time was made before Android IME APIs were introduced in Android 1.5 and many public APIs in `InputMethodService` have already relied heavily on that assumption (hereafter called "single-client scenario"). Updating `InputMethodService` class to support multi-client scenario is, however, quite challenging because:
+
+ 1. doing so would introduce an unacceptable amount of complexity into `InputMethodService`, which is already hard to maintain,
+ 2. IME developers still need to update their implementation to be able to support parallel requests from multiple focused IME client, which may require non-trivial redesign in their side (e.g. input decoder, typing history database, ...), and
+ 3. actual use cases for multi IME clients are expected to be evolved rapidly hence the new protocol is not yet stable and not yet ready to be exposed as public APIs.
+
+Thus the first decision we made was that to support such special multi-display environments a new type of IME (hereafter called "multi-client IME") needs to be designed and implemented rather than reusing `InputMethodService` public class. On top of this decision, following decisions were also made:
+
+ * Multi-client IME V1 will be built on top of private APIs. This means:
+ * Multi-client IME must be pre-installed into the system. They cannot be distributed via application store since protocol compatibility is not guaranteed across devices and releases.
+ * The system should trust multi-client IME to some extent. System integrators are responsible for making sure that the pre-installed multi-client IME works as expected.
+ * Unlike `InputMethodService`, multiple multi-client IMEs cannot be enabled. The system pre-installs only one multi-client IME.
+ * Punt some special features of Android IMEs (e.g. fullscreen mode, InputMethodSubtype, ...) from V1 goal unless someone actually requests those features for multi-client IME scenario.
+ * Introduce `MultiClientInputMethodManagerService` (MCIMMS) for multi-client IME scenario and use it instead of `InputMethodManagerService` (IMMS) when a certain runtime flag is enabled at the device boot time. This means:
+ * basically no risk for single-client scenario,
+ * the feature can be easily deprecated, and
+ * it forces us to rewrite IME system server, which is expected to be a good chance to reconsider what Android IME protocol should look like.
+ * Most of form-factors such as Phones and TVs continue to use IMMS and support at most one focused IME client even under multi-display environment.
+
+
+## How to test
+
+On AOSP-based development devices (e.g. phones) where `android.os.Build.IS_DEBUGGABLE` returns `true` and you can have root access, you can enable multi-client IME feature by setting a valid component name that supports multi-client IME protocol to the system property `persist.debug.multi_client_ime`. Reboot is required for this to take effect.
+
+```shell
+# Build and install a sample multi-client IME
+make -j MultiClientInputMethod
+adb install -r $OUT/system/priv-app/MultiClientInputMethod/MultiClientInputMethod.apk
+
+# Enable multi-client IME for the side-loaded sample multi-client IME
+adb root
+adb shell setprop persist.debug.multi_client_ime com.example.android.multiclientinputmethod/.MultiClientInputMethod
+adb reboot
+```
+
+To disable multi-client IME on non-supported devices again, just clear `persist.debug.multi_client_ime` as follows. Reboot is still required for this to take effect.
+
+```shell
+# Disable multi-client IME again
+adb root
+adb shell "setprop persist.debug.multi_client_ime ''"
+adb reboot
+```
+
+## How to develop multi-client IMEs
+
+There is a sample multi-client IME in `development/samples/MultiClientInputMethod/`.
+
+## Versioning
+
+Neither forward nor backward compatibility is guaranteed in multi-client IME APIs. The system integrator is responsible for making sure that both the system and pre-installed multi-client IME are compatible with each other every time the system image is updated.
+
+## Implementation note
+
+### Unsupported features
+
+ * VR IME
+ * `VrManager#setVrInputMethod()` system API is not supported.
+ * InputMethodSubtype
+ * Following APIs are not supported
+ * `InputMethodManager#getEnabledInputMethodSubtypeList()`
+ * `InputMethodManager#getCurrentInputMethodSubtype()`
+ * `InputMethodManager#setCurrentInputMethodSubtype()`
+ * `InputMethodManager#getShortcutInputMethodsAndSubtypes()`
+ * `InputMethodManager#setAdditionalInputMethodSubtypes()`
+ * `InputMethodManager#getLastInputMethodSubtype()`
+ * `Settings.Secure#SELECTED_INPUT_METHOD_SUBTYPE`
+ * IME switching
+ * Following APIs are not supported
+ * `InputMethodManager#showInputMethodPicker()`
+ * `InputMethodManager#showInputMethodAndSubtypeEnabler()`
+ * `InputMethodManager#setInputMethod()`
+ * `InputMethodManager#setInputMethodAndSubtype()`
+ * `InputMethodManager#switchToLastInputMethod()`
+ * `InputMethodManager#switchToNextInputMethod()`
+ * `InputMethodManager#shouldOfferSwitchingToNextInputMethod()`
+ * `Settings.Secure#DEFAULT_INPUT_METHOD`
+ * `Settings.Secure#ENABLED_INPUT_METHODS`
+ * Direct-boot aware multi-client IME
+ * Device manufacturer can work around this by integrating in-app keyboard into the initial unlock screen.
+ * Full-screen mode
+ * Following API always returns `false`.
+ * `InputMethodManager#isFullscreenMode()`
+ * Custom inset
+ * For instance, floating IME cannot be implemented right now.
+ * Custom touchable region (`InputMethodService.Insets#touchableRegion`)
+ * Image Insertion API
+ * `InputConnection#commitContent()` API is silently ignored.
+ * `adb shell dumpsys` does not include any log from MCIMMS yet.
+
+### Security
+
+#### Root permission is required to enable MCIMMS on non-supported devices
+
+In order to override `persist.debug.multi_client_ime` device property, an explicit root permission is needed.
+
+#### Multi-client IME must be pre-installed
+
+Multi-client IME must be pre-installed since it is considered as part of the system component. This is verified by checking `ApplicationInfo.FLAG_SYSTEM` bit. This security check can be bypassed when `Build.IS_DEBUGGABLE` is `true` so that IME developers can easily side-load their APKs during development phase.
+
+```java
+public final class MultiClientInputMethodManagerService {
+ ...
+ @Nullable
+ private static InputMethodInfo queryInputMethod(Context context, @UserIdInt int userId,
+ @Nullable ComponentName componentName) {
+
+ ...
+
+ if (! && (si.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) == 0) {
+ Slog.e(TAG, imeId + " must be pre-installed when Build.IS_DEBUGGABLE is false");
+ return null;
+ }
+```
+[services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java](MultiClientInputMethodManagerService.java)
+
+
+#### Integer handle vs IBinder token
+
+Sometimes MCIMMS needs to issue certain types of identifiers to the multi-client IME so that the IME can later specify to which entity or resource it intends to access. A good example is the IME client identifier. Multi-client IME definitely need to be able to specify which IME client to be interacted with for certain operations. The problem is that MCIMMS cannot simply pass `IInputMethodClient` to the multi-client IME as an ID because it would allow the IME to make IPC calls to the IME client. For this kind of situations, we usually use `Binder` object just as a non-spoofable token. For instance, IMMS creates another 'Binder' token then pass it to the IME, instead of directly passing 'IWindow' Binder token.
+
+```java
+public class InputMethodManagerService extends IInputMethodManager.Stub
+ implements ServiceConnection, Handler.Callback {
+ ...
+ @GuardedBy("mMethodMap")
+ private final WeakHashMap<IBinder, IBinder> mImeTargetWindowMap = new WeakHashMap<>();
+
+ ...
+
+ @GuardedBy("mMethodMap")
+ @NonNull
+ InputBindResult attachNewInputLocked(@StartInputReason int startInputReason, boolean initial) {
+ ...
+ final Binder startInputToken = new Binder();
+ final StartInputInfo info = new StartInputInfo(mCurToken, mCurId, startInputReason,
+ !initial, mCurFocusedWindow, mCurAttribute, mCurFocusedWindowSoftInputMode,
+ mCurSeq);
+ mImeTargetWindowMap.put(startInputToken, mCurFocusedWindow);
+ ...
+ }
+
+ ...
+
+ @BinderThread
+ private void reportStartInput(IBinder token, IBinder startInputToken) {
+ if (!calledWithValidToken(token)) {
+ return;
+ }
+
+ synchronized (mMethodMap) {
+ final IBinder targetWindow = mImeTargetWindowMap.get(startInputToken);
+ if (targetWindow != null && mLastImeTargetWindow != targetWindow) {
+ mWindowManagerInternal.updateInputMethodTargetWindow(token, targetWindow);
+ }
+ mLastImeTargetWindow = targetWindow;
+ }
+ }
+```
+[services/core/java/com/android/server/inputmethod/InputMethodManagerService.java](InputMethodManagerService.java)
+
+However, in MCIMMS, for certain cases we decided to use a simple integer token, which can be spoofable and can be messed up if integer overflow happens. This is because:
+
+ * It does not make much sense to worry about malicious multi-client IMEs, because it is guaranteed to be a pre-installed system component.
+ * Integer token is expected to be a more lightweight that `Binder` token.
+ * For that use case, integer overflow is unrealistic.
+ * Strict user separation is still enforced. Multi-client IMEs are still not allowed to interact with other users' resources by any means.
+
+Currently the following IDs are implemented as integer tokens:
+
+ * Client ID
+ * Window Handle
+ * Note that each IME client has its own Window Handle mapping table. Window Handle is valid only within the associated IME client.
diff --git a/services/core/java/com/android/server/job/JobSchedulerService.java b/services/core/java/com/android/server/job/JobSchedulerService.java
index dd993b8..b3f0629 100644
--- a/services/core/java/com/android/server/job/JobSchedulerService.java
+++ b/services/core/java/com/android/server/job/JobSchedulerService.java
@@ -1740,11 +1740,6 @@
/**
* The state of at least one job has changed. Here is where we could enforce various
* policies on when we want to execute jobs.
- * Right now the policy is such:
- * If >1 of the ready jobs is idle mode we send all of them off
- * if more than 2 network connectivity jobs are ready we send them all off.
- * If more than 4 jobs total are ready we send them all off.
- * TODO: It would be nice to consolidate these sort of high-level policies somewhere.
*/
final class MaybeReadyJobQueueFunctor implements Consumer<JobStatus> {
int chargingCount;
diff --git a/services/core/java/com/android/server/job/controllers/JobStatus.java b/services/core/java/com/android/server/job/controllers/JobStatus.java
index 4ece538d..35fc29e 100644
--- a/services/core/java/com/android/server/job/controllers/JobStatus.java
+++ b/services/core/java/com/android/server/job/controllers/JobStatus.java
@@ -68,10 +68,10 @@
public static final long NO_LATEST_RUNTIME = Long.MAX_VALUE;
public static final long NO_EARLIEST_RUNTIME = 0L;
- static final int CONSTRAINT_CHARGING = JobInfo.CONSTRAINT_FLAG_CHARGING;
- static final int CONSTRAINT_IDLE = JobInfo.CONSTRAINT_FLAG_DEVICE_IDLE;
- static final int CONSTRAINT_BATTERY_NOT_LOW = JobInfo.CONSTRAINT_FLAG_BATTERY_NOT_LOW;
- static final int CONSTRAINT_STORAGE_NOT_LOW = JobInfo.CONSTRAINT_FLAG_STORAGE_NOT_LOW;
+ static final int CONSTRAINT_CHARGING = JobInfo.CONSTRAINT_FLAG_CHARGING; // 1 < 0
+ static final int CONSTRAINT_IDLE = JobInfo.CONSTRAINT_FLAG_DEVICE_IDLE; // 1 << 2
+ static final int CONSTRAINT_BATTERY_NOT_LOW = JobInfo.CONSTRAINT_FLAG_BATTERY_NOT_LOW; // 1 << 1
+ static final int CONSTRAINT_STORAGE_NOT_LOW = JobInfo.CONSTRAINT_FLAG_STORAGE_NOT_LOW; // 1 << 3
static final int CONSTRAINT_TIMING_DELAY = 1<<31;
static final int CONSTRAINT_DEADLINE = 1<<30;
static final int CONSTRAINT_CONNECTIVITY = 1<<28;
@@ -975,8 +975,7 @@
}
/**
- * @return Whether or not this job is ready to run, based on its requirements. This is true if
- * the constraints are satisfied <strong>or</strong> the deadline on the job has expired.
+ * @return Whether or not this job is ready to run, based on its requirements.
*/
public boolean isReady() {
// Deadline constraint trumps other constraints (except for periodic jobs where deadline
@@ -1234,16 +1233,18 @@
proto.end(token);
}
- // normalized bucket indices, not the AppStandby constants
- private String bucketName(int bucket) {
- switch (bucket) {
+ /**
+ * Returns a bucket name based on the normalized bucket indices, not the AppStandby constants.
+ */
+ String getBucketName() {
+ switch (standbyBucket) {
case 0: return "ACTIVE";
case 1: return "WORKING_SET";
case 2: return "FREQUENT";
case 3: return "RARE";
case 4: return "NEVER";
default:
- return "Unknown: " + bucket;
+ return "Unknown: " + standbyBucket;
}
}
@@ -1385,6 +1386,17 @@
if ((trackingControllers&TRACKING_TIME) != 0) pw.print(" TIME");
pw.println();
}
+
+ pw.print(prefix); pw.println("Implicit constraints:");
+ pw.print(prefix); pw.print(" readyNotDozing: ");
+ pw.println(mReadyNotDozing);
+ pw.print(prefix); pw.print(" readyNotRestrictedInBg: ");
+ pw.println(mReadyNotRestrictedInBg);
+ if (!job.isPeriodic() && hasDeadlineConstraint()) {
+ pw.print(prefix); pw.print(" readyDeadlineSatisfied: ");
+ pw.println(mReadyDeadlineSatisfied);
+ }
+
if (changedAuthorities != null) {
pw.print(prefix); pw.println("Changed authorities:");
for (int i=0; i<changedAuthorities.size(); i++) {
@@ -1413,7 +1425,7 @@
}
}
pw.print(prefix); pw.print("Standby bucket: ");
- pw.println(bucketName(standbyBucket));
+ pw.println(getBucketName());
if (standbyBucket > 0) {
pw.print(prefix); pw.print("Base heartbeat: ");
pw.println(baseHeartbeat);
@@ -1564,6 +1576,13 @@
JobStatusDumpProto.TRACKING_TIME);
}
+ // Implicit constraints
+ final long icToken = proto.start(JobStatusDumpProto.IMPLICIT_CONSTRAINTS);
+ proto.write(JobStatusDumpProto.ImplicitConstraints.IS_NOT_DOZING, mReadyNotDozing);
+ proto.write(JobStatusDumpProto.ImplicitConstraints.IS_NOT_RESTRICTED_IN_BG,
+ mReadyNotRestrictedInBg);
+ proto.end(icToken);
+
if (changedAuthorities != null) {
for (int k = 0; k < changedAuthorities.size(); k++) {
proto.write(JobStatusDumpProto.CHANGED_AUTHORITIES, changedAuthorities.valueAt(k));
diff --git a/services/core/java/com/android/server/location/GnssLocationProvider.java b/services/core/java/com/android/server/location/GnssLocationProvider.java
index f074573..9e6e381 100644
--- a/services/core/java/com/android/server/location/GnssLocationProvider.java
+++ b/services/core/java/com/android/server/location/GnssLocationProvider.java
@@ -2075,8 +2075,8 @@
// note that this assumes the message will not be removed from the queue before
// it is handled (otherwise the wake lock would be leaked).
mWakeLock.acquire();
- if (Log.isLoggable(TAG, Log.INFO)) {
- Log.i(TAG, "WakeLock acquired by sendMessage(" + messageIdAsString(message) + ", " + arg
+ if (DEBUG) {
+ Log.d(TAG, "WakeLock acquired by sendMessage(" + messageIdAsString(message) + ", " + arg
+ ", " + obj + ")");
}
mHandler.obtainMessage(message, arg, 1, obj).sendToTarget();
@@ -2133,8 +2133,8 @@
if (msg.arg2 == 1) {
// wakelock was taken for this message, release it
mWakeLock.release();
- if (Log.isLoggable(TAG, Log.INFO)) {
- Log.i(TAG, "WakeLock released by handleMessage(" + messageIdAsString(message)
+ if (DEBUG) {
+ Log.d(TAG, "WakeLock released by handleMessage(" + messageIdAsString(message)
+ ", " + msg.arg1 + ", " + msg.obj + ")");
}
}
diff --git a/services/core/java/com/android/server/pm/PackageDexOptimizer.java b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
index 95c70d5..95d2154 100644
--- a/services/core/java/com/android/server/pm/PackageDexOptimizer.java
+++ b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
@@ -49,7 +49,7 @@
import dalvik.system.DexFile;
-import static android.content.pm.ApplicationInfo.HIDDEN_API_ENFORCEMENT_NONE;
+import static android.content.pm.ApplicationInfo.HIDDEN_API_ENFORCEMENT_DISABLED;
import static com.android.server.pm.Installer.DEXOPT_BOOTCOMPLETE;
import static com.android.server.pm.Installer.DEXOPT_DEBUGGABLE;
@@ -548,7 +548,7 @@
// Some apps are executed with restrictions on hidden API usage. If this app is one
// of them, pass a flag to dexopt to enable the same restrictions during compilation.
// TODO we should pass the actual flag value to dexopt, rather than assuming blacklist
- int hiddenApiFlag = info.getHiddenApiEnforcementPolicy() == HIDDEN_API_ENFORCEMENT_NONE
+ int hiddenApiFlag = info.getHiddenApiEnforcementPolicy() == HIDDEN_API_ENFORCEMENT_DISABLED
? 0
: DEXOPT_ENABLE_HIDDEN_API_CHECKS;
// Avoid generating CompactDex for modes that are latency critical.
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index ed5b33b6..77045fb 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -3203,9 +3203,8 @@
}
// The base directory for the package parser cache lives under /data/system/.
- final File cacheBaseDir = FileUtils.createDir(Environment.getDataSystemDirectory(),
- "package_cache");
- if (cacheBaseDir == null) {
+ final File cacheBaseDir = Environment.getPackageCacheDirectory();
+ if (!FileUtils.createDir(cacheBaseDir)) {
return null;
}
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index 13bb817..4c93441 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -2777,13 +2777,13 @@
// dataPath - path to package's data path
// seinfo - seinfo label for the app (assigned at install time)
// gids - supplementary gids this app launches with
- // profileableFromShellFlag - 0 or 1 if the package is profileable from shell.
//
// NOTE: We prefer not to expose all ApplicationInfo flags for now.
//
// DO NOT MODIFY THIS FORMAT UNLESS YOU CAN ALSO MODIFY ITS USERS
// FROM NATIVE CODE. AT THE MOMENT, LOOK AT THE FOLLOWING SOURCES:
- // system/core/libpackagelistparser
+ // frameworks/base/libs/packagelistparser
+ // system/core/run-as/run-as.c
//
sb.setLength(0);
sb.append(ai.packageName);
@@ -2803,8 +2803,6 @@
} else {
sb.append("none");
}
- sb.append(" ");
- sb.append(ai.isProfileableByShell() ? "1" : "0");
sb.append("\n");
writer.append(sb);
}
diff --git a/services/core/java/com/android/server/stats/StatsCompanionService.java b/services/core/java/com/android/server/stats/StatsCompanionService.java
index 70c86a1..cef484f 100644
--- a/services/core/java/com/android/server/stats/StatsCompanionService.java
+++ b/services/core/java/com/android/server/stats/StatsCompanionService.java
@@ -15,7 +15,13 @@
*/
package com.android.server.stats;
+import static android.os.Process.getPidsForCommands;
+import static android.os.Process.getUidForPid;
+
import static com.android.internal.util.Preconditions.checkNotNull;
+import static com.android.server.am.MemoryStatUtil.MEMORY_STAT_INTERESTING_NATIVE_PROCESSES;
+import static com.android.server.am.MemoryStatUtil.readCmdlineFromProcfs;
+import static com.android.server.am.MemoryStatUtil.readMemoryStatFromProcfs;
import android.annotation.Nullable;
import android.app.ActivityManagerInternal;
@@ -96,6 +102,7 @@
import com.android.server.LocalServices;
import com.android.server.SystemService;
import com.android.server.SystemServiceManager;
+import com.android.server.am.MemoryStatUtil.MemoryStat;
import com.android.server.storage.DiskStatsFileLogger;
import com.android.server.storage.DiskStatsLoggingService;
@@ -1012,17 +1019,23 @@
private void pullNativeProcessMemoryState(
int tagId, long elapsedNanos, long wallClockNanos,
List<StatsLogEventWrapper> pulledData) {
- List<ProcessMemoryState> processMemoryStates = LocalServices.getService(
- ActivityManagerInternal.class).getMemoryStateForNativeProcesses();
- for (ProcessMemoryState processMemoryState : processMemoryStates) {
+ int[] pids = getPidsForCommands(MEMORY_STAT_INTERESTING_NATIVE_PROCESSES);
+ for (int i = 0; i < pids.length; i++) {
+ int pid = pids[i];
+ MemoryStat memoryStat = readMemoryStatFromProcfs(pid);
+ if (memoryStat == null) {
+ continue;
+ }
+ int uid = getUidForPid(pid);
+ String processName = readCmdlineFromProcfs(pid);
StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
- e.writeInt(processMemoryState.uid);
- e.writeString(processMemoryState.processName);
- e.writeLong(processMemoryState.pgfault);
- e.writeLong(processMemoryState.pgmajfault);
- e.writeLong(processMemoryState.rssInBytes);
- e.writeLong(processMemoryState.rssHighWatermarkInBytes);
- e.writeLong(processMemoryState.startTimeNanos);
+ e.writeInt(uid);
+ e.writeString(processName);
+ e.writeLong(memoryStat.pgfault);
+ e.writeLong(memoryStat.pgmajfault);
+ e.writeLong(memoryStat.rssInBytes);
+ e.writeLong(memoryStat.rssHighWatermarkInBytes);
+ e.writeLong(memoryStat.startTimeNanos);
pulledData.add(e);
}
}
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
index 79eab6b..ec78560 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
@@ -140,13 +140,14 @@
switch (which) {
case 1:
what1 = what;
- return;
+ break;
case 2:
what2 = what;
- return;
+ break;
default:
Slog.w(TAG, "Can't set unsupported disable flag " + which
+ ": 0x" + Integer.toHexString(what));
+ break;
}
this.pkg = pkg;
}
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index b01d67d..767a6ef 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -2011,9 +2011,7 @@
mDisplay.getDisplayInfo(mDisplayInfo);
mDisplay.getMetrics(mDisplayMetrics);
- for (int i = mTaskStackContainers.getChildCount() - 1; i >= 0; --i) {
- mTaskStackContainers.getChildAt(i).updateDisplayInfo(null);
- }
+ onDisplayChanged(this);
}
void initializeDisplayBaseInfo() {
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 30eca89..c9800f8 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -237,7 +237,7 @@
super.onParentSet();
// Update task bounds if needed.
- updateDisplayInfo(getDisplayContent());
+ adjustBoundsForDisplayChangeIfNeeded(getDisplayContent());
if (getWindowConfiguration().windowsAreScaleable()) {
// We force windows out of SCALING_MODE_FREEZE so that we can continue to animate them
@@ -303,6 +303,7 @@
@Override
void onDisplayChanged(DisplayContent dc) {
updateSurfaceSize(dc);
+ adjustBoundsForDisplayChangeIfNeeded(dc);
super.onDisplayChanged(dc);
}
@@ -501,7 +502,7 @@
return mDragResizeMode;
}
- void updateDisplayInfo(final DisplayContent displayContent) {
+ private void adjustBoundsForDisplayChangeIfNeeded(final DisplayContent displayContent) {
if (displayContent == null) {
return;
}
diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java
index 5a9e259..3493111 100644
--- a/services/core/java/com/android/server/wm/TaskStack.java
+++ b/services/core/java/com/android/server/wm/TaskStack.java
@@ -418,32 +418,6 @@
getBounds(out);
}
- void updateDisplayInfo(Rect bounds) {
- if (mDisplayContent == null) {
- return;
- }
-
- for (int taskNdx = mChildren.size() - 1; taskNdx >= 0; --taskNdx) {
- mChildren.get(taskNdx).updateDisplayInfo(mDisplayContent);
- }
- if (bounds != null) {
- setBounds(bounds);
- return;
- } else if (matchParentBounds()) {
- setBounds(null);
- return;
- }
-
- mTmpRect2.set(getRawBounds());
- final int newRotation = mDisplayContent.getDisplayInfo().rotation;
- final int newDensity = mDisplayContent.getDisplayInfo().logicalDensityDpi;
- if (mRotation == newRotation && mDensity == newDensity) {
- setBounds(mTmpRect2);
- }
-
- // If the rotation or density didn't match, we'll update it in onConfigurationChanged.
- }
-
/**
* Updates the passed-in {@code inOutBounds} based on how it would change when this container's
* override configuration is applied to the specified {@code parentConfig} and
@@ -820,16 +794,24 @@
@Override
void onDisplayChanged(DisplayContent dc) {
- if (mDisplayContent != null) {
+ if (mDisplayContent != null && mDisplayContent != dc) {
throw new IllegalStateException("onDisplayChanged: Already attached");
}
+ final boolean movedToNewDisplay = mDisplayContent == null;
mDisplayContent = dc;
- updateBoundsForWindowModeChange();
- mAnimationBackgroundSurface = makeChildSurface(null).setColorLayer(true)
- .setName("animation background stackId=" + mStackId)
- .build();
+ if (movedToNewDisplay) {
+ updateBoundsForWindowModeChange();
+ } else {
+ updateBoundsForDisplayChanges();
+ }
+
+ if (mAnimationBackgroundSurface != null) {
+ mAnimationBackgroundSurface = makeChildSurface(null).setColorLayer(true)
+ .setName("animation background stackId=" + mStackId)
+ .build();
+ }
super.onDisplayChanged(dc);
}
@@ -845,10 +827,41 @@
}, true);
}
- updateDisplayInfo(bounds);
+ setBoundsForWindowModeChange(bounds);
updateSurfaceBounds();
}
+ private void setBoundsForWindowModeChange(Rect bounds) {
+ if (mDisplayContent == null) {
+ return;
+ }
+
+ if (bounds != null) {
+ setBounds(bounds);
+ return;
+ }
+
+ updateBoundsForDisplayChanges();
+ }
+
+ private void updateBoundsForDisplayChanges() {
+ // Avoid setting override bounds to bounds inherited from parent if there was no override
+ // bounds set.
+ if (matchParentBounds()) {
+ setBounds(null);
+ return;
+ }
+
+ mTmpRect2.set(getRawBounds());
+ final int newRotation = mDisplayContent.getDisplayInfo().rotation;
+ final int newDensity = mDisplayContent.getDisplayInfo().logicalDensityDpi;
+ if (mRotation == newRotation && mDensity == newDensity) {
+ setBounds(mTmpRect2);
+ }
+
+ // If the rotation or density didn't match, we'll update it in onConfigurationChanged.
+ }
+
private Rect calculateBoundsForWindowModeChange() {
final boolean inSplitScreenPrimary = inSplitScreenPrimaryWindowingMode();
final TaskStack splitScreenStack =
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index abc3826..266006d 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -42,8 +42,10 @@
import android.view.SurfaceControl;
import android.view.SurfaceControl.Builder;
import android.view.SurfaceSession;
+
import com.android.internal.util.ToBooleanFunction;
import com.android.server.wm.SurfaceAnimator.Animatable;
+
import java.io.PrintWriter;
import java.util.Comparator;
import java.util.LinkedList;
@@ -501,8 +503,10 @@
}
/**
- * Notify that the display this container is on has changed.
- * @param dc The new display this container is on.
+ * Notify that the display this container is on has changed. This could be either this container
+ * is moved to a new display, or some configurations on the display it is on changes.
+ *
+ * @param dc The display this container is on after changes.
*/
void onDisplayChanged(DisplayContent dc) {
for (int i = mChildren.size() - 1; i >= 0; --i) {
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index b88165e..a7542d7 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -491,6 +491,11 @@
*/
final boolean mIsWatch;
+ /**
+ * Whether this device has the telephony feature.
+ */
+ final boolean mHasTelephonyFeature;
+
private final CertificateMonitor mCertificateMonitor;
private final SecurityLogMonitor mSecurityLogMonitor;
@@ -2133,6 +2138,8 @@
mHasFeature = mInjector.hasFeature();
mIsWatch = mInjector.getPackageManager()
.hasSystemFeature(PackageManager.FEATURE_WATCH);
+ mHasTelephonyFeature = mInjector.getPackageManager()
+ .hasSystemFeature(PackageManager.FEATURE_TELEPHONY);
mBackgroundHandler = BackgroundThread.getHandler();
// Needed when mHasFeature == false, because it controls the certificate warning text.
@@ -12927,7 +12934,7 @@
@Override
public int addOverrideApn(@NonNull ComponentName who, @NonNull ApnSetting apnSetting) {
- if (!mHasFeature) {
+ if (!mHasFeature || !mHasTelephonyFeature) {
return -1;
}
Preconditions.checkNotNull(who, "ComponentName is null in addOverrideApn");
@@ -12956,7 +12963,7 @@
@Override
public boolean updateOverrideApn(@NonNull ComponentName who, int apnId,
@NonNull ApnSetting apnSetting) {
- if (!mHasFeature) {
+ if (!mHasFeature || !mHasTelephonyFeature) {
return false;
}
Preconditions.checkNotNull(who, "ComponentName is null in updateOverrideApn");
@@ -12978,7 +12985,7 @@
@Override
public boolean removeOverrideApn(@NonNull ComponentName who, int apnId) {
- if (!mHasFeature) {
+ if (!mHasFeature || !mHasTelephonyFeature) {
return false;
}
Preconditions.checkNotNull(who, "ComponentName is null in removeOverrideApn");
@@ -13004,7 +13011,7 @@
@Override
public List<ApnSetting> getOverrideApns(@NonNull ComponentName who) {
- if (!mHasFeature) {
+ if (!mHasFeature || !mHasTelephonyFeature) {
return Collections.emptyList();
}
Preconditions.checkNotNull(who, "ComponentName is null in getOverrideApns");
@@ -13040,7 +13047,7 @@
@Override
public void setOverrideApnsEnabled(@NonNull ComponentName who, boolean enabled) {
- if (!mHasFeature) {
+ if (!mHasFeature || !mHasTelephonyFeature) {
return;
}
Preconditions.checkNotNull(who, "ComponentName is null in setOverrideApnEnabled");
@@ -13063,7 +13070,7 @@
@Override
public boolean isOverrideApnEnabled(@NonNull ComponentName who) {
- if (!mHasFeature) {
+ if (!mHasFeature || !mHasTelephonyFeature) {
return false;
}
Preconditions.checkNotNull(who, "ComponentName is null in isOverrideApnEnabled");
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 18a79d8..73990f8 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -89,6 +89,7 @@
import com.android.server.hdmi.HdmiControlService;
import com.android.server.input.InputManagerService;
import com.android.server.inputmethod.InputMethodManagerService;
+import com.android.server.inputmethod.MultiClientInputMethodManagerService;
import com.android.server.job.JobSchedulerService;
import com.android.server.lights.LightsService;
import com.android.server.media.MediaResourceMonitorService;
@@ -1017,7 +1018,12 @@
// Bring up services needed for UI.
if (mFactoryTestMode != FactoryTest.FACTORY_TEST_LOW_LEVEL) {
traceBeginAndSlog("StartInputMethodManagerLifecycle");
- mSystemServiceManager.startService(InputMethodManagerService.Lifecycle.class);
+ if (MultiClientInputMethodManagerService.isConfiguredToUse()) {
+ mSystemServiceManager.startService(
+ MultiClientInputMethodManagerService.Lifecycle.class);
+ } else {
+ mSystemServiceManager.startService(InputMethodManagerService.Lifecycle.class);
+ }
traceEnd();
traceBeginAndSlog("StartAccessibilityManagerService");
diff --git a/services/tests/servicestests/src/com/android/server/StorageManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/StorageManagerServiceTest.java
index 69d8e25..ee42ce8 100644
--- a/services/tests/servicestests/src/com/android/server/StorageManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/StorageManagerServiceTest.java
@@ -21,11 +21,11 @@
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.when;
+import android.app.ActivityManagerInternal;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
-import android.os.UserHandle;
import android.os.UserManagerInternal;
import android.os.storage.StorageManagerInternal;
@@ -48,6 +48,7 @@
@Mock private PackageManager mPm;
@Mock private PackageManagerInternal mPmi;
@Mock private UserManagerInternal mUmi;
+ @Mock private ActivityManagerInternal mAmi;
private static final String PKG_GREY = "com.grey";
private static final String PKG_RED = "com.red";
@@ -56,6 +57,10 @@
private static final int UID_GREY = 10000;
private static final int UID_COLORS = 10001;
+ private static final int PID_GREY = 1111;
+ private static final int PID_RED = 2222;
+ private static final int PID_BLUE = 3333;
+
private static final String NAME_COLORS = "colors";
private static ApplicationInfo buildApplicationInfo(String packageName, int uid) {
@@ -75,6 +80,8 @@
LocalServices.addService(PackageManagerInternal.class, mPmi);
LocalServices.removeServiceForTest(UserManagerInternal.class);
LocalServices.addService(UserManagerInternal.class, mUmi);
+ LocalServices.removeServiceForTest(ActivityManagerInternal.class);
+ LocalServices.addService(ActivityManagerInternal.class, mAmi);
when(mContext.getPackageManager()).thenReturn(mPm);
@@ -87,37 +94,52 @@
when(mPmi.getPackagesForSharedUserId(eq(NAME_COLORS), anyInt()))
.thenReturn(new String[] { PKG_RED, PKG_BLUE });
+ when(mPm.getPackagesForUid(eq(UID_GREY))).thenReturn(new String[] { PKG_GREY });
+ when(mPm.getPackagesForUid(eq(UID_COLORS))).thenReturn(new String[] { PKG_RED, PKG_BLUE });
+
+ setIsAppStorageSandboxed(PID_BLUE, UID_COLORS, true);
+ setIsAppStorageSandboxed(PID_GREY, UID_GREY, true);
+ setIsAppStorageSandboxed(PID_RED, UID_COLORS, true);
+
mService = new StorageManagerService(mContext);
}
+ private void setIsAppStorageSandboxed(int pid, int uid, boolean sandboxed) {
+ when(mAmi.isAppStorageSandboxed(pid, uid)).thenReturn(sandboxed);
+ }
+
@Test
public void testNone() throws Exception {
assertTranslation(
"/dev/null",
- "/dev/null", PKG_GREY);
+ "/dev/null", PID_GREY, UID_GREY);
assertTranslation(
"/dev/null",
- "/dev/null", PKG_RED);
+ "/dev/null", PID_RED, UID_COLORS);
}
@Test
public void testPrimary() throws Exception {
assertTranslation(
"/storage/emulated/0/Android/sandbox/com.grey/foo.jpg",
- "/storage/emulated/0/foo.jpg", PKG_GREY);
+ "/storage/emulated/0/foo.jpg",
+ PID_GREY, UID_GREY);
assertTranslation(
"/storage/emulated/0/Android/sandbox/shared:colors/foo.jpg",
- "/storage/emulated/0/foo.jpg", PKG_RED);
+ "/storage/emulated/0/foo.jpg",
+ PID_RED, UID_COLORS);
}
@Test
public void testSecondary() throws Exception {
assertTranslation(
"/storage/0000-0000/Android/sandbox/com.grey/foo/bar.jpg",
- "/storage/0000-0000/foo/bar.jpg", PKG_GREY);
+ "/storage/0000-0000/foo/bar.jpg",
+ PID_GREY, UID_GREY);
assertTranslation(
"/storage/0000-0000/Android/sandbox/shared:colors/foo/bar.jpg",
- "/storage/0000-0000/foo/bar.jpg", PKG_RED);
+ "/storage/0000-0000/foo/bar.jpg",
+ PID_RED, UID_COLORS);
}
@Test
@@ -125,13 +147,15 @@
// Accessing their own paths goes straight through
assertTranslation(
"/storage/emulated/0/Android/data/com.grey/foo.jpg",
- "/storage/emulated/0/Android/data/com.grey/foo.jpg", PKG_GREY);
+ "/storage/emulated/0/Android/data/com.grey/foo.jpg",
+ PID_GREY, UID_GREY);
// Accessing other package paths goes into sandbox
assertTranslation(
"/storage/emulated/0/Android/sandbox/shared:colors/"
+ "Android/data/com.grey/foo.jpg",
- "/storage/emulated/0/Android/data/com.grey/foo.jpg", PKG_RED);
+ "/storage/emulated/0/Android/data/com.grey/foo.jpg",
+ PID_RED, UID_COLORS);
}
@Test
@@ -139,16 +163,19 @@
// Accessing their own paths goes straight through
assertTranslation(
"/storage/emulated/0/Android/data/com.red/foo.jpg",
- "/storage/emulated/0/Android/data/com.red/foo.jpg", PKG_RED);
+ "/storage/emulated/0/Android/data/com.red/foo.jpg",
+ PID_RED, UID_COLORS);
assertTranslation(
"/storage/emulated/0/Android/data/com.red/foo.jpg",
- "/storage/emulated/0/Android/data/com.red/foo.jpg", PKG_BLUE);
+ "/storage/emulated/0/Android/data/com.red/foo.jpg",
+ PID_BLUE, UID_COLORS);
// Accessing other package paths goes into sandbox
assertTranslation(
"/storage/emulated/0/Android/sandbox/com.grey/"
+ "Android/data/com.red/foo.jpg",
- "/storage/emulated/0/Android/data/com.red/foo.jpg", PKG_GREY);
+ "/storage/emulated/0/Android/data/com.red/foo.jpg",
+ PID_GREY, UID_GREY);
}
@Test
@@ -157,7 +184,7 @@
try {
mService.translateAppToSystem(
"/storage/emulated/0/../foo.jpg",
- PKG_GREY, UserHandle.USER_SYSTEM);
+ PID_GREY, UID_GREY);
fail();
} catch (SecurityException expected) {
}
@@ -166,7 +193,7 @@
try {
mService.translateSystemToApp(
"/storage/emulated/0/foo.jpg",
- PKG_GREY, UserHandle.USER_SYSTEM);
+ PID_GREY, UID_GREY);
fail();
} catch (SecurityException expected) {
}
@@ -175,17 +202,33 @@
try {
mService.translateSystemToApp(
"/storage/emulated/0/Android/sandbox/shared:colors/foo.jpg",
- PKG_GREY, UserHandle.USER_SYSTEM);
+ PID_GREY, UID_GREY);
fail();
} catch (SecurityException expected) {
}
}
- private void assertTranslation(String system, String sandbox, String packageName)
- throws Exception {
+ @Test
+ public void testPackageNotSandboxed() throws Exception {
+ setIsAppStorageSandboxed(PID_RED, UID_COLORS, false);
+
+ // Both app and system have the same view
+ assertTranslation(
+ "/storage/emulated/0/Android/data/com.red/foo.jpg",
+ "/storage/emulated/0/Android/data/com.red/foo.jpg",
+ PID_RED, UID_COLORS);
+
+ assertTranslation(
+ "/storage/emulated/0/Android/sandbox/com.grey/bar.jpg",
+ "/storage/emulated/0/Android/sandbox/com.grey/bar.jpg",
+ PID_RED, UID_COLORS);
+ }
+
+ private void assertTranslation(String system, String sandbox,
+ int pid, int uid) throws Exception {
assertEquals(system,
- mService.translateAppToSystem(sandbox, packageName, UserHandle.USER_SYSTEM));
+ mService.translateAppToSystem(sandbox, pid, uid));
assertEquals(sandbox,
- mService.translateSystemToApp(system, packageName, UserHandle.USER_SYSTEM));
+ mService.translateSystemToApp(system, pid, uid));
}
}
diff --git a/services/tests/servicestests/src/com/android/server/usage/AppTimeLimitControllerTests.java b/services/tests/servicestests/src/com/android/server/usage/AppTimeLimitControllerTests.java
index 793d6b0..8496a96 100644
--- a/services/tests/servicestests/src/com/android/server/usage/AppTimeLimitControllerTests.java
+++ b/services/tests/servicestests/src/com/android/server/usage/AppTimeLimitControllerTests.java
@@ -19,12 +19,13 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
import android.app.PendingIntent;
import android.os.HandlerThread;
import android.os.Looper;
-import androidx.test.filters.MediumTest;
+import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.After;
@@ -32,11 +33,13 @@
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.io.PrintWriter;
+import java.io.StringWriter;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
@RunWith(AndroidJUnit4.class)
-@MediumTest
+@LargeTest
public class AppTimeLimitControllerTests {
private static final String PKG_SOC1 = "package.soc1";
@@ -159,7 +162,6 @@
assertTrue("Observer wasn't added", hasUsageSessionObserver(UID, OBS_ID1));
addUsageSessionObserver(OBS_ID2, GROUP_GAME, TIME_30_MIN, TIME_1_MIN);
assertTrue("Observer wasn't added", hasUsageSessionObserver(UID, OBS_ID2));
- assertTrue("Observer wasn't added", hasUsageSessionObserver(UID, OBS_ID1));
}
/** Verify app usage observer is removed */
@@ -180,6 +182,42 @@
assertFalse("Observer wasn't removed", hasUsageSessionObserver(UID, OBS_ID1));
}
+ /** Verify nothing happens when a nonexistent app usage observer is removed */
+ @Test
+ public void testAppUsageObserver_RemoveMissingObserver() {
+ assertFalse("Observer should not exist", hasAppUsageObserver(UID, OBS_ID1));
+ try {
+ mController.removeAppUsageObserver(UID, OBS_ID1, USER_ID);
+ } catch (Exception e) {
+ StringWriter sw = new StringWriter();
+ sw.write("Hit exception trying to remove nonexistent observer:\n");
+ sw.write(e.toString());
+ PrintWriter pw = new PrintWriter(sw);
+ e.printStackTrace(pw);
+ sw.write("\nTest Failed!");
+ fail(sw.toString());
+ }
+ assertFalse("Observer should not exist", hasAppUsageObserver(UID, OBS_ID1));
+ }
+
+ /** Verify nothing happens when a nonexistent usage session observer is removed */
+ @Test
+ public void testUsageSessionObserver_RemoveMissingObserver() {
+ assertFalse("Observer should not exist", hasUsageSessionObserver(UID, OBS_ID1));
+ try {
+ mController.removeUsageSessionObserver(UID, OBS_ID1, USER_ID);
+ } catch (Exception e) {
+ StringWriter sw = new StringWriter();
+ sw.write("Hit exception trying to remove nonexistent observer:");
+ sw.write(e.toString());
+ PrintWriter pw = new PrintWriter(sw);
+ e.printStackTrace(pw);
+ sw.write("\nTest Failed!");
+ fail(sw.toString());
+ }
+ assertFalse("Observer should not exist", hasUsageSessionObserver(UID, OBS_ID1));
+ }
+
/** Re-adding an observer should result in only one copy */
@Test
public void testAppUsageObserver_ObserverReAdd() {
diff --git a/services/tests/servicestests/src/com/android/server/usage/UsageStatsDatabaseTest.java b/services/tests/servicestests/src/com/android/server/usage/UsageStatsDatabaseTest.java
index 473682d..bf7f53d 100644
--- a/services/tests/servicestests/src/com/android/server/usage/UsageStatsDatabaseTest.java
+++ b/services/tests/servicestests/src/com/android/server/usage/UsageStatsDatabaseTest.java
@@ -16,13 +16,12 @@
package com.android.server.usage;
-import static junit.framework.TestCase.assertNull;
import static junit.framework.TestCase.fail;
import static org.testng.Assert.assertEquals;
import android.app.usage.EventList;
-import android.app.usage.UsageEvents;
+import android.app.usage.UsageEvents.Event;
import android.app.usage.UsageStats;
import android.app.usage.UsageStatsManager;
import android.content.Context;
@@ -112,6 +111,8 @@
long time = System.currentTimeMillis() - (numberOfEvents*timeProgression);
mIntervalStats = new IntervalStats();
+ mIntervalStats.majorVersion = 7;
+ mIntervalStats.minorVersion = 8;
mIntervalStats.beginTime = time;
mIntervalStats.interactiveTracker.count = 2;
mIntervalStats.interactiveTracker.duration = 111111;
@@ -127,41 +128,39 @@
}
for (int i = 0; i < numberOfEvents; i++) {
- UsageEvents.Event event = new UsageEvents.Event();
+ Event event = new Event();
final int packageInt = ((i / 3) % 7);
event.mPackage = "fake.package.name" + packageInt; //clusters of 3 events from 7 "apps"
if (packageInt == 3) {
// Third app is an instant app
- event.mFlags |= UsageEvents.Event.FLAG_IS_PACKAGE_INSTANT_APP;
- } else if (packageInt == 2 || packageInt == 4) {
- event.mClass = ".fake.class.name" + i % 11;
+ event.mFlags |= Event.FLAG_IS_PACKAGE_INSTANT_APP;
}
-
+ event.mClass = ".fake.class.name" + i % 11;
event.mTimeStamp = time;
event.mEventType = i % 19; //"random" event type
switch (event.mEventType) {
- case UsageEvents.Event.CONFIGURATION_CHANGE:
+ case Event.CONFIGURATION_CHANGE:
//empty config,
event.mConfiguration = new Configuration();
break;
- case UsageEvents.Event.SHORTCUT_INVOCATION:
+ case Event.SHORTCUT_INVOCATION:
//"random" shortcut
event.mShortcutId = "shortcut" + (i % 8);
break;
- case UsageEvents.Event.STANDBY_BUCKET_CHANGED:
+ case Event.STANDBY_BUCKET_CHANGED:
//"random" bucket and reason
event.mBucketAndReason = (((i % 5 + 1) * 10) << 16) & (i % 5 + 1) << 8;
break;
- case UsageEvents.Event.NOTIFICATION_INTERRUPTION:
+ case Event.NOTIFICATION_INTERRUPTION:
//"random" channel
event.mNotificationChannelId = "channel" + (i % 5);
break;
}
mIntervalStats.events.insert(event);
- mIntervalStats.update(event.mPackage, event.mTimeStamp, event.mEventType);
+ mIntervalStats.update(event.mPackage, event.mClass, event.mTimeStamp, event.mEventType);
time += timeProgression; // Arbitrary progression of time
}
@@ -221,29 +220,30 @@
// mEndTimeStamp is based on the enclosing IntervalStats, don't bother checking
assertEquals(us1.mLastTimeUsed, us2.mLastTimeUsed);
assertEquals(us1.mTotalTimeInForeground, us2.mTotalTimeInForeground);
+ assertEquals(us1.mLastTimeForegroundServiceUsed, us2.mLastTimeForegroundServiceUsed);
+ assertEquals(us1.mTotalTimeForegroundServiceUsed, us2.mTotalTimeForegroundServiceUsed);
// mLaunchCount not persisted, so skipped
assertEquals(us1.mAppLaunchCount, us2.mAppLaunchCount);
- assertEquals(us1.mLastEvent, us2.mLastEvent);
assertEquals(us1.mChooserCounts, us2.mChooserCounts);
}
- void compareUsageEvent(UsageEvents.Event e1, UsageEvents.Event e2, int debugId) {
+ void compareUsageEvent(Event e1, Event e2, int debugId) {
assertEquals(e1.mPackage, e2.mPackage, "Usage event " + debugId);
assertEquals(e1.mClass, e2.mClass, "Usage event " + debugId);
assertEquals(e1.mTimeStamp, e2.mTimeStamp, "Usage event " + debugId);
assertEquals(e1.mEventType, e2.mEventType, "Usage event " + debugId);
switch (e1.mEventType) {
- case UsageEvents.Event.CONFIGURATION_CHANGE:
+ case Event.CONFIGURATION_CHANGE:
assertEquals(e1.mConfiguration, e2.mConfiguration,
"Usage event " + debugId + e2.mConfiguration.toString());
break;
- case UsageEvents.Event.SHORTCUT_INVOCATION:
+ case Event.SHORTCUT_INVOCATION:
assertEquals(e1.mShortcutId, e2.mShortcutId, "Usage event " + debugId);
break;
- case UsageEvents.Event.STANDBY_BUCKET_CHANGED:
+ case Event.STANDBY_BUCKET_CHANGED:
assertEquals(e1.mBucketAndReason, e2.mBucketAndReason, "Usage event " + debugId);
break;
- case UsageEvents.Event.NOTIFICATION_INTERRUPTION:
+ case Event.NOTIFICATION_INTERRUPTION:
assertEquals(e1.mNotificationChannelId, e2.mNotificationChannelId,
"Usage event " + debugId);
break;
@@ -252,6 +252,8 @@
}
void compareIntervalStats(IntervalStats stats1, IntervalStats stats2) {
+ assertEquals(stats1.majorVersion, stats2.majorVersion);
+ assertEquals(stats1.minorVersion, stats2.minorVersion);
assertEquals(stats1.beginTime, stats2.beginTime);
assertEquals(stats1.endTime, stats2.endTime);
assertEquals(stats1.interactiveTracker.count, stats2.interactiveTracker.count);
diff --git a/services/usage/java/com/android/server/usage/AppStandbyController.java b/services/usage/java/com/android/server/usage/AppStandbyController.java
index 4f573a4..152831f 100644
--- a/services/usage/java/com/android/server/usage/AppStandbyController.java
+++ b/services/usage/java/com/android/server/usage/AppStandbyController.java
@@ -27,6 +27,8 @@
import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_EXEMPTED_SYNC_SCHEDULED_DOZE;
import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_EXEMPTED_SYNC_SCHEDULED_NON_DOZE;
import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_EXEMPTED_SYNC_START;
+import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_FOREGROUND_SERVICE_START;
+import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_FOREGROUND_SERVICE_STOP;
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;
@@ -844,6 +846,8 @@
// Inform listeners if necessary
if ((event.mEventType == UsageEvents.Event.MOVE_TO_FOREGROUND
|| event.mEventType == UsageEvents.Event.MOVE_TO_BACKGROUND
+ || event.mEventType == UsageEvents.Event.FOREGROUND_SERVICE_START
+ || event.mEventType == UsageEvents.Event.FOREGROUND_SERVICE_STOP
|| event.mEventType == UsageEvents.Event.SYSTEM_INTERACTION
|| event.mEventType == UsageEvents.Event.USER_INTERACTION
|| event.mEventType == UsageEvents.Event.NOTIFICATION_SEEN
@@ -896,6 +900,10 @@
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.FOREGROUND_SERVICE_START:
+ return REASON_SUB_USAGE_FOREGROUND_SERVICE_START;
+ case UsageEvents.Event.FOREGROUND_SERVICE_STOP:
+ return REASON_SUB_USAGE_FOREGROUND_SERVICE_STOP;
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;
diff --git a/services/usage/java/com/android/server/usage/AppTimeLimitController.java b/services/usage/java/com/android/server/usage/AppTimeLimitController.java
index eaaf9b2..8e1ede1 100644
--- a/services/usage/java/com/android/server/usage/AppTimeLimitController.java
+++ b/services/usage/java/com/android/server/usage/AppTimeLimitController.java
@@ -118,9 +118,14 @@
void removeUsageGroup(UsageGroup group) {
final int size = group.mObserved.length;
for (int i = 0; i < size; i++) {
- final ArrayList<UsageGroup> list = observedMap.get(group.mObserved[i]);
+ final String observed = group.mObserved[i];
+ final ArrayList<UsageGroup> list = observedMap.get(observed);
if (list != null) {
list.remove(group);
+ if (list.isEmpty()) {
+ // No more observers for this observed entity, remove from map
+ observedMap.remove(observed);
+ }
}
}
}
@@ -137,7 +142,7 @@
}
pw.println();
pw.print(" Observed Entities:");
- final int nEntities = currentlyActive.size();
+ final int nEntities = observedMap.size();
for (int i = 0; i < nEntities; i++) {
pw.print(observedMap.keyAt(i));
pw.print(", ");
@@ -183,7 +188,7 @@
pw.println();
}
pw.println(" Session Usage Groups:");
- final int nSessionUsageGroups = appUsageGroups.size();
+ final int nSessionUsageGroups = sessionUsageGroups.size();
for (int i = 0; i < nSessionUsageGroups; i++) {
sessionUsageGroups.valueAt(i).dump(pw);
pw.println();
@@ -616,7 +621,7 @@
AppUsageGroup group = observerApp.appUsageGroups.get(observerId);
if (group != null) {
// Remove previous app usage group associated with observerId
- observerApp.appUsageGroups.get(observerId).remove();
+ group.remove();
}
final int observerIdCount = observerApp.appUsageGroups.size();
@@ -646,8 +651,12 @@
*/
public void removeAppUsageObserver(int requestingUid, int observerId, @UserIdInt int userId) {
synchronized (mLock) {
- ObserverAppData observerApp = getOrCreateObserverAppDataLocked(requestingUid);
- observerApp.appUsageGroups.get(observerId).remove();
+ final ObserverAppData observerApp = getOrCreateObserverAppDataLocked(requestingUid);
+ final AppUsageGroup group = observerApp.appUsageGroups.get(observerId);
+ if (group != null) {
+ // Remove previous app usage group associated with observerId
+ group.remove();
+ }
}
}
@@ -668,8 +677,8 @@
ObserverAppData observerApp = getOrCreateObserverAppDataLocked(requestingUid);
SessionUsageGroup group = observerApp.sessionUsageGroups.get(observerId);
if (group != null) {
- // Remove previous app usage group associated with observerId
- observerApp.sessionUsageGroups.get(observerId).remove();
+ // Remove previous session usage group associated with observerId
+ group.remove();
}
final int observerIdCount = observerApp.sessionUsageGroups.size();
@@ -696,8 +705,12 @@
public void removeUsageSessionObserver(int requestingUid, int observerId,
@UserIdInt int userId) {
synchronized (mLock) {
- ObserverAppData observerApp = getOrCreateObserverAppDataLocked(requestingUid);
- observerApp.sessionUsageGroups.get(observerId).remove();
+ final ObserverAppData observerApp = getOrCreateObserverAppDataLocked(requestingUid);
+ final SessionUsageGroup group = observerApp.sessionUsageGroups.get(observerId);
+ if (group != null) {
+ // Remove previous app usage group associated with observerId
+ group.remove();
+ }
}
}
diff --git a/services/usage/java/com/android/server/usage/IntervalStats.java b/services/usage/java/com/android/server/usage/IntervalStats.java
index db9972f..8405267 100644
--- a/services/usage/java/com/android/server/usage/IntervalStats.java
+++ b/services/usage/java/com/android/server/usage/IntervalStats.java
@@ -18,7 +18,6 @@
import android.app.usage.ConfigurationStats;
import android.app.usage.EventList;
import android.app.usage.EventStats;
-import android.app.usage.TimeSparseArray;
import android.app.usage.UsageEvents;
import android.app.usage.UsageStats;
import android.content.res.Configuration;
@@ -26,12 +25,16 @@
import android.util.ArraySet;
import android.util.proto.ProtoInputStream;
+import com.android.internal.annotations.VisibleForTesting;
+
import java.io.IOException;
import java.util.List;
-import com.android.internal.annotations.VisibleForTesting;
-
public class IntervalStats {
+ public static final int CURRENT_MAJOR_VERSION = 1;
+ public static final int CURRENT_MINOR_VERSION = 1;
+ public int majorVersion = CURRENT_MAJOR_VERSION;
+ public int minorVersion = CURRENT_MINOR_VERSION;
public long beginTime;
public long endTime;
public long lastTimeSaved;
@@ -219,8 +222,12 @@
switch (eventType) {
case UsageEvents.Event.MOVE_TO_FOREGROUND:
case UsageEvents.Event.MOVE_TO_BACKGROUND:
+ case UsageEvents.Event.FOREGROUND_SERVICE_START:
+ case UsageEvents.Event.FOREGROUND_SERVICE_STOP:
case UsageEvents.Event.END_OF_DAY:
+ case UsageEvents.Event.ROLLOVER_FOREGROUND_SERVICE:
case UsageEvents.Event.CONTINUE_PREVIOUS_DAY:
+ case UsageEvents.Event.CONTINUING_FOREGROUND_SERVICE:
return true;
}
return false;
@@ -239,32 +246,9 @@
* @hide
*/
@VisibleForTesting
- public void update(String packageName, long timeStamp, int eventType) {
+ public void update(String packageName, String className, long timeStamp, int eventType) {
UsageStats usageStats = getOrCreateUsageStats(packageName);
-
- // TODO(adamlesinski): Ensure that we recover from incorrect event sequences
- // like double MOVE_TO_BACKGROUND, etc.
- if (eventType == UsageEvents.Event.MOVE_TO_BACKGROUND ||
- eventType == UsageEvents.Event.END_OF_DAY) {
- if (usageStats.mLastEvent == UsageEvents.Event.MOVE_TO_FOREGROUND ||
- usageStats.mLastEvent == UsageEvents.Event.CONTINUE_PREVIOUS_DAY) {
- usageStats.mTotalTimeInForeground += timeStamp - usageStats.mLastTimeUsed;
- }
- }
-
- if (isStatefulEvent(eventType)) {
- usageStats.mLastEvent = eventType;
- }
-
- if (isUserVisibleEvent(eventType)) {
- usageStats.mLastTimeUsed = timeStamp;
- }
- usageStats.mEndTimeStamp = timeStamp;
-
- if (eventType == UsageEvents.Event.MOVE_TO_FOREGROUND) {
- usageStats.mLaunchCount += 1;
- }
-
+ usageStats.update(className, timeStamp, eventType);
endTime = timeStamp;
}
@@ -372,4 +356,19 @@
}
return mStringCache.valueAt(index);
}
+
+ /**
+ * When an IntervalStats object is deserialized, if the object's version number
+ * is lower than current version number, optionally perform a upgrade.
+ */
+ void upgradeIfNeeded() {
+ // We only uprade on majorVersion change, no need to upgrade on minorVersion change.
+ if (!(majorVersion < CURRENT_MAJOR_VERSION)) {
+ return;
+ }
+ /*
+ Optional upgrade code here.
+ */
+ majorVersion = CURRENT_MAJOR_VERSION;
+ }
}
diff --git a/services/usage/java/com/android/server/usage/UsageStatsProto.java b/services/usage/java/com/android/server/usage/UsageStatsProto.java
index 30d303f..8e1c060 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsProto.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsProto.java
@@ -21,13 +21,12 @@
import android.app.usage.UsageStats;
import android.content.res.Configuration;
import android.util.ArrayMap;
-
import android.util.Slog;
import android.util.proto.ProtoInputStream;
import android.util.proto.ProtoOutputStream;
-import java.io.InputStream;
import java.io.IOException;
+import java.io.InputStream;
import java.io.OutputStream;
import java.net.ProtocolException;
import java.util.ArrayList;
@@ -105,6 +104,7 @@
stats = tempPackageIndex;
break;
case (int) IntervalStatsProto.UsageStats.LAST_TIME_ACTIVE_MS:
+ // Time attributes stored is an offset of the beginTime.
stats.mLastTimeUsed = statsOut.beginTime + proto.readLong(
IntervalStatsProto.UsageStats.LAST_TIME_ACTIVE_MS);
break;
@@ -113,7 +113,8 @@
IntervalStatsProto.UsageStats.TOTAL_TIME_ACTIVE_MS);
break;
case (int) IntervalStatsProto.UsageStats.LAST_EVENT:
- stats.mLastEvent = proto.readInt(IntervalStatsProto.UsageStats.LAST_EVENT);
+ stats.mLastEvent =
+ proto.readInt(IntervalStatsProto.UsageStats.LAST_EVENT);
break;
case (int) IntervalStatsProto.UsageStats.APP_LAUNCH_COUNT:
stats.mAppLaunchCount = proto.readInt(
@@ -125,6 +126,15 @@
loadChooserCounts(proto, stats);
proto.end(chooserToken);
break;
+ case (int) IntervalStatsProto.UsageStats.LAST_TIME_SERVICE_USED_MS:
+ // Time attributes stored is an offset of the beginTime.
+ stats.mLastTimeForegroundServiceUsed = statsOut.beginTime + proto.readLong(
+ IntervalStatsProto.UsageStats.LAST_TIME_SERVICE_USED_MS);
+ break;
+ case (int) IntervalStatsProto.UsageStats.TOTAL_TIME_SERVICE_USED_MS:
+ stats.mTotalTimeForegroundServiceUsed = proto.readLong(
+ IntervalStatsProto.UsageStats.TOTAL_TIME_SERVICE_USED_MS);
+ break;
}
}
if (stats.mLastTimeUsed == 0) {
@@ -315,11 +325,18 @@
+ ") not found in IntervalStats string cache");
proto.write(IntervalStatsProto.UsageStats.PACKAGE, usageStats.mPackageName);
}
+ // Time attributes stored as an offset of the beginTime.
proto.write(IntervalStatsProto.UsageStats.LAST_TIME_ACTIVE_MS,
usageStats.mLastTimeUsed - stats.beginTime);
proto.write(IntervalStatsProto.UsageStats.TOTAL_TIME_ACTIVE_MS,
usageStats.mTotalTimeInForeground);
- proto.write(IntervalStatsProto.UsageStats.LAST_EVENT, usageStats.mLastEvent);
+ proto.write(IntervalStatsProto.UsageStats.LAST_EVENT,
+ usageStats.mLastEvent);
+ // Time attributes stored as an offset of the beginTime.
+ proto.write(IntervalStatsProto.UsageStats.LAST_TIME_SERVICE_USED_MS,
+ usageStats.mLastTimeForegroundServiceUsed - stats.beginTime);
+ proto.write(IntervalStatsProto.UsageStats.TOTAL_TIME_SERVICE_USED_MS,
+ usageStats.mTotalTimeForegroundServiceUsed);
proto.write(IntervalStatsProto.UsageStats.APP_LAUNCH_COUNT, usageStats.mAppLaunchCount);
writeChooserCounts(proto, usageStats);
proto.end(token);
@@ -471,6 +488,14 @@
statsOut.endTime = statsOut.beginTime + proto.readLong(
IntervalStatsProto.END_TIME_MS);
break;
+ case (int) IntervalStatsProto.MAJOR_VERSION:
+ statsOut.majorVersion = proto.readInt(
+ IntervalStatsProto.MAJOR_VERSION);
+ break;
+ case (int) IntervalStatsProto.MINOR_VERSION:
+ statsOut.minorVersion = proto.readInt(
+ IntervalStatsProto.MINOR_VERSION);
+ break;
case (int) IntervalStatsProto.INTERACTIVE:
loadCountAndTime(proto, IntervalStatsProto.INTERACTIVE,
statsOut.interactiveTracker);
@@ -505,6 +530,7 @@
// endTime not assigned, assume default value of 0 plus beginTime
statsOut.endTime = statsOut.beginTime;
}
+ statsOut.upgradeIfNeeded();
return;
}
}
@@ -519,6 +545,8 @@
public static void write(OutputStream out, IntervalStats stats) throws IOException {
final ProtoOutputStream proto = new ProtoOutputStream(out);
proto.write(IntervalStatsProto.END_TIME_MS, stats.endTime - stats.beginTime);
+ proto.write(IntervalStatsProto.MAJOR_VERSION, stats.majorVersion);
+ proto.write(IntervalStatsProto.MINOR_VERSION, stats.minorVersion);
// String pool should be written before the rest of the usage stats
writeStringPool(proto, stats);
diff --git a/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java b/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java
index a68f9d3..d940620 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java
@@ -15,19 +15,18 @@
*/
package com.android.server.usage;
+import android.app.usage.ConfigurationStats;
+import android.app.usage.UsageEvents;
+import android.app.usage.UsageStats;
+import android.content.res.Configuration;
+import android.util.ArrayMap;
+
import com.android.internal.util.XmlUtils;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlSerializer;
-import android.app.usage.ConfigurationStats;
-import android.app.usage.EventList;
-import android.app.usage.UsageEvents;
-import android.app.usage.UsageStats;
-import android.content.res.Configuration;
-import android.util.ArrayMap;
-
import java.io.IOException;
import java.net.ProtocolException;
@@ -61,6 +60,7 @@
private static final String FLAGS_ATTR = "flags";
private static final String CLASS_ATTR = "class";
private static final String TOTAL_TIME_ACTIVE_ATTR = "timeActive";
+ private static final String TOTAL_TIME_SERVICE_USED_ATTR = "timeServiceUsed";
private static final String COUNT_ATTR = "count";
private static final String ACTIVE_ATTR = "active";
private static final String LAST_EVENT_ATTR = "lastEvent";
@@ -69,9 +69,12 @@
private static final String STANDBY_BUCKET_ATTR = "standbyBucket";
private static final String APP_LAUNCH_COUNT_ATTR = "appLaunchCount";
private static final String NOTIFICATION_CHANNEL_ATTR = "notificationChannel";
+ private static final String MAJOR_VERSION_ATTR = "majorVersion";
+ private static final String MINOR_VERSION_ATTR = "minorVersion";
// Time attributes stored as an offset of the beginTime.
private static final String LAST_TIME_ACTIVE_ATTR = "lastTimeActive";
+ private static final String LAST_TIME_SERVICE_USED_ATTR = "lastTimeServiceUsed";
private static final String END_TIME_ATTR = "endTime";
private static final String TIME_ATTR = "time";
@@ -86,9 +89,14 @@
// Apply the offset to the beginTime to find the absolute time.
stats.mLastTimeUsed = statsOut.beginTime + XmlUtils.readLongAttribute(
parser, LAST_TIME_ACTIVE_ATTR);
+ stats.mLastTimeForegroundServiceUsed = statsOut.beginTime + XmlUtils.readLongAttribute(
+ parser, LAST_TIME_SERVICE_USED_ATTR);
stats.mTotalTimeInForeground = XmlUtils.readLongAttribute(parser, TOTAL_TIME_ACTIVE_ATTR);
+ stats.mTotalTimeForegroundServiceUsed = XmlUtils.readLongAttribute(parser,
+ TOTAL_TIME_SERVICE_USED_ATTR);
stats.mLastEvent = XmlUtils.readIntAttribute(parser, LAST_EVENT_ATTR);
- stats.mAppLaunchCount = XmlUtils.readIntAttribute(parser, APP_LAUNCH_COUNT_ATTR, 0);
+ stats.mAppLaunchCount = XmlUtils.readIntAttribute(parser, APP_LAUNCH_COUNT_ATTR,
+ 0);
int eventCode;
while ((eventCode = parser.next()) != XmlPullParser.END_DOCUMENT) {
final String tag = parser.getName();
@@ -206,9 +214,12 @@
// Write the time offset.
XmlUtils.writeLongAttribute(xml, LAST_TIME_ACTIVE_ATTR,
usageStats.mLastTimeUsed - stats.beginTime);
-
+ XmlUtils.writeLongAttribute(xml, LAST_TIME_SERVICE_USED_ATTR,
+ usageStats.mLastTimeForegroundServiceUsed - stats.beginTime);
XmlUtils.writeStringAttribute(xml, PACKAGE_ATTR, usageStats.mPackageName);
XmlUtils.writeLongAttribute(xml, TOTAL_TIME_ACTIVE_ATTR, usageStats.mTotalTimeInForeground);
+ XmlUtils.writeLongAttribute(xml, TOTAL_TIME_SERVICE_USED_ATTR,
+ usageStats.mTotalTimeForegroundServiceUsed);
XmlUtils.writeIntAttribute(xml, LAST_EVENT_ATTR, usageStats.mLastEvent);
if (usageStats.mAppLaunchCount > 0) {
XmlUtils.writeIntAttribute(xml, APP_LAUNCH_COUNT_ATTR, usageStats.mAppLaunchCount);
@@ -339,6 +350,8 @@
}
statsOut.endTime = statsOut.beginTime + XmlUtils.readLongAttribute(parser, END_TIME_ATTR);
+ statsOut.majorVersion = XmlUtils.readIntAttribute(parser, MAJOR_VERSION_ATTR);
+ statsOut.minorVersion = XmlUtils.readIntAttribute(parser, MINOR_VERSION_ATTR);
int eventCode;
int outerDepth = parser.getDepth();
@@ -391,6 +404,8 @@
*/
public static void write(XmlSerializer xml, IntervalStats stats) throws IOException {
XmlUtils.writeLongAttribute(xml, END_TIME_ATTR, stats.endTime - stats.beginTime);
+ XmlUtils.writeIntAttribute(xml, MAJOR_VERSION_ATTR, stats.majorVersion);
+ XmlUtils.writeIntAttribute(xml, MINOR_VERSION_ATTR, stats.minorVersion);
writeCountAndTime(xml, INTERACTIVE_TAG, stats.interactiveTracker.count,
stats.interactiveTracker.duration);
diff --git a/services/usage/java/com/android/server/usage/UserUsageStatsService.java b/services/usage/java/com/android/server/usage/UserUsageStatsService.java
index 1a8aba0..32875da 100644
--- a/services/usage/java/com/android/server/usage/UserUsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UserUsageStatsService.java
@@ -22,9 +22,9 @@
import android.app.usage.UsageEvents;
import android.app.usage.UsageStats;
import android.app.usage.UsageStatsManager;
+import android.content.Context;
import android.content.res.Configuration;
import android.os.SystemClock;
-import android.content.Context;
import android.text.format.DateUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
@@ -129,11 +129,17 @@
for (IntervalStats stat : mCurrentStats) {
final int pkgCount = stat.packageStats.size();
for (int i = 0; i < pkgCount; i++) {
- UsageStats pkgStats = stat.packageStats.valueAt(i);
- if (pkgStats.mLastEvent == UsageEvents.Event.MOVE_TO_FOREGROUND ||
- pkgStats.mLastEvent == UsageEvents.Event.CONTINUE_PREVIOUS_DAY) {
- stat.update(pkgStats.mPackageName, stat.lastTimeSaved,
- UsageEvents.Event.END_OF_DAY);
+ final UsageStats pkgStats = stat.packageStats.valueAt(i);
+ if (!pkgStats.mLastForegroundActivityEventMap.isEmpty()
+ || !pkgStats.mLastForegroundServiceEventMap.isEmpty()) {
+ if (!pkgStats.mLastForegroundActivityEventMap.isEmpty()) {
+ stat.update(pkgStats.mPackageName, null, stat.lastTimeSaved,
+ UsageEvents.Event.END_OF_DAY);
+ }
+ if (!pkgStats.mLastForegroundServiceEventMap.isEmpty()) {
+ stat.update(pkgStats.mPackageName, null , stat.lastTimeSaved,
+ UsageEvents.Event.ROLLOVER_FOREGROUND_SERVICE);
+ }
notifyStatsChanged();
}
}
@@ -218,7 +224,8 @@
stats.updateKeyguardHidden(event.mTimeStamp);
} break;
default: {
- stats.update(event.mPackage, event.mTimeStamp, event.mEventType);
+ stats.update(event.mPackage, event.getClassName(),
+ event.mTimeStamp, event.mEventType);
if (incrementAppLaunch) {
stats.incrementAppLaunchCount(event.mPackage);
}
@@ -481,25 +488,43 @@
final long startTime = SystemClock.elapsedRealtime();
Slog.i(TAG, mLogPrefix + "Rolling over usage stats");
- // Finish any ongoing events with an END_OF_DAY event. Make a note of which components
- // need a new CONTINUE_PREVIOUS_DAY entry.
+ // Finish any ongoing events with an END_OF_DAY or ROLLOVER_FOREGROUND_SERVICE event.
+ // Make a note of which components need a new CONTINUE_PREVIOUS_DAY or
+ // CONTINUING_FOREGROUND_SERVICE entry.
final Configuration previousConfig =
mCurrentStats[UsageStatsManager.INTERVAL_DAILY].activeConfiguration;
ArraySet<String> continuePreviousDay = new ArraySet<>();
+ ArrayMap<String, ArrayMap<String, Integer>> continuePreviousDayForegroundActivity =
+ new ArrayMap<>();
+ ArrayMap<String, ArrayMap<String, Integer>> continuePreviousDayForegroundService =
+ new ArrayMap<>();
for (IntervalStats stat : mCurrentStats) {
final int pkgCount = stat.packageStats.size();
for (int i = 0; i < pkgCount; i++) {
- UsageStats pkgStats = stat.packageStats.valueAt(i);
- if (pkgStats.mLastEvent == UsageEvents.Event.MOVE_TO_FOREGROUND ||
- pkgStats.mLastEvent == UsageEvents.Event.CONTINUE_PREVIOUS_DAY) {
+ final UsageStats pkgStats = stat.packageStats.valueAt(i);
+ if (!pkgStats.mLastForegroundActivityEventMap.isEmpty()
+ || !pkgStats.mLastForegroundServiceEventMap.isEmpty()) {
+ if (!pkgStats.mLastForegroundActivityEventMap.isEmpty()) {
+ continuePreviousDayForegroundActivity.put(pkgStats.mPackageName,
+ pkgStats.mLastForegroundActivityEventMap);
+ stat.update(pkgStats.mPackageName, null,
+ mDailyExpiryDate.getTimeInMillis() - 1,
+ UsageEvents.Event.END_OF_DAY);
+ }
+ if (!pkgStats.mLastForegroundServiceEventMap.isEmpty()) {
+ continuePreviousDayForegroundService.put(pkgStats.mPackageName,
+ pkgStats.mLastForegroundServiceEventMap);
+ stat.update(pkgStats.mPackageName, null,
+ mDailyExpiryDate.getTimeInMillis() - 1,
+ UsageEvents.Event.ROLLOVER_FOREGROUND_SERVICE);
+ }
continuePreviousDay.add(pkgStats.mPackageName);
- stat.update(pkgStats.mPackageName, mDailyExpiryDate.getTimeInMillis() - 1,
- UsageEvents.Event.END_OF_DAY);
notifyStatsChanged();
}
}
- stat.updateConfigurationStats(null, mDailyExpiryDate.getTimeInMillis() - 1);
+ stat.updateConfigurationStats(null,
+ mDailyExpiryDate.getTimeInMillis() - 1);
stat.commitTime(mDailyExpiryDate.getTimeInMillis() - 1);
}
@@ -509,10 +534,27 @@
final int continueCount = continuePreviousDay.size();
for (int i = 0; i < continueCount; i++) {
- String name = continuePreviousDay.valueAt(i);
+ String pkgName = continuePreviousDay.valueAt(i);
final long beginTime = mCurrentStats[UsageStatsManager.INTERVAL_DAILY].beginTime;
for (IntervalStats stat : mCurrentStats) {
- stat.update(name, beginTime, UsageEvents.Event.CONTINUE_PREVIOUS_DAY);
+ if (continuePreviousDayForegroundActivity.containsKey(pkgName)) {
+ final ArrayMap<String, Integer> foregroundActivityEventMap =
+ continuePreviousDayForegroundActivity.get(pkgName);
+ final int size = foregroundActivityEventMap.size();
+ for (int j = 0; j < size; j++) {
+ stat.update(pkgName, foregroundActivityEventMap.keyAt(j), beginTime,
+ UsageEvents.Event.CONTINUE_PREVIOUS_DAY);
+ }
+ }
+ if (continuePreviousDayForegroundService.containsKey(pkgName)) {
+ final ArrayMap<String, Integer> foregroundServiceEventMap =
+ continuePreviousDayForegroundService.get(pkgName);
+ final int size = foregroundServiceEventMap.size();
+ for (int j = 0; j < size; j++) {
+ stat.update(pkgName, foregroundServiceEventMap.keyAt(j), beginTime,
+ UsageEvents.Event.CONTINUING_FOREGROUND_SERVICE);
+ }
+ }
stat.updateConfigurationStats(previousConfig, beginTime);
notifyStatsChanged();
}
@@ -837,10 +879,18 @@
return "MOVE_TO_BACKGROUND";
case UsageEvents.Event.MOVE_TO_FOREGROUND:
return "MOVE_TO_FOREGROUND";
+ case UsageEvents.Event.FOREGROUND_SERVICE_START:
+ return "FOREGROUND_SERVICE_START";
+ case UsageEvents.Event.FOREGROUND_SERVICE_STOP:
+ return "FOREGROUND_SERVICE_STOP";
case UsageEvents.Event.END_OF_DAY:
return "END_OF_DAY";
+ case UsageEvents.Event.ROLLOVER_FOREGROUND_SERVICE:
+ return "ROLLOVER_FOREGROUND_SERVICE";
case UsageEvents.Event.CONTINUE_PREVIOUS_DAY:
return "CONTINUE_PREVIOUS_DAY";
+ case UsageEvents.Event.CONTINUING_FOREGROUND_SERVICE:
+ return "CONTINUING_FOREGROUND_SERVICE";
case UsageEvents.Event.CONFIGURATION_CHANGE:
return "CONFIGURATION_CHANGE";
case UsageEvents.Event.SYSTEM_INTERACTION:
diff --git a/telecomm/java/android/telecom/Call.java b/telecomm/java/android/telecom/Call.java
index b6ac91d..cef99865 100644
--- a/telecomm/java/android/telecom/Call.java
+++ b/telecomm/java/android/telecom/Call.java
@@ -21,6 +21,7 @@
import android.annotation.SystemApi;
import android.annotation.UnsupportedAppUsage;
import android.net.Uri;
+import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.ParcelFileDescriptor;
@@ -322,8 +323,11 @@
/**
* Call can be upgraded to a video call.
* @hide
+ * @deprecated Use {@link #CAPABILITY_SUPPORTS_VT_LOCAL_BIDIRECTIONAL} and
+ * {@link #CAPABILITY_SUPPORTS_VT_REMOTE_BIDIRECTIONAL} to indicate for a call
+ * whether or not video calling is supported.
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 119305590)
public static final int CAPABILITY_CAN_UPGRADE_TO_VIDEO = 0x00080000;
/**
diff --git a/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java
index 34603a3..0589cd4 100644
--- a/telecomm/java/android/telecom/Connection.java
+++ b/telecomm/java/android/telecom/Connection.java
@@ -272,6 +272,9 @@
/**
* Call can be upgraded to a video call.
+ * @deprecated Use {@link #CAPABILITY_SUPPORTS_VT_LOCAL_BIDIRECTIONAL} and
+ * {@link #CAPABILITY_SUPPORTS_VT_REMOTE_BIDIRECTIONAL} to indicate for a call whether or not
+ * video calling is supported.
*/
public static final int CAPABILITY_CAN_UPGRADE_TO_VIDEO = 0x00080000;
diff --git a/telecomm/java/android/telecom/PhoneAccount.java b/telecomm/java/android/telecom/PhoneAccount.java
index 9a4ea9e7..2ffad03 100644
--- a/telecomm/java/android/telecom/PhoneAccount.java
+++ b/telecomm/java/android/telecom/PhoneAccount.java
@@ -985,10 +985,10 @@
/**
* Generates a string representation of a capabilities bitmask.
*
- * @param capabilities The capabilities bitmask.
* @return String representation of the capabilities bitmask.
+ * @hide
*/
- private String capabilitiesToString() {
+ public String capabilitiesToString() {
StringBuilder sb = new StringBuilder();
if (hasCapabilities(CAPABILITY_SELF_MANAGED)) {
sb.append("SelfManaged ");
diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java
index fa16bfe..9f0bdd7 100644
--- a/telecomm/java/android/telecom/TelecomManager.java
+++ b/telecomm/java/android/telecom/TelecomManager.java
@@ -15,6 +15,7 @@
package android.telecom;
import android.Manifest;
+import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.RequiresPermission;
import android.annotation.SuppressAutoDoc;
@@ -36,6 +37,8 @@
import com.android.internal.telecom.ITelecomService;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@@ -413,8 +416,10 @@
* <p>
* The phone number of the call used by Telecom to determine which call should be handed over.
* @hide
+ * @deprecated Use the public handover APIs. See
+ * {@link Call#handoverTo(PhoneAccountHandle, int, Bundle)} for more information.
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 119305590)
public static final String EXTRA_IS_HANDOVER = "android.telecom.extra.IS_HANDOVER";
/**
@@ -528,11 +533,19 @@
public static final char DTMF_CHARACTER_WAIT = ';';
/**
+ * @hide
+ */
+ @IntDef(prefix = { "TTY_MODE_" },
+ value = {TTY_MODE_OFF, TTY_MODE_FULL, TTY_MODE_HCO, TTY_MODE_VCO})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface TtyMode {}
+
+ /**
* TTY (teletypewriter) mode is off.
*
* @hide
*/
- @UnsupportedAppUsage
+ @SystemApi
public static final int TTY_MODE_OFF = 0;
/**
@@ -541,6 +554,7 @@
*
* @hide
*/
+ @SystemApi
public static final int TTY_MODE_FULL = 1;
/**
@@ -550,6 +564,7 @@
*
* @hide
*/
+ @SystemApi
public static final int TTY_MODE_HCO = 2;
/**
@@ -559,6 +574,7 @@
*
* @hide
*/
+ @SystemApi
public static final int TTY_MODE_VCO = 3;
/**
@@ -827,8 +843,9 @@
* @return The phone account handle of the current sim call manager.
*
* @hide
+ * @deprecated Use {@link #getSimCallManager()}.
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 119305590)
public PhoneAccountHandle getSimCallManager(int userId) {
try {
if (isServiceConnected()) {
@@ -929,10 +946,12 @@
* Returns a list of {@link PhoneAccountHandle}s including those which have not been enabled
* by the user.
*
+ * @param includeDisabledAccounts When {@code true}, disabled phone accounts will be included,
+ * when {@code false}, only
* @return A list of {@code PhoneAccountHandle} objects.
* @hide
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 119305590)
public List<PhoneAccountHandle> getCallCapablePhoneAccounts(boolean includeDisabledAccounts) {
try {
if (isServiceConnected()) {
@@ -1155,7 +1174,7 @@
/**
* Used to set the default dialer package.
*
- * @param packageName to set the default dialer to..
+ * @param packageName to set the default dialer to.
*
* @result {@code true} if the default dialer was successfully changed, {@code false} if
* the specified package does not correspond to an installed dialer, or is already
@@ -1166,7 +1185,10 @@
*
* @hide
*/
- @UnsupportedAppUsage
+ @SystemApi
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.MODIFY_PHONE_STATE,
+ android.Manifest.permission.WRITE_SECURE_SETTINGS})
public boolean setDefaultDialer(String packageName) {
try {
if (isServiceConnected()) {
@@ -1179,12 +1201,10 @@
}
/**
- * Used to determine the dialer package that is preloaded on the system partition.
+ * Determines the package name of the system-provided default phone app.
*
* @return package name for the system dialer package or null if no system dialer is preloaded.
- * @hide
*/
- @UnsupportedAppUsage
public String getSystemDialerPackage() {
try {
if (isServiceConnected()) {
@@ -1545,8 +1565,9 @@
* - {@link TelecomManager#TTY_MODE_VCO}
* @hide
*/
- @UnsupportedAppUsage
- public int getCurrentTtyMode() {
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ public @TtyMode int getCurrentTtyMode() {
try {
if (isServiceConnected()) {
return getTelecomService().getCurrentTtyMode(mContext.getOpPackageName());
diff --git a/telephony/java/android/telephony/NetworkRegistrationState.java b/telephony/java/android/telephony/NetworkRegistrationState.java
index c3d8898..68e512e 100644
--- a/telephony/java/android/telephony/NetworkRegistrationState.java
+++ b/telephony/java/android/telephony/NetworkRegistrationState.java
@@ -237,10 +237,9 @@
}
/**
- * @return {@link ServiceState.RoamingType roaming type}. This could return
- * overridden roaming type based on resource overlay or carrier config.
- * @hide
+ * @return the current network roaming type.
*/
+
public @ServiceState.RoamingType int getRoamingType() {
return mRoamingType;
}
diff --git a/telephony/java/android/telephony/PhoneStateListener.java b/telephony/java/android/telephony/PhoneStateListener.java
index 26e1fb7..f5dff20 100644
--- a/telephony/java/android/telephony/PhoneStateListener.java
+++ b/telephony/java/android/telephony/PhoneStateListener.java
@@ -858,4 +858,4 @@
private void log(String s) {
Rlog.d(LOG_TAG, s);
}
-}
+}
\ No newline at end of file
diff --git a/telephony/java/android/telephony/ServiceState.java b/telephony/java/android/telephony/ServiceState.java
index c407681..0937b10 100644
--- a/telephony/java/android/telephony/ServiceState.java
+++ b/telephony/java/android/telephony/ServiceState.java
@@ -211,29 +211,30 @@
ROAMING_TYPE_INTERNATIONAL
})
public @interface RoamingType {}
+
/**
- * Roaming type
- * HOME : in home network
+ * Not roaming, registered in home network.
* @hide
*/
+ @SystemApi
public static final int ROAMING_TYPE_NOT_ROAMING = 0;
/**
- * Roaming type
- * UNKNOWN : in a roaming network, but we can not tell if it's domestic or international
+ * registered in a roaming network, but can not tell if it's domestic or international.
* @hide
*/
+ @SystemApi
public static final int ROAMING_TYPE_UNKNOWN = 1;
/**
- * Roaming type
- * DOMESTIC : in domestic roaming network
+ * registered in a domestic roaming network
* @hide
*/
+ @SystemApi
public static final int ROAMING_TYPE_DOMESTIC = 2;
/**
- * Roaming type
- * INTERNATIONAL : in international roaming network
+ * registered in an international roaming network
* @hide
*/
+ @SystemApi
public static final int ROAMING_TYPE_INTERNATIONAL = 3;
/**
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index d3e27eb..a188ef6 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -9224,7 +9224,7 @@
public static final int NETWORK_TYPE_BITMASK_LTE_CA = (1 << NETWORK_TYPE_LTE_CA);
/**
- * @return Modem supported radio access family bitmask {@link NetworkTypeBitMask}
+ * @return Modem supported radio access family bitmask
*
* <p>Requires permission: {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE} or
* that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
diff --git a/telephony/java/android/telephony/RcsManager.java b/telephony/java/android/telephony/rcs/RcsManager.java
similarity index 86%
rename from telephony/java/android/telephony/RcsManager.java
rename to telephony/java/android/telephony/rcs/RcsManager.java
index 00ce03a..0ef4e15 100644
--- a/telephony/java/android/telephony/RcsManager.java
+++ b/telephony/java/android/telephony/rcs/RcsManager.java
@@ -14,18 +14,22 @@
* limitations under the License.
*/
-package android.telephony;
+package android.telephony.rcs;
+import android.annotation.SystemService;
+import android.content.Context;
import android.os.RemoteException;
import android.os.ServiceManager;
+import android.telephony.Rlog;
-import com.android.internal.telephony.IRcs;
+import com.android.internal.telephony.rcs.IRcs;
/**
* RcsManager is the application interface to RcsProvider and provides access methods to
* RCS related database tables.
* @hide - TODO make this public
*/
+@SystemService(Context.TELEPHONY_RCS_SERVICE)
public class RcsManager {
private static final String TAG = "RcsManager";
private static final boolean VDBG = false;
diff --git a/telephony/java/android/telephony/rcs/RcsThread.aidl b/telephony/java/android/telephony/rcs/RcsThread.aidl
new file mode 100644
index 0000000..e2e0da5d
--- /dev/null
+++ b/telephony/java/android/telephony/rcs/RcsThread.aidl
@@ -0,0 +1,20 @@
+/*
+**
+** 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.telephony;
+
+parcelable RcsThread;
\ No newline at end of file
diff --git a/telephony/java/android/telephony/rcs/RcsThread.java b/telephony/java/android/telephony/rcs/RcsThread.java
new file mode 100644
index 0000000..83eb973
--- /dev/null
+++ b/telephony/java/android/telephony/rcs/RcsThread.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.rcs;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+
+import com.android.internal.telephony.rcs.IRcs;
+
+/**
+ * RcsThread represents a single RCS conversation thread. It holds messages that were sent and
+ * received and events that occured on that thread.
+ * @hide - TODO(sahinc) make this public
+ */
+public class RcsThread implements Parcelable {
+ public static final Creator<RcsThread> CREATOR = new Creator<RcsThread>() {
+ @Override
+ public RcsThread createFromParcel(Parcel in) {
+ return new RcsThread(in);
+ }
+
+ @Override
+ public RcsThread[] newArray(int size) {
+ return new RcsThread[size];
+ }
+ };
+
+ protected RcsThread(Parcel in) {
+ }
+
+ /**
+ * Returns the number of messages in this RCS thread.
+ *
+ * @hide
+ */
+ public int getMessageCount() {
+ try {
+ IRcs iRcs = IRcs.Stub.asInterface(ServiceManager.getService("ircs"));
+ if (iRcs != null) {
+ // TODO(sahinc): substitute to the regular thread id once we have database
+ // TODO(sahinc): connection in place
+ return iRcs.getMessageCount(/* rcsThreadId= */ 123);
+ }
+ } catch (RemoteException re) {
+ // TODO(sahinc): Log something meaningful
+ }
+ return 0;
+ }
+
+ /** Implement the Parcelable interface */
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ }
+}
diff --git a/telephony/java/com/android/internal/telephony/TelephonyIntents.java b/telephony/java/com/android/internal/telephony/TelephonyIntents.java
index 5ecb43e..2a648bd 100644
--- a/telephony/java/com/android/internal/telephony/TelephonyIntents.java
+++ b/telephony/java/com/android/internal/telephony/TelephonyIntents.java
@@ -480,9 +480,9 @@
public static final String EXTRA_PCO_VALUE_KEY = "pcoValue";
public static final String EXTRA_DEFAULT_NETWORK_AVAILABLE_KEY = "defaultNetworkAvailable";
- /**
+ /**
* Broadcast action to trigger CI OMA-DM Session.
- */
+ */
public static final String ACTION_REQUEST_OMADM_CONFIGURATION_UPDATE =
"com.android.omadm.service.CONFIGURATION_UPDATE";
@@ -491,4 +491,14 @@
*/
public static final String ACTION_CARRIER_CERTIFICATE_DOWNLOAD =
"com.android.internal.telephony.ACTION_CARRIER_CERTIFICATE_DOWNLOAD";
+
+ /**
+ * Broadcast action to indicate an error related to Line1Number has been detected.
+ *
+ * Requires the READ_PRIVILEGED_PHONE_STATE permission.
+ *
+ * @hide
+ */
+ public static final String ACTION_LINE1_NUMBER_ERROR_DETECTED =
+ "com.android.internal.telephony.ACTION_LINE1_NUMBER_ERROR_DETECTED";
}
diff --git a/telephony/java/com/android/internal/telephony/IRcs.aidl b/telephony/java/com/android/internal/telephony/rcs/IRcs.aidl
similarity index 83%
rename from telephony/java/com/android/internal/telephony/IRcs.aidl
rename to telephony/java/com/android/internal/telephony/rcs/IRcs.aidl
index ede8695..4c289ac 100644
--- a/telephony/java/com/android/internal/telephony/IRcs.aidl
+++ b/telephony/java/com/android/internal/telephony/rcs/IRcs.aidl
@@ -14,8 +14,12 @@
* limitations under the License.
*/
-package com.android.internal.telephony;
+package com.android.internal.telephony.rcs;
interface IRcs {
+ // RcsManager APIs
void deleteThread(int threadId);
+
+ // RcsThread APIs
+ int getMessageCount(int rcsThreadId);
}
\ No newline at end of file
diff --git a/tests/RcsTests/Android.mk b/tests/RcsTests/Android.mk
new file mode 100644
index 0000000..adc7cab
--- /dev/null
+++ b/tests/RcsTests/Android.mk
@@ -0,0 +1,19 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+
+# Only compile source java files in this apk.
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_PACKAGE_NAME := RcsTests
+LOCAL_PRIVATE_PLATFORM_APIS := true
+
+LOCAL_CERTIFICATE := platform
+LOCAL_JAVA_LIBRARIES := android.test.runner android.test.base
+LOCAL_STATIC_JAVA_LIBRARIES := junit android-support-test mockito-target-minus-junit4
+
+include $(BUILD_PACKAGE)
+
+# Use the following include to make our test apk.
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/tests/RcsTests/AndroidManifest.xml b/tests/RcsTests/AndroidManifest.xml
new file mode 100644
index 0000000..a7e7d47
--- /dev/null
+++ b/tests/RcsTests/AndroidManifest.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.tests.rcs">
+ <application android:label="RCS Test">
+ <uses-library android:name="android.test.runner" />
+ </application>
+
+ <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.android.tests.rcs"/>
+</manifest>
diff --git a/tests/RcsTests/src/com/android/tests/rcs/RcsManagerTest.java b/tests/RcsTests/src/com/android/tests/rcs/RcsManagerTest.java
new file mode 100644
index 0000000..7f5f03e
--- /dev/null
+++ b/tests/RcsTests/src/com/android/tests/rcs/RcsManagerTest.java
@@ -0,0 +1,32 @@
+/*
+ * 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.tests.rcs;
+
+import android.support.test.runner.AndroidJUnit4;
+import android.telephony.rcs.RcsManager;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class RcsManagerTest {
+ //TODO(sahinc): Add meaningful tests once we have more of the implementation in place
+ @Test
+ public void testDeleteThreadDoesntCrash() {
+ RcsManager mRcsManager = new RcsManager();
+ mRcsManager.deleteThread(0);
+ }
+}
diff --git a/tests/UsageStatsPerfTests/src/com/android/frameworks/perftests/usage/tests/UsageStatsDatabasePerfTest.java b/tests/UsageStatsPerfTests/src/com/android/frameworks/perftests/usage/tests/UsageStatsDatabasePerfTest.java
index 8467bee..be74a6d 100644
--- a/tests/UsageStatsPerfTests/src/com/android/frameworks/perftests/usage/tests/UsageStatsDatabasePerfTest.java
+++ b/tests/UsageStatsPerfTests/src/com/android/frameworks/perftests/usage/tests/UsageStatsDatabasePerfTest.java
@@ -18,10 +18,6 @@
import static junit.framework.Assert.assertEquals;
-import com.android.server.usage.UsageStatsDatabase;
-import com.android.server.usage.UsageStatsDatabase.StatCombiner;
-import com.android.server.usage.IntervalStats;
-
import android.app.usage.EventList;
import android.app.usage.UsageEvents;
import android.app.usage.UsageStatsManager;
@@ -29,10 +25,14 @@
import android.os.SystemClock;
import android.perftests.utils.ManualBenchmarkState;
import android.perftests.utils.PerfManualStatusReporter;
-import android.support.test.filters.LargeTest;
import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.LargeTest;
import android.support.test.runner.AndroidJUnit4;
+import com.android.server.usage.IntervalStats;
+import com.android.server.usage.UsageStatsDatabase;
+import com.android.server.usage.UsageStatsDatabase.StatCombiner;
+
import org.junit.BeforeClass;
import org.junit.Rule;
import org.junit.Test;
@@ -90,11 +90,13 @@
for (int pkg = 0; pkg < packageCount; pkg++) {
UsageEvents.Event event = new UsageEvents.Event();
event.mPackage = "fake.package.name" + pkg;
+ event.mClass = event.mPackage + ".class1";
event.mTimeStamp = 1;
event.mEventType = UsageEvents.Event.MOVE_TO_FOREGROUND;
for (int evt = 0; evt < eventsPerPackage; evt++) {
intervalStats.events.insert(event);
- intervalStats.update(event.mPackage, event.mTimeStamp, event.mEventType);
+ intervalStats.update(event.mPackage, event.mClass, event.mTimeStamp,
+ event.mEventType);
}
}
}