Merge "Show selection handles even when toolbar is empty."
diff --git a/Android.bp b/Android.bp
index 5c1ccb7..2c4963c 100644
--- a/Android.bp
+++ b/Android.bp
@@ -58,6 +58,7 @@
// runtime, as well as the only protos that are actually
// needed by the device.
srcs: [
+ "core/proto/android/os/cpuinfo.proto",
"core/proto/android/os/kernelwake.proto",
"core/proto/android/os/pagetypeinfo.proto",
"core/proto/android/os/procrank.proto",
@@ -81,6 +82,7 @@
],
srcs: [
+ "core/proto/android/os/cpuinfo.proto",
"core/proto/android/os/kernelwake.proto",
"core/proto/android/os/pagetypeinfo.proto",
"core/proto/android/os/procrank.proto",
diff --git a/Android.mk b/Android.mk
index a7cb362..b847425 100644
--- a/Android.mk
+++ b/Android.mk
@@ -137,6 +137,7 @@
../../system/bt/binder/android/bluetooth/IBluetoothPbap.aidl \
../../system/bt/binder/android/bluetooth/IBluetoothPbapClient.aidl \
../../system/bt/binder/android/bluetooth/IBluetoothSap.aidl \
+ ../../system/bt/binder/android/bluetooth/IBluetoothSocketManager.aidl \
../../system/bt/binder/android/bluetooth/IBluetoothStateChangeCallback.aidl \
../../system/bt/binder/android/bluetooth/IBluetoothHeadsetClient.aidl \
../../system/bt/binder/android/bluetooth/IBluetoothHidDevice.aidl \
@@ -158,6 +159,7 @@
core/java/android/content/ISyncServiceAdapter.aidl \
core/java/android/content/ISyncStatusObserver.aidl \
core/java/android/content/om/IOverlayManager.aidl \
+ core/java/android/content/pm/crossprofile/ICrossProfileApps.aidl \
core/java/android/content/pm/IDexModuleRegisterCallback.aidl \
core/java/android/content/pm/ILauncherApps.aidl \
core/java/android/content/pm/IOnAppsChangedListener.aidl \
@@ -398,6 +400,7 @@
core/java/com/android/internal/backup/IObbBackupService.aidl \
core/java/com/android/internal/car/ICarServiceHelper.aidl \
core/java/com/android/internal/inputmethod/IInputContentUriToken.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 \
core/java/com/android/internal/policy/IKeyguardExitCallback.aidl \
@@ -1555,6 +1558,7 @@
LOCAL_SOURCE_FILES_ALL_GENERATED := true
LOCAL_SRC_FILES := \
tools/streaming_proto/stream.proto \
+ cmds/am/proto/instrumentation_data.proto \
$(call all-proto-files-under, core/proto) \
$(call all-proto-files-under, libs/incident/proto)
include $(BUILD_HOST_JAVA_LIBRARY)
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
index 50a5974..711c12d 100644
--- a/PREUPLOAD.cfg
+++ b/PREUPLOAD.cfg
@@ -7,3 +7,4 @@
services/print/
services/usb/
+api_lint_hook = ${REPO_ROOT}/frameworks/base/tools/apilint/apilint_sha.sh ${PREUPLOAD_COMMIT}
diff --git a/api/current.txt b/api/current.txt
index 6a8bb01..387e815 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -3609,11 +3609,11 @@
method public android.transition.Scene getContentScene();
method public android.transition.TransitionManager getContentTransitionManager();
method public android.view.View getCurrentFocus();
- method public android.app.FragmentManager getFragmentManager();
+ method public deprecated android.app.FragmentManager getFragmentManager();
method public android.content.Intent getIntent();
method public java.lang.Object getLastNonConfigurationInstance();
method public android.view.LayoutInflater getLayoutInflater();
- method public android.app.LoaderManager getLoaderManager();
+ method public deprecated android.app.LoaderManager getLoaderManager();
method public java.lang.String getLocalClassName();
method public int getMaxNumPictureInPictureActions();
method public final android.media.session.MediaController getMediaController();
@@ -3653,7 +3653,7 @@
method public void onActionModeStarted(android.view.ActionMode);
method public void onActivityReenter(int, android.content.Intent);
method protected void onActivityResult(int, int, android.content.Intent);
- method public void onAttachFragment(android.app.Fragment);
+ method public deprecated void onAttachFragment(android.app.Fragment);
method public void onAttachedToWindow();
method public void onBackPressed();
method protected void onChildTitleChanged(android.app.Activity, java.lang.CharSequence);
@@ -3795,8 +3795,8 @@
method public void startActivityForResult(android.content.Intent, int, android.os.Bundle);
method public void startActivityFromChild(android.app.Activity, android.content.Intent, int);
method public void startActivityFromChild(android.app.Activity, android.content.Intent, int, android.os.Bundle);
- method public void startActivityFromFragment(android.app.Fragment, android.content.Intent, int);
- method public void startActivityFromFragment(android.app.Fragment, android.content.Intent, int, android.os.Bundle);
+ method public deprecated void startActivityFromFragment(android.app.Fragment, android.content.Intent, int);
+ method public deprecated void startActivityFromFragment(android.app.Fragment, android.content.Intent, int, android.os.Bundle);
method public boolean startActivityIfNeeded(android.content.Intent, int);
method public boolean startActivityIfNeeded(android.content.Intent, int, android.os.Bundle);
method public void startIntentSenderForResult(android.content.IntentSender, int, android.content.Intent, int, int, int) throws android.content.IntentSender.SendIntentException;
@@ -4455,7 +4455,7 @@
method public void unregisterForContextMenu(android.view.View);
}
- public class DialogFragment extends android.app.Fragment implements android.content.DialogInterface.OnCancelListener android.content.DialogInterface.OnDismissListener {
+ public deprecated class DialogFragment extends android.app.Fragment implements android.content.DialogInterface.OnCancelListener android.content.DialogInterface.OnDismissListener {
ctor public DialogFragment();
method public void dismiss();
method public void dismissAllowingStateLoss();
@@ -4573,7 +4573,7 @@
method public void setSelectedGroup(int);
}
- public class Fragment implements android.content.ComponentCallbacks2 android.view.View.OnCreateContextMenuListener {
+ public deprecated class Fragment implements android.content.ComponentCallbacks2 android.view.View.OnCreateContextMenuListener {
ctor public Fragment();
method public void dump(java.lang.String, java.io.FileDescriptor, java.io.PrintWriter, java.lang.String[]);
method public final boolean equals(java.lang.Object);
@@ -4589,7 +4589,7 @@
method public final java.lang.Object getHost();
method public final int getId();
method public final android.view.LayoutInflater getLayoutInflater();
- method public android.app.LoaderManager getLoaderManager();
+ method public deprecated android.app.LoaderManager getLoaderManager();
method public final android.app.Fragment getParentFragment();
method public android.transition.Transition getReenterTransition();
method public final android.content.res.Resources getResources();
@@ -4684,11 +4684,11 @@
method public void unregisterForContextMenu(android.view.View);
}
- public static class Fragment.InstantiationException extends android.util.AndroidRuntimeException {
+ public static deprecated class Fragment.InstantiationException extends android.util.AndroidRuntimeException {
ctor public Fragment.InstantiationException(java.lang.String, java.lang.Exception);
}
- public static class Fragment.SavedState implements android.os.Parcelable {
+ public static deprecated class Fragment.SavedState implements android.os.Parcelable {
method public int describeContents();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.ClassLoaderCreator<android.app.Fragment.SavedState> CREATOR;
@@ -4707,17 +4707,17 @@
method public void setTitle(java.lang.CharSequence, java.lang.CharSequence);
}
- public static abstract interface FragmentBreadCrumbs.OnBreadCrumbClickListener {
+ public static abstract deprecated interface FragmentBreadCrumbs.OnBreadCrumbClickListener {
method public abstract boolean onBreadCrumbClick(android.app.FragmentManager.BackStackEntry, int);
}
- public abstract class FragmentContainer {
+ public abstract deprecated class FragmentContainer {
ctor public FragmentContainer();
method public abstract <T extends android.view.View> T onFindViewById(int);
method public abstract boolean onHasView();
}
- public class FragmentController {
+ public deprecated class FragmentController {
method public void attachHost(android.app.Fragment);
method public static final android.app.FragmentController createController(android.app.FragmentHostCallback<?>);
method public void dispatchActivityCreated();
@@ -4760,7 +4760,7 @@
method public android.os.Parcelable saveAllState();
}
- public abstract class FragmentHostCallback<E> extends android.app.FragmentContainer {
+ public abstract deprecated class FragmentHostCallback<E> extends android.app.FragmentContainer {
ctor public FragmentHostCallback(android.content.Context, android.os.Handler, int);
method public void onAttachFragment(android.app.Fragment);
method public void onDump(java.lang.String, java.io.FileDescriptor, java.io.PrintWriter, java.lang.String[]);
@@ -4778,7 +4778,7 @@
method public boolean onUseFragmentManagerInflaterFactory();
}
- public abstract class FragmentManager {
+ public abstract deprecated class FragmentManager {
ctor public FragmentManager();
method public abstract void addOnBackStackChangedListener(android.app.FragmentManager.OnBackStackChangedListener);
method public abstract android.app.FragmentTransaction beginTransaction();
@@ -4809,7 +4809,7 @@
field public static final int POP_BACK_STACK_INCLUSIVE = 1; // 0x1
}
- public static abstract interface FragmentManager.BackStackEntry {
+ public static abstract deprecated interface FragmentManager.BackStackEntry {
method public abstract java.lang.CharSequence getBreadCrumbShortTitle();
method public abstract int getBreadCrumbShortTitleRes();
method public abstract java.lang.CharSequence getBreadCrumbTitle();
@@ -4818,7 +4818,7 @@
method public abstract java.lang.String getName();
}
- public static abstract class FragmentManager.FragmentLifecycleCallbacks {
+ public static abstract deprecated class FragmentManager.FragmentLifecycleCallbacks {
ctor public FragmentManager.FragmentLifecycleCallbacks();
method public void onFragmentActivityCreated(android.app.FragmentManager, android.app.Fragment, android.os.Bundle);
method public void onFragmentAttached(android.app.FragmentManager, android.app.Fragment, android.content.Context);
@@ -4836,14 +4836,14 @@
method public void onFragmentViewDestroyed(android.app.FragmentManager, android.app.Fragment);
}
- public static abstract interface FragmentManager.OnBackStackChangedListener {
+ public static abstract deprecated interface FragmentManager.OnBackStackChangedListener {
method public abstract void onBackStackChanged();
}
- public class FragmentManagerNonConfig {
+ public deprecated class FragmentManagerNonConfig {
}
- public abstract class FragmentTransaction {
+ public abstract deprecated class FragmentTransaction {
ctor public FragmentTransaction();
method public abstract android.app.FragmentTransaction add(android.app.Fragment, java.lang.String);
method public abstract android.app.FragmentTransaction add(int, android.app.Fragment);
@@ -4943,6 +4943,7 @@
method public void setInTouchMode(boolean);
method public void start();
method public android.app.Activity startActivitySync(android.content.Intent);
+ method public android.app.Activity startActivitySync(android.content.Intent, android.os.Bundle);
method public deprecated void startAllocCounting();
method public void startPerformanceSnapshot();
method public void startProfiling();
@@ -5048,7 +5049,7 @@
method public void setSelection(int);
}
- public class ListFragment extends android.app.Fragment {
+ public deprecated class ListFragment extends android.app.Fragment {
ctor public ListFragment();
method public android.widget.ListAdapter getListAdapter();
method public android.widget.ListView getListView();
@@ -5062,7 +5063,7 @@
method public void setSelection(int);
}
- public abstract class LoaderManager {
+ public abstract deprecated class LoaderManager {
ctor public LoaderManager();
method public abstract void destroyLoader(int);
method public abstract void dump(java.lang.String, java.io.FileDescriptor, java.io.PrintWriter, java.lang.String[]);
@@ -5072,7 +5073,7 @@
method public abstract <D> android.content.Loader<D> restartLoader(int, android.os.Bundle, android.app.LoaderManager.LoaderCallbacks<D>);
}
- public static abstract interface LoaderManager.LoaderCallbacks<D> {
+ public static abstract deprecated interface LoaderManager.LoaderCallbacks<D> {
method public abstract android.content.Loader<D> onCreateLoader(int, android.os.Bundle);
method public abstract void onLoadFinished(android.content.Loader<D>, D);
method public abstract void onLoaderReset(android.content.Loader<D>);
@@ -6541,6 +6542,7 @@
field public static final int LOCK_TASK_FEATURE_NOTIFICATIONS = 2; // 0x2
field public static final int LOCK_TASK_FEATURE_RECENTS = 8; // 0x8
field public static final int LOCK_TASK_FEATURE_SYSTEM_INFO = 1; // 0x1
+ field public static final int MAKE_USER_EPHEMERAL = 2; // 0x2
field public static final java.lang.String MIME_TYPE_PROVISIONING_NFC = "application/com.android.managedprovisioning";
field public static final int PASSWORD_QUALITY_ALPHABETIC = 262144; // 0x40000
field public static final int PASSWORD_QUALITY_ALPHANUMERIC = 327680; // 0x50000
@@ -6846,7 +6848,8 @@
method public static final long getMinFlexMillis();
method public long getMinLatencyMillis();
method public static final long getMinPeriodMillis();
- method public int getNetworkType();
+ method public deprecated int getNetworkType();
+ method public android.net.NetworkRequest getRequiredNetwork();
method public android.content.ComponentName getService();
method public android.os.Bundle getTransientExtras();
method public long getTriggerContentMaxDelay();
@@ -6866,7 +6869,8 @@
field public static final long MAX_BACKOFF_DELAY_MILLIS = 18000000L; // 0x112a880L
field public static final int NETWORK_BYTES_UNKNOWN = -1; // 0xffffffff
field public static final int NETWORK_TYPE_ANY = 1; // 0x1
- field public static final int NETWORK_TYPE_METERED = 4; // 0x4
+ field public static final int NETWORK_TYPE_CELLULAR = 4; // 0x4
+ field public static final deprecated int NETWORK_TYPE_METERED = 4; // 0x4
field public static final int NETWORK_TYPE_NONE = 0; // 0x0
field public static final int NETWORK_TYPE_NOT_ROAMING = 3; // 0x3
field public static final int NETWORK_TYPE_UNMETERED = 2; // 0x2
@@ -6885,6 +6889,7 @@
method public android.app.job.JobInfo.Builder setPeriodic(long);
method public android.app.job.JobInfo.Builder setPeriodic(long, long);
method public android.app.job.JobInfo.Builder setPersisted(boolean);
+ method public android.app.job.JobInfo.Builder setRequiredNetwork(android.net.NetworkRequest);
method public android.app.job.JobInfo.Builder setRequiredNetworkType(int);
method public android.app.job.JobInfo.Builder setRequiresBatteryNotLow(boolean);
method public android.app.job.JobInfo.Builder setRequiresCharging(boolean);
@@ -8528,7 +8533,7 @@
ctor public AsyncQueryHandler.WorkerHandler(android.os.Looper);
}
- public abstract class AsyncTaskLoader<D> extends android.content.Loader {
+ public abstract deprecated class AsyncTaskLoader<D> extends android.content.Loader {
ctor public AsyncTaskLoader(android.content.Context);
method public void cancelLoadInBackground();
method public boolean isLoadInBackgroundCanceled();
@@ -9101,6 +9106,7 @@
field public static final int CONTEXT_IGNORE_SECURITY = 2; // 0x2
field public static final int CONTEXT_INCLUDE_CODE = 1; // 0x1
field public static final int CONTEXT_RESTRICTED = 4; // 0x4
+ field public static final java.lang.String CROSS_PROFILE_APPS_SERVICE = "crossprofileapps";
field public static final java.lang.String DEVICE_POLICY_SERVICE = "device_policy";
field public static final java.lang.String DISPLAY_SERVICE = "display";
field public static final java.lang.String DOWNLOAD_SERVICE = "download";
@@ -9267,7 +9273,7 @@
method public void unregisterReceiver(android.content.BroadcastReceiver);
}
- public class CursorLoader extends android.content.AsyncTaskLoader {
+ public deprecated class CursorLoader extends android.content.AsyncTaskLoader {
ctor public CursorLoader(android.content.Context);
ctor public CursorLoader(android.content.Context, android.net.Uri, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String);
method public void deliverResult(android.database.Cursor);
@@ -9873,7 +9879,7 @@
ctor public IntentSender.SendIntentException(java.lang.Exception);
}
- public class Loader<D> {
+ public deprecated class Loader<D> {
ctor public Loader(android.content.Context);
method public void abandon();
method public boolean cancelLoad();
@@ -9906,15 +9912,15 @@
method public void unregisterOnLoadCanceledListener(android.content.Loader.OnLoadCanceledListener<D>);
}
- public final class Loader.ForceLoadContentObserver extends android.database.ContentObserver {
+ public final deprecated class Loader.ForceLoadContentObserver extends android.database.ContentObserver {
ctor public Loader.ForceLoadContentObserver();
}
- public static abstract interface Loader.OnLoadCanceledListener<D> {
+ public static abstract deprecated interface Loader.OnLoadCanceledListener<D> {
method public abstract void onLoadCanceled(android.content.Loader<D>);
}
- public static abstract interface Loader.OnLoadCompleteListener<D> {
+ public static abstract deprecated interface Loader.OnLoadCompleteListener<D> {
method public abstract void onLoadComplete(android.content.Loader<D>, D);
}
@@ -11222,6 +11228,15 @@
}
+package android.content.pm.crossprofile {
+
+ public class CrossProfileApps {
+ method public java.util.List<android.os.UserHandle> getTargetUserProfiles();
+ method public void startMainActivity(android.content.ComponentName, android.os.UserHandle, android.graphics.Rect, android.os.Bundle);
+ }
+
+}
+
package android.content.res {
public class AssetFileDescriptor implements java.io.Closeable android.os.Parcelable {
@@ -15263,6 +15278,7 @@
method public abstract int setRepeatingBurst(java.util.List<android.hardware.camera2.CaptureRequest>, android.hardware.camera2.CameraCaptureSession.CaptureCallback, android.os.Handler) throws android.hardware.camera2.CameraAccessException;
method public abstract int setRepeatingRequest(android.hardware.camera2.CaptureRequest, android.hardware.camera2.CameraCaptureSession.CaptureCallback, android.os.Handler) throws android.hardware.camera2.CameraAccessException;
method public abstract void stopRepeating() throws android.hardware.camera2.CameraAccessException;
+ method public void updateOutputConfiguration(android.hardware.camera2.params.OutputConfiguration) throws android.hardware.camera2.CameraAccessException;
}
public static abstract class CameraCaptureSession.CaptureCallback {
@@ -15904,9 +15920,11 @@
method public void addSurface(android.view.Surface);
method public int describeContents();
method public void enableSurfaceSharing();
+ method public static int getMaxSharedSurfaceCount();
method public android.view.Surface getSurface();
method public int getSurfaceGroupId();
method public java.util.List<android.view.Surface> getSurfaces();
+ method public void removeSurface(android.view.Surface);
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.hardware.camera2.params.OutputConfiguration> CREATOR;
field public static final int SURFACE_GROUP_ID_NONE = -1; // 0xffffffff
@@ -18272,11 +18290,13 @@
method public synchronized void applyLocalizedPattern(java.lang.String);
method public synchronized void applyPattern(java.lang.String);
method public synchronized boolean areSignificantDigitsUsed();
+ method public synchronized boolean equals(java.lang.Object);
method public java.lang.StringBuffer format(double, java.lang.StringBuffer, java.text.FieldPosition);
method public java.lang.StringBuffer format(long, java.lang.StringBuffer, java.text.FieldPosition);
method public java.lang.StringBuffer format(java.math.BigInteger, java.lang.StringBuffer, java.text.FieldPosition);
method public java.lang.StringBuffer format(java.math.BigDecimal, java.lang.StringBuffer, java.text.FieldPosition);
method public java.lang.StringBuffer format(android.icu.math.BigDecimal, java.lang.StringBuffer, java.text.FieldPosition);
+ method public synchronized android.icu.util.Currency getCurrency();
method public synchronized android.icu.text.CurrencyPluralInfo getCurrencyPluralInfo();
method public synchronized android.icu.util.Currency.CurrencyUsage getCurrencyUsage();
method public synchronized android.icu.text.DecimalFormatSymbols getDecimalFormatSymbols();
@@ -18284,8 +18304,12 @@
method public synchronized int getGroupingSize();
method public synchronized java.math.MathContext getMathContext();
method public synchronized android.icu.math.MathContext getMathContextICU();
+ method public synchronized int getMaximumFractionDigits();
+ method public synchronized int getMaximumIntegerDigits();
method public synchronized int getMaximumSignificantDigits();
method public synchronized byte getMinimumExponentDigits();
+ method public synchronized int getMinimumFractionDigits();
+ method public synchronized int getMinimumIntegerDigits();
method public synchronized int getMinimumSignificantDigits();
method public synchronized int getMultiplier();
method public synchronized java.lang.String getNegativePrefix();
@@ -18296,13 +18320,19 @@
method public synchronized java.lang.String getPositivePrefix();
method public synchronized java.lang.String getPositiveSuffix();
method public synchronized java.math.BigDecimal getRoundingIncrement();
+ method public synchronized int getRoundingMode();
method public synchronized int getSecondaryGroupingSize();
+ method public synchronized int hashCode();
method public synchronized boolean isDecimalPatternMatchRequired();
method public synchronized boolean isDecimalSeparatorAlwaysShown();
method public synchronized boolean isExponentSignAlwaysShown();
+ method public synchronized boolean isGroupingUsed();
method public synchronized boolean isParseBigDecimal();
+ method public synchronized boolean isParseIntegerOnly();
+ method public synchronized boolean isParseStrict();
method public synchronized boolean isScientificNotation();
method public java.lang.Number parse(java.lang.String, java.text.ParsePosition);
+ method public synchronized void setCurrency(android.icu.util.Currency);
method public synchronized void setCurrencyPluralInfo(android.icu.text.CurrencyPluralInfo);
method public synchronized void setCurrencyUsage(android.icu.util.Currency.CurrencyUsage);
method public synchronized void setDecimalFormatSymbols(android.icu.text.DecimalFormatSymbols);
@@ -18311,10 +18341,15 @@
method public synchronized void setExponentSignAlwaysShown(boolean);
method public synchronized void setFormatWidth(int);
method public synchronized void setGroupingSize(int);
+ method public synchronized void setGroupingUsed(boolean);
method public synchronized void setMathContext(java.math.MathContext);
method public synchronized void setMathContextICU(android.icu.math.MathContext);
+ method public synchronized void setMaximumFractionDigits(int);
+ method public synchronized void setMaximumIntegerDigits(int);
method public synchronized void setMaximumSignificantDigits(int);
method public synchronized void setMinimumExponentDigits(byte);
+ method public synchronized void setMinimumFractionDigits(int);
+ method public synchronized void setMinimumIntegerDigits(int);
method public synchronized void setMinimumSignificantDigits(int);
method public synchronized void setMultiplier(int);
method public synchronized void setNegativePrefix(java.lang.String);
@@ -18322,12 +18357,15 @@
method public synchronized void setPadCharacter(char);
method public synchronized void setPadPosition(int);
method public synchronized void setParseBigDecimal(boolean);
+ method public synchronized void setParseIntegerOnly(boolean);
method public deprecated void setParseMaxDigits(int);
+ method public synchronized void setParseStrict(boolean);
method public synchronized void setPositivePrefix(java.lang.String);
method public synchronized void setPositiveSuffix(java.lang.String);
method public synchronized void setRoundingIncrement(java.math.BigDecimal);
method public synchronized void setRoundingIncrement(android.icu.math.BigDecimal);
method public synchronized void setRoundingIncrement(double);
+ method public synchronized void setRoundingMode(int);
method public synchronized void setScientificNotation(boolean);
method public synchronized void setSecondaryGroupingSize(int);
method public synchronized void setSignificantDigitsUsed(boolean);
@@ -31816,6 +31854,14 @@
method public static void setVmPolicy(android.os.StrictMode.VmPolicy);
}
+ public static abstract interface StrictMode.OnThreadViolationListener {
+ method public abstract void onThreadViolation(android.os.strictmode.Violation);
+ }
+
+ public static abstract interface StrictMode.OnVmViolationListener {
+ method public abstract void onVmViolation(android.os.strictmode.Violation);
+ }
+
public static final class StrictMode.ThreadPolicy {
field public static final android.os.StrictMode.ThreadPolicy LAX;
}
@@ -31836,6 +31882,7 @@
method public android.os.StrictMode.ThreadPolicy.Builder penaltyDialog();
method public android.os.StrictMode.ThreadPolicy.Builder penaltyDropBox();
method public android.os.StrictMode.ThreadPolicy.Builder penaltyFlashScreen();
+ method public android.os.StrictMode.ThreadPolicy.Builder penaltyListener(android.os.StrictMode.OnThreadViolationListener, java.util.concurrent.Executor);
method public android.os.StrictMode.ThreadPolicy.Builder penaltyLog();
method public android.os.StrictMode.ThreadPolicy.Builder permitAll();
method public android.os.StrictMode.ThreadPolicy.Builder permitCustomSlowCalls();
@@ -31867,6 +31914,7 @@
method public android.os.StrictMode.VmPolicy.Builder penaltyDeathOnCleartextNetwork();
method public android.os.StrictMode.VmPolicy.Builder penaltyDeathOnFileUriExposure();
method public android.os.StrictMode.VmPolicy.Builder penaltyDropBox();
+ method public android.os.StrictMode.VmPolicy.Builder penaltyListener(android.os.StrictMode.OnVmViolationListener, java.util.concurrent.Executor);
method public android.os.StrictMode.VmPolicy.Builder penaltyLog();
method public android.os.StrictMode.VmPolicy.Builder setClassInstanceLimit(java.lang.Class, int);
}
@@ -31874,10 +31922,12 @@
public final class SystemClock {
method public static long currentThreadTimeMillis();
method public static long elapsedRealtime();
+ method public static java.time.Clock elapsedRealtimeClock();
method public static long elapsedRealtimeNanos();
method public static boolean setCurrentTimeMillis(long);
method public static void sleep(long);
method public static long uptimeMillis();
+ method public static java.time.Clock uptimeMillisClock();
}
public class TestLooperManager {
@@ -31959,6 +32009,7 @@
field public static final java.lang.String DISALLOW_CONFIG_CELL_BROADCASTS = "no_config_cell_broadcasts";
field public static final java.lang.String DISALLOW_CONFIG_CREDENTIALS = "no_config_credentials";
field public static final java.lang.String DISALLOW_CONFIG_DATE_TIME = "no_config_date_time";
+ field public static final java.lang.String DISALLOW_CONFIG_LOCALE = "no_config_locale";
field public static final java.lang.String DISALLOW_CONFIG_MOBILE_NETWORKS = "no_config_mobile_networks";
field public static final java.lang.String DISALLOW_CONFIG_TETHERING = "no_config_tethering";
field public static final java.lang.String DISALLOW_CONFIG_VPN = "no_config_vpn";
@@ -31987,6 +32038,7 @@
field public static final java.lang.String DISALLOW_UNINSTALL_APPS = "no_uninstall_apps";
field public static final java.lang.String DISALLOW_UNMUTE_MICROPHONE = "no_unmute_microphone";
field public static final java.lang.String DISALLOW_USB_FILE_TRANSFER = "no_usb_file_transfer";
+ field public static final java.lang.String DISALLOW_USER_SWITCH = "no_user_switch";
field public static final java.lang.String ENSURE_VERIFY_APPS = "ensure_verify_apps";
field public static final java.lang.String KEY_RESTRICTIONS_PENDING = "restrictions_pending";
field public static final int USER_CREATION_FAILED_NOT_PERMITTED = 1; // 0x1
@@ -32227,6 +32279,62 @@
}
+package android.os.strictmode {
+
+ public final class CleartextNetworkViolation extends android.os.strictmode.Violation {
+ }
+
+ public final class ContentUriWithoutPermissionViolation extends android.os.strictmode.Violation {
+ }
+
+ public final class CustomViolation extends android.os.strictmode.Violation {
+ }
+
+ public final class DiskReadViolation extends android.os.strictmode.Violation {
+ }
+
+ public final class DiskWriteViolation extends android.os.strictmode.Violation {
+ }
+
+ public final class FileUriExposedViolation extends android.os.strictmode.Violation {
+ }
+
+ public class InstanceCountViolation extends android.os.strictmode.Violation {
+ method public long getNumberOfInstances();
+ }
+
+ public final class IntentReceiverLeakedViolation extends android.os.strictmode.Violation {
+ }
+
+ public final class LeakedClosableViolation extends android.os.strictmode.Violation {
+ }
+
+ public final class NetworkViolation extends android.os.strictmode.Violation {
+ }
+
+ public final class ResourceMismatchViolation extends android.os.strictmode.Violation {
+ }
+
+ public final class ServiceConnectionLeakedViolation extends android.os.strictmode.Violation {
+ }
+
+ public final class SqliteObjectLeakedViolation extends android.os.strictmode.Violation {
+ }
+
+ public final class UnbufferedIoViolation extends android.os.strictmode.Violation {
+ }
+
+ public final class UntaggedSocketViolation extends android.os.strictmode.Violation {
+ }
+
+ public abstract class Violation extends java.lang.Throwable {
+ }
+
+ public final class WebViewMethodCalledOnWrongThreadViolation extends android.os.strictmode.Violation {
+ }
+
+}
+
package android.preference {
public class CheckBoxPreference extends android.preference.TwoStatePreference {
@@ -32513,7 +32621,7 @@
method public default void putStringSet(java.lang.String, java.util.Set<java.lang.String>);
}
- public abstract class PreferenceFragment extends android.app.Fragment {
+ public abstract deprecated class PreferenceFragment extends android.app.Fragment {
ctor public PreferenceFragment();
method public void addPreferencesFromIntent(android.content.Intent);
method public void addPreferencesFromResource(int);
@@ -32524,7 +32632,7 @@
method public void setPreferenceScreen(android.preference.PreferenceScreen);
}
- public static abstract interface PreferenceFragment.OnPreferenceStartFragmentCallback {
+ public static abstract deprecated interface PreferenceFragment.OnPreferenceStartFragmentCallback {
method public abstract boolean onPreferenceStartFragment(android.preference.PreferenceFragment, android.preference.Preference);
}
@@ -38429,6 +38537,16 @@
field public final int errno;
}
+ public class Int32Ref {
+ ctor public Int32Ref(int);
+ field public int value;
+ }
+
+ public class Int64Ref {
+ ctor public Int64Ref(long);
+ field public long value;
+ }
+
public final class Os {
method public static java.io.FileDescriptor accept(java.io.FileDescriptor, java.net.InetSocketAddress) throws android.system.ErrnoException, java.net.SocketException;
method public static boolean access(java.lang.String, int) throws android.system.ErrnoException;
@@ -38498,7 +38616,8 @@
method public static void remove(java.lang.String) throws android.system.ErrnoException;
method public static void removexattr(java.lang.String, java.lang.String) throws android.system.ErrnoException;
method public static void rename(java.lang.String, java.lang.String) throws android.system.ErrnoException;
- method public static long sendfile(java.io.FileDescriptor, java.io.FileDescriptor, android.util.MutableLong, long) throws android.system.ErrnoException;
+ method public static deprecated long sendfile(java.io.FileDescriptor, java.io.FileDescriptor, android.util.MutableLong, long) throws android.system.ErrnoException;
+ method public static long sendfile(java.io.FileDescriptor, java.io.FileDescriptor, android.system.Int64Ref, long) throws android.system.ErrnoException;
method public static int sendto(java.io.FileDescriptor, java.nio.ByteBuffer, int, java.net.InetAddress, int) throws android.system.ErrnoException, java.net.SocketException;
method public static int sendto(java.io.FileDescriptor, byte[], int, int, int, java.net.InetAddress, int) throws android.system.ErrnoException, java.net.SocketException;
method public static void setegid(int) throws android.system.ErrnoException;
@@ -38523,7 +38642,8 @@
method public static int umask(int);
method public static android.system.StructUtsname uname();
method public static void unsetenv(java.lang.String) throws android.system.ErrnoException;
- method public static int waitpid(int, android.util.MutableInt, int) throws android.system.ErrnoException;
+ method public static deprecated int waitpid(int, android.util.MutableInt, int) throws android.system.ErrnoException;
+ method public static int waitpid(int, android.system.Int32Ref, int) throws android.system.ErrnoException;
method public static int write(java.io.FileDescriptor, java.nio.ByteBuffer) throws android.system.ErrnoException, java.io.InterruptedIOException;
method public static int write(java.io.FileDescriptor, byte[], int, int) throws android.system.ErrnoException, java.io.InterruptedIOException;
method public static int writev(java.io.FileDescriptor, java.lang.Object[], int[], int[]) throws android.system.ErrnoException, java.io.InterruptedIOException;
@@ -39219,7 +39339,9 @@
ctor public CallAudioState(boolean, int, int);
method public static java.lang.String audioRouteToString(int);
method public int describeContents();
+ method public android.bluetooth.BluetoothDevice getActiveBluetoothDevice();
method public int getRoute();
+ method public java.util.Collection<android.bluetooth.BluetoothDevice> getSupportedBluetoothDevices();
method public int getSupportedRouteMask();
method public boolean isMuted();
method public void writeToParcel(android.os.Parcel, int);
@@ -39351,6 +39473,7 @@
method public final void putExtras(android.os.Bundle);
method public final void removeExtras(java.util.List<java.lang.String>);
method public final void removeExtras(java.lang.String...);
+ method public void requestBluetoothAudio(java.lang.String);
method public void sendConnectionEvent(java.lang.String, android.os.Bundle);
method public final void setActive();
method public final void setAddress(android.net.Uri, int);
@@ -39541,6 +39664,7 @@
method public void onCanAddCallChanged(boolean);
method public void onConnectionEvent(android.telecom.Call, java.lang.String, android.os.Bundle);
method public void onSilenceRinger();
+ method public final void requestBluetoothAudio(java.lang.String);
method public final void setAudioRoute(int);
method public final void setMuted(boolean);
field public static final java.lang.String SERVICE_INTERFACE = "android.telecom.InCallService";
@@ -49863,7 +49987,7 @@
method public abstract void setHttpAuthUsernamePassword(java.lang.String, java.lang.String, java.lang.String, java.lang.String);
}
- public class WebViewFragment extends android.app.Fragment {
+ public deprecated class WebViewFragment extends android.app.Fragment {
ctor public WebViewFragment();
method public android.webkit.WebView getWebView();
}
diff --git a/api/system-current.txt b/api/system-current.txt
index e485e964e..fb9c73d 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -3754,11 +3754,11 @@
method public android.transition.Scene getContentScene();
method public android.transition.TransitionManager getContentTransitionManager();
method public android.view.View getCurrentFocus();
- method public android.app.FragmentManager getFragmentManager();
+ method public deprecated android.app.FragmentManager getFragmentManager();
method public android.content.Intent getIntent();
method public java.lang.Object getLastNonConfigurationInstance();
method public android.view.LayoutInflater getLayoutInflater();
- method public android.app.LoaderManager getLoaderManager();
+ method public deprecated android.app.LoaderManager getLoaderManager();
method public java.lang.String getLocalClassName();
method public int getMaxNumPictureInPictureActions();
method public final android.media.session.MediaController getMediaController();
@@ -3799,7 +3799,7 @@
method public void onActionModeStarted(android.view.ActionMode);
method public void onActivityReenter(int, android.content.Intent);
method protected void onActivityResult(int, int, android.content.Intent);
- method public void onAttachFragment(android.app.Fragment);
+ method public deprecated void onAttachFragment(android.app.Fragment);
method public void onAttachedToWindow();
method public void onBackPressed();
method public deprecated void onBackgroundVisibleBehindChanged(boolean);
@@ -3943,8 +3943,8 @@
method public void startActivityForResult(android.content.Intent, int, android.os.Bundle);
method public void startActivityFromChild(android.app.Activity, android.content.Intent, int);
method public void startActivityFromChild(android.app.Activity, android.content.Intent, int, android.os.Bundle);
- method public void startActivityFromFragment(android.app.Fragment, android.content.Intent, int);
- method public void startActivityFromFragment(android.app.Fragment, android.content.Intent, int, android.os.Bundle);
+ method public deprecated void startActivityFromFragment(android.app.Fragment, android.content.Intent, int);
+ method public deprecated void startActivityFromFragment(android.app.Fragment, android.content.Intent, int, android.os.Bundle);
method public boolean startActivityIfNeeded(android.content.Intent, int);
method public boolean startActivityIfNeeded(android.content.Intent, int, android.os.Bundle);
method public void startIntentSenderForResult(android.content.IntentSender, int, android.content.Intent, int, int, int) throws android.content.IntentSender.SendIntentException;
@@ -4628,7 +4628,7 @@
method public void unregisterForContextMenu(android.view.View);
}
- public class DialogFragment extends android.app.Fragment implements android.content.DialogInterface.OnCancelListener android.content.DialogInterface.OnDismissListener {
+ public deprecated class DialogFragment extends android.app.Fragment implements android.content.DialogInterface.OnCancelListener android.content.DialogInterface.OnDismissListener {
ctor public DialogFragment();
method public void dismiss();
method public void dismissAllowingStateLoss();
@@ -4747,7 +4747,7 @@
method public void setSelectedGroup(int);
}
- public class Fragment implements android.content.ComponentCallbacks2 android.view.View.OnCreateContextMenuListener {
+ public deprecated class Fragment implements android.content.ComponentCallbacks2 android.view.View.OnCreateContextMenuListener {
ctor public Fragment();
method public void dump(java.lang.String, java.io.FileDescriptor, java.io.PrintWriter, java.lang.String[]);
method public final boolean equals(java.lang.Object);
@@ -4763,7 +4763,7 @@
method public final java.lang.Object getHost();
method public final int getId();
method public final android.view.LayoutInflater getLayoutInflater();
- method public android.app.LoaderManager getLoaderManager();
+ method public deprecated android.app.LoaderManager getLoaderManager();
method public final android.app.Fragment getParentFragment();
method public android.transition.Transition getReenterTransition();
method public final android.content.res.Resources getResources();
@@ -4858,11 +4858,11 @@
method public void unregisterForContextMenu(android.view.View);
}
- public static class Fragment.InstantiationException extends android.util.AndroidRuntimeException {
+ public static deprecated class Fragment.InstantiationException extends android.util.AndroidRuntimeException {
ctor public Fragment.InstantiationException(java.lang.String, java.lang.Exception);
}
- public static class Fragment.SavedState implements android.os.Parcelable {
+ public static deprecated class Fragment.SavedState implements android.os.Parcelable {
method public int describeContents();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.ClassLoaderCreator<android.app.Fragment.SavedState> CREATOR;
@@ -4881,17 +4881,17 @@
method public void setTitle(java.lang.CharSequence, java.lang.CharSequence);
}
- public static abstract interface FragmentBreadCrumbs.OnBreadCrumbClickListener {
+ public static abstract deprecated interface FragmentBreadCrumbs.OnBreadCrumbClickListener {
method public abstract boolean onBreadCrumbClick(android.app.FragmentManager.BackStackEntry, int);
}
- public abstract class FragmentContainer {
+ public abstract deprecated class FragmentContainer {
ctor public FragmentContainer();
method public abstract <T extends android.view.View> T onFindViewById(int);
method public abstract boolean onHasView();
}
- public class FragmentController {
+ public deprecated class FragmentController {
method public void attachHost(android.app.Fragment);
method public static final android.app.FragmentController createController(android.app.FragmentHostCallback<?>);
method public void dispatchActivityCreated();
@@ -4934,7 +4934,7 @@
method public android.os.Parcelable saveAllState();
}
- public abstract class FragmentHostCallback<E> extends android.app.FragmentContainer {
+ public abstract deprecated class FragmentHostCallback<E> extends android.app.FragmentContainer {
ctor public FragmentHostCallback(android.content.Context, android.os.Handler, int);
method public void onAttachFragment(android.app.Fragment);
method public void onDump(java.lang.String, java.io.FileDescriptor, java.io.PrintWriter, java.lang.String[]);
@@ -4952,7 +4952,7 @@
method public boolean onUseFragmentManagerInflaterFactory();
}
- public abstract class FragmentManager {
+ public abstract deprecated class FragmentManager {
ctor public FragmentManager();
method public abstract void addOnBackStackChangedListener(android.app.FragmentManager.OnBackStackChangedListener);
method public abstract android.app.FragmentTransaction beginTransaction();
@@ -4983,7 +4983,7 @@
field public static final int POP_BACK_STACK_INCLUSIVE = 1; // 0x1
}
- public static abstract interface FragmentManager.BackStackEntry {
+ public static abstract deprecated interface FragmentManager.BackStackEntry {
method public abstract java.lang.CharSequence getBreadCrumbShortTitle();
method public abstract int getBreadCrumbShortTitleRes();
method public abstract java.lang.CharSequence getBreadCrumbTitle();
@@ -4992,7 +4992,7 @@
method public abstract java.lang.String getName();
}
- public static abstract class FragmentManager.FragmentLifecycleCallbacks {
+ public static abstract deprecated class FragmentManager.FragmentLifecycleCallbacks {
ctor public FragmentManager.FragmentLifecycleCallbacks();
method public void onFragmentActivityCreated(android.app.FragmentManager, android.app.Fragment, android.os.Bundle);
method public void onFragmentAttached(android.app.FragmentManager, android.app.Fragment, android.content.Context);
@@ -5010,14 +5010,14 @@
method public void onFragmentViewDestroyed(android.app.FragmentManager, android.app.Fragment);
}
- public static abstract interface FragmentManager.OnBackStackChangedListener {
+ public static abstract deprecated interface FragmentManager.OnBackStackChangedListener {
method public abstract void onBackStackChanged();
}
- public class FragmentManagerNonConfig {
+ public deprecated class FragmentManagerNonConfig {
}
- public abstract class FragmentTransaction {
+ public abstract deprecated class FragmentTransaction {
ctor public FragmentTransaction();
method public abstract android.app.FragmentTransaction add(android.app.Fragment, java.lang.String);
method public abstract android.app.FragmentTransaction add(int, android.app.Fragment);
@@ -5129,6 +5129,7 @@
method public void setInTouchMode(boolean);
method public void start();
method public android.app.Activity startActivitySync(android.content.Intent);
+ method public android.app.Activity startActivitySync(android.content.Intent, android.os.Bundle);
method public deprecated void startAllocCounting();
method public void startPerformanceSnapshot();
method public void startProfiling();
@@ -5234,7 +5235,7 @@
method public void setSelection(int);
}
- public class ListFragment extends android.app.Fragment {
+ public deprecated class ListFragment extends android.app.Fragment {
ctor public ListFragment();
method public android.widget.ListAdapter getListAdapter();
method public android.widget.ListView getListView();
@@ -5248,7 +5249,7 @@
method public void setSelection(int);
}
- public abstract class LoaderManager {
+ public abstract deprecated class LoaderManager {
ctor public LoaderManager();
method public abstract void destroyLoader(int);
method public abstract void dump(java.lang.String, java.io.FileDescriptor, java.io.PrintWriter, java.lang.String[]);
@@ -5258,7 +5259,7 @@
method public abstract <D> android.content.Loader<D> restartLoader(int, android.os.Bundle, android.app.LoaderManager.LoaderCallbacks<D>);
}
- public static abstract interface LoaderManager.LoaderCallbacks<D> {
+ public static abstract deprecated interface LoaderManager.LoaderCallbacks<D> {
method public abstract android.content.Loader<D> onCreateLoader(int, android.os.Bundle);
method public abstract void onLoadFinished(android.content.Loader<D>, D);
method public abstract void onLoaderReset(android.content.Loader<D>);
@@ -6788,6 +6789,7 @@
field public static final int LOCK_TASK_FEATURE_NOTIFICATIONS = 2; // 0x2
field public static final int LOCK_TASK_FEATURE_RECENTS = 8; // 0x8
field public static final int LOCK_TASK_FEATURE_SYSTEM_INFO = 1; // 0x1
+ field public static final int MAKE_USER_EPHEMERAL = 2; // 0x2
field public static final java.lang.String MIME_TYPE_PROVISIONING_NFC = "application/com.android.managedprovisioning";
field public static final int PASSWORD_QUALITY_ALPHABETIC = 262144; // 0x40000
field public static final int PASSWORD_QUALITY_ALPHANUMERIC = 327680; // 0x50000
@@ -7289,7 +7291,8 @@
method public static final long getMinFlexMillis();
method public long getMinLatencyMillis();
method public static final long getMinPeriodMillis();
- method public int getNetworkType();
+ method public deprecated int getNetworkType();
+ method public android.net.NetworkRequest getRequiredNetwork();
method public android.content.ComponentName getService();
method public android.os.Bundle getTransientExtras();
method public long getTriggerContentMaxDelay();
@@ -7309,7 +7312,8 @@
field public static final long MAX_BACKOFF_DELAY_MILLIS = 18000000L; // 0x112a880L
field public static final int NETWORK_BYTES_UNKNOWN = -1; // 0xffffffff
field public static final int NETWORK_TYPE_ANY = 1; // 0x1
- field public static final int NETWORK_TYPE_METERED = 4; // 0x4
+ field public static final int NETWORK_TYPE_CELLULAR = 4; // 0x4
+ field public static final deprecated int NETWORK_TYPE_METERED = 4; // 0x4
field public static final int NETWORK_TYPE_NONE = 0; // 0x0
field public static final int NETWORK_TYPE_NOT_ROAMING = 3; // 0x3
field public static final int NETWORK_TYPE_UNMETERED = 2; // 0x2
@@ -7328,6 +7332,7 @@
method public android.app.job.JobInfo.Builder setPeriodic(long);
method public android.app.job.JobInfo.Builder setPeriodic(long, long);
method public android.app.job.JobInfo.Builder setPersisted(boolean);
+ method public android.app.job.JobInfo.Builder setRequiredNetwork(android.net.NetworkRequest);
method public android.app.job.JobInfo.Builder setRequiredNetworkType(int);
method public android.app.job.JobInfo.Builder setRequiresBatteryNotLow(boolean);
method public android.app.job.JobInfo.Builder setRequiresCharging(boolean);
@@ -9040,7 +9045,7 @@
ctor public AsyncQueryHandler.WorkerHandler(android.os.Looper);
}
- public abstract class AsyncTaskLoader<D> extends android.content.Loader {
+ public abstract deprecated class AsyncTaskLoader<D> extends android.content.Loader {
ctor public AsyncTaskLoader(android.content.Context);
method public void cancelLoadInBackground();
method public boolean isLoadInBackgroundCanceled();
@@ -9622,6 +9627,7 @@
field public static final int CONTEXT_IGNORE_SECURITY = 2; // 0x2
field public static final int CONTEXT_INCLUDE_CODE = 1; // 0x1
field public static final int CONTEXT_RESTRICTED = 4; // 0x4
+ field public static final java.lang.String CROSS_PROFILE_APPS_SERVICE = "crossprofileapps";
field public static final java.lang.String DEVICE_POLICY_SERVICE = "device_policy";
field public static final java.lang.String DISPLAY_SERVICE = "display";
field public static final java.lang.String DOWNLOAD_SERVICE = "download";
@@ -9801,7 +9807,7 @@
method public void unregisterReceiver(android.content.BroadcastReceiver);
}
- public class CursorLoader extends android.content.AsyncTaskLoader {
+ public deprecated class CursorLoader extends android.content.AsyncTaskLoader {
ctor public CursorLoader(android.content.Context);
ctor public CursorLoader(android.content.Context, android.net.Uri, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String);
method public void deliverResult(android.database.Cursor);
@@ -10436,7 +10442,7 @@
ctor public IntentSender.SendIntentException(java.lang.Exception);
}
- public class Loader<D> {
+ public deprecated class Loader<D> {
ctor public Loader(android.content.Context);
method public void abandon();
method public boolean cancelLoad();
@@ -10469,15 +10475,15 @@
method public void unregisterOnLoadCanceledListener(android.content.Loader.OnLoadCanceledListener<D>);
}
- public final class Loader.ForceLoadContentObserver extends android.database.ContentObserver {
+ public final deprecated class Loader.ForceLoadContentObserver extends android.database.ContentObserver {
ctor public Loader.ForceLoadContentObserver();
}
- public static abstract interface Loader.OnLoadCanceledListener<D> {
+ public static abstract deprecated interface Loader.OnLoadCanceledListener<D> {
method public abstract void onLoadCanceled(android.content.Loader<D>);
}
- public static abstract interface Loader.OnLoadCompleteListener<D> {
+ public static abstract deprecated interface Loader.OnLoadCompleteListener<D> {
method public abstract void onLoadComplete(android.content.Loader<D>, D);
}
@@ -11955,6 +11961,15 @@
}
+package android.content.pm.crossprofile {
+
+ public class CrossProfileApps {
+ method public java.util.List<android.os.UserHandle> getTargetUserProfiles();
+ method public void startMainActivity(android.content.ComponentName, android.os.UserHandle, android.graphics.Rect, android.os.Bundle);
+ }
+
+}
+
package android.content.pm.permission {
public final class RuntimePermissionPresentationInfo implements android.os.Parcelable {
@@ -16018,6 +16033,7 @@
method public abstract int setRepeatingBurst(java.util.List<android.hardware.camera2.CaptureRequest>, android.hardware.camera2.CameraCaptureSession.CaptureCallback, android.os.Handler) throws android.hardware.camera2.CameraAccessException;
method public abstract int setRepeatingRequest(android.hardware.camera2.CaptureRequest, android.hardware.camera2.CameraCaptureSession.CaptureCallback, android.os.Handler) throws android.hardware.camera2.CameraAccessException;
method public abstract void stopRepeating() throws android.hardware.camera2.CameraAccessException;
+ method public void updateOutputConfiguration(android.hardware.camera2.params.OutputConfiguration) throws android.hardware.camera2.CameraAccessException;
}
public static abstract class CameraCaptureSession.CaptureCallback {
@@ -16665,10 +16681,12 @@
method public void addSurface(android.view.Surface);
method public int describeContents();
method public void enableSurfaceSharing();
+ method public static int getMaxSharedSurfaceCount();
method public int getRotation();
method public android.view.Surface getSurface();
method public int getSurfaceGroupId();
method public java.util.List<android.view.Surface> getSurfaces();
+ method public void removeSurface(android.view.Surface);
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.hardware.camera2.params.OutputConfiguration> CREATOR;
field public static final int ROTATION_0 = 0; // 0x0
@@ -19831,11 +19849,13 @@
method public synchronized void applyLocalizedPattern(java.lang.String);
method public synchronized void applyPattern(java.lang.String);
method public synchronized boolean areSignificantDigitsUsed();
+ method public synchronized boolean equals(java.lang.Object);
method public java.lang.StringBuffer format(double, java.lang.StringBuffer, java.text.FieldPosition);
method public java.lang.StringBuffer format(long, java.lang.StringBuffer, java.text.FieldPosition);
method public java.lang.StringBuffer format(java.math.BigInteger, java.lang.StringBuffer, java.text.FieldPosition);
method public java.lang.StringBuffer format(java.math.BigDecimal, java.lang.StringBuffer, java.text.FieldPosition);
method public java.lang.StringBuffer format(android.icu.math.BigDecimal, java.lang.StringBuffer, java.text.FieldPosition);
+ method public synchronized android.icu.util.Currency getCurrency();
method public synchronized android.icu.text.CurrencyPluralInfo getCurrencyPluralInfo();
method public synchronized android.icu.util.Currency.CurrencyUsage getCurrencyUsage();
method public synchronized android.icu.text.DecimalFormatSymbols getDecimalFormatSymbols();
@@ -19843,8 +19863,12 @@
method public synchronized int getGroupingSize();
method public synchronized java.math.MathContext getMathContext();
method public synchronized android.icu.math.MathContext getMathContextICU();
+ method public synchronized int getMaximumFractionDigits();
+ method public synchronized int getMaximumIntegerDigits();
method public synchronized int getMaximumSignificantDigits();
method public synchronized byte getMinimumExponentDigits();
+ method public synchronized int getMinimumFractionDigits();
+ method public synchronized int getMinimumIntegerDigits();
method public synchronized int getMinimumSignificantDigits();
method public synchronized int getMultiplier();
method public synchronized java.lang.String getNegativePrefix();
@@ -19855,13 +19879,19 @@
method public synchronized java.lang.String getPositivePrefix();
method public synchronized java.lang.String getPositiveSuffix();
method public synchronized java.math.BigDecimal getRoundingIncrement();
+ method public synchronized int getRoundingMode();
method public synchronized int getSecondaryGroupingSize();
+ method public synchronized int hashCode();
method public synchronized boolean isDecimalPatternMatchRequired();
method public synchronized boolean isDecimalSeparatorAlwaysShown();
method public synchronized boolean isExponentSignAlwaysShown();
+ method public synchronized boolean isGroupingUsed();
method public synchronized boolean isParseBigDecimal();
+ method public synchronized boolean isParseIntegerOnly();
+ method public synchronized boolean isParseStrict();
method public synchronized boolean isScientificNotation();
method public java.lang.Number parse(java.lang.String, java.text.ParsePosition);
+ method public synchronized void setCurrency(android.icu.util.Currency);
method public synchronized void setCurrencyPluralInfo(android.icu.text.CurrencyPluralInfo);
method public synchronized void setCurrencyUsage(android.icu.util.Currency.CurrencyUsage);
method public synchronized void setDecimalFormatSymbols(android.icu.text.DecimalFormatSymbols);
@@ -19870,10 +19900,15 @@
method public synchronized void setExponentSignAlwaysShown(boolean);
method public synchronized void setFormatWidth(int);
method public synchronized void setGroupingSize(int);
+ method public synchronized void setGroupingUsed(boolean);
method public synchronized void setMathContext(java.math.MathContext);
method public synchronized void setMathContextICU(android.icu.math.MathContext);
+ method public synchronized void setMaximumFractionDigits(int);
+ method public synchronized void setMaximumIntegerDigits(int);
method public synchronized void setMaximumSignificantDigits(int);
method public synchronized void setMinimumExponentDigits(byte);
+ method public synchronized void setMinimumFractionDigits(int);
+ method public synchronized void setMinimumIntegerDigits(int);
method public synchronized void setMinimumSignificantDigits(int);
method public synchronized void setMultiplier(int);
method public synchronized void setNegativePrefix(java.lang.String);
@@ -19881,12 +19916,15 @@
method public synchronized void setPadCharacter(char);
method public synchronized void setPadPosition(int);
method public synchronized void setParseBigDecimal(boolean);
+ method public synchronized void setParseIntegerOnly(boolean);
method public deprecated void setParseMaxDigits(int);
+ method public synchronized void setParseStrict(boolean);
method public synchronized void setPositivePrefix(java.lang.String);
method public synchronized void setPositiveSuffix(java.lang.String);
method public synchronized void setRoundingIncrement(java.math.BigDecimal);
method public synchronized void setRoundingIncrement(android.icu.math.BigDecimal);
method public synchronized void setRoundingIncrement(double);
+ method public synchronized void setRoundingMode(int);
method public synchronized void setScientificNotation(boolean);
method public synchronized void setSecondaryGroupingSize(int);
method public synchronized void setSignificantDigitsUsed(boolean);
@@ -34605,6 +34643,14 @@
method public static void setVmPolicy(android.os.StrictMode.VmPolicy);
}
+ public static abstract interface StrictMode.OnThreadViolationListener {
+ method public abstract void onThreadViolation(android.os.strictmode.Violation);
+ }
+
+ public static abstract interface StrictMode.OnVmViolationListener {
+ method public abstract void onVmViolation(android.os.strictmode.Violation);
+ }
+
public static final class StrictMode.ThreadPolicy {
field public static final android.os.StrictMode.ThreadPolicy LAX;
}
@@ -34625,6 +34671,7 @@
method public android.os.StrictMode.ThreadPolicy.Builder penaltyDialog();
method public android.os.StrictMode.ThreadPolicy.Builder penaltyDropBox();
method public android.os.StrictMode.ThreadPolicy.Builder penaltyFlashScreen();
+ method public android.os.StrictMode.ThreadPolicy.Builder penaltyListener(android.os.StrictMode.OnThreadViolationListener, java.util.concurrent.Executor);
method public android.os.StrictMode.ThreadPolicy.Builder penaltyLog();
method public android.os.StrictMode.ThreadPolicy.Builder permitAll();
method public android.os.StrictMode.ThreadPolicy.Builder permitCustomSlowCalls();
@@ -34656,6 +34703,7 @@
method public android.os.StrictMode.VmPolicy.Builder penaltyDeathOnCleartextNetwork();
method public android.os.StrictMode.VmPolicy.Builder penaltyDeathOnFileUriExposure();
method public android.os.StrictMode.VmPolicy.Builder penaltyDropBox();
+ method public android.os.StrictMode.VmPolicy.Builder penaltyListener(android.os.StrictMode.OnVmViolationListener, java.util.concurrent.Executor);
method public android.os.StrictMode.VmPolicy.Builder penaltyLog();
method public android.os.StrictMode.VmPolicy.Builder setClassInstanceLimit(java.lang.Class, int);
}
@@ -34663,10 +34711,12 @@
public final class SystemClock {
method public static long currentThreadTimeMillis();
method public static long elapsedRealtime();
+ method public static java.time.Clock elapsedRealtimeClock();
method public static long elapsedRealtimeNanos();
method public static boolean setCurrentTimeMillis(long);
method public static void sleep(long);
method public static long uptimeMillis();
+ method public static java.time.Clock uptimeMillisClock();
}
public class TestLooperManager {
@@ -34782,11 +34832,13 @@
method public java.util.List<android.os.UserManager.EnforcingUser> getUserRestrictionSources(java.lang.String, android.os.UserHandle);
method public android.os.Bundle getUserRestrictions();
method public android.os.Bundle getUserRestrictions(android.os.UserHandle);
+ method public boolean hasRestrictedProfiles();
method public boolean hasUserRestriction(java.lang.String);
method public boolean isDemoUser();
method public boolean isManagedProfile();
method public boolean isManagedProfile(int);
method public boolean isQuietModeEnabled(android.os.UserHandle);
+ method public boolean isRestrictedProfile();
method public boolean isSystemUser();
method public boolean isUserAGoat();
method public boolean isUserRunning(android.os.UserHandle);
@@ -34811,6 +34863,7 @@
field public static final java.lang.String DISALLOW_CONFIG_CELL_BROADCASTS = "no_config_cell_broadcasts";
field public static final java.lang.String DISALLOW_CONFIG_CREDENTIALS = "no_config_credentials";
field public static final java.lang.String DISALLOW_CONFIG_DATE_TIME = "no_config_date_time";
+ field public static final java.lang.String DISALLOW_CONFIG_LOCALE = "no_config_locale";
field public static final java.lang.String DISALLOW_CONFIG_MOBILE_NETWORKS = "no_config_mobile_networks";
field public static final java.lang.String DISALLOW_CONFIG_TETHERING = "no_config_tethering";
field public static final java.lang.String DISALLOW_CONFIG_VPN = "no_config_vpn";
@@ -34840,6 +34893,7 @@
field public static final java.lang.String DISALLOW_UNINSTALL_APPS = "no_uninstall_apps";
field public static final java.lang.String DISALLOW_UNMUTE_MICROPHONE = "no_unmute_microphone";
field public static final java.lang.String DISALLOW_USB_FILE_TRANSFER = "no_usb_file_transfer";
+ field public static final java.lang.String DISALLOW_USER_SWITCH = "no_user_switch";
field public static final java.lang.String ENSURE_VERIFY_APPS = "ensure_verify_apps";
field public static final java.lang.String KEY_RESTRICTIONS_PENDING = "restrictions_pending";
field public static final int RESTRICTION_NOT_SET = 0; // 0x0
@@ -35099,6 +35153,62 @@
}
+package android.os.strictmode {
+
+ public final class CleartextNetworkViolation extends android.os.strictmode.Violation {
+ }
+
+ public final class ContentUriWithoutPermissionViolation extends android.os.strictmode.Violation {
+ }
+
+ public final class CustomViolation extends android.os.strictmode.Violation {
+ }
+
+ public final class DiskReadViolation extends android.os.strictmode.Violation {
+ }
+
+ public final class DiskWriteViolation extends android.os.strictmode.Violation {
+ }
+
+ public final class FileUriExposedViolation extends android.os.strictmode.Violation {
+ }
+
+ public class InstanceCountViolation extends android.os.strictmode.Violation {
+ method public long getNumberOfInstances();
+ }
+
+ public final class IntentReceiverLeakedViolation extends android.os.strictmode.Violation {
+ }
+
+ public final class LeakedClosableViolation extends android.os.strictmode.Violation {
+ }
+
+ public final class NetworkViolation extends android.os.strictmode.Violation {
+ }
+
+ public final class ResourceMismatchViolation extends android.os.strictmode.Violation {
+ }
+
+ public final class ServiceConnectionLeakedViolation extends android.os.strictmode.Violation {
+ }
+
+ public final class SqliteObjectLeakedViolation extends android.os.strictmode.Violation {
+ }
+
+ public final class UnbufferedIoViolation extends android.os.strictmode.Violation {
+ }
+
+ public final class UntaggedSocketViolation extends android.os.strictmode.Violation {
+ }
+
+ public abstract class Violation extends java.lang.Throwable {
+ }
+
+ public final class WebViewMethodCalledOnWrongThreadViolation extends android.os.strictmode.Violation {
+ }
+
+}
+
package android.permissionpresenterservice {
public abstract class RuntimePermissionPresenterService extends android.app.Service {
@@ -35397,7 +35507,7 @@
method public default void putStringSet(java.lang.String, java.util.Set<java.lang.String>);
}
- public abstract class PreferenceFragment extends android.app.Fragment {
+ public abstract deprecated class PreferenceFragment extends android.app.Fragment {
ctor public PreferenceFragment();
method public void addPreferencesFromIntent(android.content.Intent);
method public void addPreferencesFromResource(int);
@@ -35408,7 +35518,7 @@
method public void setPreferenceScreen(android.preference.PreferenceScreen);
}
- public static abstract interface PreferenceFragment.OnPreferenceStartFragmentCallback {
+ public static abstract deprecated interface PreferenceFragment.OnPreferenceStartFragmentCallback {
method public abstract boolean onPreferenceStartFragment(android.preference.PreferenceFragment, android.preference.Preference);
}
@@ -41730,6 +41840,16 @@
field public final int errno;
}
+ public class Int32Ref {
+ ctor public Int32Ref(int);
+ field public int value;
+ }
+
+ public class Int64Ref {
+ ctor public Int64Ref(long);
+ field public long value;
+ }
+
public final class Os {
method public static java.io.FileDescriptor accept(java.io.FileDescriptor, java.net.InetSocketAddress) throws android.system.ErrnoException, java.net.SocketException;
method public static boolean access(java.lang.String, int) throws android.system.ErrnoException;
@@ -41799,7 +41919,8 @@
method public static void remove(java.lang.String) throws android.system.ErrnoException;
method public static void removexattr(java.lang.String, java.lang.String) throws android.system.ErrnoException;
method public static void rename(java.lang.String, java.lang.String) throws android.system.ErrnoException;
- method public static long sendfile(java.io.FileDescriptor, java.io.FileDescriptor, android.util.MutableLong, long) throws android.system.ErrnoException;
+ method public static deprecated long sendfile(java.io.FileDescriptor, java.io.FileDescriptor, android.util.MutableLong, long) throws android.system.ErrnoException;
+ method public static long sendfile(java.io.FileDescriptor, java.io.FileDescriptor, android.system.Int64Ref, long) throws android.system.ErrnoException;
method public static int sendto(java.io.FileDescriptor, java.nio.ByteBuffer, int, java.net.InetAddress, int) throws android.system.ErrnoException, java.net.SocketException;
method public static int sendto(java.io.FileDescriptor, byte[], int, int, int, java.net.InetAddress, int) throws android.system.ErrnoException, java.net.SocketException;
method public static void setegid(int) throws android.system.ErrnoException;
@@ -41824,7 +41945,8 @@
method public static int umask(int);
method public static android.system.StructUtsname uname();
method public static void unsetenv(java.lang.String) throws android.system.ErrnoException;
- method public static int waitpid(int, android.util.MutableInt, int) throws android.system.ErrnoException;
+ method public static deprecated int waitpid(int, android.util.MutableInt, int) throws android.system.ErrnoException;
+ method public static int waitpid(int, android.system.Int32Ref, int) throws android.system.ErrnoException;
method public static int write(java.io.FileDescriptor, java.nio.ByteBuffer) throws android.system.ErrnoException, java.io.InterruptedIOException;
method public static int write(java.io.FileDescriptor, byte[], int, int) throws android.system.ErrnoException, java.io.InterruptedIOException;
method public static int writev(java.io.FileDescriptor, java.lang.Object[], int[], int[]) throws android.system.ErrnoException, java.io.InterruptedIOException;
@@ -42545,7 +42667,9 @@
ctor public CallAudioState(boolean, int, int);
method public static java.lang.String audioRouteToString(int);
method public int describeContents();
+ method public android.bluetooth.BluetoothDevice getActiveBluetoothDevice();
method public int getRoute();
+ method public java.util.Collection<android.bluetooth.BluetoothDevice> getSupportedBluetoothDevices();
method public int getSupportedRouteMask();
method public boolean isMuted();
method public void writeToParcel(android.os.Parcel, int);
@@ -42684,6 +42808,7 @@
method public final void putExtras(android.os.Bundle);
method public final void removeExtras(java.util.List<java.lang.String>);
method public final void removeExtras(java.lang.String...);
+ method public void requestBluetoothAudio(java.lang.String);
method public void sendConnectionEvent(java.lang.String, android.os.Bundle);
method public final void setActive();
method public final void setAddress(android.net.Uri, int);
@@ -42877,6 +43002,7 @@
method public deprecated void onPhoneCreated(android.telecom.Phone);
method public deprecated void onPhoneDestroyed(android.telecom.Phone);
method public void onSilenceRinger();
+ method public final void requestBluetoothAudio(java.lang.String);
method public final void setAudioRoute(int);
method public final void setMuted(boolean);
field public static final java.lang.String SERVICE_INTERFACE = "android.telecom.InCallService";
@@ -43015,6 +43141,7 @@
method public final android.telecom.CallAudioState getCallAudioState();
method public final java.util.List<android.telecom.Call> getCalls();
method public final void removeListener(android.telecom.Phone.Listener);
+ method public void requestBluetoothAudio(java.lang.String);
method public final void setAudioRoute(int);
method public final void setMuted(boolean);
}
@@ -53788,7 +53915,7 @@
method public abstract void setWebContentsDebuggingEnabled(boolean);
}
- public class WebViewFragment extends android.app.Fragment {
+ public deprecated class WebViewFragment extends android.app.Fragment {
ctor public WebViewFragment();
method public android.webkit.WebView getWebView();
}
diff --git a/api/test-current.txt b/api/test-current.txt
index 40f732c..9f5ab62 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -3611,11 +3611,11 @@
method public android.transition.Scene getContentScene();
method public android.transition.TransitionManager getContentTransitionManager();
method public android.view.View getCurrentFocus();
- method public android.app.FragmentManager getFragmentManager();
+ method public deprecated android.app.FragmentManager getFragmentManager();
method public android.content.Intent getIntent();
method public java.lang.Object getLastNonConfigurationInstance();
method public android.view.LayoutInflater getLayoutInflater();
- method public android.app.LoaderManager getLoaderManager();
+ method public deprecated android.app.LoaderManager getLoaderManager();
method public java.lang.String getLocalClassName();
method public int getMaxNumPictureInPictureActions();
method public final android.media.session.MediaController getMediaController();
@@ -3655,7 +3655,7 @@
method public void onActionModeStarted(android.view.ActionMode);
method public void onActivityReenter(int, android.content.Intent);
method protected void onActivityResult(int, int, android.content.Intent);
- method public void onAttachFragment(android.app.Fragment);
+ method public deprecated void onAttachFragment(android.app.Fragment);
method public void onAttachedToWindow();
method public void onBackPressed();
method protected void onChildTitleChanged(android.app.Activity, java.lang.CharSequence);
@@ -3797,8 +3797,8 @@
method public void startActivityForResult(android.content.Intent, int, android.os.Bundle);
method public void startActivityFromChild(android.app.Activity, android.content.Intent, int);
method public void startActivityFromChild(android.app.Activity, android.content.Intent, int, android.os.Bundle);
- method public void startActivityFromFragment(android.app.Fragment, android.content.Intent, int);
- method public void startActivityFromFragment(android.app.Fragment, android.content.Intent, int, android.os.Bundle);
+ method public deprecated void startActivityFromFragment(android.app.Fragment, android.content.Intent, int);
+ method public deprecated void startActivityFromFragment(android.app.Fragment, android.content.Intent, int, android.os.Bundle);
method public boolean startActivityIfNeeded(android.content.Intent, int);
method public boolean startActivityIfNeeded(android.content.Intent, int, android.os.Bundle);
method public void startIntentSenderForResult(android.content.IntentSender, int, android.content.Intent, int, int, int) throws android.content.IntentSender.SendIntentException;
@@ -4484,7 +4484,7 @@
method public void unregisterForContextMenu(android.view.View);
}
- public class DialogFragment extends android.app.Fragment implements android.content.DialogInterface.OnCancelListener android.content.DialogInterface.OnDismissListener {
+ public deprecated class DialogFragment extends android.app.Fragment implements android.content.DialogInterface.OnCancelListener android.content.DialogInterface.OnDismissListener {
ctor public DialogFragment();
method public void dismiss();
method public void dismissAllowingStateLoss();
@@ -4602,7 +4602,7 @@
method public void setSelectedGroup(int);
}
- public class Fragment implements android.content.ComponentCallbacks2 android.view.View.OnCreateContextMenuListener {
+ public deprecated class Fragment implements android.content.ComponentCallbacks2 android.view.View.OnCreateContextMenuListener {
ctor public Fragment();
method public void dump(java.lang.String, java.io.FileDescriptor, java.io.PrintWriter, java.lang.String[]);
method public final boolean equals(java.lang.Object);
@@ -4618,7 +4618,7 @@
method public final java.lang.Object getHost();
method public final int getId();
method public final android.view.LayoutInflater getLayoutInflater();
- method public android.app.LoaderManager getLoaderManager();
+ method public deprecated android.app.LoaderManager getLoaderManager();
method public final android.app.Fragment getParentFragment();
method public android.transition.Transition getReenterTransition();
method public final android.content.res.Resources getResources();
@@ -4713,11 +4713,11 @@
method public void unregisterForContextMenu(android.view.View);
}
- public static class Fragment.InstantiationException extends android.util.AndroidRuntimeException {
+ public static deprecated class Fragment.InstantiationException extends android.util.AndroidRuntimeException {
ctor public Fragment.InstantiationException(java.lang.String, java.lang.Exception);
}
- public static class Fragment.SavedState implements android.os.Parcelable {
+ public static deprecated class Fragment.SavedState implements android.os.Parcelable {
method public int describeContents();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.ClassLoaderCreator<android.app.Fragment.SavedState> CREATOR;
@@ -4736,17 +4736,17 @@
method public void setTitle(java.lang.CharSequence, java.lang.CharSequence);
}
- public static abstract interface FragmentBreadCrumbs.OnBreadCrumbClickListener {
+ public static abstract deprecated interface FragmentBreadCrumbs.OnBreadCrumbClickListener {
method public abstract boolean onBreadCrumbClick(android.app.FragmentManager.BackStackEntry, int);
}
- public abstract class FragmentContainer {
+ public abstract deprecated class FragmentContainer {
ctor public FragmentContainer();
method public abstract <T extends android.view.View> T onFindViewById(int);
method public abstract boolean onHasView();
}
- public class FragmentController {
+ public deprecated class FragmentController {
method public void attachHost(android.app.Fragment);
method public static final android.app.FragmentController createController(android.app.FragmentHostCallback<?>);
method public void dispatchActivityCreated();
@@ -4789,7 +4789,7 @@
method public android.os.Parcelable saveAllState();
}
- public abstract class FragmentHostCallback<E> extends android.app.FragmentContainer {
+ public abstract deprecated class FragmentHostCallback<E> extends android.app.FragmentContainer {
ctor public FragmentHostCallback(android.content.Context, android.os.Handler, int);
method public void onAttachFragment(android.app.Fragment);
method public void onDump(java.lang.String, java.io.FileDescriptor, java.io.PrintWriter, java.lang.String[]);
@@ -4807,7 +4807,7 @@
method public boolean onUseFragmentManagerInflaterFactory();
}
- public abstract class FragmentManager {
+ public abstract deprecated class FragmentManager {
ctor public FragmentManager();
method public abstract void addOnBackStackChangedListener(android.app.FragmentManager.OnBackStackChangedListener);
method public abstract android.app.FragmentTransaction beginTransaction();
@@ -4838,7 +4838,7 @@
field public static final int POP_BACK_STACK_INCLUSIVE = 1; // 0x1
}
- public static abstract interface FragmentManager.BackStackEntry {
+ public static abstract deprecated interface FragmentManager.BackStackEntry {
method public abstract java.lang.CharSequence getBreadCrumbShortTitle();
method public abstract int getBreadCrumbShortTitleRes();
method public abstract java.lang.CharSequence getBreadCrumbTitle();
@@ -4847,7 +4847,7 @@
method public abstract java.lang.String getName();
}
- public static abstract class FragmentManager.FragmentLifecycleCallbacks {
+ public static abstract deprecated class FragmentManager.FragmentLifecycleCallbacks {
ctor public FragmentManager.FragmentLifecycleCallbacks();
method public void onFragmentActivityCreated(android.app.FragmentManager, android.app.Fragment, android.os.Bundle);
method public void onFragmentAttached(android.app.FragmentManager, android.app.Fragment, android.content.Context);
@@ -4865,14 +4865,14 @@
method public void onFragmentViewDestroyed(android.app.FragmentManager, android.app.Fragment);
}
- public static abstract interface FragmentManager.OnBackStackChangedListener {
+ public static abstract deprecated interface FragmentManager.OnBackStackChangedListener {
method public abstract void onBackStackChanged();
}
- public class FragmentManagerNonConfig {
+ public deprecated class FragmentManagerNonConfig {
}
- public abstract class FragmentTransaction {
+ public abstract deprecated class FragmentTransaction {
ctor public FragmentTransaction();
method public abstract android.app.FragmentTransaction add(android.app.Fragment, java.lang.String);
method public abstract android.app.FragmentTransaction add(int, android.app.Fragment);
@@ -4972,6 +4972,7 @@
method public void setInTouchMode(boolean);
method public void start();
method public android.app.Activity startActivitySync(android.content.Intent);
+ method public android.app.Activity startActivitySync(android.content.Intent, android.os.Bundle);
method public deprecated void startAllocCounting();
method public void startPerformanceSnapshot();
method public void startProfiling();
@@ -5077,7 +5078,7 @@
method public void setSelection(int);
}
- public class ListFragment extends android.app.Fragment {
+ public deprecated class ListFragment extends android.app.Fragment {
ctor public ListFragment();
method public android.widget.ListAdapter getListAdapter();
method public android.widget.ListView getListView();
@@ -5091,7 +5092,7 @@
method public void setSelection(int);
}
- public abstract class LoaderManager {
+ public abstract deprecated class LoaderManager {
ctor public LoaderManager();
method public abstract void destroyLoader(int);
method public abstract void dump(java.lang.String, java.io.FileDescriptor, java.io.PrintWriter, java.lang.String[]);
@@ -5101,7 +5102,7 @@
method public abstract <D> android.content.Loader<D> restartLoader(int, android.os.Bundle, android.app.LoaderManager.LoaderCallbacks<D>);
}
- public static abstract interface LoaderManager.LoaderCallbacks<D> {
+ public static abstract deprecated interface LoaderManager.LoaderCallbacks<D> {
method public abstract android.content.Loader<D> onCreateLoader(int, android.os.Bundle);
method public abstract void onLoadFinished(android.content.Loader<D>, D);
method public abstract void onLoaderReset(android.content.Loader<D>);
@@ -6615,6 +6616,7 @@
field public static final int LOCK_TASK_FEATURE_NOTIFICATIONS = 2; // 0x2
field public static final int LOCK_TASK_FEATURE_RECENTS = 8; // 0x8
field public static final int LOCK_TASK_FEATURE_SYSTEM_INFO = 1; // 0x1
+ field public static final int MAKE_USER_EPHEMERAL = 2; // 0x2
field public static final java.lang.String MIME_TYPE_PROVISIONING_NFC = "application/com.android.managedprovisioning";
field public static final int PASSWORD_QUALITY_ALPHABETIC = 262144; // 0x40000
field public static final int PASSWORD_QUALITY_ALPHANUMERIC = 327680; // 0x50000
@@ -6920,7 +6922,8 @@
method public static final long getMinFlexMillis();
method public long getMinLatencyMillis();
method public static final long getMinPeriodMillis();
- method public int getNetworkType();
+ method public deprecated int getNetworkType();
+ method public android.net.NetworkRequest getRequiredNetwork();
method public android.content.ComponentName getService();
method public android.os.Bundle getTransientExtras();
method public long getTriggerContentMaxDelay();
@@ -6940,7 +6943,8 @@
field public static final long MAX_BACKOFF_DELAY_MILLIS = 18000000L; // 0x112a880L
field public static final int NETWORK_BYTES_UNKNOWN = -1; // 0xffffffff
field public static final int NETWORK_TYPE_ANY = 1; // 0x1
- field public static final int NETWORK_TYPE_METERED = 4; // 0x4
+ field public static final int NETWORK_TYPE_CELLULAR = 4; // 0x4
+ field public static final deprecated int NETWORK_TYPE_METERED = 4; // 0x4
field public static final int NETWORK_TYPE_NONE = 0; // 0x0
field public static final int NETWORK_TYPE_NOT_ROAMING = 3; // 0x3
field public static final int NETWORK_TYPE_UNMETERED = 2; // 0x2
@@ -6959,6 +6963,7 @@
method public android.app.job.JobInfo.Builder setPeriodic(long);
method public android.app.job.JobInfo.Builder setPeriodic(long, long);
method public android.app.job.JobInfo.Builder setPersisted(boolean);
+ method public android.app.job.JobInfo.Builder setRequiredNetwork(android.net.NetworkRequest);
method public android.app.job.JobInfo.Builder setRequiredNetworkType(int);
method public android.app.job.JobInfo.Builder setRequiresBatteryNotLow(boolean);
method public android.app.job.JobInfo.Builder setRequiresCharging(boolean);
@@ -8603,7 +8608,7 @@
ctor public AsyncQueryHandler.WorkerHandler(android.os.Looper);
}
- public abstract class AsyncTaskLoader<D> extends android.content.Loader {
+ public abstract deprecated class AsyncTaskLoader<D> extends android.content.Loader {
ctor public AsyncTaskLoader(android.content.Context);
method public void cancelLoadInBackground();
method public boolean isLoadInBackgroundCanceled();
@@ -9178,6 +9183,7 @@
field public static final int CONTEXT_IGNORE_SECURITY = 2; // 0x2
field public static final int CONTEXT_INCLUDE_CODE = 1; // 0x1
field public static final int CONTEXT_RESTRICTED = 4; // 0x4
+ field public static final java.lang.String CROSS_PROFILE_APPS_SERVICE = "crossprofileapps";
field public static final java.lang.String DEVICE_POLICY_SERVICE = "device_policy";
field public static final java.lang.String DISPLAY_SERVICE = "display";
field public static final java.lang.String DOWNLOAD_SERVICE = "download";
@@ -9345,7 +9351,7 @@
method public void unregisterReceiver(android.content.BroadcastReceiver);
}
- public class CursorLoader extends android.content.AsyncTaskLoader {
+ public deprecated class CursorLoader extends android.content.AsyncTaskLoader {
ctor public CursorLoader(android.content.Context);
ctor public CursorLoader(android.content.Context, android.net.Uri, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String);
method public void deliverResult(android.database.Cursor);
@@ -9951,7 +9957,7 @@
ctor public IntentSender.SendIntentException(java.lang.Exception);
}
- public class Loader<D> {
+ public deprecated class Loader<D> {
ctor public Loader(android.content.Context);
method public void abandon();
method public boolean cancelLoad();
@@ -9984,15 +9990,15 @@
method public void unregisterOnLoadCanceledListener(android.content.Loader.OnLoadCanceledListener<D>);
}
- public final class Loader.ForceLoadContentObserver extends android.database.ContentObserver {
+ public final deprecated class Loader.ForceLoadContentObserver extends android.database.ContentObserver {
ctor public Loader.ForceLoadContentObserver();
}
- public static abstract interface Loader.OnLoadCanceledListener<D> {
+ public static abstract deprecated interface Loader.OnLoadCanceledListener<D> {
method public abstract void onLoadCanceled(android.content.Loader<D>);
}
- public static abstract interface Loader.OnLoadCompleteListener<D> {
+ public static abstract deprecated interface Loader.OnLoadCompleteListener<D> {
method public abstract void onLoadComplete(android.content.Loader<D>, D);
}
@@ -11311,6 +11317,15 @@
}
+package android.content.pm.crossprofile {
+
+ public class CrossProfileApps {
+ method public java.util.List<android.os.UserHandle> getTargetUserProfiles();
+ method public void startMainActivity(android.content.ComponentName, android.os.UserHandle, android.graphics.Rect, android.os.Bundle);
+ }
+
+}
+
package android.content.res {
public class AssetFileDescriptor implements java.io.Closeable android.os.Parcelable {
@@ -12308,6 +12323,7 @@
method public static int getWALAutoCheckpoint();
method public static int getWALConnectionPoolSize();
method public static java.lang.String getWALSyncMode();
+ method public static boolean isCompatibilityWalSupported();
method public static int releaseMemory();
}
@@ -15404,6 +15420,7 @@
method public abstract int setRepeatingBurst(java.util.List<android.hardware.camera2.CaptureRequest>, android.hardware.camera2.CameraCaptureSession.CaptureCallback, android.os.Handler) throws android.hardware.camera2.CameraAccessException;
method public abstract int setRepeatingRequest(android.hardware.camera2.CaptureRequest, android.hardware.camera2.CameraCaptureSession.CaptureCallback, android.os.Handler) throws android.hardware.camera2.CameraAccessException;
method public abstract void stopRepeating() throws android.hardware.camera2.CameraAccessException;
+ method public void updateOutputConfiguration(android.hardware.camera2.params.OutputConfiguration) throws android.hardware.camera2.CameraAccessException;
}
public static abstract class CameraCaptureSession.CaptureCallback {
@@ -16049,9 +16066,11 @@
method public void addSurface(android.view.Surface);
method public int describeContents();
method public void enableSurfaceSharing();
+ method public static int getMaxSharedSurfaceCount();
method public android.view.Surface getSurface();
method public int getSurfaceGroupId();
method public java.util.List<android.view.Surface> getSurfaces();
+ method public void removeSurface(android.view.Surface);
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.hardware.camera2.params.OutputConfiguration> CREATOR;
field public static final int SURFACE_GROUP_ID_NONE = -1; // 0xffffffff
@@ -18417,11 +18436,13 @@
method public synchronized void applyLocalizedPattern(java.lang.String);
method public synchronized void applyPattern(java.lang.String);
method public synchronized boolean areSignificantDigitsUsed();
+ method public synchronized boolean equals(java.lang.Object);
method public java.lang.StringBuffer format(double, java.lang.StringBuffer, java.text.FieldPosition);
method public java.lang.StringBuffer format(long, java.lang.StringBuffer, java.text.FieldPosition);
method public java.lang.StringBuffer format(java.math.BigInteger, java.lang.StringBuffer, java.text.FieldPosition);
method public java.lang.StringBuffer format(java.math.BigDecimal, java.lang.StringBuffer, java.text.FieldPosition);
method public java.lang.StringBuffer format(android.icu.math.BigDecimal, java.lang.StringBuffer, java.text.FieldPosition);
+ method public synchronized android.icu.util.Currency getCurrency();
method public synchronized android.icu.text.CurrencyPluralInfo getCurrencyPluralInfo();
method public synchronized android.icu.util.Currency.CurrencyUsage getCurrencyUsage();
method public synchronized android.icu.text.DecimalFormatSymbols getDecimalFormatSymbols();
@@ -18429,8 +18450,12 @@
method public synchronized int getGroupingSize();
method public synchronized java.math.MathContext getMathContext();
method public synchronized android.icu.math.MathContext getMathContextICU();
+ method public synchronized int getMaximumFractionDigits();
+ method public synchronized int getMaximumIntegerDigits();
method public synchronized int getMaximumSignificantDigits();
method public synchronized byte getMinimumExponentDigits();
+ method public synchronized int getMinimumFractionDigits();
+ method public synchronized int getMinimumIntegerDigits();
method public synchronized int getMinimumSignificantDigits();
method public synchronized int getMultiplier();
method public synchronized java.lang.String getNegativePrefix();
@@ -18441,13 +18466,19 @@
method public synchronized java.lang.String getPositivePrefix();
method public synchronized java.lang.String getPositiveSuffix();
method public synchronized java.math.BigDecimal getRoundingIncrement();
+ method public synchronized int getRoundingMode();
method public synchronized int getSecondaryGroupingSize();
+ method public synchronized int hashCode();
method public synchronized boolean isDecimalPatternMatchRequired();
method public synchronized boolean isDecimalSeparatorAlwaysShown();
method public synchronized boolean isExponentSignAlwaysShown();
+ method public synchronized boolean isGroupingUsed();
method public synchronized boolean isParseBigDecimal();
+ method public synchronized boolean isParseIntegerOnly();
+ method public synchronized boolean isParseStrict();
method public synchronized boolean isScientificNotation();
method public java.lang.Number parse(java.lang.String, java.text.ParsePosition);
+ method public synchronized void setCurrency(android.icu.util.Currency);
method public synchronized void setCurrencyPluralInfo(android.icu.text.CurrencyPluralInfo);
method public synchronized void setCurrencyUsage(android.icu.util.Currency.CurrencyUsage);
method public synchronized void setDecimalFormatSymbols(android.icu.text.DecimalFormatSymbols);
@@ -18456,10 +18487,15 @@
method public synchronized void setExponentSignAlwaysShown(boolean);
method public synchronized void setFormatWidth(int);
method public synchronized void setGroupingSize(int);
+ method public synchronized void setGroupingUsed(boolean);
method public synchronized void setMathContext(java.math.MathContext);
method public synchronized void setMathContextICU(android.icu.math.MathContext);
+ method public synchronized void setMaximumFractionDigits(int);
+ method public synchronized void setMaximumIntegerDigits(int);
method public synchronized void setMaximumSignificantDigits(int);
method public synchronized void setMinimumExponentDigits(byte);
+ method public synchronized void setMinimumFractionDigits(int);
+ method public synchronized void setMinimumIntegerDigits(int);
method public synchronized void setMinimumSignificantDigits(int);
method public synchronized void setMultiplier(int);
method public synchronized void setNegativePrefix(java.lang.String);
@@ -18467,12 +18503,15 @@
method public synchronized void setPadCharacter(char);
method public synchronized void setPadPosition(int);
method public synchronized void setParseBigDecimal(boolean);
+ method public synchronized void setParseIntegerOnly(boolean);
method public deprecated void setParseMaxDigits(int);
+ method public synchronized void setParseStrict(boolean);
method public synchronized void setPositivePrefix(java.lang.String);
method public synchronized void setPositiveSuffix(java.lang.String);
method public synchronized void setRoundingIncrement(java.math.BigDecimal);
method public synchronized void setRoundingIncrement(android.icu.math.BigDecimal);
method public synchronized void setRoundingIncrement(double);
+ method public synchronized void setRoundingMode(int);
method public synchronized void setScientificNotation(boolean);
method public synchronized void setSecondaryGroupingSize(int);
method public synchronized void setSignificantDigitsUsed(boolean);
@@ -32057,6 +32096,14 @@
field public static final int DETECT_VM_UNTAGGED_SOCKET = -2147483648; // 0x80000000
}
+ public static abstract interface StrictMode.OnThreadViolationListener {
+ method public abstract void onThreadViolation(android.os.strictmode.Violation);
+ }
+
+ public static abstract interface StrictMode.OnVmViolationListener {
+ method public abstract void onVmViolation(android.os.strictmode.Violation);
+ }
+
public static final class StrictMode.ThreadPolicy {
field public static final android.os.StrictMode.ThreadPolicy LAX;
}
@@ -32077,6 +32124,7 @@
method public android.os.StrictMode.ThreadPolicy.Builder penaltyDialog();
method public android.os.StrictMode.ThreadPolicy.Builder penaltyDropBox();
method public android.os.StrictMode.ThreadPolicy.Builder penaltyFlashScreen();
+ method public android.os.StrictMode.ThreadPolicy.Builder penaltyListener(android.os.StrictMode.OnThreadViolationListener, java.util.concurrent.Executor);
method public android.os.StrictMode.ThreadPolicy.Builder penaltyLog();
method public android.os.StrictMode.ThreadPolicy.Builder permitAll();
method public android.os.StrictMode.ThreadPolicy.Builder permitCustomSlowCalls();
@@ -32132,6 +32180,7 @@
method public android.os.StrictMode.VmPolicy.Builder penaltyDeathOnCleartextNetwork();
method public android.os.StrictMode.VmPolicy.Builder penaltyDeathOnFileUriExposure();
method public android.os.StrictMode.VmPolicy.Builder penaltyDropBox();
+ method public android.os.StrictMode.VmPolicy.Builder penaltyListener(android.os.StrictMode.OnVmViolationListener, java.util.concurrent.Executor);
method public android.os.StrictMode.VmPolicy.Builder penaltyLog();
method public android.os.StrictMode.VmPolicy.Builder setClassInstanceLimit(java.lang.Class, int);
}
@@ -32139,10 +32188,12 @@
public final class SystemClock {
method public static long currentThreadTimeMillis();
method public static long elapsedRealtime();
+ method public static java.time.Clock elapsedRealtimeClock();
method public static long elapsedRealtimeNanos();
method public static boolean setCurrentTimeMillis(long);
method public static void sleep(long);
method public static long uptimeMillis();
+ method public static java.time.Clock uptimeMillisClock();
}
public class TestLooperManager {
@@ -32227,6 +32278,7 @@
field public static final java.lang.String DISALLOW_CONFIG_CELL_BROADCASTS = "no_config_cell_broadcasts";
field public static final java.lang.String DISALLOW_CONFIG_CREDENTIALS = "no_config_credentials";
field public static final java.lang.String DISALLOW_CONFIG_DATE_TIME = "no_config_date_time";
+ field public static final java.lang.String DISALLOW_CONFIG_LOCALE = "no_config_locale";
field public static final java.lang.String DISALLOW_CONFIG_MOBILE_NETWORKS = "no_config_mobile_networks";
field public static final java.lang.String DISALLOW_CONFIG_TETHERING = "no_config_tethering";
field public static final java.lang.String DISALLOW_CONFIG_VPN = "no_config_vpn";
@@ -32255,6 +32307,7 @@
field public static final java.lang.String DISALLOW_UNINSTALL_APPS = "no_uninstall_apps";
field public static final java.lang.String DISALLOW_UNMUTE_MICROPHONE = "no_unmute_microphone";
field public static final java.lang.String DISALLOW_USB_FILE_TRANSFER = "no_usb_file_transfer";
+ field public static final java.lang.String DISALLOW_USER_SWITCH = "no_user_switch";
field public static final java.lang.String ENSURE_VERIFY_APPS = "ensure_verify_apps";
field public static final java.lang.String KEY_RESTRICTIONS_PENDING = "restrictions_pending";
field public static final int USER_CREATION_FAILED_NOT_PERMITTED = 1; // 0x1
@@ -32495,6 +32548,62 @@
}
+package android.os.strictmode {
+
+ public final class CleartextNetworkViolation extends android.os.strictmode.Violation {
+ }
+
+ public final class ContentUriWithoutPermissionViolation extends android.os.strictmode.Violation {
+ }
+
+ public final class CustomViolation extends android.os.strictmode.Violation {
+ }
+
+ public final class DiskReadViolation extends android.os.strictmode.Violation {
+ }
+
+ public final class DiskWriteViolation extends android.os.strictmode.Violation {
+ }
+
+ public final class FileUriExposedViolation extends android.os.strictmode.Violation {
+ }
+
+ public class InstanceCountViolation extends android.os.strictmode.Violation {
+ method public long getNumberOfInstances();
+ }
+
+ public final class IntentReceiverLeakedViolation extends android.os.strictmode.Violation {
+ }
+
+ public final class LeakedClosableViolation extends android.os.strictmode.Violation {
+ }
+
+ public final class NetworkViolation extends android.os.strictmode.Violation {
+ }
+
+ public final class ResourceMismatchViolation extends android.os.strictmode.Violation {
+ }
+
+ public final class ServiceConnectionLeakedViolation extends android.os.strictmode.Violation {
+ }
+
+ public final class SqliteObjectLeakedViolation extends android.os.strictmode.Violation {
+ }
+
+ public final class UnbufferedIoViolation extends android.os.strictmode.Violation {
+ }
+
+ public final class UntaggedSocketViolation extends android.os.strictmode.Violation {
+ }
+
+ public abstract class Violation extends java.lang.Throwable {
+ }
+
+ public final class WebViewMethodCalledOnWrongThreadViolation extends android.os.strictmode.Violation {
+ }
+
+}
+
package android.preference {
public class CheckBoxPreference extends android.preference.TwoStatePreference {
@@ -32781,7 +32890,7 @@
method public default void putStringSet(java.lang.String, java.util.Set<java.lang.String>);
}
- public abstract class PreferenceFragment extends android.app.Fragment {
+ public abstract deprecated class PreferenceFragment extends android.app.Fragment {
ctor public PreferenceFragment();
method public void addPreferencesFromIntent(android.content.Intent);
method public void addPreferencesFromResource(int);
@@ -32792,7 +32901,7 @@
method public void setPreferenceScreen(android.preference.PreferenceScreen);
}
- public static abstract interface PreferenceFragment.OnPreferenceStartFragmentCallback {
+ public static abstract deprecated interface PreferenceFragment.OnPreferenceStartFragmentCallback {
method public abstract boolean onPreferenceStartFragment(android.preference.PreferenceFragment, android.preference.Preference);
}
@@ -35545,6 +35654,7 @@
field public static final java.lang.String ALLOWED_GEOLOCATION_ORIGINS = "allowed_geolocation_origins";
field public static final deprecated java.lang.String ALLOW_MOCK_LOCATION = "mock_location";
field public static final java.lang.String ANDROID_ID = "android_id";
+ field public static final java.lang.String AUTOFILL_FEATURE_FIELD_DETECTION = "autofill_field_detection";
field public static final java.lang.String AUTOFILL_SERVICE = "autofill_service";
field public static final deprecated java.lang.String BACKGROUND_DATA = "background_data";
field public static final deprecated java.lang.String BLUETOOTH_ON = "bluetooth_on";
@@ -37600,6 +37710,13 @@
method public android.service.autofill.Dataset.Builder setValue(android.view.autofill.AutofillId, android.view.autofill.AutofillValue, java.util.regex.Pattern, android.widget.RemoteViews);
}
+ public final class FieldsDetection implements android.os.Parcelable {
+ ctor public FieldsDetection(android.view.autofill.AutofillId, java.lang.String, java.lang.String);
+ method public int describeContents();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.service.autofill.FieldsDetection> CREATOR;
+ }
+
public final class FillCallback {
method public void onFailure(java.lang.CharSequence);
method public void onSuccess(android.service.autofill.FillResponse);
@@ -37625,6 +37742,7 @@
method public java.util.Map<android.view.autofill.AutofillId, java.lang.String> getChangedFields();
method public android.os.Bundle getClientState();
method public java.lang.String getDatasetId();
+ method public java.util.Map<java.lang.String, java.lang.Integer> getDetectedFields();
method public java.util.Set<java.lang.String> getIgnoredDatasetIds();
method public java.util.Map<android.view.autofill.AutofillId, java.util.Set<java.lang.String>> getManuallyEnteredField();
method public java.util.Set<java.lang.String> getSelectedDatasetIds();
@@ -37662,6 +37780,7 @@
method public android.service.autofill.FillResponse.Builder disableAutofill(long);
method public android.service.autofill.FillResponse.Builder setAuthentication(android.view.autofill.AutofillId[], android.content.IntentSender, android.widget.RemoteViews);
method public android.service.autofill.FillResponse.Builder setClientState(android.os.Bundle);
+ method public android.service.autofill.FillResponse.Builder setFieldsDetection(android.service.autofill.FieldsDetection);
method public android.service.autofill.FillResponse.Builder setFlags(int);
method public android.service.autofill.FillResponse.Builder setIgnoredIds(android.view.autofill.AutofillId...);
method public android.service.autofill.FillResponse.Builder setSaveInfo(android.service.autofill.SaveInfo);
@@ -38809,6 +38928,16 @@
field public final int errno;
}
+ public class Int32Ref {
+ ctor public Int32Ref(int);
+ field public int value;
+ }
+
+ public class Int64Ref {
+ ctor public Int64Ref(long);
+ field public long value;
+ }
+
public final class Os {
method public static java.io.FileDescriptor accept(java.io.FileDescriptor, java.net.InetSocketAddress) throws android.system.ErrnoException, java.net.SocketException;
method public static boolean access(java.lang.String, int) throws android.system.ErrnoException;
@@ -38878,7 +39007,8 @@
method public static void remove(java.lang.String) throws android.system.ErrnoException;
method public static void removexattr(java.lang.String, java.lang.String) throws android.system.ErrnoException;
method public static void rename(java.lang.String, java.lang.String) throws android.system.ErrnoException;
- method public static long sendfile(java.io.FileDescriptor, java.io.FileDescriptor, android.util.MutableLong, long) throws android.system.ErrnoException;
+ method public static deprecated long sendfile(java.io.FileDescriptor, java.io.FileDescriptor, android.util.MutableLong, long) throws android.system.ErrnoException;
+ method public static long sendfile(java.io.FileDescriptor, java.io.FileDescriptor, android.system.Int64Ref, long) throws android.system.ErrnoException;
method public static int sendto(java.io.FileDescriptor, java.nio.ByteBuffer, int, java.net.InetAddress, int) throws android.system.ErrnoException, java.net.SocketException;
method public static int sendto(java.io.FileDescriptor, byte[], int, int, int, java.net.InetAddress, int) throws android.system.ErrnoException, java.net.SocketException;
method public static void setegid(int) throws android.system.ErrnoException;
@@ -38903,7 +39033,8 @@
method public static int umask(int);
method public static android.system.StructUtsname uname();
method public static void unsetenv(java.lang.String) throws android.system.ErrnoException;
- method public static int waitpid(int, android.util.MutableInt, int) throws android.system.ErrnoException;
+ method public static deprecated int waitpid(int, android.util.MutableInt, int) throws android.system.ErrnoException;
+ method public static int waitpid(int, android.system.Int32Ref, int) throws android.system.ErrnoException;
method public static int write(java.io.FileDescriptor, java.nio.ByteBuffer) throws android.system.ErrnoException, java.io.InterruptedIOException;
method public static int write(java.io.FileDescriptor, byte[], int, int) throws android.system.ErrnoException, java.io.InterruptedIOException;
method public static int writev(java.io.FileDescriptor, java.lang.Object[], int[], int[]) throws android.system.ErrnoException, java.io.InterruptedIOException;
@@ -39599,7 +39730,9 @@
ctor public CallAudioState(boolean, int, int);
method public static java.lang.String audioRouteToString(int);
method public int describeContents();
+ method public android.bluetooth.BluetoothDevice getActiveBluetoothDevice();
method public int getRoute();
+ method public java.util.Collection<android.bluetooth.BluetoothDevice> getSupportedBluetoothDevices();
method public int getSupportedRouteMask();
method public boolean isMuted();
method public void writeToParcel(android.os.Parcel, int);
@@ -39734,6 +39867,7 @@
method public final void putExtras(android.os.Bundle);
method public final void removeExtras(java.util.List<java.lang.String>);
method public final void removeExtras(java.lang.String...);
+ method public void requestBluetoothAudio(java.lang.String);
method public void sendConnectionEvent(java.lang.String, android.os.Bundle);
method public final void sendRemoteRttRequest();
method public final void sendRttInitiationFailure(int);
@@ -39937,6 +40071,7 @@
method public void onCanAddCallChanged(boolean);
method public void onConnectionEvent(android.telecom.Call, java.lang.String, android.os.Bundle);
method public void onSilenceRinger();
+ method public final void requestBluetoothAudio(java.lang.String);
method public final void setAudioRoute(int);
method public final void setMuted(boolean);
field public static final java.lang.String SERVICE_INTERFACE = "android.telecom.InCallService";
@@ -41326,6 +41461,7 @@
}
public final class FileInfo implements android.os.Parcelable {
+ ctor public FileInfo(android.net.Uri, java.lang.String);
method public int describeContents();
method public java.lang.String getMimeType();
method public android.net.Uri getUri();
@@ -41439,6 +41575,14 @@
field public static final android.os.Parcelable.Creator<android.telephony.mbms.StreamingServiceInfo> CREATOR;
}
+ public final class UriPathPair implements android.os.Parcelable {
+ method public int describeContents();
+ method public android.net.Uri getContentUri();
+ method public android.net.Uri getFilePathUri();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.telephony.mbms.UriPathPair> CREATOR;
+ }
+
}
package android.telephony.mbms.vendor {
@@ -41470,6 +41614,23 @@
method public void stopStreaming(int, java.lang.String) throws android.os.RemoteException;
}
+ public class VendorUtils {
+ ctor public VendorUtils();
+ method public static android.content.ComponentName getAppReceiverFromPackageName(android.content.Context, java.lang.String);
+ field public static final java.lang.String ACTION_CLEANUP = "android.telephony.mbms.action.CLEANUP";
+ field public static final java.lang.String ACTION_DOWNLOAD_RESULT_INTERNAL = "android.telephony.mbms.action.DOWNLOAD_RESULT_INTERNAL";
+ field public static final java.lang.String ACTION_FILE_DESCRIPTOR_REQUEST = "android.telephony.mbms.action.FILE_DESCRIPTOR_REQUEST";
+ field public static final java.lang.String EXTRA_FD_COUNT = "android.telephony.mbms.extra.FD_COUNT";
+ field public static final java.lang.String EXTRA_FINAL_URI = "android.telephony.mbms.extra.FINAL_URI";
+ field public static final java.lang.String EXTRA_FREE_URI_LIST = "android.telephony.mbms.extra.FREE_URI_LIST";
+ field public static final java.lang.String EXTRA_PAUSED_LIST = "android.telephony.mbms.extra.PAUSED_LIST";
+ field public static final java.lang.String EXTRA_PAUSED_URI_LIST = "android.telephony.mbms.extra.PAUSED_URI_LIST";
+ field public static final java.lang.String EXTRA_SERVICE_ID = "android.telephony.mbms.extra.SERVICE_ID";
+ field public static final java.lang.String EXTRA_TEMP_FILES_IN_USE = "android.telephony.mbms.extra.TEMP_FILES_IN_USE";
+ field public static final java.lang.String EXTRA_TEMP_FILE_ROOT = "android.telephony.mbms.extra.TEMP_FILE_ROOT";
+ field public static final java.lang.String EXTRA_TEMP_LIST = "android.telephony.mbms.extra.TEMP_LIST";
+ }
+
}
package android.test {
@@ -50500,7 +50661,7 @@
method public abstract void setHttpAuthUsernamePassword(java.lang.String, java.lang.String, java.lang.String, java.lang.String);
}
- public class WebViewFragment extends android.app.Fragment {
+ public deprecated class WebViewFragment extends android.app.Fragment {
ctor public WebViewFragment();
method public android.webkit.WebView getWebView();
}
diff --git a/cmds/am/src/com/android/commands/am/Instrument.java b/cmds/am/src/com/android/commands/am/Instrument.java
index 93b9f58..d79b1a6 100644
--- a/cmds/am/src/com/android/commands/am/Instrument.java
+++ b/cmds/am/src/com/android/commands/am/Instrument.java
@@ -47,6 +47,16 @@
/**
* Runs the am instrument command
+ *
+ * Test Result Code:
+ * 1 - Test running
+ * 0 - Test passed
+ * -2 - assertion failure
+ * -1 - other exceptions
+ *
+ * Session Result Code:
+ * -1: Success
+ * other: Failure
*/
public class Instrument {
public static final String DEFAULT_LOG_DIR = "instrument-logs";
diff --git a/cmds/incident_helper/src/ih_util.cpp b/cmds/incident_helper/src/ih_util.cpp
index c7d1ca2..0b51e66 100644
--- a/cmds/incident_helper/src/ih_util.cpp
+++ b/cmds/incident_helper/src/ih_util.cpp
@@ -22,19 +22,28 @@
#include <sstream>
#include <unistd.h>
-const ssize_t BUFFER_SIZE = 16 * 1024; // 4KB
+bool isValidChar(char c) {
+ uint8_t v = (uint8_t)c;
+ return (v >= (uint8_t)'a' && v <= (uint8_t)'z')
+ || (v >= (uint8_t)'A' && v <= (uint8_t)'Z')
+ || (v >= (uint8_t)'0' && v <= (uint8_t)'9')
+ || (v == (uint8_t)'_');
+}
-
-static std::string trim(const std::string& s) {
- const auto head = s.find_first_not_of(DEFAULT_WHITESPACE);
+static std::string trim(const std::string& s, const std::string& chars) {
+ const auto head = s.find_first_not_of(chars);
if (head == std::string::npos) return "";
- const auto tail = s.find_last_not_of(DEFAULT_WHITESPACE);
+ const auto tail = s.find_last_not_of(chars);
return s.substr(head, tail - head + 1);
}
+static std::string trimDefault(const std::string& s) {
+ return trim(s, DEFAULT_WHITESPACE);
+}
+
static std::string trimHeader(const std::string& s) {
- std::string res = trim(s);
+ std::string res = trimDefault(s);
std::transform(res.begin(), res.end(), res.begin(), ::tolower);
return res;
}
@@ -68,22 +77,68 @@
record_t parseRecord(const std::string& line, const std::string& delimiters) {
record_t record;
- trans_func f = &trim;
+ trans_func f = &trimDefault;
split(line, record, f, delimiters);
return record;
}
-bool hasPrefix(std::string* line, const char* key) {
+record_t parseRecordByColumns(const std::string& line, const std::vector<int>& indices, const std::string& delimiters) {
+ record_t record;
+ int lastIndex = 0;
+ int lineSize = (int)line.size();
+ for (std::vector<int>::const_iterator it = indices.begin(); it != indices.end(); ++it) {
+ int idx = *it;
+ if (lastIndex > idx || idx > lineSize) {
+ record.clear(); // The indices is wrong, return empty;
+ return record;
+ }
+ while (idx < lineSize && delimiters.find(line[idx++]) == std::string::npos);
+ record.push_back(trimDefault(line.substr(lastIndex, idx - lastIndex)));
+ lastIndex = idx;
+ }
+ record.push_back(trimDefault(line.substr(lastIndex, lineSize - lastIndex)));
+ return record;
+}
+
+bool stripPrefix(std::string* line, const char* key, bool endAtDelimiter) {
const auto head = line->find_first_not_of(DEFAULT_WHITESPACE);
if (head == std::string::npos) return false;
- auto i = 0;
- auto j = head;
+ int len = (int)line->length();
+ int i = 0;
+ int j = head;
while (key[i] != '\0') {
- if (j >= line->size() || key[i++] != line->at(j++)) {
+ if (j >= len || key[i++] != line->at(j++)) {
return false;
}
}
- line->assign(trim(line->substr(j)));
+
+ if (endAtDelimiter) {
+ // this means if the line only have prefix or no delimiter, we still return false.
+ if (j == len || isValidChar(line->at(j))) return false;
+ }
+
+ line->assign(trimDefault(line->substr(j)));
+ return true;
+}
+
+bool stripSuffix(std::string* line, const char* key, bool endAtDelimiter) {
+ const auto tail = line->find_last_not_of(DEFAULT_WHITESPACE);
+ if (tail == std::string::npos) return false;
+ int i = 0;
+ while (key[++i] != '\0'); // compute the size of the key
+ int j = tail;
+ while (i > 0) {
+ if (j < 0 || key[--i] != line->at(j--)) {
+ return false;
+ }
+ }
+
+ if (endAtDelimiter) {
+ // this means if the line only have suffix or no delimiter, we still return false.
+ if (j < 0 || isValidChar(line->at(j))) return false;
+ }
+
+ line->assign(trimDefault(line->substr(0, j+1)));
return true;
}
@@ -95,65 +150,36 @@
return atoll(s.c_str());
}
-// ==============================================================================
-Reader::Reader(const int fd) : Reader(fd, BUFFER_SIZE) {};
+double toDouble(const std::string& s) {
+ return atof(s.c_str());
+}
-Reader::Reader(const int fd, const size_t capacity)
- : mFd(fd), mMaxSize(capacity), mBufSize(0), mRead(0), mFlushed(0)
+// ==============================================================================
+Reader::Reader(const int fd)
{
- mBuf = capacity > 0 ? (char*)malloc(capacity * sizeof(char)) : NULL;
- mStatus = mFd < 0 ? "Negative fd" : (capacity == 0 ? "Zero buffer capacity" : "");
+ mFile = fdopen(fd, "r");
+ mStatus = mFile == NULL ? "Invalid fd " + std::to_string(fd) : "";
}
Reader::~Reader()
{
- free(mBuf);
+ if (mFile != NULL) fclose(mFile);
}
-bool Reader::readLine(std::string* line, const char newline) {
- if (!ok(line)) return false; // bad status
- line->clear();
- std::stringstream ss;
- while (!EOR()) {
- // read if available
- if (mFd != -1 && mBufSize != mMaxSize) {
- ssize_t amt = 0;
- if (mRead >= mFlushed) {
- amt = ::read(mFd, mBuf + mRead, mMaxSize - mRead);
- } else {
- amt = ::read(mFd, mBuf + mRead, mFlushed - mRead);
- }
- if (amt < 0) {
- mStatus = "Fail to read from fd";
- return false;
- } else if (amt == 0) {
- close(mFd);
- mFd = -1;
- }
- mRead += amt;
- mBufSize += amt;
- }
+bool Reader::readLine(std::string* line) {
+ if (mFile == NULL) return false;
- bool meetsNewLine = false;
- if (mBufSize > 0) {
- int start = mFlushed;
- int end = mFlushed < mRead ? mRead : mMaxSize;
- while (mFlushed < end && mBuf[mFlushed++] != newline && mBufSize > 0) mBufSize--;
- meetsNewLine = (mBuf[mFlushed-1] == newline);
- if (meetsNewLine) mBufSize--; // deduct the new line character
- size_t len = meetsNewLine ? mFlushed - start - 1 : mFlushed - start;
- ss.write(mBuf + start, len);
- }
-
- if (mRead >= (int) mMaxSize) mRead = 0;
- if (mFlushed >= (int) mMaxSize) mFlushed = 0;
-
- if (EOR() || meetsNewLine) {
- line->assign(ss.str());
- return true;
- }
+ char* buf = NULL;
+ size_t len = 0;
+ ssize_t read = getline(&buf, &len, mFile);
+ if (read != -1) {
+ std::string s(buf);
+ line->assign(trim(s, DEFAULT_NEWLINE));
+ } else if (errno == EINVAL) {
+ mStatus = "Bad Argument";
}
- return false;
+ free(buf);
+ return read != -1;
}
bool Reader::ok(std::string* error) {
@@ -162,10 +188,41 @@
}
// ==============================================================================
+static int
+lookupName(const char** names, const int size, const char* name)
+{
+ for (int i=0; i<size; i++) {
+ if (strcmp(name, names[i]) == 0) {
+ return i;
+ }
+ }
+ return -1;
+}
+
+EnumTypeMap::EnumTypeMap(const char* enumNames[], const uint32_t enumValues[], const int enumCount)
+ :mEnumNames(enumNames),
+ mEnumValues(enumValues),
+ mEnumCount(enumCount)
+{
+}
+
+EnumTypeMap::~EnumTypeMap()
+{
+}
+
+int
+EnumTypeMap::parseValue(const std::string& value)
+{
+ int index = lookupName(mEnumNames, mEnumCount, value.c_str());
+ if (index < 0) return mEnumValues[0]; // Assume value 0 is default
+ return mEnumValues[index];
+}
+
Table::Table(const char* names[], const uint64_t ids[], const int count)
:mFieldNames(names),
mFieldIds(ids),
- mFieldCount(count)
+ mFieldCount(count),
+ mEnums()
{
}
@@ -173,41 +230,54 @@
{
}
-bool
-Table::insertField(ProtoOutputStream& proto, const std::string& name, const std::string& value)
+void
+Table::addEnumTypeMap(const char* field, const char* enumNames[], const uint32_t enumValues[], const int enumSize)
{
- uint64_t found = 0;
- for (int i=0; i<mFieldCount; i++) {
- if (strcmp(name.c_str(), mFieldNames[i]) == 0) {
- found = mFieldIds[i];
- break;
- }
- }
+ int index = lookupName(mFieldNames, mFieldCount, field);
+ if (index < 0) return;
+ EnumTypeMap enu(enumNames, enumValues, enumSize);
+ mEnums[index] = enu;
+}
+
+bool
+Table::insertField(ProtoOutputStream* proto, const std::string& name, const std::string& value)
+{
+ int index = lookupName(mFieldNames, mFieldCount, name.c_str());
+ if (index < 0) return false;
+
+ uint64_t found = mFieldIds[index];
switch (found & FIELD_TYPE_MASK) {
case FIELD_TYPE_DOUBLE:
case FIELD_TYPE_FLOAT:
- // TODO: support parse string to float/double
- return false;
+ proto->write(found, toDouble(value));
+ break;
case FIELD_TYPE_STRING:
case FIELD_TYPE_BYTES:
- proto.write(found, value);
+ proto->write(found, value);
break;
case FIELD_TYPE_INT64:
case FIELD_TYPE_SINT64:
case FIELD_TYPE_UINT64:
case FIELD_TYPE_FIXED64:
case FIELD_TYPE_SFIXED64:
- proto.write(found, toLongLong(value));
+ proto->write(found, toLongLong(value));
break;
case FIELD_TYPE_BOOL:
+ return false;
case FIELD_TYPE_ENUM:
+ if (mEnums.find(index) == mEnums.end()) {
+ // forget to add enum type mapping
+ return false;
+ }
+ proto->write(found, mEnums[index].parseValue(value));
+ break;
case FIELD_TYPE_INT32:
case FIELD_TYPE_SINT32:
case FIELD_TYPE_UINT32:
case FIELD_TYPE_FIXED32:
case FIELD_TYPE_SFIXED32:
- proto.write(found, toInt(value));
+ proto->write(found, toInt(value));
break;
default:
return false;
diff --git a/cmds/incident_helper/src/ih_util.h b/cmds/incident_helper/src/ih_util.h
index 86761e9..e8366fa 100644
--- a/cmds/incident_helper/src/ih_util.h
+++ b/cmds/incident_helper/src/ih_util.h
@@ -17,9 +17,9 @@
#ifndef INCIDENT_HELPER_UTIL_H
#define INCIDENT_HELPER_UTIL_H
+#include <map>
#include <string>
#include <vector>
-#include <sstream>
#include <android/util/ProtoOutputStream.h>
@@ -29,8 +29,13 @@
typedef std::vector<std::string> record_t;
typedef std::string (*trans_func) (const std::string&);
-const char DEFAULT_NEWLINE = '\n';
const std::string DEFAULT_WHITESPACE = " \t";
+const std::string DEFAULT_NEWLINE = "\r\n";
+const std::string TAB_DELIMITER = "\t";
+const std::string COMMA_DELIMITER = ",";
+
+// returns true if c is a-zA-Z0-9 or underscore _
+bool isValidChar(char c);
/**
* When a text has a table format like this
@@ -47,19 +52,33 @@
record_t parseRecord(const std::string& line, const std::string& delimiters = DEFAULT_WHITESPACE);
/**
- * When the line starts with the given key, the function returns true
- * as well as the line argument is changed to the rest part of the original.
+ * When a text-format table aligns by its vertical position, it is not possible to split them by purely delimiters.
+ * This function allows to parse record by its header's column position' indices, must in ascending order.
+ * At the same time, it still looks at the char at index, if it doesn't belong to delimiters, moves forward to find the delimiters.
+ */
+record_t parseRecordByColumns(const std::string& line, const std::vector<int>& indices, const std::string& delimiters = DEFAULT_WHITESPACE);
+
+/**
+ * When the line starts/ends with the given key, the function returns true
+ * as well as the line argument is changed to the rest trimmed part of the original.
* e.g. "ZRAM: 6828K physical used for 31076K in swap (524284K total swap)" becomes
* "6828K physical used for 31076K in swap (524284K total swap)" when given key "ZRAM:",
* otherwise the line is not changed.
+ *
+ * In order to prevent two values have same prefix which cause entering to incorrect conditions,
+ * stripPrefix and stripSuffix can turn on a flag that requires the ending char in the line must not be a valid
+ * character or digits, this feature is off by default.
+ * i.e. ABC%some value, ABCD%other value
*/
-bool hasPrefix(std::string* line, const char* key);
+bool stripPrefix(std::string* line, const char* key, bool endAtDelimiter = false);
+bool stripSuffix(std::string* line, const char* key, bool endAtDelimiter = false);
/**
* Converts string to the desired type
*/
int toInt(const std::string& s);
long long toLongLong(const std::string& s);
+double toDouble(const std::string& s);
/**
* Reader class reads data from given fd in streaming fashion.
@@ -69,23 +88,29 @@
{
public:
Reader(const int fd);
- Reader(const int fd, const size_t capacity);
~Reader();
- bool readLine(std::string* line, const char newline = DEFAULT_NEWLINE);
+ bool readLine(std::string* line);
bool ok(std::string* error);
private:
- int mFd; // set mFd to -1 when read EOF()
- const size_t mMaxSize;
- size_t mBufSize;
- char* mBuf; // implements a circular buffer
-
- int mRead;
- int mFlushed;
+ FILE* mFile;
std::string mStatus;
- // end of read
- inline bool EOR() { return mFd == -1 && mBufSize == 0; };
+};
+
+class EnumTypeMap
+{
+public:
+ EnumTypeMap() {};
+ EnumTypeMap(const char* enumNames[], const uint32_t enumValues[], const int enumCount);
+ ~EnumTypeMap();
+
+ int parseValue(const std::string& value);
+
+private:
+ const char** mEnumNames;
+ const uint32_t* mEnumValues;
+ int mEnumCount;
};
/**
@@ -98,12 +123,15 @@
Table(const char* names[], const uint64_t ids[], const int count);
~Table();
- bool insertField(ProtoOutputStream& proto, const std::string& name, const std::string& value);
+ // Add enum names to values for parsing purpose.
+ void addEnumTypeMap(const char* field, const char* enumNames[], const uint32_t enumValues[], const int enumSize);
+ bool insertField(ProtoOutputStream* proto, const std::string& name, const std::string& value);
private:
const char** mFieldNames;
const uint64_t* mFieldIds;
const int mFieldCount;
+ map<int, EnumTypeMap> mEnums;
};
#endif // INCIDENT_HELPER_UTIL_H
diff --git a/cmds/incident_helper/src/main.cpp b/cmds/incident_helper/src/main.cpp
index 3da87b9c..5ebe9bd 100644
--- a/cmds/incident_helper/src/main.cpp
+++ b/cmds/incident_helper/src/main.cpp
@@ -16,6 +16,7 @@
#define LOG_TAG "incident_helper"
+#include "parsers/CpuInfoParser.h"
#include "parsers/KernelWakesParser.h"
#include "parsers/PageTypeInfoParser.h"
#include "parsers/ProcrankParser.h"
@@ -54,6 +55,8 @@
return new PageTypeInfoParser();
case 2002:
return new KernelWakesParser();
+ case 2003:
+ return new CpuInfoParser();
default:
return NULL;
}
diff --git a/cmds/incident_helper/src/parsers/CpuInfoParser.cpp b/cmds/incident_helper/src/parsers/CpuInfoParser.cpp
new file mode 100644
index 0000000..3faca00
--- /dev/null
+++ b/cmds/incident_helper/src/parsers/CpuInfoParser.cpp
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#define LOG_TAG "incident_helper"
+
+#include <android/util/ProtoOutputStream.h>
+
+#include "frameworks/base/core/proto/android/os/cpuinfo.proto.h"
+#include "ih_util.h"
+#include "CpuInfoParser.h"
+
+using namespace android::os;
+
+static void writeSuffixLine(ProtoOutputStream* proto, uint64_t fieldId,
+ const string& line, const string& delimiter,
+ const int count, const char* names[], const uint64_t ids[])
+{
+ record_t record = parseRecord(line, delimiter);
+ long long token = proto->start(fieldId);
+ for (int i=0; i<(int)record.size(); i++) {
+ for (int j=0; j<count; j++) {
+ if (stripSuffix(&record[i], names[j], true)) {
+ proto->write(ids[j], toInt(record[i]));
+ break;
+ }
+ }
+ }
+ proto->end(token);
+}
+
+status_t
+CpuInfoParser::Parse(const int in, const int out) const
+{
+ Reader reader(in);
+ string line;
+ header_t header;
+ vector<int> columnIndices; // task table can't be split by purely delimiter, needs column positions.
+ record_t record;
+ int nline = 0;
+ bool nextToSwap = false;
+ bool nextToUsage = false;
+
+ ProtoOutputStream proto;
+ Table table(CpuInfo::Task::_FIELD_NAMES, CpuInfo::Task::_FIELD_IDS, CpuInfo::Task::_FIELD_COUNT);
+ table.addEnumTypeMap("s", CpuInfo::Task::_ENUM_STATUS_NAMES,
+ CpuInfo::Task::_ENUM_STATUS_VALUES, CpuInfo::Task::_ENUM_STATUS_COUNT);
+ table.addEnumTypeMap("pcy", CpuInfo::Task::_ENUM_POLICY_NAMES,
+ CpuInfo::Task::_ENUM_POLICY_VALUES, CpuInfo::Task::_ENUM_POLICY_COUNT);
+
+ // parse line by line
+ while (reader.readLine(&line)) {
+ if (line.empty()) continue;
+
+ nline++;
+
+ if (stripPrefix(&line, "Tasks:")) {
+ writeSuffixLine(&proto, CpuInfo::TASK_STATS, line, COMMA_DELIMITER,
+ CpuInfo::TaskStats::_FIELD_COUNT,
+ CpuInfo::TaskStats::_FIELD_NAMES,
+ CpuInfo::TaskStats::_FIELD_IDS);
+ continue;
+ }
+ if (stripPrefix(&line, "Mem:")) {
+ writeSuffixLine(&proto, CpuInfo::MEM, line, COMMA_DELIMITER,
+ CpuInfo::MemStats::_FIELD_COUNT,
+ CpuInfo::MemStats::_FIELD_NAMES,
+ CpuInfo::MemStats::_FIELD_IDS);
+ continue;
+ }
+ if (stripPrefix(&line, "Swap:")) {
+ writeSuffixLine(&proto, CpuInfo::SWAP, line, COMMA_DELIMITER,
+ CpuInfo::MemStats::_FIELD_COUNT,
+ CpuInfo::MemStats::_FIELD_NAMES,
+ CpuInfo::MemStats::_FIELD_IDS);
+ nextToSwap = true;
+ continue;
+ }
+
+ if (nextToSwap) {
+ writeSuffixLine(&proto, CpuInfo::CPU_USAGE, line, DEFAULT_WHITESPACE,
+ CpuInfo::CpuUsage::_FIELD_COUNT,
+ CpuInfo::CpuUsage::_FIELD_NAMES,
+ CpuInfo::CpuUsage::_FIELD_IDS);
+ nextToUsage = true;
+ nextToSwap = false;
+ continue;
+ }
+
+ // Header of tasks must be next to usage line
+ if (nextToUsage) {
+ // How to parse Header of Tasks:
+ // PID TID USER PR NI[%CPU]S VIRT RES PCY CMD NAME
+ // After parsing, header = { PID, TID, USER, PR, NI, CPU, S, VIRT, RES, PCY, CMD, NAME }
+ // And columnIndices will contain end index of each word.
+ header = parseHeader(line, "[ %]");
+ nextToUsage = false;
+
+ // NAME is not in the list since the last split index is default to the end of line.
+ const char* headerNames[11] = { "PID", "TID", "USER", "PR", "NI", "CPU", "S", "VIRT", "RES", "PCY", "CMD" };
+ size_t lastIndex = 0;
+ for (int i = 0; i < 11; i++) {
+ string s = headerNames[i];
+ lastIndex = line.find(s, lastIndex);
+ if (lastIndex == string::npos) {
+ fprintf(stderr, "Bad Task Header: %s\n", line.c_str());
+ return -1;
+ }
+ lastIndex += s.length();
+ columnIndices.push_back(lastIndex);
+ }
+ // Need to remove the end index of CMD and use the start index of NAME because CMD values contain spaces.
+ // for example: ... CMD NAME
+ // ... Jit thread pool com.google.android.gms.feedback
+ // If use end index of CMD, parsed result = { "Jit", "thread pool com.google.android.gms.feedback" }
+ // If use start index of NAME, parsed result = { "Jit thread pool", "com.google.android.gms.feedback" }
+ int endCMD = columnIndices.back();
+ columnIndices.pop_back();
+ columnIndices.push_back(line.find("NAME", endCMD) - 1);
+ continue;
+ }
+
+ record = parseRecordByColumns(line, columnIndices);
+ if (record.size() != header.size()) {
+ fprintf(stderr, "[%s]Line %d has missing fields:\n%s\n", this->name.string(), nline, line.c_str());
+ continue;
+ }
+
+ long long token = proto.start(CpuInfo::TASKS);
+ for (int i=0; i<(int)record.size(); i++) {
+ if (!table.insertField(&proto, header[i], record[i])) {
+ fprintf(stderr, "[%s]Line %d fails to insert field %s with value %s\n",
+ this->name.string(), nline, header[i].c_str(), record[i].c_str());
+ }
+ }
+ proto.end(token);
+ }
+
+ if (!reader.ok(&line)) {
+ fprintf(stderr, "Bad read from fd %d: %s\n", in, line.c_str());
+ return -1;
+ }
+
+ if (!proto.flush(out)) {
+ fprintf(stderr, "[%s]Error writing proto back\n", this->name.string());
+ return -1;
+ }
+ fprintf(stderr, "[%s]Proto size: %zu bytes\n", this->name.string(), proto.size());
+ return NO_ERROR;
+}
diff --git a/cmds/incident_helper/src/parsers/CpuInfoParser.h b/cmds/incident_helper/src/parsers/CpuInfoParser.h
new file mode 100644
index 0000000..f57bb4e
--- /dev/null
+++ b/cmds/incident_helper/src/parsers/CpuInfoParser.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef CPU_INFO_PARSER_H
+#define CPU_INFO_PARSER_H
+
+#include "TextParserBase.h"
+
+using namespace android;
+
+/**
+ * Cpu info parser, parses text produced by command
+ * 'top -b -n 1 -H -s 6 -o pid,tid,user,pr,ni,%cpu,s,virt,res,pcy,cmd,name'
+ */
+class CpuInfoParser : public TextParserBase {
+public:
+ CpuInfoParser() : TextParserBase(String8("CpuInfoParser")) {};
+ ~CpuInfoParser() {};
+
+ virtual status_t Parse(const int in, const int out) const;
+};
+
+#endif // CPU_INFO_PARSER_H
diff --git a/cmds/incident_helper/src/parsers/KernelWakesParser.cpp b/cmds/incident_helper/src/parsers/KernelWakesParser.cpp
index cc4a1e1..ada4a5d 100644
--- a/cmds/incident_helper/src/parsers/KernelWakesParser.cpp
+++ b/cmds/incident_helper/src/parsers/KernelWakesParser.cpp
@@ -23,8 +23,6 @@
using namespace android::os;
-const std::string LINE_DELIMITER = "\t";
-
status_t
KernelWakesParser::Parse(const int in, const int out) const
{
@@ -42,12 +40,12 @@
if (line.empty()) continue;
// parse head line
if (nline++ == 0) {
- header = parseHeader(line, LINE_DELIMITER);
+ header = parseHeader(line, TAB_DELIMITER);
continue;
}
// parse for each record, the line delimiter is \t only!
- record = parseRecord(line, LINE_DELIMITER);
+ record = parseRecord(line, TAB_DELIMITER);
if (record.size() != header.size()) {
// TODO: log this to incident report!
@@ -57,7 +55,7 @@
long long token = proto.start(KernelWakeSources::WAKEUP_SOURCES);
for (int i=0; i<(int)record.size(); i++) {
- if (!table.insertField(proto, header[i], record[i])) {
+ if (!table.insertField(&proto, header[i], record[i])) {
fprintf(stderr, "[%s]Line %d has bad value %s of %s\n",
this->name.string(), nline, header[i].c_str(), record[i].c_str());
}
diff --git a/cmds/incident_helper/src/parsers/PageTypeInfoParser.cpp b/cmds/incident_helper/src/parsers/PageTypeInfoParser.cpp
index 6047bd1..f1b93ff 100644
--- a/cmds/incident_helper/src/parsers/PageTypeInfoParser.cpp
+++ b/cmds/incident_helper/src/parsers/PageTypeInfoParser.cpp
@@ -23,8 +23,6 @@
using namespace android::os;
-const std::string LINE_DELIMITER = ",";
-
status_t
PageTypeInfoParser::Parse(const int in, const int out) const
{
@@ -44,37 +42,37 @@
continue;
}
- if (hasPrefix(&line, "Page block order:")) {
+ if (stripPrefix(&line, "Page block order:")) {
pageBlockOrder = toInt(line);
proto.write(PageTypeInfo::PAGE_BLOCK_ORDER, pageBlockOrder);
continue;
}
- if (hasPrefix(&line, "Pages per block:")) {
+ if (stripPrefix(&line, "Pages per block:")) {
proto.write(PageTypeInfo::PAGES_PER_BLOCK, toInt(line));
continue;
}
- if (hasPrefix(&line, "Free pages count per migrate type at order")) {
+ if (stripPrefix(&line, "Free pages count per migrate type at order")) {
migrateTypeSession = true;
continue;
}
- if (hasPrefix(&line, "Number of blocks type")) {
+ if (stripPrefix(&line, "Number of blocks type")) {
blockHeader = parseHeader(line);
continue;
}
- record_t record = parseRecord(line, LINE_DELIMITER);
+ record_t record = parseRecord(line, COMMA_DELIMITER);
if (migrateTypeSession && record.size() == 3) {
long long token = proto.start(PageTypeInfo::MIGRATE_TYPES);
// expect part 0 starts with "Node"
- if (hasPrefix(&record[0], "Node")) {
+ if (stripPrefix(&record[0], "Node")) {
proto.write(MigrateTypeProto::NODE, toInt(record[0]));
} else return BAD_VALUE;
// expect part 1 starts with "zone"
- if (hasPrefix(&record[1], "zone")) {
+ if (stripPrefix(&record[1], "zone")) {
proto.write(MigrateTypeProto::ZONE, record[1]);
} else return BAD_VALUE;
// expect part 2 starts with "type"
- if (hasPrefix(&record[2], "type")) {
+ if (stripPrefix(&record[2], "type")) {
// expect the rest of part 2 has number of (pageBlockOrder + 2) parts
// An example looks like:
// header line: type 0 1 2 3 4 5 6 7 8 9 10
@@ -94,16 +92,16 @@
proto.end(token);
} else if (!blockHeader.empty() && record.size() == 2) {
long long token = proto.start(PageTypeInfo::BLOCKS);
- if (hasPrefix(&record[0], "Node")) {
+ if (stripPrefix(&record[0], "Node")) {
proto.write(BlockProto::NODE, toInt(record[0]));
} else return BAD_VALUE;
- if (hasPrefix(&record[1], "zone")) {
+ if (stripPrefix(&record[1], "zone")) {
record_t blockCounts = parseRecord(record[1]);
proto.write(BlockProto::ZONE, blockCounts[0]);
for (size_t i=0; i<blockHeader.size(); i++) {
- if (!table.insertField(proto, blockHeader[i], blockCounts[i+1])) {
+ if (!table.insertField(&proto, blockHeader[i], blockCounts[i+1])) {
return BAD_VALUE;
}
}
diff --git a/cmds/incident_helper/src/parsers/ProcrankParser.cpp b/cmds/incident_helper/src/parsers/ProcrankParser.cpp
index 93f970f..a4eb0fd 100644
--- a/cmds/incident_helper/src/parsers/ProcrankParser.cpp
+++ b/cmds/incident_helper/src/parsers/ProcrankParser.cpp
@@ -46,11 +46,11 @@
continue;
}
- if (hasPrefix(&line, "ZRAM:")) {
+ if (stripPrefix(&line, "ZRAM:")) {
zram = line;
continue;
}
- if (hasPrefix(&line, "RAM:")) {
+ if (stripPrefix(&line, "RAM:")) {
ram = line;
continue;
}
@@ -68,7 +68,7 @@
long long token = proto.start(Procrank::PROCESSES);
for (int i=0; i<(int)record.size(); i++) {
- if (!table.insertField(proto, header[i], record[i])) {
+ if (!table.insertField(&proto, header[i], record[i])) {
fprintf(stderr, "[%s]Line %d has bad value %s of %s\n",
this->name.string(), nline, header[i].c_str(), record[i].c_str());
}
@@ -82,7 +82,7 @@
record = parseRecord(total);
long long token = proto.start(SummaryProto::TOTAL);
for (int i=(int)record.size(); i>0; i--) {
- table.insertField(proto, header[header.size() - i].c_str(), record[record.size() - i].c_str());
+ table.insertField(&proto, header[header.size() - i].c_str(), record[record.size() - i].c_str());
}
proto.end(token);
}
diff --git a/cmds/incident_helper/testdata/cpuinfo.txt b/cmds/incident_helper/testdata/cpuinfo.txt
new file mode 100644
index 0000000..ec4a839
--- /dev/null
+++ b/cmds/incident_helper/testdata/cpuinfo.txt
@@ -0,0 +1,15 @@
+Tasks: 2038 total, 1 running,2033 sleeping, 0 stopped, 0 zombie
+
+Mem: 3842668k total, 3761936k used, 80732k free, 220188k buffers
+
+Swap: 524284k total, 25892k used, 498392k free, 1316952k cached
+
+400%cpu 17%user 0%nice 43%sys 338%idle 0%iow 0%irq 1%sirq 0%host
+
+ PID TID USER PR NI[%CPU]S VIRT RES PCY CMD NAME
+
+
+29438 29438 rootabcdefghij 20 0 57.9 R 14M 3.8M top test top
+ 916 916 system 18 -2 1.4 S 4.6G 404M fg system_server system_server
+ 28 28 root -2 0 1.4 S 0 0 bg rcuc/3 [rcuc/3]
+ 27 27 root RT 0 1.4 S 0 0 ta migration/3 [migration/3]
\ No newline at end of file
diff --git a/cmds/incident_helper/tests/CpuInfoParser_test.cpp b/cmds/incident_helper/tests/CpuInfoParser_test.cpp
new file mode 100644
index 0000000..57ad15c
--- /dev/null
+++ b/cmds/incident_helper/tests/CpuInfoParser_test.cpp
@@ -0,0 +1,158 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "CpuInfoParser.h"
+
+#include "frameworks/base/core/proto/android/os/cpuinfo.pb.h"
+
+#include <android-base/file.h>
+#include <android-base/test_utils.h>
+#include <gmock/gmock.h>
+#include <google/protobuf/message.h>
+#include <gtest/gtest.h>
+#include <string.h>
+#include <fcntl.h>
+
+using namespace android::base;
+using namespace android::os;
+using namespace std;
+using ::testing::StrEq;
+using ::testing::Test;
+using ::testing::internal::CaptureStderr;
+using ::testing::internal::CaptureStdout;
+using ::testing::internal::GetCapturedStderr;
+using ::testing::internal::GetCapturedStdout;
+
+class CpuInfoParserTest : public Test {
+public:
+ virtual void SetUp() override {
+ ASSERT_TRUE(tf.fd != -1);
+ }
+
+ string getSerializedString(::google::protobuf::Message& message) {
+ string expectedStr;
+ message.SerializeToFileDescriptor(tf.fd);
+ ReadFileToString(tf.path, &expectedStr);
+ return expectedStr;
+ }
+
+protected:
+ TemporaryFile tf;
+
+ const string kTestPath = GetExecutableDirectory();
+ const string kTestDataPath = kTestPath + "/testdata/";
+};
+
+TEST_F(CpuInfoParserTest, HasSwapInfo) {
+ const string testFile = kTestDataPath + "cpuinfo.txt";
+ CpuInfoParser parser;
+ CpuInfo expected;
+
+ CpuInfo::TaskStats* taskStats = expected.mutable_task_stats();
+ taskStats->set_total(2038);
+ taskStats->set_running(1);
+ taskStats->set_sleeping(2033);
+ taskStats->set_stopped(0);
+ taskStats->set_zombie(0);
+
+ CpuInfo::MemStats* mem = expected.mutable_mem();
+ mem->set_total(3842668);
+ mem->set_used(3761936);
+ mem->set_free(80732);
+ mem->set_buffers(220188);
+
+ CpuInfo::MemStats* swap = expected.mutable_swap();
+ swap->set_total(524284);
+ swap->set_used(25892);
+ swap->set_free(498392);
+ swap->set_cached(1316952);
+
+ CpuInfo::CpuUsage* usage = expected.mutable_cpu_usage();
+ usage->set_cpu(400);
+ usage->set_user(17);
+ usage->set_nice(0);
+ usage->set_sys(43);
+ usage->set_idle(338);
+ usage->set_iow(0);
+ usage->set_irq(0);
+ usage->set_sirq(1);
+ usage->set_host(0);
+
+ // This is a special line which is able to be parsed by the CpuInfoParser
+ CpuInfo::Task* task1 = expected.add_tasks();
+ task1->set_pid(29438);
+ task1->set_tid(29438);
+ task1->set_user("rootabcdefghij");
+ task1->set_pr("20");
+ task1->set_ni(0);
+ task1->set_cpu(57.9);
+ task1->set_s(CpuInfo::Task::STATUS_R);
+ task1->set_virt("14M");
+ task1->set_res("3.8M");
+ task1->set_pcy(CpuInfo::Task::POLICY_UNKNOWN);
+ task1->set_cmd("top test");
+ task1->set_name("top");
+
+ CpuInfo::Task* task2 = expected.add_tasks();
+ task2->set_pid(916);
+ task2->set_tid(916);
+ task2->set_user("system");
+ task2->set_pr("18");
+ task2->set_ni(-2);
+ task2->set_cpu(1.4);
+ task2->set_s(CpuInfo::Task::STATUS_S);
+ task2->set_virt("4.6G");
+ task2->set_res("404M");
+ task2->set_pcy(CpuInfo::Task::POLICY_fg);
+ task2->set_cmd("system_server");
+ task2->set_name("system_server");
+
+ CpuInfo::Task* task3 = expected.add_tasks();
+ task3->set_pid(28);
+ task3->set_tid(28);
+ task3->set_user("root");
+ task3->set_pr("-2");
+ task3->set_ni(0);
+ task3->set_cpu(1.4);
+ task3->set_s(CpuInfo::Task::STATUS_S);
+ task3->set_virt("0");
+ task3->set_res("0");
+ task3->set_pcy(CpuInfo::Task::POLICY_bg);
+ task3->set_cmd("rcuc/3");
+ task3->set_name("[rcuc/3]");
+
+ CpuInfo::Task* task4 = expected.add_tasks();
+ task4->set_pid(27);
+ task4->set_tid(27);
+ task4->set_user("root");
+ task4->set_pr("RT");
+ task4->set_ni(0);
+ task4->set_cpu(1.4);
+ task4->set_s(CpuInfo::Task::STATUS_S);
+ task4->set_virt("0");
+ task4->set_res("0");
+ task4->set_pcy(CpuInfo::Task::POLICY_ta);
+ task4->set_cmd("migration/3");
+ task4->set_name("[migration/3]");
+
+ int fd = open(testFile.c_str(), O_RDONLY);
+ ASSERT_TRUE(fd != -1);
+
+ CaptureStdout();
+ ASSERT_EQ(NO_ERROR, parser.Parse(fd, STDOUT_FILENO));
+ EXPECT_EQ(GetCapturedStdout(), getSerializedString(expected));
+ close(fd);
+}
diff --git a/cmds/incident_helper/tests/ih_util_test.cpp b/cmds/incident_helper/tests/ih_util_test.cpp
index da88ee3..5740b33 100644
--- a/cmds/incident_helper/tests/ih_util_test.cpp
+++ b/cmds/incident_helper/tests/ih_util_test.cpp
@@ -62,10 +62,63 @@
EXPECT_EQ(expected, result);
}
+TEST(IhUtilTest, ParseRecordByColumns) {
+ record_t result, expected;
+ std::vector<int> indices = { 3, 10 };
+
+ result = parseRecordByColumns("12345", indices);
+ expected = {};
+ EXPECT_EQ(expected, result);
+
+ result = parseRecordByColumns("abc \t2345 6789 ", indices);
+ expected = { "abc", "2345", "6789" };
+ EXPECT_EQ(expected, result);
+
+ result = parseRecordByColumns("abc \t23456789 bob", indices);
+ expected = { "abc", "23456789", "bob" };
+ EXPECT_EQ(expected, result);
+}
+
+TEST(IhUtilTest, stripPrefix) {
+ string data1 = "Swap: abc ";
+ EXPECT_TRUE(stripPrefix(&data1, "Swap:"));
+ EXPECT_THAT(data1, StrEq("abc"));
+
+ string data2 = "Swap: abc ";
+ EXPECT_FALSE(stripPrefix(&data2, "Total:"));
+ EXPECT_THAT(data2, StrEq("Swap: abc "));
+
+ string data3 = "Swap: abc ";
+ EXPECT_TRUE(stripPrefix(&data3, "Swa"));
+ EXPECT_THAT(data3, StrEq("p: abc"));
+
+ string data4 = "Swap: abc ";
+ EXPECT_FALSE(stripPrefix(&data4, "Swa", true));
+ EXPECT_THAT(data4, StrEq("Swap: abc "));
+}
+
+TEST(IhUtilTest, stripSuffix) {
+ string data1 = " 243%abc";
+ EXPECT_TRUE(stripSuffix(&data1, "abc"));
+ EXPECT_THAT(data1, StrEq("243%"));
+
+ string data2 = " 243%abc";
+ EXPECT_FALSE(stripSuffix(&data2, "Not right"));
+ EXPECT_THAT(data2, StrEq(" 243%abc"));
+
+ string data3 = " 243%abc";
+ EXPECT_TRUE(stripSuffix(&data3, "bc"));
+ EXPECT_THAT(data3, StrEq("243%a"));
+
+ string data4 = " 243%abc";
+ EXPECT_FALSE(stripSuffix(&data4, "bc", true));
+ EXPECT_THAT(data4, StrEq(" 243%abc"));
+}
+
TEST(IhUtilTest, Reader) {
TemporaryFile tf;
ASSERT_NE(tf.fd, -1);
- ASSERT_TRUE(WriteStringToFile("test string\nsecond\nooo\n", tf.path, false));
+ ASSERT_TRUE(WriteStringToFile("test string\nsecond\nooo\n", tf.path));
Reader r(tf.fd);
string line;
@@ -79,40 +132,22 @@
ASSERT_TRUE(r.ok(&line));
}
-TEST(IhUtilTest, ReaderSmallBufSize) {
- TemporaryFile tf;
- ASSERT_NE(tf.fd, -1);
- ASSERT_TRUE(WriteStringToFile("test string\nsecond\nooiecccojreo", tf.path, false));
-
- Reader r(tf.fd, 5);
- string line;
- ASSERT_TRUE(r.readLine(&line));
- EXPECT_THAT(line, StrEq("test string"));
- ASSERT_TRUE(r.readLine(&line));
- EXPECT_THAT(line, StrEq("second"));
- ASSERT_TRUE(r.readLine(&line));
- EXPECT_THAT(line, StrEq("ooiecccojreo"));
- ASSERT_FALSE(r.readLine(&line));
- ASSERT_TRUE(r.ok(&line));
-}
-
TEST(IhUtilTest, ReaderEmpty) {
TemporaryFile tf;
ASSERT_NE(tf.fd, -1);
- ASSERT_TRUE(WriteStringToFile("", tf.path, false));
+ ASSERT_TRUE(WriteStringToFile("", tf.path));
Reader r(tf.fd);
string line;
- ASSERT_TRUE(r.readLine(&line));
- EXPECT_THAT(line, StrEq(""));
ASSERT_FALSE(r.readLine(&line));
+ EXPECT_THAT(line, StrEq(""));
ASSERT_TRUE(r.ok(&line));
}
TEST(IhUtilTest, ReaderMultipleEmptyLines) {
TemporaryFile tf;
ASSERT_NE(tf.fd, -1);
- ASSERT_TRUE(WriteStringToFile("\n\n", tf.path, false));
+ ASSERT_TRUE(WriteStringToFile("\n\n", tf.path));
Reader r(tf.fd);
string line;
@@ -130,15 +165,7 @@
string line;
EXPECT_FALSE(r.readLine(&line));
EXPECT_FALSE(r.ok(&line));
- EXPECT_THAT(line, StrEq("Negative fd"));
-}
-
-TEST(IhUtilTest, ReaderFailedZeroBufferSize) {
- Reader r(23, 0);
- string line;
- EXPECT_FALSE(r.readLine(&line));
- EXPECT_FALSE(r.ok(&line));
- EXPECT_THAT(line, StrEq("Zero buffer capacity"));
+ EXPECT_THAT(line, StrEq("Invalid fd -123"));
}
TEST(IhUtilTest, ReaderFailedBadFd) {
@@ -146,5 +173,5 @@
string line;
EXPECT_FALSE(r.readLine(&line));
EXPECT_FALSE(r.ok(&line));
- EXPECT_THAT(line, StrEq("Fail to read from fd"));
+ EXPECT_THAT(line, StrEq("Invalid fd 1231432"));
}
diff --git a/cmds/incidentd/tests/FdBuffer_test.cpp b/cmds/incidentd/tests/FdBuffer_test.cpp
index 2afa778..3fd2ed8 100644
--- a/cmds/incidentd/tests/FdBuffer_test.cpp
+++ b/cmds/incidentd/tests/FdBuffer_test.cpp
@@ -84,7 +84,7 @@
TEST_F(FdBufferTest, ReadAndWrite) {
std::string testdata = "FdBuffer test string";
- ASSERT_TRUE(WriteStringToFile(testdata, tf.path, false));
+ ASSERT_TRUE(WriteStringToFile(testdata, tf.path));
ASSERT_EQ(NO_ERROR, buffer.read(tf.fd, READ_TIMEOUT));
AssertBufferReadSuccessful(testdata.size());
AssertBufferContent(testdata.c_str());
@@ -97,7 +97,7 @@
TEST_F(FdBufferTest, ReadAndIterate) {
std::string testdata = "FdBuffer test string";
- ASSERT_TRUE(WriteStringToFile(testdata, tf.path, false));
+ ASSERT_TRUE(WriteStringToFile(testdata, tf.path));
ASSERT_EQ(NO_ERROR, buffer.read(tf.fd, READ_TIMEOUT));
int i=0;
@@ -137,7 +137,7 @@
TEST_F(FdBufferTest, ReadInStreamAndWrite) {
std::string testdata = "simply test read in stream";
std::string expected = HEAD + testdata;
- ASSERT_TRUE(WriteStringToFile(testdata, tf.path, false));
+ ASSERT_TRUE(WriteStringToFile(testdata, tf.path));
int pid = fork();
ASSERT_TRUE(pid != -1);
@@ -166,7 +166,7 @@
TEST_F(FdBufferTest, ReadInStreamAndWriteAllAtOnce) {
std::string testdata = "child process flushes only after all data are read.";
std::string expected = HEAD + testdata;
- ASSERT_TRUE(WriteStringToFile(testdata, tf.path, false));
+ ASSERT_TRUE(WriteStringToFile(testdata, tf.path));
int pid = fork();
ASSERT_TRUE(pid != -1);
@@ -196,7 +196,7 @@
}
TEST_F(FdBufferTest, ReadInStreamEmpty) {
- ASSERT_TRUE(WriteStringToFile("", tf.path, false));
+ ASSERT_TRUE(WriteStringToFile("", tf.path));
int pid = fork();
ASSERT_TRUE(pid != -1);
@@ -260,7 +260,7 @@
TEST_F(FdBufferTest, ReadInStreamTimeOut) {
std::string testdata = "timeout test";
- ASSERT_TRUE(WriteStringToFile(testdata, tf.path, false));
+ ASSERT_TRUE(WriteStringToFile(testdata, tf.path));
int pid = fork();
ASSERT_TRUE(pid != -1);
diff --git a/cmds/incidentd/tests/PrivacyBuffer_test.cpp b/cmds/incidentd/tests/PrivacyBuffer_test.cpp
index 84a2a82..ca94623 100644
--- a/cmds/incidentd/tests/PrivacyBuffer_test.cpp
+++ b/cmds/incidentd/tests/PrivacyBuffer_test.cpp
@@ -60,7 +60,7 @@
}
void writeToFdBuffer(string str) {
- ASSERT_TRUE(WriteStringToFile(str, tf.path, false));
+ ASSERT_TRUE(WriteStringToFile(str, tf.path));
ASSERT_EQ(NO_ERROR, buffer.read(tf.fd, 10000));
ASSERT_EQ(str.size(), buffer.size());
}
diff --git a/cmds/incidentd/tests/Section_test.cpp b/cmds/incidentd/tests/Section_test.cpp
index 25b05b2..649e908 100644
--- a/cmds/incidentd/tests/Section_test.cpp
+++ b/cmds/incidentd/tests/Section_test.cpp
@@ -99,7 +99,7 @@
ReportRequestSet requests;
ASSERT_TRUE(tf.fd != -1);
- ASSERT_TRUE(WriteStringToFile("iamtestdata", tf.path, false));
+ ASSERT_TRUE(WriteStringToFile("iamtestdata", tf.path));
requests.setMainFd(STDOUT_FILENO);
@@ -173,7 +173,7 @@
ReportRequestSet requests;
ASSERT_TRUE(tf.fd != -1);
- ASSERT_TRUE(WriteStringToFile(VARINT_FIELD_1 + STRING_FIELD_2 + FIX64_FIELD_3, tf.path, false));
+ ASSERT_TRUE(WriteStringToFile(VARINT_FIELD_1 + STRING_FIELD_2 + FIX64_FIELD_3, tf.path));
requests.setMainFd(STDOUT_FILENO);
@@ -186,7 +186,7 @@
TemporaryFile input;
FileSection fs(NOOP_PARSER, input.path);
ReportRequestSet requests;
- ASSERT_TRUE(WriteStringToFile(VARINT_FIELD_1 + STRING_FIELD_2 + FIX64_FIELD_3, input.path, false));
+ ASSERT_TRUE(WriteStringToFile(VARINT_FIELD_1 + STRING_FIELD_2 + FIX64_FIELD_3, input.path));
IncidentReportArgs args;
args.setAll(true);
@@ -205,7 +205,7 @@
TemporaryFile input;
FileSection fs(NOOP_PARSER, input.path);
ReportRequestSet requests;
- ASSERT_TRUE(WriteStringToFile(VARINT_FIELD_1 + STRING_FIELD_2 + FIX64_FIELD_3, input.path, false));
+ ASSERT_TRUE(WriteStringToFile(VARINT_FIELD_1 + STRING_FIELD_2 + FIX64_FIELD_3, input.path));
IncidentReportArgs args;
args.setAll(true);
@@ -223,7 +223,7 @@
ASSERT_TRUE(output1.fd != -1);
ASSERT_TRUE(output2.fd != -1);
ASSERT_TRUE(output3.fd != -1);
- ASSERT_TRUE(WriteStringToFile(VARINT_FIELD_1 + STRING_FIELD_2 + FIX64_FIELD_3, input.path, false));
+ ASSERT_TRUE(WriteStringToFile(VARINT_FIELD_1 + STRING_FIELD_2 + FIX64_FIELD_3, input.path));
IncidentReportArgs args1, args2, args3;
args1.setAll(true);
@@ -265,7 +265,7 @@
ASSERT_TRUE(output2.fd != -1);
ASSERT_TRUE(output3.fd != -1);
- ASSERT_TRUE(WriteStringToFile(VARINT_FIELD_1 + STRING_FIELD_2 + FIX64_FIELD_3, input.path, false));
+ ASSERT_TRUE(WriteStringToFile(VARINT_FIELD_1 + STRING_FIELD_2 + FIX64_FIELD_3, input.path));
IncidentReportArgs args1, args2, args3, args4;
args1.setAll(true);
diff --git a/cmds/statsd/Android.mk b/cmds/statsd/Android.mk
index 87d318b..0f6d868 100644
--- a/cmds/statsd/Android.mk
+++ b/cmds/statsd/Android.mk
@@ -31,14 +31,16 @@
src/config/ConfigManager.cpp \
src/external/StatsCompanionServicePuller.cpp \
src/external/ResourcePowerManagerPuller.cpp \
- src/external/StatsPullerManager.cpp \
+ src/external/CpuTimePerUidPuller.cpp \
+ src/external/CpuTimePerUidFreqPuller.cpp \
+ src/external/StatsPullerManagerImpl.cpp \
src/logd/LogEvent.cpp \
src/logd/LogListener.cpp \
src/logd/LogReader.cpp \
src/matchers/CombinationLogMatchingTracker.cpp \
src/matchers/matcher_util.cpp \
src/matchers/SimpleLogMatchingTracker.cpp \
- src/metrics/CountAnomalyTracker.cpp \
+ src/anomaly/DiscreteAnomalyTracker.cpp \
src/metrics/MetricProducer.cpp \
src/metrics/EventMetricProducer.cpp \
src/metrics/CountMetricProducer.cpp \
@@ -150,18 +152,20 @@
LOCAL_SRC_FILES := \
$(statsd_common_src) \
tests/AnomalyMonitor_test.cpp \
- tests/ConditionTracker_test.cpp \
+ tests/anomaly/AnomalyTracker_test.cpp \
tests/ConfigManager_test.cpp \
tests/indexed_priority_queue_test.cpp \
tests/LogEntryMatcher_test.cpp \
tests/LogReader_test.cpp \
tests/MetricsManager_test.cpp \
tests/UidMap_test.cpp \
+ tests/condition/CombinationConditionTracker_test.cpp \
+ tests/condition/SimpleConditionTracker_test.cpp \
tests/metrics/OringDurationTracker_test.cpp \
tests/metrics/MaxDurationTracker_test.cpp \
tests/metrics/CountMetricProducer_test.cpp \
- tests/metrics/EventMetricProducer_test.cpp
-
+ tests/metrics/EventMetricProducer_test.cpp \
+ tests/metrics/ValueMetricProducer_test.cpp
LOCAL_STATIC_LIBRARIES := \
libgmock
diff --git a/cmds/statsd/src/StatsLogProcessor.cpp b/cmds/statsd/src/StatsLogProcessor.cpp
index 8910523..8c70bb5 100644
--- a/cmds/statsd/src/StatsLogProcessor.cpp
+++ b/cmds/statsd/src/StatsLogProcessor.cpp
@@ -15,9 +15,9 @@
*/
#include "Log.h"
+#include "statslog.h"
#include "StatsLogProcessor.h"
-#include "frameworks/base/cmds/statsd/src/stats_log.pb.h"
#include "metrics/CountMetricProducer.h"
#include "stats_util.h"
@@ -25,6 +25,14 @@
#include <utils/Errors.h>
using namespace android;
+using android::util::FIELD_COUNT_REPEATED;
+using android::util::FIELD_TYPE_BOOL;
+using android::util::FIELD_TYPE_FLOAT;
+using android::util::FIELD_TYPE_INT32;
+using android::util::FIELD_TYPE_INT64;
+using android::util::FIELD_TYPE_MESSAGE;
+using android::util::FIELD_TYPE_STRING;
+using android::util::ProtoOutputStream;
using std::make_unique;
using std::unique_ptr;
using std::vector;
@@ -33,6 +41,14 @@
namespace os {
namespace statsd {
+// for ConfigMetricsReport
+const int FIELD_ID_CONFIG_KEY = 1;
+const int FIELD_ID_METRICS = 2;
+const int FIELD_ID_UID_MAP = 3;
+// for ConfigKey
+const int FIELD_ID_UID = 1;
+const int FIELD_ID_NAME = 2;
+
StatsLogProcessor::StatsLogProcessor(const sp<UidMap>& uidMap,
const std::function<void(const vector<uint8_t>&)>& pushLog)
: mUidMap(uidMap), mPushLog(pushLog) {
@@ -48,6 +64,22 @@
pair.second->onLogEvent(msg);
flushIfNecessary(msg.GetTimestampNs(), pair.first, pair.second);
}
+
+ // Hard-coded logic to update the isolated uid's in the uid-map.
+ // The field numbers need to be currently updated by hand with stats_events.proto
+ if (msg.GetTagId() == android::util::ISOLATED_UID_CHANGED) {
+ status_t err = NO_ERROR, err2 = NO_ERROR, err3 = NO_ERROR;
+ bool is_create = msg.GetBool(3, &err);
+ auto parent_uid = int(msg.GetLong(1, &err2));
+ auto isolated_uid = int(msg.GetLong(2, &err3));
+ if (err == NO_ERROR && err2 == NO_ERROR && err3 == NO_ERROR) {
+ if (is_create) {
+ mUidMap->assignIsolatedUid(isolated_uid, parent_uid);
+ } else {
+ mUidMap->removeIsolatedUid(isolated_uid, parent_uid);
+ }
+ }
+ }
}
void StatsLogProcessor::OnConfigUpdated(const ConfigKey& key, const StatsdConfig& config) {
@@ -70,27 +102,46 @@
}
}
-ConfigMetricsReport StatsLogProcessor::onDumpReport(const ConfigKey& key) {
- ConfigMetricsReport report;
-
+vector<uint8_t> StatsLogProcessor::onDumpReport(const ConfigKey& key) {
auto it = mMetricsManagers.find(key);
if (it == mMetricsManagers.end()) {
ALOGW("Config source %s does not exist", key.ToString().c_str());
- return report;
+ return vector<uint8_t>();
}
- auto set_key = report.mutable_config_key();
- set_key->set_uid(key.GetUid());
- set_key->set_name(key.GetName());
- for (auto m : it->second->onDumpReport()) {
- // Transfer the vector of StatsLogReport into a field
- // TODO: perhaps we just have bytes being returned from onDumpReport and transfer bytes
- auto dest = report.add_metrics();
- *dest = m;
+ ProtoOutputStream proto;
+
+ // Fill in ConfigKey.
+ long long configKeyToken = proto.start(FIELD_TYPE_MESSAGE | FIELD_ID_CONFIG_KEY);
+ proto.write(FIELD_TYPE_INT32 | FIELD_ID_UID, key.GetUid());
+ proto.write(FIELD_TYPE_STRING | FIELD_ID_NAME, key.GetName());
+ proto.end(configKeyToken);
+
+ // Fill in StatsLogReport's.
+ for (auto& m : it->second->onDumpReport()) {
+ // Add each vector of StatsLogReport into a repeated field.
+ proto.write(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_METRICS,
+ reinterpret_cast<char*>(m.get()->data()), m.get()->size());
}
- auto temp = mUidMap->getOutput(key);
- report.mutable_uid_map()->Swap(&temp);
- return report;
+
+ // Fill in UidMap.
+ auto uidMap = mUidMap->getOutput(key);
+ const int uidMapSize = uidMap.ByteSize();
+ char uidMapBuffer[uidMapSize];
+ uidMap.SerializeToArray(&uidMapBuffer[0], uidMapSize);
+ proto.write(FIELD_TYPE_MESSAGE | FIELD_ID_UID_MAP, uidMapBuffer, uidMapSize);
+
+ vector<uint8_t> buffer(proto.size());
+ size_t pos = 0;
+ auto iter = proto.data();
+ while (iter.readBuffer() != NULL) {
+ size_t toRead = iter.currentToRead();
+ std::memcpy(&buffer[pos], iter.readBuffer(), toRead);
+ pos += toRead;
+ iter.rp()->move(toRead);
+ }
+
+ return buffer;
}
void StatsLogProcessor::OnConfigRemoved(const ConfigKey& key) {
diff --git a/cmds/statsd/src/StatsLogProcessor.h b/cmds/statsd/src/StatsLogProcessor.h
index 0083827..f38d715 100644
--- a/cmds/statsd/src/StatsLogProcessor.h
+++ b/cmds/statsd/src/StatsLogProcessor.h
@@ -41,8 +41,7 @@
void OnConfigUpdated(const ConfigKey& key, const StatsdConfig& config);
void OnConfigRemoved(const ConfigKey& key);
- // TODO: Once we have the ProtoOutputStream in c++, we can just return byte array.
- ConfigMetricsReport onDumpReport(const ConfigKey& key);
+ vector<uint8_t> onDumpReport(const ConfigKey& key);
/* Request a flush through a binder call. */
void flush();
diff --git a/cmds/statsd/src/StatsService.h b/cmds/statsd/src/StatsService.h
index 1d7e5a61..fa92f65 100644
--- a/cmds/statsd/src/StatsService.h
+++ b/cmds/statsd/src/StatsService.h
@@ -157,7 +157,7 @@
/**
* Fetches external metrics.
*/
- StatsPullerManager& mStatsPullerManager = StatsPullerManager::GetInstance();
+ StatsPullerManager mStatsPullerManager;
/**
* Tracks the configurations that have been passed to statsd.
diff --git a/cmds/statsd/src/anomaly/AnomalyMonitor.h b/cmds/statsd/src/anomaly/AnomalyMonitor.h
index e2ac623..d9207e9 100644
--- a/cmds/statsd/src/anomaly/AnomalyMonitor.h
+++ b/cmds/statsd/src/anomaly/AnomalyMonitor.h
@@ -55,6 +55,7 @@
};
};
+// TODO: Rename this file to AnomalyAlarmMonitor.
/**
* Manages alarms for Anomaly Detection.
*/
@@ -95,6 +96,15 @@
unordered_set<sp<const AnomalyAlarm>, SpHash<AnomalyAlarm>> popSoonerThan(
uint32_t timestampSec);
+ // TODO: Function that uses popSoonerThan to get all alarms that have fired, and then
+ // iterates over all DurationAnomalyTracker, looking for those alarms. When they're found,
+ // have them declareAnomaly on those alarms. This means that DurationAnomalyTracker
+ // must be thread-safe (since this is being called on a different thread). There is no
+ // worry about missing the alarms (due to them being cancelled after this function being called)
+ // because DurationAnomalyTracker guarantees that it checks for anaomlies when it cancels
+ // alarms anyway.
+ // void declareAnomalies(uint32_t timestampSec);
+
/**
* Returns the projected alarm timestamp that is registered with
* StatsCompanionService. This may not be equal to the soonest alarm,
diff --git a/cmds/statsd/src/anomaly/DiscreteAnomalyTracker.cpp b/cmds/statsd/src/anomaly/DiscreteAnomalyTracker.cpp
new file mode 100644
index 0000000..6492177
--- /dev/null
+++ b/cmds/statsd/src/anomaly/DiscreteAnomalyTracker.cpp
@@ -0,0 +1,153 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define DEBUG true // STOPSHIP if true
+#include "Log.h"
+
+#include "DiscreteAnomalyTracker.h"
+
+#include <time.h>
+
+namespace android {
+namespace os {
+namespace statsd {
+
+DiscreteAnomalyTracker::DiscreteAnomalyTracker(const Alert& alert) : mAlert(alert) {
+ VLOG("DiscreteAnomalyTracker() called");
+ if (mAlert.number_of_buckets() <= 0) {
+ ALOGE("Cannot create DiscreteAnomalyTracker with %lld buckets",
+ (long long)mAlert.number_of_buckets());
+ return;
+ }
+ mPastBuckets.resize(mAlert.number_of_buckets());
+ reset(); // initialization
+}
+
+DiscreteAnomalyTracker::~DiscreteAnomalyTracker() {
+ VLOG("~DiscreteAnomalyTracker() called");
+}
+
+void DiscreteAnomalyTracker::reset() {
+ VLOG("reset() called.");
+ mPastBuckets.clear();
+ mPastBuckets.resize(mAlert.number_of_buckets());
+ mSumOverPastBuckets.clear();
+ mCurrentBucketIndex = -1;
+ mLastAlarmAtBucketIndex = -1;
+ mAnomalyDeclared = 0;
+}
+
+size_t DiscreteAnomalyTracker::index(int64_t bucketNum) {
+ return bucketNum % mAlert.number_of_buckets();
+}
+
+void DiscreteAnomalyTracker::addOrUpdateBucket(std::shared_ptr<const DimToValMap> BucketValues,
+ int64_t bucketIndex) {
+ VLOG("addPastBucket() called.");
+ if (bucketIndex <= mCurrentBucketIndex - mAlert.number_of_buckets()) {
+ ALOGE("Cannot add a past bucket %lld units in past", (long long)bucketIndex);
+ return;
+ }
+
+ // Empty out old mPastBuckets[i] values and update mSumOverPastBuckets.
+ if (bucketIndex - mCurrentBucketIndex >= mAlert.number_of_buckets()) {
+ mPastBuckets.clear();
+ mPastBuckets.resize(mAlert.number_of_buckets());
+ mSumOverPastBuckets.clear();
+ } else {
+ for (int64_t i = std::max(
+ 0LL, (long long)(mCurrentBucketIndex - mAlert.number_of_buckets() + 1));
+ i < bucketIndex - mAlert.number_of_buckets(); i++) {
+ const int idx = index(i);
+ subtractBucketFromSum(mPastBuckets[idx]);
+ mPastBuckets[idx] = nullptr; // release (but not clear) the old bucket.
+ }
+ }
+ subtractBucketFromSum(mPastBuckets[index(bucketIndex)]);
+ mPastBuckets[index(bucketIndex)] = nullptr; // release (but not clear) the old bucket.
+
+ // Replace the oldest bucket with the new bucket we are adding.
+ mPastBuckets[index(bucketIndex)] = BucketValues;
+ addBucketToSum(BucketValues);
+
+ mCurrentBucketIndex = std::max(mCurrentBucketIndex, bucketIndex);
+}
+
+void DiscreteAnomalyTracker::subtractBucketFromSum(const shared_ptr<const DimToValMap>& bucket) {
+ if (bucket == nullptr) {
+ return;
+ }
+ // For each dimension present in the bucket, subtract its value from its corresponding sum.
+ for (const auto& keyValuePair : *bucket) {
+ auto itr = mSumOverPastBuckets.find(keyValuePair.first);
+ if (itr == mSumOverPastBuckets.end()) {
+ continue;
+ }
+ itr->second -= keyValuePair.second;
+ // TODO: No need to look up the object twice like this. Use a var.
+ if (itr->second == 0) {
+ mSumOverPastBuckets.erase(itr);
+ }
+ }
+}
+
+void DiscreteAnomalyTracker::addBucketToSum(const shared_ptr<const DimToValMap>& bucket) {
+ if (bucket == nullptr) {
+ return;
+ }
+ // For each dimension present in the bucket, add its value to its corresponding sum.
+ for (const auto& keyValuePair : *bucket) {
+ mSumOverPastBuckets[keyValuePair.first] += keyValuePair.second;
+ }
+}
+
+bool DiscreteAnomalyTracker::detectAnomaly() {
+ for (auto itr = mSumOverPastBuckets.begin(); itr != mSumOverPastBuckets.end(); itr++) {
+ if (mAlert.has_trigger_if_sum_gt() && itr->second > mAlert.trigger_if_sum_gt()) {
+ return true;
+ }
+ }
+ return false;
+}
+
+void DiscreteAnomalyTracker::declareAndDeclareAnomaly() {
+ if (detectAnomaly()) {
+ declareAnomaly();
+ }
+}
+
+void DiscreteAnomalyTracker::declareAnomaly() {
+ if (mLastAlarmAtBucketIndex >= 0 && mCurrentBucketIndex - mLastAlarmAtBucketIndex <=
+ (long long)mAlert.refractory_period_in_buckets()) {
+ VLOG("Skipping anomaly check since within refractory period");
+ return;
+ }
+ mAnomalyDeclared++;
+ // TODO(guardrail): Consider guarding against too short refractory periods.
+ mLastAlarmAtBucketIndex = mCurrentBucketIndex;
+
+ if (mAlert.has_incidentd_details()) {
+ // TODO: Can construct a name based on the criteria (and/or relay the criteria).
+ ALOGW("An anomaly (nameless) has occurred! Informing incidentd.");
+ // TODO: Send incidentd_details.name and incidentd_details.incidentd_sections to incidentd
+ } else {
+ ALOGW("An anomaly has occurred! (But informing incidentd not requested.)");
+ }
+}
+
+} // namespace statsd
+} // namespace os
+} // namespace android
diff --git a/cmds/statsd/src/anomaly/DiscreteAnomalyTracker.h b/cmds/statsd/src/anomaly/DiscreteAnomalyTracker.h
new file mode 100644
index 0000000..ed7d5d7
--- /dev/null
+++ b/cmds/statsd/src/anomaly/DiscreteAnomalyTracker.h
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <gtest/gtest_prod.h>
+#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" // Alert
+#include "stats_util.h" // HashableDimensionKey and DimToValMap
+
+#include <memory> // unique_ptr
+#include <stdlib.h>
+
+namespace android {
+namespace os {
+namespace statsd {
+
+using std::unordered_map;
+using std::shared_ptr;
+
+// This anomaly track assmues that all values are non-negative.
+class DiscreteAnomalyTracker {
+ public:
+ DiscreteAnomalyTracker(const Alert& alert);
+
+ virtual ~DiscreteAnomalyTracker();
+
+ // Adds a new bucket or updates an existing bucket.
+ // Bucket index starts from 0.
+ void addOrUpdateBucket(std::shared_ptr<const DimToValMap> BucketValues, int64_t bucketIndex);
+
+ // Returns true if detected anomaly for the existing buckets on one or more dimension keys.
+ bool detectAnomaly();
+
+ // Informs incidentd about the detected alert.
+ void declareAnomaly();
+
+ // Detects the alert and informs the incidentd when applicable.
+ void declareAndDeclareAnomaly();
+
+private:
+ // statsd_config.proto Alert message that defines this tracker.
+ const Alert mAlert;
+
+ // The exisiting bucket list.
+ std::vector<shared_ptr<const DimToValMap>> mPastBuckets;
+
+ // Sum over all existing buckets cached in mPastBuckets.
+ DimToValMap mSumOverPastBuckets;
+
+ // Current bucket index of the current anomaly detection window. Bucket index starts from 0.
+ int64_t mCurrentBucketIndex = -1;
+
+ // The bucket index when the last anomaly was declared.
+ int64_t mLastAlarmAtBucketIndex = -1;
+
+ // The total number of declared anomalies.
+ int64_t mAnomalyDeclared = 0;
+
+ // Add the information in the given bucket to mSumOverPastBuckets.
+ void addBucketToSum(const shared_ptr<const DimToValMap>& bucket);
+
+ // Subtract the information in the given bucket from mSumOverPastBuckets
+ // and remove any items with value 0.
+ void subtractBucketFromSum(const shared_ptr<const DimToValMap>& bucket);
+
+ // Calculates the corresponding bucket index within the circular array.
+ size_t index(int64_t bucketNum);
+
+ // Resets all data. For use when all the data gets stale.
+ void reset();
+
+ FRIEND_TEST(AnomalyTrackerTest, TestConsecutiveBuckets);
+ FRIEND_TEST(AnomalyTrackerTest, TestSparseBuckets);
+};
+
+} // namespace statsd
+} // namespace os
+} // namespace android
diff --git a/cmds/statsd/src/condition/CombinationConditionTracker.cpp b/cmds/statsd/src/condition/CombinationConditionTracker.cpp
index 953bcb3..41f5fca 100644
--- a/cmds/statsd/src/condition/CombinationConditionTracker.cpp
+++ b/cmds/statsd/src/condition/CombinationConditionTracker.cpp
@@ -115,49 +115,47 @@
evaluateCombinationCondition(mChildren, mLogicalOperation, conditionCache);
}
-bool CombinationConditionTracker::evaluateCondition(
+void CombinationConditionTracker::evaluateCondition(
const LogEvent& event, const std::vector<MatchingState>& eventMatcherValues,
const std::vector<sp<ConditionTracker>>& mAllConditions,
std::vector<ConditionState>& nonSlicedConditionCache,
- std::vector<bool>& nonSlicedChangedCache, vector<bool>& slicedConditionChanged) {
+ std::vector<bool>& conditionChangedCache) {
// value is up to date.
if (nonSlicedConditionCache[mIndex] != ConditionState::kNotEvaluated) {
- return false;
+ return;
}
for (const int childIndex : mChildren) {
if (nonSlicedConditionCache[childIndex] == ConditionState::kNotEvaluated) {
const sp<ConditionTracker>& child = mAllConditions[childIndex];
child->evaluateCondition(event, eventMatcherValues, mAllConditions,
- nonSlicedConditionCache, nonSlicedChangedCache,
- slicedConditionChanged);
+ nonSlicedConditionCache, conditionChangedCache);
}
}
- ConditionState newCondition =
- evaluateCombinationCondition(mChildren, mLogicalOperation, nonSlicedConditionCache);
+ if (!mSliced) {
+ ConditionState newCondition =
+ evaluateCombinationCondition(mChildren, mLogicalOperation, nonSlicedConditionCache);
- bool nonSlicedChanged = (mNonSlicedConditionState != newCondition);
- mNonSlicedConditionState = newCondition;
+ bool nonSlicedChanged = (mNonSlicedConditionState != newCondition);
+ mNonSlicedConditionState = newCondition;
- nonSlicedConditionCache[mIndex] = mNonSlicedConditionState;
+ nonSlicedConditionCache[mIndex] = mNonSlicedConditionState;
- nonSlicedChangedCache[mIndex] = nonSlicedChanged;
-
- if (mSliced) {
+ conditionChangedCache[mIndex] = nonSlicedChanged;
+ } else {
for (const int childIndex : mChildren) {
// If any of the sliced condition in children condition changes, the combination
// condition may be changed too.
- if (slicedConditionChanged[childIndex]) {
- slicedConditionChanged[mIndex] = true;
+ if (conditionChangedCache[childIndex]) {
+ conditionChangedCache[mIndex] = true;
break;
}
}
+ nonSlicedConditionCache[mIndex] = ConditionState::kUnknown;
ALOGD("CombinationCondition %s sliced may changed? %d", mName.c_str(),
- slicedConditionChanged[mIndex] == true);
+ conditionChangedCache[mIndex] == true);
}
-
- return nonSlicedChanged;
}
} // namespace statsd
diff --git a/cmds/statsd/src/condition/CombinationConditionTracker.h b/cmds/statsd/src/condition/CombinationConditionTracker.h
index dbdb3b7..3d2c6bb 100644
--- a/cmds/statsd/src/condition/CombinationConditionTracker.h
+++ b/cmds/statsd/src/condition/CombinationConditionTracker.h
@@ -35,12 +35,11 @@
const std::unordered_map<std::string, int>& conditionNameIndexMap,
std::vector<bool>& stack) override;
- bool evaluateCondition(const LogEvent& event,
+ void evaluateCondition(const LogEvent& event,
const std::vector<MatchingState>& eventMatcherValues,
const std::vector<sp<ConditionTracker>>& mAllConditions,
std::vector<ConditionState>& conditionCache,
- std::vector<bool>& changedCache,
- std::vector<bool>& slicedConditionMayChanged) override;
+ std::vector<bool>& changedCache) override;
void isConditionMet(const std::map<std::string, HashableDimensionKey>& conditionParameters,
const std::vector<sp<ConditionTracker>>& allConditions,
diff --git a/cmds/statsd/src/condition/ConditionTracker.h b/cmds/statsd/src/condition/ConditionTracker.h
index bb5ddeb..0ac7ef3 100644
--- a/cmds/statsd/src/condition/ConditionTracker.h
+++ b/cmds/statsd/src/condition/ConditionTracker.h
@@ -56,25 +56,20 @@
std::vector<bool>& stack) = 0;
// evaluate current condition given the new event.
- // return true if the condition state changed, false if the condition state is not changed.
// event: the new log event
// eventMatcherValues: the results of the LogMatcherTrackers. LogMatcherTrackers always process
// event before ConditionTrackers, because ConditionTracker depends on
// LogMatchingTrackers.
// mAllConditions: the list of all ConditionTracker
// conditionCache: the cached non-sliced condition of the ConditionTrackers for this new event.
- // nonSlicedConditionChanged: the bit map to record whether non-sliced condition has changed.
- // slicedConditionMayChanged: the bit map to record whether sliced condition may have changed.
- // Because sliced condition needs parameters to determine the value. So the sliced
- // condition is not pushed to metrics. We only inform the relevant metrics that the sliced
- // condition may have changed, and metrics should pull the conditions that they are
- // interested in.
- virtual bool evaluateCondition(const LogEvent& event,
+ // conditionChanged: the bit map to record whether the condition has changed.
+ // If the condition has dimension, then any sub condition changes will report
+ // conditionChanged.
+ virtual void evaluateCondition(const LogEvent& event,
const std::vector<MatchingState>& eventMatcherValues,
const std::vector<sp<ConditionTracker>>& mAllConditions,
std::vector<ConditionState>& conditionCache,
- std::vector<bool>& nonSlicedConditionChanged,
- std::vector<bool>& slicedConditionMayChanged) = 0;
+ std::vector<bool>& conditionChanged) = 0;
// Return the current condition state.
virtual ConditionState isConditionMet() {
diff --git a/cmds/statsd/src/condition/SimpleConditionTracker.cpp b/cmds/statsd/src/condition/SimpleConditionTracker.cpp
index b691faea..a694dbf 100644
--- a/cmds/statsd/src/condition/SimpleConditionTracker.cpp
+++ b/cmds/statsd/src/condition/SimpleConditionTracker.cpp
@@ -74,13 +74,21 @@
mStopAllLogMatcherIndex = -1;
}
- mDimension.insert(mDimension.begin(), simpleCondition.dimension().begin(),
- simpleCondition.dimension().end());
+ mOutputDimension.insert(mOutputDimension.begin(), simpleCondition.dimension().begin(),
+ simpleCondition.dimension().end());
- if (mDimension.size() > 0) {
+ if (mOutputDimension.size() > 0) {
mSliced = true;
}
+ if (simpleCondition.initial_value() == SimpleCondition_InitialValue_FALSE) {
+ mInitialValue = ConditionState::kFalse;
+ } else {
+ mInitialValue = ConditionState::kUnknown;
+ }
+
+ mNonSlicedConditionState = mInitialValue;
+
mInitialized = true;
}
@@ -97,127 +105,166 @@
return mInitialized;
}
-void print(unordered_map<HashableDimensionKey, ConditionState>& conditions, const string& name) {
+void print(map<HashableDimensionKey, int>& conditions, const string& name) {
VLOG("%s DUMP:", name.c_str());
-
for (const auto& pair : conditions) {
- VLOG("\t%s %d", pair.first.c_str(), pair.second);
+ VLOG("\t%s : %d", pair.first.c_str(), pair.second);
}
}
-bool SimpleConditionTracker::evaluateCondition(const LogEvent& event,
+void SimpleConditionTracker::handleStopAll(std::vector<ConditionState>& conditionCache,
+ std::vector<bool>& conditionChangedCache) {
+ // Unless the default condition is false, and there was nothing started, otherwise we have
+ // triggered a condition change.
+ conditionChangedCache[mIndex] =
+ (mInitialValue == ConditionState::kFalse && mSlicedConditionState.empty()) ? false
+ : true;
+
+ // After StopAll, we know everything has stopped. From now on, default condition is false.
+ mInitialValue = ConditionState::kFalse;
+ mSlicedConditionState.clear();
+ conditionCache[mIndex] = ConditionState::kFalse;
+}
+
+void SimpleConditionTracker::handleConditionEvent(const HashableDimensionKey& outputKey,
+ bool matchStart,
+ std::vector<ConditionState>& conditionCache,
+ std::vector<bool>& conditionChangedCache) {
+ bool changed = false;
+ auto outputIt = mSlicedConditionState.find(outputKey);
+ ConditionState newCondition;
+ if (outputIt == mSlicedConditionState.end()) {
+ // We get a new output key.
+ newCondition = matchStart ? ConditionState::kTrue : ConditionState::kFalse;
+ if (matchStart && mInitialValue != ConditionState::kTrue) {
+ mSlicedConditionState[outputKey] = 1;
+ changed = true;
+ } else if (mInitialValue != ConditionState::kFalse) {
+ // it's a stop and we don't have history about it.
+ // If the default condition is not false, it means this stop is valuable to us.
+ mSlicedConditionState[outputKey] = 0;
+ changed = true;
+ }
+ } else {
+ // we have history about this output key.
+ auto& startedCount = outputIt->second;
+ // assign the old value first.
+ newCondition = startedCount > 0 ? ConditionState::kTrue : ConditionState::kFalse;
+ if (matchStart) {
+ if (startedCount == 0) {
+ // This condition for this output key will change from false -> true
+ changed = true;
+ }
+
+ // it's ok to do ++ here, even if we don't count nesting. The >1 counts will be treated
+ // as 1 if not counting nesting.
+ startedCount++;
+ newCondition = ConditionState::kTrue;
+ } else {
+ // This is a stop event.
+ if (startedCount > 0) {
+ if (mCountNesting) {
+ startedCount--;
+ if (startedCount == 0) {
+ newCondition = ConditionState::kFalse;
+ }
+ } else {
+ // not counting nesting, so ignore the number of starts, stop now.
+ startedCount = 0;
+ newCondition = ConditionState::kFalse;
+ }
+ // if everything has stopped for this output key, condition true -> false;
+ if (startedCount == 0) {
+ changed = true;
+ }
+ }
+
+ // if default condition is false, it means we don't need to keep the false values.
+ if (mInitialValue == ConditionState::kFalse && startedCount == 0) {
+ mSlicedConditionState.erase(outputIt);
+ VLOG("erase key %s", outputKey.c_str());
+ }
+ }
+ }
+
+ // dump all dimensions for debugging
+ if (DEBUG) {
+ print(mSlicedConditionState, mName);
+ }
+
+ conditionChangedCache[mIndex] = changed;
+ conditionCache[mIndex] = newCondition;
+
+ VLOG("SimpleCondition %s nonSlicedChange? %d", mName.c_str(),
+ conditionChangedCache[mIndex] == true);
+}
+
+void SimpleConditionTracker::evaluateCondition(const LogEvent& event,
const vector<MatchingState>& eventMatcherValues,
const vector<sp<ConditionTracker>>& mAllConditions,
vector<ConditionState>& conditionCache,
- vector<bool>& nonSlicedConditionChanged,
- std::vector<bool>& slicedConditionChanged) {
+ vector<bool>& conditionChangedCache) {
if (conditionCache[mIndex] != ConditionState::kNotEvaluated) {
// it has been evaluated.
VLOG("Yes, already evaluated, %s %d", mName.c_str(), mNonSlicedConditionState);
- return false;
+ return;
}
- // Ignore nesting, because we know we cannot trust ourselves on tracking nesting conditions.
+ if (mStopAllLogMatcherIndex >= 0 &&
+ eventMatcherValues[mStopAllLogMatcherIndex] == MatchingState::kMatched) {
+ handleStopAll(conditionCache, conditionChangedCache);
+ return;
+ }
- ConditionState newCondition = mNonSlicedConditionState;
- bool matched = false;
+ int matchedState = -1;
// Note: The order to evaluate the following start, stop, stop_all matters.
// The priority of overwrite is stop_all > stop > start.
if (mStartLogMatcherIndex >= 0 &&
eventMatcherValues[mStartLogMatcherIndex] == MatchingState::kMatched) {
- matched = true;
- newCondition = ConditionState::kTrue;
+ matchedState = 1;
}
if (mStopLogMatcherIndex >= 0 &&
eventMatcherValues[mStopLogMatcherIndex] == MatchingState::kMatched) {
- matched = true;
- newCondition = ConditionState::kFalse;
+ matchedState = 0;
}
- bool stopAll = false;
- if (mStopAllLogMatcherIndex >= 0 &&
- eventMatcherValues[mStopAllLogMatcherIndex] == MatchingState::kMatched) {
- matched = true;
- newCondition = ConditionState::kFalse;
- stopAll = true;
- }
-
- if (matched == false) {
- slicedConditionChanged[mIndex] = false;
- nonSlicedConditionChanged[mIndex] = false;
+ if (matchedState < 0) {
+ conditionChangedCache[mIndex] = false;
conditionCache[mIndex] = mNonSlicedConditionState;
- return false;
+ return;
}
- bool nonSlicedChanged = mNonSlicedConditionState != newCondition;
-
- bool slicedChanged = false;
-
- if (stopAll) {
- // TODO: handle stop all; all dimension should be cleared.
- }
-
-
- if (mDimension.size() > 0) {
- HashableDimensionKey hashableKey = getHashableKey(getDimensionKey(event, mDimension));
- if (mSlicedConditionState.find(hashableKey) == mSlicedConditionState.end() ||
- mSlicedConditionState[hashableKey] != newCondition) {
- slicedChanged = true;
- mSlicedConditionState[hashableKey] = newCondition;
- }
- VLOG("key: %s %d", hashableKey.c_str(), newCondition);
- // dump all dimensions for debugging
- if (DEBUG) {
- print(mSlicedConditionState, mName);
- }
- }
-
- // even if this SimpleCondition is not sliced, it may be part of a sliced CombinationCondition
- // if the nonSliced condition changed, it may affect the sliced condition in the parent node.
- // so mark the slicedConditionChanged to be true.
- // For example: APP_IN_BACKGROUND_OR_SCREEN_OFF
- // APP_IN_BACKGROUND is sliced [App_A->True, App_B->False].
- // SCREEN_OFF is not sliced, and it changes from False -> True;
- // We need to populate this change to parent condition. Because for App_B,
- // the APP_IN_BACKGROUND_OR_SCREEN_OFF condition would change from False->True.
- slicedConditionChanged[mIndex] = mSliced ? slicedChanged : nonSlicedChanged;
- nonSlicedConditionChanged[mIndex] = nonSlicedChanged;
-
- VLOG("SimpleCondition %s nonSlicedChange? %d SlicedChanged? %d", mName.c_str(),
- nonSlicedConditionChanged[mIndex] == true, slicedConditionChanged[mIndex] == true);
- mNonSlicedConditionState = newCondition;
- conditionCache[mIndex] = mNonSlicedConditionState;
-
- return nonSlicedConditionChanged[mIndex];
+ // outputKey is the output key values. e.g, uid:1234
+ const HashableDimensionKey outputKey = getHashableKey(getDimensionKey(event, mOutputDimension));
+ handleConditionEvent(outputKey, matchedState == 1, conditionCache, conditionChangedCache);
}
void SimpleConditionTracker::isConditionMet(
const map<string, HashableDimensionKey>& conditionParameters,
const vector<sp<ConditionTracker>>& allConditions, vector<ConditionState>& conditionCache) {
const auto pair = conditionParameters.find(mName);
- if (pair == conditionParameters.end()) {
- // the query does not need my sliced condition. just return the non sliced condition.
- conditionCache[mIndex] = mNonSlicedConditionState;
- VLOG("Condition %s return %d", mName.c_str(), mNonSlicedConditionState);
+ HashableDimensionKey key =
+ (pair == conditionParameters.end()) ? DEFAULT_DIMENSION_KEY : pair->second;
+
+ if (pair == conditionParameters.end() && mOutputDimension.size() > 0) {
+ ALOGE("Condition %s output has dimension, but it's not specified in the query!",
+ mName.c_str());
+ conditionCache[mIndex] = mInitialValue;
return;
}
- const HashableDimensionKey& key = pair->second;
VLOG("simpleCondition %s query key: %s", mName.c_str(), key.c_str());
- if (mSlicedConditionState.find(key) == mSlicedConditionState.end()) {
- // never seen this key before. the condition is unknown to us.
- conditionCache[mIndex] = ConditionState::kUnknown;
+ auto startedCountIt = mSlicedConditionState.find(key);
+ if (startedCountIt == mSlicedConditionState.end()) {
+ conditionCache[mIndex] = mInitialValue;
} else {
- conditionCache[mIndex] = mSlicedConditionState[key];
+ conditionCache[mIndex] =
+ startedCountIt->second > 0 ? ConditionState::kTrue : ConditionState::kFalse;
}
VLOG("Condition %s return %d", mName.c_str(), conditionCache[mIndex]);
-
- if (DEBUG) {
- print(mSlicedConditionState, mName);
- }
}
} // namespace statsd
diff --git a/cmds/statsd/src/condition/SimpleConditionTracker.h b/cmds/statsd/src/condition/SimpleConditionTracker.h
index b72157b..2eda0b1 100644
--- a/cmds/statsd/src/condition/SimpleConditionTracker.h
+++ b/cmds/statsd/src/condition/SimpleConditionTracker.h
@@ -17,6 +17,7 @@
#ifndef SIMPLE_CONDITION_TRACKER_H
#define SIMPLE_CONDITION_TRACKER_H
+#include <gtest/gtest_prod.h>
#include "ConditionTracker.h"
#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
#include "stats_util.h"
@@ -38,12 +39,11 @@
const std::unordered_map<std::string, int>& conditionNameIndexMap,
std::vector<bool>& stack) override;
- bool evaluateCondition(const LogEvent& event,
+ void evaluateCondition(const LogEvent& event,
const std::vector<MatchingState>& eventMatcherValues,
const std::vector<sp<ConditionTracker>>& mAllConditions,
std::vector<ConditionState>& conditionCache,
- std::vector<bool>& changedCache,
- std::vector<bool>& slicedChangedCache) override;
+ std::vector<bool>& changedCache) override;
void isConditionMet(const std::map<std::string, HashableDimensionKey>& conditionParameters,
const std::vector<sp<ConditionTracker>>& allConditions,
@@ -62,15 +62,22 @@
// The index of the LogEventMatcher which defines the stop all.
int mStopAllLogMatcherIndex;
- // The dimension defines at the atom level, how start and stop should match.
- // e.g., APP_IN_FOREGROUND, the dimension should be the uid field. Each "start" and
- // "stop" tells you the state change of a particular app. Without this dimension, this
- // condition does not make sense.
- std::vector<KeyMatcher> mDimension;
+ ConditionState mInitialValue;
- // Keep the map from the internal HashableDimensionKey to std::vector<KeyValuePair>
- // that StatsLogReport wants.
- std::unordered_map<HashableDimensionKey, ConditionState> mSlicedConditionState;
+ std::vector<KeyMatcher> mOutputDimension;
+
+ std::map<HashableDimensionKey, int> mSlicedConditionState;
+
+ void handleStopAll(std::vector<ConditionState>& conditionCache,
+ std::vector<bool>& changedCache);
+
+ void handleConditionEvent(const HashableDimensionKey& outputKey, bool matchStart,
+ std::vector<ConditionState>& conditionCache,
+ std::vector<bool>& changedCache);
+
+ FRIEND_TEST(SimpleConditionTrackerTest, TestSlicedCondition);
+ FRIEND_TEST(SimpleConditionTrackerTest, TestSlicedWithNoOutputDim);
+ FRIEND_TEST(SimpleConditionTrackerTest, TestStopAll);
};
} // namespace statsd
diff --git a/cmds/statsd/src/condition/condition_util.cpp b/cmds/statsd/src/condition/condition_util.cpp
index 40d41be..2618a21 100644
--- a/cmds/statsd/src/condition/condition_util.cpp
+++ b/cmds/statsd/src/condition/condition_util.cpp
@@ -25,7 +25,6 @@
#include <unordered_map>
#include "../matchers/matcher_util.h"
#include "ConditionTracker.h"
-#include "frameworks/base/cmds/statsd/src/stats_log.pb.h"
#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
#include "stats_util.h"
diff --git a/cmds/statsd/src/condition/condition_util.h b/cmds/statsd/src/condition/condition_util.h
index 47e245e..4167bf9 100644
--- a/cmds/statsd/src/condition/condition_util.h
+++ b/cmds/statsd/src/condition/condition_util.h
@@ -19,7 +19,6 @@
#include <vector>
#include "../matchers/matcher_util.h"
-#include "frameworks/base/cmds/statsd/src/stats_log.pb.h"
#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
namespace android {
diff --git a/cmds/statsd/src/config/ConfigListener.h b/cmds/statsd/src/config/ConfigListener.h
index a58766d..19ccfcf 100644
--- a/cmds/statsd/src/config/ConfigListener.h
+++ b/cmds/statsd/src/config/ConfigListener.h
@@ -16,7 +16,6 @@
#pragma once
-#include <frameworks/base/cmds/statsd/src/stats_log.pb.h>
#include "config/ConfigKey.h"
#include <utils/RefBase.h>
diff --git a/cmds/statsd/src/config/ConfigManager.cpp b/cmds/statsd/src/config/ConfigManager.cpp
index 02e6903..1e1d88e 100644
--- a/cmds/statsd/src/config/ConfigManager.cpp
+++ b/cmds/statsd/src/config/ConfigManager.cpp
@@ -116,7 +116,7 @@
static StatsdConfig build_fake_config() {
// HACK: Hard code a test metric for counting screen on events...
StatsdConfig config;
- config.set_config_id(12345L);
+ config.set_name("12345");
int WAKE_LOCK_TAG_ID = 1111; // put a fake id here to make testing easier.
int WAKE_LOCK_UID_KEY_ID = 1;
@@ -147,27 +147,35 @@
// Count Screen ON events.
CountMetric* metric = config.add_count_metric();
- metric->set_metric_id(1);
+ metric->set_name("1");
metric->set_what("SCREEN_TURNED_ON");
metric->mutable_bucket()->set_bucket_size_millis(30 * 1000L);
// Anomaly threshold for screen-on count.
- Alert* alert = metric->add_alerts();
+ Alert* alert = config.add_alerts();
+ alert->set_name("1");
alert->set_number_of_buckets(6);
alert->set_trigger_if_sum_gt(10);
alert->set_refractory_period_secs(30);
// Count process state changes, slice by uid.
metric = config.add_count_metric();
- metric->set_metric_id(2);
+ metric->set_name("2");
metric->set_what("PROCESS_STATE_CHANGE");
metric->mutable_bucket()->set_bucket_size_millis(30 * 1000L);
KeyMatcher* keyMatcher = metric->add_dimension();
keyMatcher->set_key(UID_PROCESS_STATE_UID_KEY);
+ // Anomaly threshold for background count.
+ alert = config.add_alerts();
+ alert->set_name("2");
+ alert->set_number_of_buckets(4);
+ alert->set_trigger_if_sum_gt(30);
+ alert->set_refractory_period_secs(20);
+
// Count process state changes, slice by uid, while SCREEN_IS_OFF
metric = config.add_count_metric();
- metric->set_metric_id(3);
+ metric->set_name("3");
metric->set_what("PROCESS_STATE_CHANGE");
metric->mutable_bucket()->set_bucket_size_millis(30 * 1000L);
keyMatcher = metric->add_dimension();
@@ -176,7 +184,7 @@
// Count wake lock, slice by uid, while SCREEN_IS_ON and app in background
metric = config.add_count_metric();
- metric->set_metric_id(4);
+ metric->set_name("4");
metric->set_what("APP_GET_WL");
metric->mutable_bucket()->set_bucket_size_millis(30 * 1000L);
keyMatcher = metric->add_dimension();
@@ -189,12 +197,12 @@
// Duration of an app holding any wl, while screen on and app in background, slice by uid
DurationMetric* durationMetric = config.add_duration_metric();
- durationMetric->set_metric_id(5);
+ durationMetric->set_name("5");
durationMetric->mutable_bucket()->set_bucket_size_millis(30 * 1000L);
durationMetric->set_type(DurationMetric_AggregationType_DURATION_SUM);
keyMatcher = durationMetric->add_dimension();
keyMatcher->set_key(WAKE_LOCK_UID_KEY_ID);
- durationMetric->set_what("WL_STATE_PER_APP_PER_NAME");
+ durationMetric->set_what("WL_HELD_PER_APP_PER_NAME");
durationMetric->set_predicate("APP_IS_BACKGROUND_AND_SCREEN_ON");
link = durationMetric->add_links();
link->set_condition("APP_IS_BACKGROUND");
@@ -203,12 +211,12 @@
// max Duration of an app holding any wl, while screen on and app in background, slice by uid
durationMetric = config.add_duration_metric();
- durationMetric->set_metric_id(6);
+ durationMetric->set_name("6");
durationMetric->mutable_bucket()->set_bucket_size_millis(30 * 1000L);
durationMetric->set_type(DurationMetric_AggregationType_DURATION_MAX_SPARSE);
keyMatcher = durationMetric->add_dimension();
keyMatcher->set_key(WAKE_LOCK_UID_KEY_ID);
- durationMetric->set_what("WL_STATE_PER_APP_PER_NAME");
+ durationMetric->set_what("WL_HELD_PER_APP_PER_NAME");
durationMetric->set_predicate("APP_IS_BACKGROUND_AND_SCREEN_ON");
link = durationMetric->add_links();
link->set_condition("APP_IS_BACKGROUND");
@@ -217,10 +225,10 @@
// Duration of an app holding any wl, while screen on and app in background
durationMetric = config.add_duration_metric();
- durationMetric->set_metric_id(7);
+ durationMetric->set_name("7");
durationMetric->mutable_bucket()->set_bucket_size_millis(30 * 1000L);
durationMetric->set_type(DurationMetric_AggregationType_DURATION_MAX_SPARSE);
- durationMetric->set_what("WL_STATE_PER_APP_PER_NAME");
+ durationMetric->set_what("WL_HELD_PER_APP_PER_NAME");
durationMetric->set_predicate("APP_IS_BACKGROUND_AND_SCREEN_ON");
link = durationMetric->add_links();
link->set_condition("APP_IS_BACKGROUND");
@@ -229,14 +237,14 @@
// Duration of screen on time.
durationMetric = config.add_duration_metric();
- durationMetric->set_metric_id(8);
+ durationMetric->set_name("8");
durationMetric->mutable_bucket()->set_bucket_size_millis(10 * 1000L);
durationMetric->set_type(DurationMetric_AggregationType_DURATION_SUM);
durationMetric->set_what("SCREEN_IS_ON");
// Value metric to count KERNEL_WAKELOCK when screen turned on
ValueMetric* valueMetric = config.add_value_metric();
- valueMetric->set_metric_id(6);
+ valueMetric->set_name("6");
valueMetric->set_what("KERNEL_WAKELOCK");
valueMetric->set_value_field(1);
valueMetric->set_condition("SCREEN_IS_ON");
@@ -247,12 +255,12 @@
// Add an EventMetric to log process state change events.
EventMetric* eventMetric = config.add_event_metric();
- eventMetric->set_metric_id(9);
+ eventMetric->set_name("9");
eventMetric->set_what("SCREEN_TURNED_ON");
// Add an GaugeMetric.
GaugeMetric* gaugeMetric = config.add_gauge_metric();
- gaugeMetric->set_metric_id(10);
+ gaugeMetric->set_name("10");
gaugeMetric->set_what("DEVICE_TEMPERATURE");
gaugeMetric->set_gauge_field(DEVICE_TEMPERATURE_KEY);
gaugeMetric->mutable_bucket()->set_bucket_size_millis(60 * 1000L);
@@ -328,12 +336,14 @@
SimpleCondition* simpleCondition = condition->mutable_simple_condition();
simpleCondition->set_start("SCREEN_TURNED_ON");
simpleCondition->set_stop("SCREEN_TURNED_OFF");
+ simpleCondition->set_count_nesting(false);
condition = config.add_condition();
condition->set_name("SCREEN_IS_OFF");
simpleCondition = condition->mutable_simple_condition();
simpleCondition->set_start("SCREEN_TURNED_OFF");
simpleCondition->set_stop("SCREEN_TURNED_ON");
+ simpleCondition->set_count_nesting(false);
condition = config.add_condition();
condition->set_name("APP_IS_BACKGROUND");
@@ -342,6 +352,7 @@
simpleCondition->set_stop("APP_GOES_FOREGROUND");
KeyMatcher* condition_dimension1 = simpleCondition->add_dimension();
condition_dimension1->set_key(APP_USAGE_UID_KEY_ID);
+ simpleCondition->set_count_nesting(false);
condition = config.add_condition();
condition->set_name("APP_IS_BACKGROUND_AND_SCREEN_ON");
@@ -351,7 +362,7 @@
combination_condition->add_condition("SCREEN_IS_ON");
condition = config.add_condition();
- condition->set_name("WL_STATE_PER_APP_PER_NAME");
+ condition->set_name("WL_HELD_PER_APP_PER_NAME");
simpleCondition = condition->mutable_simple_condition();
simpleCondition->set_start("APP_GET_WL");
simpleCondition->set_stop("APP_RELEASE_WL");
@@ -359,6 +370,17 @@
condition_dimension->set_key(WAKE_LOCK_UID_KEY_ID);
condition_dimension = simpleCondition->add_dimension();
condition_dimension->set_key(WAKE_LOCK_NAME_KEY);
+ simpleCondition->set_count_nesting(true);
+
+ condition = config.add_condition();
+ condition->set_name("WL_HELD_PER_APP");
+ simpleCondition = condition->mutable_simple_condition();
+ simpleCondition->set_start("APP_GET_WL");
+ simpleCondition->set_stop("APP_RELEASE_WL");
+ simpleCondition->set_initial_value(SimpleCondition_InitialValue_FALSE);
+ condition_dimension = simpleCondition->add_dimension();
+ condition_dimension->set_key(WAKE_LOCK_UID_KEY_ID);
+ simpleCondition->set_count_nesting(true);
return config;
}
diff --git a/cmds/statsd/src/external/CpuTimePerUidFreqPuller.cpp b/cmds/statsd/src/external/CpuTimePerUidFreqPuller.cpp
new file mode 100644
index 0000000..e004d21
--- /dev/null
+++ b/cmds/statsd/src/external/CpuTimePerUidFreqPuller.cpp
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define DEBUG true // STOPSHIP if true
+#include "Log.h"
+
+#include <fstream>
+#include "external/CpuTimePerUidFreqPuller.h"
+
+#include "logd/LogEvent.h"
+#include "statslog.h"
+
+using std::make_shared;
+using std::shared_ptr;
+using std::ifstream;
+
+namespace android {
+namespace os {
+namespace statsd {
+
+static const string sProcFile = "/proc/uid_cputime/show_uid_stat";
+static const int kLineBufferSize = 1024;
+
+/**
+ * Reads /proc/uid_time_in_state which has the format:
+ *
+ * uid: [freq1] [freq2] [freq3] ...
+ * [uid1]: [time in freq1] [time in freq2] [time in freq3] ...
+ * [uid2]: [time in freq1] [time in freq2] [time in freq3] ...
+ * ...
+ *
+ * This provides the times a UID's processes spent executing at each different cpu frequency.
+ * The file contains a monotonically increasing count of time for a single boot.
+ */
+bool CpuTimePerUidFreqPuller::Pull(const int tagId, vector<shared_ptr<LogEvent>>* data) {
+ data->clear();
+
+ ifstream fin;
+ fin.open(sProcFile);
+ if (!fin.good()) {
+ VLOG("Failed to read pseudo file %s", sProcFile.c_str());
+ return false;
+ }
+
+ uint64_t timestamp = time(nullptr) * NS_PER_SEC;
+ char buf[kLineBufferSize];
+ // first line prints the format and frequencies
+ fin.getline(buf, kLineBufferSize);
+ char * pch;
+ while(!fin.eof()){
+ fin.getline(buf, kLineBufferSize);
+ pch = strtok (buf, " :");
+ if (pch == NULL) break;
+ uint64_t uid = std::stoull(pch);
+ pch = strtok(NULL, " ");
+ uint64_t timeMs;
+ int idx = 0;
+ do {
+ timeMs = std::stoull(pch);
+ auto ptr = make_shared<LogEvent>(android::util::CPU_TIME_PER_UID_FREQ_PULLED, timestamp);
+ auto elemList = ptr->GetAndroidLogEventList();
+ *elemList << uid;
+ *elemList << idx;
+ *elemList << timeMs;
+ ptr->init();
+ data->push_back(ptr);
+ VLOG("uid %lld, freq idx %d, sys time %lld", (long long)uid, idx, (long long)timeMs);
+ idx ++;
+ pch = strtok(NULL, " ");
+ } while (pch != NULL);
+ }
+ return true;
+}
+
+} // namespace statsd
+} // namespace os
+} // namespace android
diff --git a/cmds/statsd/src/external/CpuTimePerUidFreqPuller.h b/cmds/statsd/src/external/CpuTimePerUidFreqPuller.h
new file mode 100644
index 0000000..839e5aa
--- /dev/null
+++ b/cmds/statsd/src/external/CpuTimePerUidFreqPuller.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <utils/String16.h>
+#include "StatsPuller.h"
+
+namespace android {
+namespace os {
+namespace statsd {
+
+/**
+ * Reads /proc/uid_cputime/show_uid_stat which has the line format:
+ *
+ * uid: user_time_micro_seconds system_time_micro_seconds
+ *
+ * This provides the time a UID's processes spent executing in user-space and kernel-space.
+ * The file contains a monotonically increasing count of time for a single boot.
+ */
+class CpuTimePerUidFreqPuller : public StatsPuller {
+ public:
+ bool Pull(const int tagId, vector<std::shared_ptr<LogEvent>>* data) override;
+};
+
+} // namespace statsd
+} // namespace os
+} // namespace android
diff --git a/cmds/statsd/src/external/CpuTimePerUidPuller.cpp b/cmds/statsd/src/external/CpuTimePerUidPuller.cpp
new file mode 100644
index 0000000..b84b877
--- /dev/null
+++ b/cmds/statsd/src/external/CpuTimePerUidPuller.cpp
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define DEBUG true // STOPSHIP if true
+#include "Log.h"
+
+#include <fstream>
+#include "external/CpuTimePerUidPuller.h"
+
+#include "logd/LogEvent.h"
+#include "statslog.h"
+
+using std::make_shared;
+using std::shared_ptr;
+using std::ifstream;
+
+namespace android {
+namespace os {
+namespace statsd {
+
+static const string sProcFile = "/proc/uid_cputime/show_uid_stat";
+static const int kLineBufferSize = 1024;
+
+/**
+ * Reads /proc/uid_cputime/show_uid_stat which has the line format:
+ *
+ * uid: user_time_micro_seconds system_time_micro_seconds power_in_milli-amp-micro_seconds
+ *
+ * This provides the time a UID's processes spent executing in user-space and kernel-space.
+ * The file contains a monotonically increasing count of time for a single boot.
+ */
+bool CpuTimePerUidPuller::Pull(const int tagId, vector<shared_ptr<LogEvent>>* data) {
+ data->clear();
+
+ ifstream fin;
+ fin.open(sProcFile);
+ if (!fin.good()) {
+ VLOG("Failed to read pseudo file %s", sProcFile.c_str());
+ return false;
+ }
+
+ uint64_t timestamp = time(nullptr) * NS_PER_SEC;
+ char buf[kLineBufferSize];
+ char * pch;
+ while(!fin.eof()){
+ fin.getline(buf, kLineBufferSize);
+ pch = strtok(buf, " :");
+ if (pch == NULL) break;
+ uint64_t uid = std::stoull(pch);
+ pch = strtok(buf, " ");
+ uint64_t userTimeMs = std::stoull(pch);
+ pch = strtok(buf, " ");
+ uint64_t sysTimeMs = std::stoull(pch);
+
+ auto ptr = make_shared<LogEvent>(android::util::CPU_TIME_PER_UID_PULLED, timestamp);
+ auto elemList = ptr->GetAndroidLogEventList();
+ *elemList << uid;
+ *elemList << userTimeMs;
+ *elemList << sysTimeMs;
+ ptr->init();
+ data->push_back(ptr);
+ VLOG("uid %lld, user time %lld, sys time %lld", (long long)uid, (long long)userTimeMs, (long long)sysTimeMs);
+ }
+ return true;
+}
+
+} // namespace statsd
+} // namespace os
+} // namespace android
diff --git a/cmds/statsd/src/external/CpuTimePerUidPuller.h b/cmds/statsd/src/external/CpuTimePerUidPuller.h
new file mode 100644
index 0000000..9bb8946
--- /dev/null
+++ b/cmds/statsd/src/external/CpuTimePerUidPuller.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <utils/String16.h>
+#include "StatsPuller.h"
+
+namespace android {
+namespace os {
+namespace statsd {
+
+/**
+ * Reads /proc/uid_cputime/show_uid_stat which has the line format:
+ *
+ * uid: user_time_micro_seconds system_time_micro_seconds
+ *
+ * This provides the time a UID's processes spent executing in user-space and kernel-space.
+ * The file contains a monotonically increasing count of time for a single boot.
+ */
+class CpuTimePerUidPuller : public StatsPuller {
+ public:
+ bool Pull(const int tagId, vector<std::shared_ptr<LogEvent>>* data) override;
+};
+
+} // namespace statsd
+} // namespace os
+} // namespace android
diff --git a/cmds/statsd/src/external/ResourcePowerManagerPuller.cpp b/cmds/statsd/src/external/ResourcePowerManagerPuller.cpp
index 3608ee4..319feef4 100644
--- a/cmds/statsd/src/external/ResourcePowerManagerPuller.cpp
+++ b/cmds/statsd/src/external/ResourcePowerManagerPuller.cpp
@@ -34,6 +34,7 @@
#include "external/StatsPuller.h"
#include "logd/LogEvent.h"
+#include "statslog.h"
using android::hardware::hidl_vec;
using android::hardware::power::V1_0::IPower;
@@ -57,10 +58,6 @@
std::mutex gPowerHalMutex;
bool gPowerHalExists = true;
-static const int power_state_platform_sleep_state_tag = 1011;
-static const int power_state_voter_tag = 1012;
-static const int power_state_subsystem_state_tag = 1013;
-
bool getPowerHal() {
if (gPowerHalExists && gPowerHalV1_0 == nullptr) {
gPowerHalV1_0 = android::hardware::power::V1_0::IPower::getService();
@@ -94,8 +91,8 @@
for (size_t i = 0; i < states.size(); i++) {
const PowerStatePlatformSleepState& state = states[i];
- auto statePtr =
- make_shared<LogEvent>(power_state_platform_sleep_state_tag, timestamp);
+ auto statePtr = make_shared<LogEvent>(
+ android::util::POWER_STATE_PLATFORM_SLEEP_STATE_PULLED, timestamp);
auto elemList = statePtr->GetAndroidLogEventList();
*elemList << state.name;
*elemList << state.residencyInMsecSinceBoot;
@@ -107,12 +104,14 @@
(long long)state.residencyInMsecSinceBoot,
(long long)state.totalTransitions, state.supportedOnlyInSuspend ? 1 : 0);
for (auto voter : state.voters) {
- auto voterPtr = make_shared<LogEvent>(power_state_voter_tag, timestamp);
+ auto voterPtr =
+ make_shared<LogEvent>(android::util::POWER_STATE_VOTER_PULLED, timestamp);
auto elemList = voterPtr->GetAndroidLogEventList();
*elemList << state.name;
*elemList << voter.name;
*elemList << voter.totalTimeInMsecVotedForSinceBoot;
*elemList << voter.totalNumberOfTimesVotedSinceBoot;
+ voterPtr->init();
data->push_back(voterPtr);
VLOG("powerstatevoter: %s, %s, %lld, %lld", state.name.c_str(),
voter.name.c_str(), (long long)voter.totalTimeInMsecVotedForSinceBoot,
@@ -141,7 +140,7 @@
for (size_t j = 0; j < subsystem.states.size(); j++) {
const PowerStateSubsystemSleepState& state = subsystem.states[j];
auto subsystemStatePtr = make_shared<LogEvent>(
- power_state_subsystem_state_tag, timestamp);
+ android::util::POWER_STATE_SUBSYSTEM_SLEEP_STATE_PULLED, timestamp);
auto elemList = subsystemStatePtr->GetAndroidLogEventList();
*elemList << subsystem.name;
*elemList << state.name;
diff --git a/cmds/statsd/src/external/StatsPullerManager.h b/cmds/statsd/src/external/StatsPullerManager.h
index 67580d6..2e803c9 100644
--- a/cmds/statsd/src/external/StatsPullerManager.h
+++ b/cmds/statsd/src/external/StatsPullerManager.h
@@ -16,71 +16,39 @@
#pragma once
-#include <android/os/IStatsCompanionService.h>
-#include <binder/IServiceManager.h>
-#include <utils/RefBase.h>
-#include <utils/String16.h>
-#include <utils/String8.h>
-#include <utils/threads.h>
-#include <string>
-#include <unordered_map>
-#include <vector>
-#include "PullDataReceiver.h"
-#include "StatsPuller.h"
-#include "logd/LogEvent.h"
+#include "StatsPullerManagerImpl.h"
namespace android {
namespace os {
namespace statsd {
-class StatsPullerManager : public virtual RefBase {
-public:
- static StatsPullerManager& GetInstance();
+class StatsPullerManager{
+ public:
+ virtual ~StatsPullerManager() {}
- void RegisterReceiver(int tagId, sp<PullDataReceiver> receiver, long intervalMs);
+ virtual void RegisterReceiver(int tagId, wp<PullDataReceiver> receiver, long intervalMs) {
+ mPullerManager.RegisterReceiver(tagId, receiver, intervalMs);
+ };
- void UnRegisterReceiver(int tagId, sp<PullDataReceiver> receiver);
+ virtual void UnRegisterReceiver(int tagId, wp<PullDataReceiver> receiver) {
+ mPullerManager.UnRegisterReceiver(tagId, receiver);
+ };
- // Verify if we know how to pull for this matcher
- bool PullerForMatcherExists(int tagId);
+ // Verify if we know how to pull for this matcher
+ bool PullerForMatcherExists(int tagId) {
+ return mPullerManager.PullerForMatcherExists(tagId);
+ }
- void OnAlarmFired();
+ void OnAlarmFired() {
+ mPullerManager.OnAlarmFired();
+ }
- bool Pull(const int pullCode, vector<std::shared_ptr<LogEvent>>* data);
+ virtual bool Pull(const int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+ return mPullerManager.Pull(tagId, data);
+ }
-private:
- StatsPullerManager();
-
- // use this to update alarm
- sp<IStatsCompanionService> mStatsCompanionService = nullptr;
-
- sp<IStatsCompanionService> get_stats_companion_service();
-
- // mapping from simple matcher tagId to puller
- std::map<int, std::shared_ptr<StatsPuller>> mPullers;
-
- typedef struct {
- // pull_interval_sec : last_pull_time_sec
- std::pair<uint64_t, uint64_t> timeInfo;
- sp<PullDataReceiver> receiver;
- } ReceiverInfo;
-
- // mapping from simple matcher tagId to receivers
- std::map<int, std::vector<ReceiverInfo>> mReceivers;
-
- Mutex mReceiversLock;
-
- long mCurrentPullingInterval;
-
- // for pulled metrics, it is important for the buckets to be aligned to multiple of smallest
- // bucket size. All pulled metrics start pulling based on this time, so that they can be
- // correctly attributed to the correct buckets. Pulled data attach a timestamp which is the
- // request time.
- const long mPullStartTimeMs;
-
- long get_pull_start_time_ms();
-
- LogEvent parse_pulled_data(String16 data);
+ private:
+ StatsPullerManagerImpl& mPullerManager = StatsPullerManagerImpl::GetInstance();
};
} // namespace statsd
diff --git a/cmds/statsd/src/external/StatsPullerManager.cpp b/cmds/statsd/src/external/StatsPullerManagerImpl.cpp
similarity index 62%
rename from cmds/statsd/src/external/StatsPullerManager.cpp
rename to cmds/statsd/src/external/StatsPullerManagerImpl.cpp
index 43543cc..07d0b3e 100644
--- a/cmds/statsd/src/external/StatsPullerManager.cpp
+++ b/cmds/statsd/src/external/StatsPullerManagerImpl.cpp
@@ -21,9 +21,11 @@
#include <cutils/log.h>
#include <algorithm>
#include <climits>
+#include "CpuTimePerUidFreqPuller.h"
+#include "CpuTimePerUidPuller.h"
#include "ResourcePowerManagerPuller.h"
#include "StatsCompanionServicePuller.h"
-#include "StatsPullerManager.h"
+#include "StatsPullerManagerImpl.h"
#include "StatsService.h"
#include "logd/LogEvent.h"
#include "statslog.h"
@@ -35,33 +37,42 @@
using std::shared_ptr;
using std::string;
using std::vector;
+using std::list;
namespace android {
namespace os {
namespace statsd {
-StatsPullerManager::StatsPullerManager()
+StatsPullerManagerImpl::StatsPullerManagerImpl()
: mCurrentPullingInterval(LONG_MAX), mPullStartTimeMs(get_pull_start_time_ms()) {
- shared_ptr<StatsPuller> statsCompanionServicePuller =
- make_shared<StatsCompanionServicePuller>();
+ shared_ptr<StatsPuller> statsCompanionServicePuller = make_shared<StatsCompanionServicePuller>();
shared_ptr<StatsPuller> resourcePowerManagerPuller = make_shared<ResourcePowerManagerPuller>();
+ shared_ptr<StatsPuller> cpuTimePerUidPuller = make_shared<CpuTimePerUidPuller>();
+ shared_ptr<StatsPuller> cpuTimePerUidFreqPuller = make_shared<CpuTimePerUidFreqPuller>();
- mPullers.insert({android::util::KERNEL_WAKELOCK_PULLED, statsCompanionServicePuller});
- mPullers.insert({android::util::WIFI_BYTES_TRANSFERRED, statsCompanionServicePuller});
- mPullers.insert({android::util::MOBILE_BYTES_TRANSFERRED, statsCompanionServicePuller});
- mPullers.insert({android::util::WIFI_BYTES_TRANSFERRED_BY_FG_BG, statsCompanionServicePuller});
- mPullers.insert(
- {android::util::MOBILE_BYTES_TRANSFERRED_BY_FG_BG, statsCompanionServicePuller});
- mPullers.insert(
- {android::util::POWER_STATE_PLATFORM_SLEEP_STATE_PULLED, resourcePowerManagerPuller});
- mPullers.insert({android::util::POWER_STATE_VOTER_PULLED, resourcePowerManagerPuller});
- mPullers.insert(
- {android::util::POWER_STATE_SUBSYSTEM_SLEEP_STATE_PULLED, resourcePowerManagerPuller});
+ mPullers.insert({android::util::KERNEL_WAKELOCK_PULLED,
+ statsCompanionServicePuller});
+ mPullers.insert({android::util::WIFI_BYTES_TRANSFERRED,
+ statsCompanionServicePuller});
+ mPullers.insert({android::util::MOBILE_BYTES_TRANSFERRED,
+ statsCompanionServicePuller});
+ mPullers.insert({android::util::WIFI_BYTES_TRANSFERRED_BY_FG_BG,
+ statsCompanionServicePuller});
+ mPullers.insert({android::util::MOBILE_BYTES_TRANSFERRED_BY_FG_BG,
+ statsCompanionServicePuller});
+ mPullers.insert({android::util::POWER_STATE_PLATFORM_SLEEP_STATE_PULLED,
+ resourcePowerManagerPuller});
+ mPullers.insert({android::util::POWER_STATE_VOTER_PULLED,
+ resourcePowerManagerPuller});
+ mPullers.insert({android::util::POWER_STATE_SUBSYSTEM_SLEEP_STATE_PULLED,
+ resourcePowerManagerPuller});
+ mPullers.insert({android::util::CPU_TIME_PER_UID_PULLED, cpuTimePerUidPuller});
+ mPullers.insert({android::util::CPU_TIME_PER_UID_FREQ_PULLED, cpuTimePerUidFreqPuller});
mStatsCompanionService = StatsService::getStatsCompanionService();
}
-bool StatsPullerManager::Pull(int tagId, vector<shared_ptr<LogEvent>>* data) {
+bool StatsPullerManagerImpl::Pull(int tagId, vector<shared_ptr<LogEvent>>* data) {
if (DEBUG) ALOGD("Initiating pulling %d", tagId);
if (mPullers.find(tagId) != mPullers.end()) {
@@ -72,26 +83,26 @@
}
}
-StatsPullerManager& StatsPullerManager::GetInstance() {
- static StatsPullerManager instance;
+StatsPullerManagerImpl& StatsPullerManagerImpl::GetInstance() {
+ static StatsPullerManagerImpl instance;
return instance;
}
-bool StatsPullerManager::PullerForMatcherExists(int tagId) {
+bool StatsPullerManagerImpl::PullerForMatcherExists(int tagId) {
return mPullers.find(tagId) != mPullers.end();
}
-long StatsPullerManager::get_pull_start_time_ms() {
+long StatsPullerManagerImpl::get_pull_start_time_ms() {
// TODO: limit and align pull intervals to 10min boundaries if this turns out to be a problem
return time(nullptr) * 1000;
}
-void StatsPullerManager::RegisterReceiver(int tagId, sp<PullDataReceiver> receiver,
- long intervalMs) {
+void StatsPullerManagerImpl::RegisterReceiver(int tagId, wp<PullDataReceiver> receiver,
+ long intervalMs) {
AutoMutex _l(mReceiversLock);
- vector<ReceiverInfo>& receivers = mReceivers[tagId];
+ auto& receivers = mReceivers[tagId];
for (auto it = receivers.begin(); it != receivers.end(); it++) {
- if (it->receiver.get() == receiver.get()) {
+ if (it->receiver == receiver) {
VLOG("Receiver already registered of %d", (int)receivers.size());
return;
}
@@ -114,7 +125,7 @@
VLOG("Puller for tagId %d registered of %d", tagId, (int)receivers.size());
}
-void StatsPullerManager::UnRegisterReceiver(int tagId, sp<PullDataReceiver> receiver) {
+void StatsPullerManagerImpl::UnRegisterReceiver(int tagId, wp<PullDataReceiver> receiver) {
AutoMutex _l(mReceiversLock);
if (mReceivers.find(tagId) == mReceivers.end()) {
VLOG("Unknown pull code or no receivers: %d", tagId);
@@ -122,7 +133,7 @@
}
auto& receivers = mReceivers.find(tagId)->second;
for (auto it = receivers.begin(); it != receivers.end(); it++) {
- if (receiver.get() == it->receiver.get()) {
+ if (receiver == it->receiver) {
receivers.erase(it);
VLOG("Puller for tagId %d unregistered of %d", tagId, (int)receivers.size());
return;
@@ -130,7 +141,7 @@
}
}
-void StatsPullerManager::OnAlarmFired() {
+void StatsPullerManagerImpl::OnAlarmFired() {
AutoMutex _l(mReceiversLock);
uint64_t currentTimeMs = time(nullptr) * 1000;
@@ -155,8 +166,13 @@
vector<shared_ptr<LogEvent>> data;
if (Pull(pullInfo.first, &data)) {
for (const auto& receiverInfo : pullInfo.second) {
- receiverInfo->receiver->onDataPulled(data);
- receiverInfo->timeInfo.second = currentTimeMs;
+ sp<PullDataReceiver> receiverPtr = receiverInfo->receiver.promote();
+ if (receiverPtr != nullptr) {
+ receiverPtr->onDataPulled(data);
+ receiverInfo->timeInfo.second = currentTimeMs;
+ } else {
+ VLOG("receiver already gone.");
+ }
}
}
}
diff --git a/cmds/statsd/src/external/StatsPullerManagerImpl.h b/cmds/statsd/src/external/StatsPullerManagerImpl.h
new file mode 100644
index 0000000..0b9f21e
--- /dev/null
+++ b/cmds/statsd/src/external/StatsPullerManagerImpl.h
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <android/os/IStatsCompanionService.h>
+#include <binder/IServiceManager.h>
+#include <utils/RefBase.h>
+#include <utils/threads.h>
+#include <string>
+#include <unordered_map>
+#include <vector>
+#include <list>
+#include "PullDataReceiver.h"
+#include "StatsPuller.h"
+#include "logd/LogEvent.h"
+
+namespace android {
+namespace os {
+namespace statsd {
+
+class StatsPullerManagerImpl : public virtual RefBase {
+public:
+ static StatsPullerManagerImpl& GetInstance();
+
+ void RegisterReceiver(int tagId, wp<PullDataReceiver> receiver, long intervalMs);
+
+ void UnRegisterReceiver(int tagId, wp<PullDataReceiver> receiver);
+
+ // Verify if we know how to pull for this matcher
+ bool PullerForMatcherExists(int tagId);
+
+ void OnAlarmFired();
+
+ bool Pull(const int tagId, vector<std::shared_ptr<LogEvent>>* data);
+
+private:
+ StatsPullerManagerImpl();
+
+ // use this to update alarm
+ sp<IStatsCompanionService> mStatsCompanionService = nullptr;
+
+ sp<IStatsCompanionService> get_stats_companion_service();
+
+ // mapping from simple matcher tagId to puller
+ std::map<int, std::shared_ptr<StatsPuller>> mPullers;
+
+ typedef struct {
+ // pull_interval_sec : last_pull_time_sec
+ std::pair<uint64_t, uint64_t> timeInfo;
+ wp<PullDataReceiver> receiver;
+ } ReceiverInfo;
+
+ // mapping from simple matcher tagId to receivers
+ std::map<int, std::list<ReceiverInfo>> mReceivers;
+
+ Mutex mReceiversLock;
+
+ long mCurrentPullingInterval;
+
+ // for pulled metrics, it is important for the buckets to be aligned to multiple of smallest
+ // bucket size. All pulled metrics start pulling based on this time, so that they can be
+ // correctly attributed to the correct buckets. Pulled data attach a timestamp which is the
+ // request time.
+ const long mPullStartTimeMs;
+
+ long get_pull_start_time_ms();
+
+ LogEvent parse_pulled_data(String16 data);
+};
+
+} // namespace statsd
+} // namespace os
+} // namespace android
diff --git a/cmds/statsd/src/matchers/CombinationLogMatchingTracker.h b/cmds/statsd/src/matchers/CombinationLogMatchingTracker.h
index 006d74c..adb691e 100644
--- a/cmds/statsd/src/matchers/CombinationLogMatchingTracker.h
+++ b/cmds/statsd/src/matchers/CombinationLogMatchingTracker.h
@@ -22,7 +22,6 @@
#include <unordered_map>
#include <vector>
#include "LogMatchingTracker.h"
-#include "frameworks/base/cmds/statsd/src/stats_log.pb.h"
#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
namespace android {
diff --git a/cmds/statsd/src/matchers/LogMatchingTracker.h b/cmds/statsd/src/matchers/LogMatchingTracker.h
index d82da3b..ffbf248 100644
--- a/cmds/statsd/src/matchers/LogMatchingTracker.h
+++ b/cmds/statsd/src/matchers/LogMatchingTracker.h
@@ -17,7 +17,6 @@
#ifndef LOG_MATCHING_TRACKER_H
#define LOG_MATCHING_TRACKER_H
-#include "frameworks/base/cmds/statsd/src/stats_log.pb.h"
#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
#include "logd/LogEvent.h"
#include "matchers/matcher_util.h"
diff --git a/cmds/statsd/src/matchers/SimpleLogMatchingTracker.h b/cmds/statsd/src/matchers/SimpleLogMatchingTracker.h
index e110ec8..5dca55e 100644
--- a/cmds/statsd/src/matchers/SimpleLogMatchingTracker.h
+++ b/cmds/statsd/src/matchers/SimpleLogMatchingTracker.h
@@ -23,7 +23,6 @@
#include <unordered_map>
#include <vector>
#include "LogMatchingTracker.h"
-#include "frameworks/base/cmds/statsd/src/stats_log.pb.h"
#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
namespace android {
diff --git a/cmds/statsd/src/matchers/matcher_util.cpp b/cmds/statsd/src/matchers/matcher_util.cpp
index 6aa2211..cccc9b3 100644
--- a/cmds/statsd/src/matchers/matcher_util.cpp
+++ b/cmds/statsd/src/matchers/matcher_util.cpp
@@ -16,7 +16,6 @@
#include "Log.h"
-#include "frameworks/base/cmds/statsd/src/stats_log.pb.h"
#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
#include "matchers/LogMatchingTracker.h"
#include "matchers/matcher_util.h"
diff --git a/cmds/statsd/src/metrics/CountAnomalyTracker.cpp b/cmds/statsd/src/metrics/CountAnomalyTracker.cpp
deleted file mode 100644
index 7aa748f..0000000
--- a/cmds/statsd/src/metrics/CountAnomalyTracker.cpp
+++ /dev/null
@@ -1,129 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define DEBUG true // STOPSHIP if true
-#include "Log.h"
-
-#include "CountAnomalyTracker.h"
-
-#include <time.h>
-
-namespace android {
-namespace os {
-namespace statsd {
-
-CountAnomalyTracker::CountAnomalyTracker(const Alert& alert)
- : mAlert(alert),
- mNumPastBuckets(alert.number_of_buckets() > 0 ? alert.number_of_buckets() - 1 : 0),
- mPastBuckets(mNumPastBuckets > 0 ? (new int[mNumPastBuckets]) : nullptr) {
-
- VLOG("CountAnomalyTracker() called");
- if (alert.number_of_buckets() < 1) {
- ALOGE("Cannot create CountAnomalyTracker with %d buckets", alert.number_of_buckets());
- }
- reset(); // initialization
-}
-
-CountAnomalyTracker::~CountAnomalyTracker() {
- VLOG("~CountAnomalyTracker() called");
-}
-
-void CountAnomalyTracker::addPastBucket(int pastBucketCount,
- time_t numberOfBucketsAgo) {
- VLOG("addPastBucket() called.");
- if (numberOfBucketsAgo < 1) {
- ALOGE("Cannot add a past bucket %ld units in past", numberOfBucketsAgo);
- return;
- }
- // If past bucket was ancient, just empty out all past info.
- // This always applies if mNumPastBuckets == 0 (i.e. store no past buckets).
- if (numberOfBucketsAgo > (time_t) mNumPastBuckets) {
- reset();
- return;
- }
-
- // Empty out old mPastBuckets[i] values and update mSumPastCounters.
- for (size_t i = mOldestBucketIndex;
- i < mOldestBucketIndex + numberOfBucketsAgo; i++) {
- mSumPastCounters -= mPastBuckets[index(i)];
- mPastBuckets[index(i)] = 0;
- }
-
- // Replace the oldest bucket with the new bucket we are adding.
- mPastBuckets[mOldestBucketIndex] = pastBucketCount;
- mSumPastCounters += pastBucketCount;
-
- // Advance the oldest bucket index by numberOfBucketsAgo units.
- mOldestBucketIndex = index(mOldestBucketIndex + numberOfBucketsAgo);
-
- // TODO: Once dimensions are added to mSumPastCounters:
- // iterate through mSumPastCounters and remove any entries that are 0.
-}
-
-void CountAnomalyTracker::reset() {
- VLOG("reset() called.");
- for (size_t i = 0; i < mNumPastBuckets; i++) {
- mPastBuckets[i] = 0;
- }
- mSumPastCounters = 0;
- mOldestBucketIndex = 0;
-}
-
-void CountAnomalyTracker::checkAnomaly(int currentCount) {
- // Skip the check if in refractory period.
- if (time(nullptr) < mRefractoryPeriodEndsSec) {
- VLOG("Skipping anomaly check since within refractory period");
- return;
- }
-
- // TODO: Remove these extremely verbose debugging log.
- VLOG("Checking whether %d + %d > %lld",
- mSumPastCounters, currentCount, mAlert.trigger_if_sum_gt());
-
- // Note that this works even if mNumPastBuckets < 1 (since then
- // mSumPastCounters = 0 so the comparison is based only on currentCount).
- if (mAlert.has_trigger_if_sum_gt() &&
- mSumPastCounters + currentCount > mAlert.trigger_if_sum_gt()) {
- declareAnomaly();
- }
-}
-
-void CountAnomalyTracker::declareAnomaly() {
- // TODO(guardrail): Consider guarding against too short refractory periods.
- time_t currTime = time(nullptr);
- mRefractoryPeriodEndsSec = currTime + mAlert.refractory_period_secs();
-
- // TODO: If we had access to the bucket_size_millis, consider calling reset()
- // if (mAlert.refractory_period_secs() > mNumPastBuckets * bucket_size_millis * 1000).
-
- if (mAlert.has_incidentd_details()) {
- const Alert_IncidentdDetails& incident = mAlert.incidentd_details();
- if (incident.has_alert_name()) {
- ALOGW("An anomaly (%s) has occurred! Informing incidentd.",
- incident.alert_name().c_str());
- } else {
- // TODO: Can construct a name based on the criteria (and/or relay the criteria).
- ALOGW("An anomaly (nameless) has occurred! Informing incidentd.");
- }
- // TODO: Send incidentd_details.name and incidentd_details.incidentd_sections to incidentd
- } else {
- ALOGW("An anomaly has occurred! (But informing incidentd not requested.)");
- }
-}
-
-} // namespace statsd
-} // namespace os
-} // namespace android
diff --git a/cmds/statsd/src/metrics/CountAnomalyTracker.h b/cmds/statsd/src/metrics/CountAnomalyTracker.h
deleted file mode 100644
index 79c47d2a..0000000
--- a/cmds/statsd/src/metrics/CountAnomalyTracker.h
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef COUNT_ANOMALY_TRACKER_H
-#define COUNT_ANOMALY_TRACKER_H
-
-#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" // Alert
-
-#include <memory> // unique_ptr
-#include <stdlib.h>
-
-namespace android {
-namespace os {
-namespace statsd {
-
-// TODO: Can probably be used for Count, Value, and Gauge. If so, rename to ValueAnomalyTracker.
-// (caveat: currently, the value cannot be negative. Probably fine for P.)
-class CountAnomalyTracker {
-public:
- CountAnomalyTracker(const Alert& alert);
-
- virtual ~CountAnomalyTracker();
-
-
- // Adds a new past bucket, holding pastBucketCount, and then advances the
- // present by numberOfBucketsAgo buckets (filling any intervening buckets
- // with 0s).
- // Thus, the newly added bucket (which holds pastBucketCount) is stored
- // numberOfBucketsAgo buckets ago.
- void addPastBucket(int pastBucketCount, time_t numberOfBucketsAgo);
-
- // Informs the anomaly tracker of the current bucket's count, so that it can
- // determine whether an anomaly has occurred. This value is not stored.
- void checkAnomaly(int currentCount);
-
-private:
- // statsd_config.proto Alert message that defines this tracker.
- const Alert mAlert;
-
- // Number of past buckets. One less than the total number of buckets needed
- // for the anomaly detection (since the current bucket is not in the past).
- const size_t mNumPastBuckets;
-
- // Count values for each of the past mNumPastBuckets buckets.
- // TODO: Add dimensions. This parallels the type of CountMetricProducer.mCounter.
- std::unique_ptr<int[]> mPastBuckets;
-
- // Sum over all of mPastBuckets (cached).
- // TODO: Add dimensions. This parallels the type of CountMetricProducer.mCounter.
- // At that point, mSumPastCounters must never contain entries of 0.
- int mSumPastCounters;
-
- // Index of the oldest bucket (i.e. the next bucket to be overwritten).
- size_t mOldestBucketIndex = 0;
-
- // Timestamp that the refractory period (if this anomaly was declared) ends, in seconds.
- // If an anomaly was never declared, set to 0.
- time_t mRefractoryPeriodEndsSec = 0;
-
- void declareAnomaly();
-
- // Calculates the corresponding index within the circular array.
- size_t index(size_t unsafeIndex) {
- return unsafeIndex % mNumPastBuckets;
- }
-
- // Resets all data. For use when all the data gets stale.
- void reset();
-};
-
-} // namespace statsd
-} // namespace os
-} // namespace android
-#endif // COUNT_ANOMALY_TRACKER_H
diff --git a/cmds/statsd/src/metrics/CountMetricProducer.cpp b/cmds/statsd/src/metrics/CountMetricProducer.cpp
index 94f4adf..f9da68e 100644
--- a/cmds/statsd/src/metrics/CountMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/CountMetricProducer.cpp
@@ -17,18 +17,20 @@
#define DEBUG true // STOPSHIP if true
#include "Log.h"
-#include "CountAnomalyTracker.h"
+#include "../anomaly/DiscreteAnomalyTracker.h"
#include "CountMetricProducer.h"
#include "stats_util.h"
#include <limits.h>
#include <stdlib.h>
+using android::util::FIELD_COUNT_REPEATED;
using android::util::FIELD_TYPE_BOOL;
using android::util::FIELD_TYPE_FLOAT;
using android::util::FIELD_TYPE_INT32;
using android::util::FIELD_TYPE_INT64;
using android::util::FIELD_TYPE_MESSAGE;
+using android::util::FIELD_TYPE_STRING;
using android::util::ProtoOutputStream;
using std::map;
using std::string;
@@ -40,7 +42,7 @@
namespace statsd {
// for StatsLogReport
-const int FIELD_ID_METRIC_ID = 1;
+const int FIELD_ID_NAME = 1;
const int FIELD_ID_START_REPORT_NANOS = 2;
const int FIELD_ID_END_REPORT_NANOS = 3;
const int FIELD_ID_COUNT_METRICS = 5;
@@ -61,6 +63,7 @@
const int FIELD_ID_COUNT = 3;
// TODO: add back AnomalyTracker.
+
CountMetricProducer::CountMetricProducer(const CountMetric& metric, const int conditionIndex,
const sp<ConditionWizard>& wizard,
const uint64_t startTimeNs)
@@ -72,17 +75,6 @@
mBucketSizeNs = LLONG_MAX;
}
- mAnomalyTrackers.reserve(metric.alerts_size());
- for (int i = 0; i < metric.alerts_size(); i++) {
- const Alert& alert = metric.alerts(i);
- if (alert.trigger_if_sum_gt() > 0 && alert.number_of_buckets() > 0) {
- mAnomalyTrackers.push_back(std::make_unique<CountAnomalyTracker>(alert));
- } else {
- ALOGW("Ignoring invalid count metric alert: threshold=%lld num_buckets= %d",
- alert.trigger_if_sum_gt(), alert.number_of_buckets());
- }
- }
-
// TODO: use UidMap if uid->pkg_name is required
mDimension.insert(mDimension.begin(), metric.dimension().begin(), metric.dimension().end());
@@ -94,7 +86,7 @@
startNewProtoOutputStream(mStartTimeNs);
- VLOG("metric %lld created. bucket size %lld start_time: %lld", metric.metric_id(),
+ VLOG("metric %s created. bucket size %lld start_time: %lld", metric.name().c_str(),
(long long)mBucketSizeNs, (long long)mStartTimeNs);
}
@@ -104,7 +96,7 @@
void CountMetricProducer::startNewProtoOutputStream(long long startTime) {
mProto = std::make_unique<ProtoOutputStream>();
- mProto->write(FIELD_TYPE_INT32 | FIELD_ID_METRIC_ID, mMetric.metric_id());
+ mProto->write(FIELD_TYPE_STRING | FIELD_ID_NAME, mMetric.name());
mProto->write(FIELD_TYPE_INT64 | FIELD_ID_START_REPORT_NANOS, startTime);
mProtoToken = mProto->start(FIELD_TYPE_MESSAGE | FIELD_ID_COUNT_METRICS);
}
@@ -113,17 +105,17 @@
}
void CountMetricProducer::onSlicedConditionMayChange(const uint64_t eventTime) {
- VLOG("Metric %lld onSlicedConditionMayChange", mMetric.metric_id());
+ VLOG("Metric %s onSlicedConditionMayChange", mMetric.name().c_str());
}
-StatsLogReport CountMetricProducer::onDumpReport() {
+std::unique_ptr<std::vector<uint8_t>> CountMetricProducer::onDumpReport() {
long long endTime = time(nullptr) * NS_PER_SEC;
// Dump current bucket if it's stale.
// If current bucket is still on-going, don't force dump current bucket.
// In finish(), We can force dump current bucket.
flushCounterIfNeeded(endTime);
- VLOG("metric %lld dump report now...", mMetric.metric_id());
+ VLOG("metric %s dump report now...", mMetric.name().c_str());
for (const auto& counter : mPastBuckets) {
const HashableDimensionKey& hashableKey = counter.first;
@@ -133,11 +125,13 @@
ALOGE("Dimension key %s not found?!?! skip...", hashableKey.c_str());
continue;
}
- long long wrapperToken = mProto->start(FIELD_TYPE_MESSAGE | FIELD_ID_DATA);
+ long long wrapperToken =
+ mProto->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_DATA);
// First fill dimension (KeyValuePairs).
for (const auto& kv : it->second) {
- long long dimensionToken = mProto->start(FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION);
+ long long dimensionToken =
+ mProto->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_DIMENSION);
mProto->write(FIELD_TYPE_INT32 | FIELD_ID_KEY, kv.key());
if (kv.has_value_str()) {
mProto->write(FIELD_TYPE_INT32 | FIELD_ID_VALUE_STR, kv.value_str());
@@ -153,7 +147,8 @@
// Then fill bucket_info (CountBucketInfo).
for (const auto& bucket : counter.second) {
- long long bucketInfoToken = mProto->start(FIELD_TYPE_MESSAGE | FIELD_ID_BUCKET_INFO);
+ long long bucketInfoToken =
+ mProto->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_BUCKET_INFO);
mProto->write(FIELD_TYPE_INT64 | FIELD_ID_START_BUCKET_NANOS,
(long long)bucket.mBucketStartNs);
mProto->write(FIELD_TYPE_INT64 | FIELD_ID_END_BUCKET_NANOS,
@@ -170,22 +165,20 @@
mProto->write(FIELD_TYPE_INT64 | FIELD_ID_END_REPORT_NANOS,
(long long)mCurrentBucketStartTimeNs);
- VLOG("metric %lld dump report now...", mMetric.metric_id());
- std::unique_ptr<uint8_t[]> buffer = serializeProto();
+ VLOG("metric %s dump report now...", mMetric.name().c_str());
+ std::unique_ptr<std::vector<uint8_t>> buffer = serializeProto();
startNewProtoOutputStream(endTime);
mPastBuckets.clear();
mByteSize = 0;
- // TODO: Once we migrate all MetricProducers to use ProtoOutputStream, we should return this:
- // return std::move(buffer);
- return StatsLogReport();
+ return buffer;
// TODO: Clear mDimensionKeyMap once the report is dumped.
}
void CountMetricProducer::onConditionChanged(const bool conditionMet, const uint64_t eventTime) {
- VLOG("Metric %lld onConditionChanged", mMetric.metric_id());
+ VLOG("Metric %s onConditionChanged", mMetric.name().c_str());
mCondition = conditionMet;
}
@@ -201,25 +194,19 @@
return;
}
- auto it = mCurrentSlicedCounter.find(eventKey);
+ auto it = mCurrentSlicedCounter->find(eventKey);
- if (it == mCurrentSlicedCounter.end()) {
+ if (it == mCurrentSlicedCounter->end()) {
// create a counter for the new key
- mCurrentSlicedCounter[eventKey] = 1;
-
+ (*mCurrentSlicedCounter)[eventKey] = 1;
} else {
// increment the existing value
auto& count = it->second;
count++;
}
- // TODO: Re-add anomaly detection (similar to):
- // for (auto& tracker : mAnomalyTrackers) {
- // tracker->checkAnomaly(mCounter);
- // }
-
- VLOG("metric %lld %s->%d", mMetric.metric_id(), eventKey.c_str(),
- mCurrentSlicedCounter[eventKey]);
+ VLOG("metric %s %s->%d", mMetric.name().c_str(), eventKey.c_str(),
+ (*mCurrentSlicedCounter)[eventKey]);
}
// When a new matched event comes in, we check if event falls into the current
@@ -230,30 +217,32 @@
}
// adjust the bucket start time
- int64_t numBucketsForward = (eventTimeNs - mCurrentBucketStartTimeNs) / mBucketSizeNs;
+ // TODO: This (and addPastBucket to which it goes) doesn't really need to be an int64.
+ uint64_t numBucketsForward = (eventTimeNs - mCurrentBucketStartTimeNs) / mBucketSizeNs;
CountBucket info;
info.mBucketStartNs = mCurrentBucketStartTimeNs;
info.mBucketEndNs = mCurrentBucketStartTimeNs + mBucketSizeNs;
- for (const auto& counter : mCurrentSlicedCounter) {
+ for (const auto& counter : *mCurrentSlicedCounter) {
info.mCount = counter.second;
auto& bucketList = mPastBuckets[counter.first];
bucketList.push_back(info);
- VLOG("metric %lld, dump key value: %s -> %d", mMetric.metric_id(), counter.first.c_str(),
+ VLOG("metric %s, dump key value: %s -> %d", mMetric.name().c_str(), counter.first.c_str(),
counter.second);
mByteSize += sizeof(info);
}
- // TODO: Re-add anomaly detection (similar to):
- // for (auto& tracker : mAnomalyTrackers) {
- // tracker->addPastBucket(mCounter, numBucketsForward);
- //}
+ for (auto& tracker : mAnomalyTrackers) {
+ tracker->addOrUpdateBucket(mCurrentSlicedCounter, mCurrentBucketNum);
+ tracker->declareAndDeclareAnomaly();
+ }
- // Reset counters
- mCurrentSlicedCounter.clear();
+ // Reset counters (do not clear, since the old one is still referenced in mAnomalyTrackers).
+ mCurrentSlicedCounter = std::make_shared<DimToValMap>();
mCurrentBucketStartTimeNs = mCurrentBucketStartTimeNs + numBucketsForward * mBucketSizeNs;
- VLOG("metric %lld: new bucket start time: %lld", mMetric.metric_id(),
+ mCurrentBucketNum += numBucketsForward;
+ VLOG("metric %s: new bucket start time: %lld", mMetric.name().c_str(),
(long long)mCurrentBucketStartTimeNs);
}
diff --git a/cmds/statsd/src/metrics/CountMetricProducer.h b/cmds/statsd/src/metrics/CountMetricProducer.h
index c3af006..b7e480c 100644
--- a/cmds/statsd/src/metrics/CountMetricProducer.h
+++ b/cmds/statsd/src/metrics/CountMetricProducer.h
@@ -23,14 +23,11 @@
#include <gtest/gtest_prod.h>
#include "../condition/ConditionTracker.h"
#include "../matchers/matcher_util.h"
-#include "CountAnomalyTracker.h"
+#include "../anomaly/DiscreteAnomalyTracker.h"
#include "MetricProducer.h"
-#include "frameworks/base/cmds/statsd/src/stats_log.pb.h"
#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
#include "stats_util.h"
-using namespace std;
-
namespace android {
namespace os {
namespace statsd {
@@ -54,7 +51,7 @@
void finish() override;
// TODO: Pass a timestamp as a parameter in onDumpReport.
- StatsLogReport onDumpReport() override;
+ std::unique_ptr<std::vector<uint8_t>> onDumpReport() override;
void onSlicedConditionMayChange(const uint64_t eventTime) override;
@@ -82,9 +79,9 @@
size_t mByteSize;
// The current bucket.
- std::unordered_map<HashableDimensionKey, int> mCurrentSlicedCounter;
+ std::shared_ptr<DimToValMap> mCurrentSlicedCounter = std::make_shared<DimToValMap>();
- vector<unique_ptr<CountAnomalyTracker>> mAnomalyTrackers;
+ vector<std::unique_ptr<DiscreteAnomalyTracker>> mAnomalyTrackers;
void flushCounterIfNeeded(const uint64_t newEventTime);
diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.cpp b/cmds/statsd/src/metrics/DurationMetricProducer.cpp
index c0a0d98..aaf3ec2 100644
--- a/cmds/statsd/src/metrics/DurationMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/DurationMetricProducer.cpp
@@ -23,11 +23,13 @@
#include <limits.h>
#include <stdlib.h>
+using android::util::FIELD_COUNT_REPEATED;
using android::util::FIELD_TYPE_BOOL;
using android::util::FIELD_TYPE_FLOAT;
using android::util::FIELD_TYPE_INT32;
using android::util::FIELD_TYPE_INT64;
using android::util::FIELD_TYPE_MESSAGE;
+using android::util::FIELD_TYPE_STRING;
using android::util::ProtoOutputStream;
using std::string;
using std::unordered_map;
@@ -38,7 +40,7 @@
namespace statsd {
// for StatsLogReport
-const int FIELD_ID_METRIC_ID = 1;
+const int FIELD_ID_NAME = 1;
const int FIELD_ID_START_REPORT_NANOS = 2;
const int FIELD_ID_END_REPORT_NANOS = 3;
const int FIELD_ID_DURATION_METRICS = 6;
@@ -90,7 +92,7 @@
startNewProtoOutputStream(mStartTimeNs);
- VLOG("metric %lld created. bucket size %lld start_time: %lld", metric.metric_id(),
+ VLOG("metric %s created. bucket size %lld start_time: %lld", metric.name().c_str(),
(long long)mBucketSizeNs, (long long)mStartTimeNs);
}
@@ -100,7 +102,7 @@
void DurationMetricProducer::startNewProtoOutputStream(long long startTime) {
mProto = std::make_unique<ProtoOutputStream>();
- mProto->write(FIELD_TYPE_INT32 | FIELD_ID_METRIC_ID, mMetric.metric_id());
+ mProto->write(FIELD_TYPE_STRING | FIELD_ID_NAME, mMetric.name());
mProto->write(FIELD_TYPE_INT64 | FIELD_ID_START_REPORT_NANOS, startTime);
mProtoToken = mProto->start(FIELD_TYPE_MESSAGE | FIELD_ID_DURATION_METRICS);
}
@@ -125,7 +127,7 @@
}
void DurationMetricProducer::onSlicedConditionMayChange(const uint64_t eventTime) {
- VLOG("Metric %lld onSlicedConditionMayChange", mMetric.metric_id());
+ VLOG("Metric %s onSlicedConditionMayChange", mMetric.name().c_str());
// Now for each of the on-going event, check if the condition has changed for them.
flushIfNeeded(eventTime);
for (auto& pair : mCurrentSlicedDuration) {
@@ -134,7 +136,7 @@
}
void DurationMetricProducer::onConditionChanged(const bool conditionMet, const uint64_t eventTime) {
- VLOG("Metric %lld onConditionChanged", mMetric.metric_id());
+ VLOG("Metric %s onConditionChanged", mMetric.name().c_str());
mCondition = conditionMet;
// TODO: need to populate the condition change time from the event which triggers the condition
// change, instead of using current time.
@@ -159,14 +161,14 @@
}
}
-StatsLogReport DurationMetricProducer::onDumpReport() {
+std::unique_ptr<std::vector<uint8_t>> DurationMetricProducer::onDumpReport() {
long long endTime = time(nullptr) * NS_PER_SEC;
// Dump current bucket if it's stale.
// If current bucket is still on-going, don't force dump current bucket.
// In finish(), We can force dump current bucket.
flushIfNeeded(endTime);
- VLOG("metric %lld dump report now...", mMetric.metric_id());
+ VLOG("metric %s dump report now...", mMetric.name().c_str());
for (const auto& pair : mPastBuckets) {
const HashableDimensionKey& hashableKey = pair.first;
@@ -176,11 +178,13 @@
ALOGW("Dimension key %s not found?!?! skip...", hashableKey.c_str());
continue;
}
- long long wrapperToken = mProto->start(FIELD_TYPE_MESSAGE | FIELD_ID_DATA);
+ long long wrapperToken =
+ mProto->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_DATA);
// First fill dimension (KeyValuePairs).
for (const auto& kv : it->second) {
- long long dimensionToken = mProto->start(FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION);
+ long long dimensionToken =
+ mProto->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_DIMENSION);
mProto->write(FIELD_TYPE_INT32 | FIELD_ID_KEY, kv.key());
if (kv.has_value_str()) {
mProto->write(FIELD_TYPE_INT32 | FIELD_ID_VALUE_STR, kv.value_str());
@@ -196,7 +200,8 @@
// Then fill bucket_info (DurationBucketInfo).
for (const auto& bucket : pair.second) {
- long long bucketInfoToken = mProto->start(FIELD_TYPE_MESSAGE | FIELD_ID_BUCKET_INFO);
+ long long bucketInfoToken =
+ mProto->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_BUCKET_INFO);
mProto->write(FIELD_TYPE_INT64 | FIELD_ID_START_BUCKET_NANOS,
(long long)bucket.mBucketStartNs);
mProto->write(FIELD_TYPE_INT64 | FIELD_ID_END_BUCKET_NANOS,
@@ -214,14 +219,12 @@
mProto->write(FIELD_TYPE_INT64 | FIELD_ID_END_REPORT_NANOS,
(long long)mCurrentBucketStartTimeNs);
- std::unique_ptr<uint8_t[]> buffer = serializeProto();
+ std::unique_ptr<std::vector<uint8_t>> buffer = serializeProto();
startNewProtoOutputStream(endTime);
mPastBuckets.clear();
- // TODO: Once we migrate all MetricProducers to use ProtoOutputStream, we should return this:
- // return std::move(buffer);
- return StatsLogReport();
+ return buffer;
}
void DurationMetricProducer::flushIfNeeded(uint64_t eventTime) {
diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.h b/cmds/statsd/src/metrics/DurationMetricProducer.h
index 8fdd0d4..eea00454 100644
--- a/cmds/statsd/src/metrics/DurationMetricProducer.h
+++ b/cmds/statsd/src/metrics/DurationMetricProducer.h
@@ -26,7 +26,6 @@
#include "duration_helper/DurationTracker.h"
#include "duration_helper/MaxDurationTracker.h"
#include "duration_helper/OringDurationTracker.h"
-#include "frameworks/base/cmds/statsd/src/stats_log.pb.h"
#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
#include "stats_util.h"
@@ -50,7 +49,7 @@
void finish() override;
// TODO: Pass a timestamp as a parameter in onDumpReport.
- StatsLogReport onDumpReport() override;
+ std::unique_ptr<std::vector<uint8_t>> onDumpReport() override;
void onSlicedConditionMayChange(const uint64_t eventTime) override;
diff --git a/cmds/statsd/src/metrics/EventMetricProducer.cpp b/cmds/statsd/src/metrics/EventMetricProducer.cpp
index ee0bfde..b2ffb2f 100644
--- a/cmds/statsd/src/metrics/EventMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/EventMetricProducer.cpp
@@ -23,10 +23,12 @@
#include <limits.h>
#include <stdlib.h>
+using android::util::FIELD_COUNT_REPEATED;
using android::util::FIELD_TYPE_BOOL;
using android::util::FIELD_TYPE_FLOAT;
using android::util::FIELD_TYPE_INT32;
using android::util::FIELD_TYPE_INT64;
+using android::util::FIELD_TYPE_STRING;
using android::util::FIELD_TYPE_MESSAGE;
using android::util::ProtoOutputStream;
using std::map;
@@ -39,7 +41,7 @@
namespace statsd {
// for StatsLogReport
-const int FIELD_ID_METRIC_ID = 1;
+const int FIELD_ID_NAME = 1;
const int FIELD_ID_START_REPORT_NANOS = 2;
const int FIELD_ID_END_REPORT_NANOS = 3;
const int FIELD_ID_EVENT_METRICS = 4;
@@ -61,7 +63,7 @@
startNewProtoOutputStream(mStartTimeNs);
- VLOG("metric %lld created. bucket size %lld start_time: %lld", metric.metric_id(),
+ VLOG("metric %s created. bucket size %lld start_time: %lld", metric.name().c_str(),
(long long)mBucketSizeNs, (long long)mStartTimeNs);
}
@@ -73,7 +75,7 @@
mProto = std::make_unique<ProtoOutputStream>();
// TODO: We need to auto-generate the field IDs for StatsLogReport, EventMetricData,
// and StatsEvent.
- mProto->write(FIELD_TYPE_INT32 | FIELD_ID_METRIC_ID, mMetric.metric_id());
+ mProto->write(FIELD_TYPE_STRING | FIELD_ID_NAME, mMetric.name());
mProto->write(FIELD_TYPE_INT64 | FIELD_ID_START_REPORT_NANOS, startTime);
mProtoToken = mProto->start(FIELD_TYPE_MESSAGE | FIELD_ID_EVENT_METRICS);
}
@@ -84,24 +86,23 @@
void EventMetricProducer::onSlicedConditionMayChange(const uint64_t eventTime) {
}
-StatsLogReport EventMetricProducer::onDumpReport() {
+std::unique_ptr<std::vector<uint8_t>> EventMetricProducer::onDumpReport() {
long long endTime = time(nullptr) * NS_PER_SEC;
mProto->end(mProtoToken);
mProto->write(FIELD_TYPE_INT64 | FIELD_ID_END_REPORT_NANOS, endTime);
size_t bufferSize = mProto->size();
- VLOG("metric %lld dump report now... proto size: %zu ", mMetric.metric_id(), bufferSize);
- std::unique_ptr<uint8_t[]> buffer = serializeProto();
+ VLOG("metric %s dump report now... proto size: %zu ", mMetric.name().c_str(), bufferSize);
+ std::unique_ptr<std::vector<uint8_t>> buffer = serializeProto();
startNewProtoOutputStream(endTime);
+ mByteSize = 0;
- // TODO: Once we migrate all MetricProducers to use ProtoOutputStream, we should return this:
- // return std::move(buffer);
- return StatsLogReport();
+ return buffer;
}
void EventMetricProducer::onConditionChanged(const bool conditionMet, const uint64_t eventTime) {
- VLOG("Metric %lld onConditionChanged", mMetric.metric_id());
+ VLOG("Metric %s onConditionChanged", mMetric.name().c_str());
mCondition = conditionMet;
}
@@ -113,16 +114,18 @@
return;
}
- long long wrapperToken = mProto->start(FIELD_TYPE_MESSAGE | FIELD_ID_DATA);
+ long long wrapperToken =
+ mProto->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_DATA);
mProto->write(FIELD_TYPE_INT64 | FIELD_ID_TIMESTAMP_NANOS, (long long)event.GetTimestampNs());
long long eventToken = mProto->start(FIELD_TYPE_MESSAGE | FIELD_ID_STATS_EVENTS);
event.ToProto(*mProto);
mProto->end(eventToken);
mProto->end(wrapperToken);
+ // TODO: Find a proper way to derive the size of incoming LogEvent.
}
size_t EventMetricProducer::byteSize() {
- return mProto->size();
+ return mByteSize;
}
} // namespace statsd
diff --git a/cmds/statsd/src/metrics/EventMetricProducer.h b/cmds/statsd/src/metrics/EventMetricProducer.h
index 2ca8181..7740621 100644
--- a/cmds/statsd/src/metrics/EventMetricProducer.h
+++ b/cmds/statsd/src/metrics/EventMetricProducer.h
@@ -24,7 +24,6 @@
#include "../condition/ConditionTracker.h"
#include "../matchers/matcher_util.h"
#include "MetricProducer.h"
-#include "frameworks/base/cmds/statsd/src/stats_log.pb.h"
#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
#include "stats_util.h"
@@ -50,7 +49,7 @@
void finish() override;
// TODO: Pass a timestamp as a parameter in onDumpReport.
- StatsLogReport onDumpReport() override;
+ std::unique_ptr<std::vector<uint8_t>> onDumpReport() override;
void onSlicedConditionMayChange(const uint64_t eventTime) override;
@@ -66,6 +65,8 @@
private:
const EventMetric mMetric;
+
+ size_t mByteSize;
};
} // namespace statsd
diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
index dcfcc19..ed18f89 100644
--- a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
@@ -24,11 +24,13 @@
#include <limits.h>
#include <stdlib.h>
+using android::util::FIELD_COUNT_REPEATED;
using android::util::FIELD_TYPE_BOOL;
using android::util::FIELD_TYPE_FLOAT;
using android::util::FIELD_TYPE_INT32;
using android::util::FIELD_TYPE_INT64;
using android::util::FIELD_TYPE_MESSAGE;
+using android::util::FIELD_TYPE_STRING;
using android::util::ProtoOutputStream;
using std::map;
using std::string;
@@ -40,7 +42,7 @@
namespace statsd {
// for StatsLogReport
-const int FIELD_ID_METRIC_ID = 1;
+const int FIELD_ID_NAME = 1;
const int FIELD_ID_START_REPORT_NANOS = 2;
const int FIELD_ID_END_REPORT_NANOS = 3;
const int FIELD_ID_GAUGE_METRICS = 8;
@@ -88,7 +90,7 @@
startNewProtoOutputStream(mStartTimeNs);
- VLOG("metric %lld created. bucket size %lld start_time: %lld", metric.metric_id(),
+ VLOG("metric %s created. bucket size %lld start_time: %lld", metric.name().c_str(),
(long long)mBucketSizeNs, (long long)mStartTimeNs);
}
@@ -98,7 +100,7 @@
void GaugeMetricProducer::startNewProtoOutputStream(long long startTime) {
mProto = std::make_unique<ProtoOutputStream>();
- mProto->write(FIELD_TYPE_INT32 | FIELD_ID_METRIC_ID, mMetric.metric_id());
+ mProto->write(FIELD_TYPE_STRING | FIELD_ID_NAME, mMetric.name());
mProto->write(FIELD_TYPE_INT64 | FIELD_ID_START_REPORT_NANOS, startTime);
mProtoToken = mProto->start(FIELD_TYPE_MESSAGE | FIELD_ID_GAUGE_METRICS);
}
@@ -106,8 +108,8 @@
void GaugeMetricProducer::finish() {
}
-StatsLogReport GaugeMetricProducer::onDumpReport() {
- VLOG("gauge metric %lld dump report now...", mMetric.metric_id());
+std::unique_ptr<std::vector<uint8_t>> GaugeMetricProducer::onDumpReport() {
+ VLOG("gauge metric %s dump report now...", mMetric.name().c_str());
// Dump current bucket if it's stale.
// If current bucket is still on-going, don't force dump current bucket.
@@ -123,11 +125,13 @@
}
VLOG(" dimension key %s", hashableKey.c_str());
- long long wrapperToken = mProto->start(FIELD_TYPE_MESSAGE | FIELD_ID_DATA);
+ long long wrapperToken =
+ mProto->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_DATA);
// First fill dimension (KeyValuePairs).
for (const auto& kv : it->second) {
- long long dimensionToken = mProto->start(FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION);
+ long long dimensionToken =
+ mProto->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_DIMENSION);
mProto->write(FIELD_TYPE_INT32 | FIELD_ID_KEY, kv.key());
if (kv.has_value_str()) {
mProto->write(FIELD_TYPE_INT32 | FIELD_ID_VALUE_STR, kv.value_str());
@@ -143,7 +147,8 @@
// Then fill bucket_info (GaugeBucketInfo).
for (const auto& bucket : pair.second) {
- long long bucketInfoToken = mProto->start(FIELD_TYPE_MESSAGE | FIELD_ID_BUCKET_INFO);
+ long long bucketInfoToken =
+ mProto->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_BUCKET_INFO);
mProto->write(FIELD_TYPE_INT64 | FIELD_ID_START_BUCKET_NANOS,
(long long)bucket.mBucketStartNs);
mProto->write(FIELD_TYPE_INT64 | FIELD_ID_END_BUCKET_NANOS,
@@ -159,22 +164,20 @@
mProto->write(FIELD_TYPE_INT64 | FIELD_ID_END_REPORT_NANOS,
(long long)mCurrentBucketStartTimeNs);
- std::unique_ptr<uint8_t[]> buffer = serializeProto();
+ std::unique_ptr<std::vector<uint8_t>> buffer = serializeProto();
startNewProtoOutputStream(time(nullptr) * NS_PER_SEC);
mPastBuckets.clear();
mByteSize = 0;
- // TODO: Once we migrate all MetricProducers to use ProtoOutputStream, we should return this:
- // return std::move(buffer);
- return StatsLogReport();
+ return buffer;
// TODO: Clear mDimensionKeyMap once the report is dumped.
}
void GaugeMetricProducer::onConditionChanged(const bool conditionMet, const uint64_t eventTime) {
AutoMutex _l(mLock);
- VLOG("Metric %lld onConditionChanged", mMetric.metric_id());
+ VLOG("Metric %s onConditionChanged", mMetric.name().c_str());
mCondition = conditionMet;
// Push mode. Nothing to do.
@@ -198,7 +201,7 @@
}
void GaugeMetricProducer::onSlicedConditionMayChange(const uint64_t eventTime) {
- VLOG("Metric %lld onSlicedConditionMayChange", mMetric.metric_id());
+ VLOG("Metric %s onSlicedConditionMayChange", mMetric.name().c_str());
}
long GaugeMetricProducer::getGauge(const LogEvent& event) {
@@ -275,14 +278,14 @@
bucketList.push_back(info);
mByteSize += sizeof(info);
- VLOG("gauge metric %lld, dump key value: %s -> %ld", mMetric.metric_id(),
+ VLOG("gauge metric %s, dump key value: %s -> %ld", mMetric.name().c_str(),
slice.first.c_str(), slice.second);
}
// Reset counters
mCurrentSlicedBucket.clear();
mCurrentBucketStartTimeNs = mCurrentBucketStartTimeNs + numBucketsForward * mBucketSizeNs;
- VLOG("metric %lld: new bucket start time: %lld", mMetric.metric_id(),
+ VLOG("metric %s: new bucket start time: %lld", mMetric.name().c_str(),
(long long)mCurrentBucketStartTimeNs);
}
diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.h b/cmds/statsd/src/metrics/GaugeMetricProducer.h
index 3757174..f9e4deb 100644
--- a/cmds/statsd/src/metrics/GaugeMetricProducer.h
+++ b/cmds/statsd/src/metrics/GaugeMetricProducer.h
@@ -24,7 +24,6 @@
#include "../external/StatsPullerManager.h"
#include "../matchers/matcher_util.h"
#include "MetricProducer.h"
-#include "frameworks/base/cmds/statsd/src/stats_log.pb.h"
#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
#include "stats_util.h"
@@ -60,7 +59,7 @@
void finish() override;
// TODO: Pass a timestamp as a parameter in onDumpReport.
- StatsLogReport onDumpReport() override;
+ std::unique_ptr<std::vector<uint8_t>> onDumpReport() override;
size_t byteSize() override;
@@ -82,7 +81,7 @@
static const uint64_t kDefaultGaugemBucketSizeNs = 1000 * 1000 * 1000;
const GaugeMetric mMetric;
- StatsPullerManager& mStatsPullerManager = StatsPullerManager::GetInstance();
+ StatsPullerManager mStatsPullerManager;
// tagId for pulled data. -1 if this is not pulled
const int mPullTagId;
diff --git a/cmds/statsd/src/metrics/MetricProducer.cpp b/cmds/statsd/src/metrics/MetricProducer.cpp
index 5ca83b2..62fb632 100644
--- a/cmds/statsd/src/metrics/MetricProducer.cpp
+++ b/cmds/statsd/src/metrics/MetricProducer.cpp
@@ -64,16 +64,16 @@
scheduledPull);
}
-std::unique_ptr<uint8_t[]> MetricProducer::serializeProto() {
+std::unique_ptr<std::vector<uint8_t>> MetricProducer::serializeProto() {
size_t bufferSize = mProto->size();
- std::unique_ptr<uint8_t[]> buffer(new uint8_t[bufferSize]);
+ std::unique_ptr<std::vector<uint8_t>> buffer(new std::vector<uint8_t>(bufferSize));
size_t pos = 0;
auto it = mProto->data();
while (it.readBuffer() != NULL) {
size_t toRead = it.currentToRead();
- std::memcpy(&buffer[pos], it.readBuffer(), toRead);
+ std::memcpy(&((*buffer)[pos]), it.readBuffer(), toRead);
pos += toRead;
it.rp()->move(toRead);
}
diff --git a/cmds/statsd/src/metrics/MetricProducer.h b/cmds/statsd/src/metrics/MetricProducer.h
index 7ac97a8..c0930e3 100644
--- a/cmds/statsd/src/metrics/MetricProducer.h
+++ b/cmds/statsd/src/metrics/MetricProducer.h
@@ -23,7 +23,6 @@
#include <log/logprint.h>
#include <utils/RefBase.h>
-#include "frameworks/base/cmds/statsd/src/stats_log.pb.h"
namespace android {
namespace os {
@@ -39,6 +38,7 @@
const sp<ConditionWizard>& wizard)
: mStartTimeNs(startTimeNs),
mCurrentBucketStartTimeNs(startTimeNs),
+ mCurrentBucketNum(0),
mCondition(conditionIndex >= 0 ? false : true),
mConditionSliced(false),
mWizard(wizard),
@@ -61,7 +61,7 @@
// TODO: Pass a timestamp as a parameter in onDumpReport and update all its
// implementations.
- virtual StatsLogReport onDumpReport() = 0;
+ virtual std::unique_ptr<std::vector<uint8_t>> onDumpReport() = 0;
virtual bool isConditionSliced() const {
return mConditionSliced;
@@ -74,6 +74,8 @@
uint64_t mCurrentBucketStartTimeNs;
+ uint64_t mCurrentBucketNum;
+
int64_t mBucketSizeNs;
bool mCondition;
@@ -118,7 +120,7 @@
virtual void startNewProtoOutputStream(long long timestamp) = 0;
- std::unique_ptr<uint8_t[]> serializeProto();
+ std::unique_ptr<std::vector<uint8_t>> serializeProto();
};
} // namespace statsd
diff --git a/cmds/statsd/src/metrics/MetricsManager.cpp b/cmds/statsd/src/metrics/MetricsManager.cpp
index 80b325f..e8a862f 100644
--- a/cmds/statsd/src/metrics/MetricsManager.cpp
+++ b/cmds/statsd/src/metrics/MetricsManager.cpp
@@ -56,10 +56,10 @@
}
}
-vector<StatsLogReport> MetricsManager::onDumpReport() {
+vector<std::unique_ptr<vector<uint8_t>>> MetricsManager::onDumpReport() {
VLOG("=========================Metric Reports Start==========================");
// one StatsLogReport per MetricProduer
- vector<StatsLogReport> reportList;
+ vector<std::unique_ptr<vector<uint8_t>>> reportList;
for (auto& metric : mAllMetricProducers) {
reportList.push_back(metric->onDumpReport());
}
@@ -80,8 +80,6 @@
return;
}
- // Since at least one of the metrics is interested in this event, we parse it now.
- ALOGD("%s", event.ToString().c_str());
vector<MatchingState> matcherCache(mAllLogEntryMatchers.size(), MatchingState::kNotComputed);
for (auto& matcher : mAllLogEntryMatchers) {
@@ -104,18 +102,17 @@
ConditionState::kNotEvaluated);
// A bitmap to track if a condition has changed value.
vector<bool> changedCache(mAllConditionTrackers.size(), false);
- vector<bool> slicedChangedCache(mAllConditionTrackers.size(), false);
for (size_t i = 0; i < mAllConditionTrackers.size(); i++) {
if (conditionToBeEvaluated[i] == false) {
continue;
}
sp<ConditionTracker>& condition = mAllConditionTrackers[i];
condition->evaluateCondition(event, matcherCache, mAllConditionTrackers, conditionCache,
- changedCache, slicedChangedCache);
+ changedCache);
}
for (size_t i = 0; i < mAllConditionTrackers.size(); i++) {
- if (changedCache[i] == false && slicedChangedCache[i] == false) {
+ if (changedCache[i] == false) {
continue;
}
auto pair = mConditionToMetricMap.find(i);
@@ -124,14 +121,13 @@
for (auto metricIndex : metricList) {
// metric cares about non sliced condition, and it's changed.
// Push the new condition to it directly.
- if (!mAllMetricProducers[metricIndex]->isConditionSliced() && changedCache[i]) {
+ if (!mAllMetricProducers[metricIndex]->isConditionSliced()) {
mAllMetricProducers[metricIndex]->onConditionChanged(conditionCache[i],
eventTime);
// metric cares about sliced conditions, and it may have changed. Send
// notification, and the metric can query the sliced conditions that are
// interesting to it.
- } else if (mAllMetricProducers[metricIndex]->isConditionSliced() &&
- slicedChangedCache[i]) {
+ } else if (mAllMetricProducers[metricIndex]->isConditionSliced()) {
mAllMetricProducers[metricIndex]->onSlicedConditionMayChange(eventTime);
}
}
diff --git a/cmds/statsd/src/metrics/MetricsManager.h b/cmds/statsd/src/metrics/MetricsManager.h
index 63e2c33..39c79f9 100644
--- a/cmds/statsd/src/metrics/MetricsManager.h
+++ b/cmds/statsd/src/metrics/MetricsManager.h
@@ -44,7 +44,7 @@
void finish();
// Config source owner can call onDumpReport() to get all the metrics collected.
- std::vector<StatsLogReport> onDumpReport();
+ std::vector<std::unique_ptr<std::vector<uint8_t>>> onDumpReport();
size_t byteSize();
diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.cpp b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
index 41b7a5d..a35070b 100644
--- a/cmds/statsd/src/metrics/ValueMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
@@ -23,13 +23,16 @@
#include <limits.h>
#include <stdlib.h>
+using android::util::FIELD_COUNT_REPEATED;
using android::util::FIELD_TYPE_BOOL;
using android::util::FIELD_TYPE_FLOAT;
using android::util::FIELD_TYPE_INT32;
using android::util::FIELD_TYPE_INT64;
using android::util::FIELD_TYPE_MESSAGE;
+using android::util::FIELD_TYPE_STRING;
using android::util::ProtoOutputStream;
using std::list;
+using std::make_pair;
using std::make_shared;
using std::map;
using std::shared_ptr;
@@ -41,7 +44,7 @@
namespace statsd {
// for StatsLogReport
-const int FIELD_ID_METRIC_ID = 1;
+const int FIELD_ID_NAME = 1;
const int FIELD_ID_START_REPORT_NANOS = 2;
const int FIELD_ID_END_REPORT_NANOS = 3;
const int FIELD_ID_VALUE_METRICS = 7;
@@ -61,13 +64,23 @@
const int FIELD_ID_END_BUCKET_NANOS = 2;
const int FIELD_ID_VALUE = 3;
+static const uint64_t kDefaultBucketSizeMillis = 60 * 60 * 1000L;
+
// ValueMetric has a minimum bucket size of 10min so that we don't pull too frequently
ValueMetricProducer::ValueMetricProducer(const ValueMetric& metric, const int conditionIndex,
const sp<ConditionWizard>& wizard, const int pullTagId,
- const uint64_t startTimeNs)
- : MetricProducer(startTimeNs, conditionIndex, wizard), mMetric(metric), mPullTagId(pullTagId) {
+ const uint64_t startTimeNs,
+ shared_ptr<StatsPullerManager> statsPullerManager)
+ : MetricProducer(startTimeNs, conditionIndex, wizard),
+ mMetric(metric),
+ mStatsPullerManager(statsPullerManager),
+ mPullTagId(pullTagId) {
// TODO: valuemetric for pushed events may need unlimited bucket length
- mBucketSizeNs = mMetric.bucket().bucket_size_millis() * 1000 * 1000;
+ if (metric.has_bucket() && metric.bucket().has_bucket_size_millis()) {
+ mBucketSizeNs = mMetric.bucket().bucket_size_millis() * 1000 * 1000;
+ } else {
+ mBucketSizeNs = kDefaultBucketSizeMillis * 1000 * 1000;
+ }
mDimension.insert(mDimension.begin(), metric.dimension().begin(), metric.dimension().end());
@@ -78,23 +91,35 @@
}
if (!metric.has_condition() && mPullTagId != -1) {
- mStatsPullerManager.RegisterReceiver(mPullTagId, this,
- metric.bucket().bucket_size_millis());
+ VLOG("Setting up periodic pulling for %d", mPullTagId);
+ mStatsPullerManager->RegisterReceiver(mPullTagId, this,
+ metric.bucket().bucket_size_millis());
}
startNewProtoOutputStream(mStartTimeNs);
- VLOG("value metric %lld created. bucket size %lld start_time: %lld", metric.metric_id(),
+ VLOG("value metric %s created. bucket size %lld start_time: %lld", metric.name().c_str(),
(long long)mBucketSizeNs, (long long)mStartTimeNs);
}
+// for testing
+ValueMetricProducer::ValueMetricProducer(const ValueMetric& metric, const int conditionIndex,
+ const sp<ConditionWizard>& wizard, const int pullTagId,
+ const uint64_t startTimeNs)
+ : ValueMetricProducer(metric, conditionIndex, wizard, pullTagId, startTimeNs,
+ make_shared<StatsPullerManager>()) {
+}
+
ValueMetricProducer::~ValueMetricProducer() {
VLOG("~ValueMetricProducer() called");
+ if (mPullTagId != -1) {
+ mStatsPullerManager->UnRegisterReceiver(mPullTagId, this);
+ }
}
void ValueMetricProducer::startNewProtoOutputStream(long long startTime) {
mProto = std::make_unique<ProtoOutputStream>();
- mProto->write(FIELD_TYPE_INT32 | FIELD_ID_METRIC_ID, mMetric.metric_id());
+ mProto->write(FIELD_TYPE_STRING | FIELD_ID_NAME, mMetric.name());
mProto->write(FIELD_TYPE_INT64 | FIELD_ID_START_REPORT_NANOS, startTime);
mProtoToken = mProto->start(FIELD_TYPE_MESSAGE | FIELD_ID_VALUE_METRICS);
}
@@ -105,11 +130,11 @@
}
void ValueMetricProducer::onSlicedConditionMayChange(const uint64_t eventTime) {
- VLOG("Metric %lld onSlicedConditionMayChange", mMetric.metric_id());
+ VLOG("Metric %s onSlicedConditionMayChange", mMetric.name().c_str());
}
-StatsLogReport ValueMetricProducer::onDumpReport() {
- VLOG("metric %lld dump report now...", mMetric.metric_id());
+std::unique_ptr<std::vector<uint8_t>> ValueMetricProducer::onDumpReport() {
+ VLOG("metric %s dump report now...", mMetric.name().c_str());
for (const auto& pair : mPastBuckets) {
const HashableDimensionKey& hashableKey = pair.first;
@@ -119,11 +144,13 @@
ALOGE("Dimension key %s not found?!?! skip...", hashableKey.c_str());
continue;
}
- long long wrapperToken = mProto->start(FIELD_TYPE_MESSAGE | FIELD_ID_DATA);
+ long long wrapperToken =
+ mProto->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_DATA);
// First fill dimension (KeyValuePairs).
for (const auto& kv : it->second) {
- long long dimensionToken = mProto->start(FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION);
+ long long dimensionToken =
+ mProto->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_DIMENSION);
mProto->write(FIELD_TYPE_INT32 | FIELD_ID_KEY, kv.key());
if (kv.has_value_str()) {
mProto->write(FIELD_TYPE_INT32 | FIELD_ID_VALUE_STR, kv.value_str());
@@ -139,7 +166,8 @@
// Then fill bucket_info (ValueBucketInfo).
for (const auto& bucket : pair.second) {
- long long bucketInfoToken = mProto->start(FIELD_TYPE_MESSAGE | FIELD_ID_BUCKET_INFO);
+ long long bucketInfoToken =
+ mProto->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_BUCKET_INFO);
mProto->write(FIELD_TYPE_INT64 | FIELD_ID_START_BUCKET_NANOS,
(long long)bucket.mBucketStartNs);
mProto->write(FIELD_TYPE_INT64 | FIELD_ID_END_BUCKET_NANOS,
@@ -155,37 +183,35 @@
mProto->write(FIELD_TYPE_INT64 | FIELD_ID_END_REPORT_NANOS,
(long long)mCurrentBucketStartTimeNs);
- VLOG("metric %lld dump report now...", mMetric.metric_id());
- std::unique_ptr<uint8_t[]> buffer = serializeProto();
+ VLOG("metric %s dump report now...", mMetric.name().c_str());
+ std::unique_ptr<std::vector<uint8_t>> buffer = serializeProto();
startNewProtoOutputStream(time(nullptr) * NS_PER_SEC);
mPastBuckets.clear();
mByteSize = 0;
- // TODO: Once we migrate all MetricProducers to use ProtoOutputStream, we should return this:
- // return std::move(buffer);
- return StatsLogReport();
+ return buffer;
// TODO: Clear mDimensionKeyMap once the report is dumped.
}
void ValueMetricProducer::onConditionChanged(const bool condition, const uint64_t eventTime) {
+ AutoMutex _l(mLock);
mCondition = condition;
if (mPullTagId != -1) {
if (mCondition == true) {
- mStatsPullerManager.RegisterReceiver(mPullTagId, this,
- mMetric.bucket().bucket_size_millis());
- } else if (mCondition == ConditionState::kFalse) {
- mStatsPullerManager.UnRegisterReceiver(mPullTagId, this);
+ mStatsPullerManager->RegisterReceiver(mPullTagId, this,
+ mMetric.bucket().bucket_size_millis());
+ } else if (mCondition == false) {
+ mStatsPullerManager->UnRegisterReceiver(mPullTagId, this);
}
vector<shared_ptr<LogEvent>> allData;
- if (mStatsPullerManager.Pull(mPullTagId, &allData)) {
+ if (mStatsPullerManager->Pull(mPullTagId, &allData)) {
if (allData.size() == 0) {
return;
}
- AutoMutex _l(mLock);
for (const auto& data : allData) {
onMatchedLogEvent(0, *data, false);
}
@@ -196,12 +222,16 @@
}
void ValueMetricProducer::onDataPulled(const std::vector<std::shared_ptr<LogEvent>>& allData) {
- if (mCondition == ConditionState::kTrue || !mMetric.has_condition()) {
- AutoMutex _l(mLock);
+ AutoMutex _l(mLock);
+ if (mCondition == true || !mMetric.has_condition()) {
if (allData.size() == 0) {
return;
}
uint64_t eventTime = allData.at(0)->GetTimestampNs();
+ // alarm is not accurate and might drift.
+ if (eventTime > mCurrentBucketStartTimeNs + mBucketSizeNs * 3 / 2) {
+ flush_if_needed(eventTime);
+ }
for (const auto& data : allData) {
onMatchedLogEvent(0, *data, true);
}
@@ -224,24 +254,36 @@
long value = get_value(event);
- if (scheduledPull) {
- if (interval.raw.size() > 0) {
- interval.raw.back().second = value;
- } else {
- interval.raw.push_back(std::make_pair(value, value));
- }
- mNextSlicedBucket[eventKey].raw[0].first = value;
- } else {
- if (mCondition == ConditionState::kTrue) {
- interval.raw.push_back(std::make_pair(value, 0));
- } else {
- if (interval.raw.size() != 0) {
+ if (mPullTagId != -1) {
+ if (scheduledPull) {
+ // scheduled pull always sets beginning of current bucket and end
+ // of next bucket
+ if (interval.raw.size() > 0) {
interval.raw.back().second = value;
+ } else {
+ interval.raw.push_back(make_pair(value, value));
+ }
+ Interval& nextInterval = mNextSlicedBucket[eventKey];
+ if (nextInterval.raw.size() == 0) {
+ nextInterval.raw.push_back(make_pair(value, 0));
+ } else {
+ nextInterval.raw.front().first = value;
+ }
+ } else {
+ if (mCondition == true) {
+ interval.raw.push_back(make_pair(value, 0));
+ } else {
+ if (interval.raw.size() != 0) {
+ interval.raw.back().second = value;
+ } else {
+ interval.tainted = true;
+ VLOG("Data on condition true missing!");
+ }
}
}
- }
- if (mPullTagId == -1) {
+ } else {
flush_if_needed(eventTimeNs);
+ interval.raw.push_back(make_pair(value, 0));
}
}
@@ -251,7 +293,7 @@
if (err == NO_ERROR) {
return val;
} else {
- VLOG("Can't find value in message.");
+ VLOG("Can't find value in message. %s", event.ToString().c_str());
return 0;
}
}
@@ -269,13 +311,21 @@
info.mBucketStartNs = mCurrentBucketStartTimeNs;
info.mBucketEndNs = mCurrentBucketStartTimeNs + mBucketSizeNs;
+ int tainted = 0;
for (const auto& slice : mCurrentSlicedBucket) {
long value = 0;
- for (const auto& pair : slice.second.raw) {
- value += pair.second - pair.first;
+ if (mPullTagId != -1) {
+ for (const auto& pair : slice.second.raw) {
+ value += (pair.second - pair.first);
+ }
+ } else {
+ for (const auto& pair : slice.second.raw) {
+ value += pair.first;
+ }
}
+ tainted += slice.second.tainted;
info.mValue = value;
- VLOG(" %s, %ld", slice.first.c_str(), value);
+ VLOG(" %s, %ld, %d", slice.first.c_str(), value, tainted);
// it will auto create new vector of ValuebucketInfo if the key is not found.
auto& bucketList = mPastBuckets[slice.first];
bucketList.push_back(info);
@@ -290,7 +340,7 @@
VLOG("Skipping forward %lld buckets", (long long)numBucketsForward);
}
mCurrentBucketStartTimeNs = mCurrentBucketStartTimeNs + numBucketsForward * mBucketSizeNs;
- VLOG("metric %lld: new bucket start time: %lld", mMetric.metric_id(),
+ VLOG("metric %s: new bucket start time: %lld", mMetric.name().c_str(),
(long long)mCurrentBucketStartTimeNs);
}
diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.h b/cmds/statsd/src/metrics/ValueMetricProducer.h
index 8437665..c6c87f5 100644
--- a/cmds/statsd/src/metrics/ValueMetricProducer.h
+++ b/cmds/statsd/src/metrics/ValueMetricProducer.h
@@ -16,14 +16,13 @@
#pragma once
+#include <gtest/gtest_prod.h>
#include <utils/threads.h>
#include <list>
#include "../condition/ConditionTracker.h"
#include "../external/PullDataReceiver.h"
#include "../external/StatsPullerManager.h"
-#include "CountAnomalyTracker.h"
#include "MetricProducer.h"
-#include "frameworks/base/cmds/statsd/src/stats_log.pb.h"
#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
namespace android {
@@ -49,7 +48,7 @@
void finish() override;
// TODO: Pass a timestamp as a parameter in onDumpReport.
- StatsLogReport onDumpReport() override;
+ std::unique_ptr<std::vector<uint8_t>> onDumpReport() override;
void onSlicedConditionMayChange(const uint64_t eventTime);
@@ -73,7 +72,13 @@
private:
const ValueMetric mMetric;
- StatsPullerManager& mStatsPullerManager = StatsPullerManager::GetInstance();
+ std::shared_ptr<StatsPullerManager> mStatsPullerManager;
+
+ // for testing
+ ValueMetricProducer(const ValueMetric& valueMetric, const int conditionIndex,
+ const sp<ConditionWizard>& wizard, const int pullTagId,
+ const uint64_t startTimeNs,
+ std::shared_ptr<StatsPullerManager> statsPullerManager);
Mutex mLock;
@@ -83,6 +88,7 @@
// internal state of a bucket.
typedef struct {
std::vector<std::pair<long, long>> raw;
+ bool tainted;
} Interval;
std::unordered_map<HashableDimensionKey, Interval> mCurrentSlicedBucket;
@@ -99,6 +105,10 @@
void flush_if_needed(const uint64_t eventTimeNs);
size_t mByteSize;
+
+ FRIEND_TEST(ValueMetricProducerTest, TestNonDimensionalEvents);
+ FRIEND_TEST(ValueMetricProducerTest, TestEventsWithNonSlicedCondition);
+ FRIEND_TEST(ValueMetricProducerTest, TestPushedEventsWithoutCondition);
};
} // namespace statsd
diff --git a/cmds/statsd/src/metrics/duration_helper/DurationTracker.h b/cmds/statsd/src/metrics/duration_helper/DurationTracker.h
index aeb234d..5c76d0e 100644
--- a/cmds/statsd/src/metrics/duration_helper/DurationTracker.h
+++ b/cmds/statsd/src/metrics/duration_helper/DurationTracker.h
@@ -20,8 +20,6 @@
#include "condition/ConditionWizard.h"
#include "stats_util.h"
-#include "frameworks/base/cmds/statsd/src/stats_log.pb.h"
-
namespace android {
namespace os {
namespace statsd {
diff --git a/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp b/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp
index a4d3098..43c21a8 100644
--- a/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp
+++ b/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp
@@ -173,7 +173,6 @@
}
void MaxDurationTracker::onSlicedConditionMayChange(const uint64_t timestamp) {
- // VLOG("Metric %lld onSlicedConditionMayChange", mMetric.metric_id());
// Now for each of the on-going event, check if the condition has changed for them.
for (auto& pair : mInfos) {
if (pair.second.state == kStopped) {
diff --git a/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.h b/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.h
index de53d62..b095884 100644
--- a/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.h
+++ b/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.h
@@ -19,8 +19,6 @@
#include "DurationTracker.h"
-#include "frameworks/base/cmds/statsd/src/stats_log.pb.h"
-
namespace android {
namespace os {
namespace statsd {
diff --git a/cmds/statsd/src/metrics/metrics_manager_util.cpp b/cmds/statsd/src/metrics/metrics_manager_util.cpp
index ca9cdfb..c2044d8 100644
--- a/cmds/statsd/src/metrics/metrics_manager_util.cpp
+++ b/cmds/statsd/src/metrics/metrics_manager_util.cpp
@@ -195,7 +195,7 @@
const int allMetricsCount = config.count_metric_size() + config.duration_metric_size() +
config.event_metric_size() + config.value_metric_size();
allMetricProducers.reserve(allMetricsCount);
- StatsPullerManager& statsPullerManager = StatsPullerManager::GetInstance();
+ StatsPullerManager statsPullerManager;
uint64_t startTimeNs = time(nullptr) * NS_PER_SEC;
// Build MetricProducers for each metric defined in config.
@@ -203,7 +203,7 @@
for (int i = 0; i < config.count_metric_size(); i++) {
const CountMetric& metric = config.count_metric(i);
if (!metric.has_what()) {
- ALOGW("cannot find what in CountMetric %lld", metric.metric_id());
+ ALOGW("cannot find what in CountMetric %s", metric.name().c_str());
return false;
}
@@ -293,8 +293,8 @@
for (int i = 0; i < config.event_metric_size(); i++) {
int metricIndex = allMetricProducers.size();
const EventMetric& metric = config.event_metric(i);
- if (!metric.has_metric_id() || !metric.has_what()) {
- ALOGW("cannot find the metric id or what in config");
+ if (!metric.has_name() || !metric.has_what()) {
+ ALOGW("cannot find the metric name or what in config");
return false;
}
int trackerIndex;
@@ -320,7 +320,7 @@
for (int i = 0; i < config.value_metric_size(); i++) {
const ValueMetric& metric = config.value_metric(i);
if (!metric.has_what()) {
- ALOGW("cannot find what in ValueMetric %lld", metric.metric_id());
+ ALOGW("cannot find what in ValueMetric %s", metric.name().c_str());
return false;
}
@@ -360,7 +360,7 @@
for (int i = 0; i < config.gauge_metric_size(); i++) {
const GaugeMetric& metric = config.gauge_metric(i);
if (!metric.has_what()) {
- ALOGW("cannot find what in ValueMetric %lld", metric.metric_id());
+ ALOGW("cannot find what in ValueMetric %s", metric.name().c_str());
return false;
}
diff --git a/cmds/statsd/src/metrics/metrics_manager_util.h b/cmds/statsd/src/metrics/metrics_manager_util.h
index e089d065..edf3af0 100644
--- a/cmds/statsd/src/metrics/metrics_manager_util.h
+++ b/cmds/statsd/src/metrics/metrics_manager_util.h
@@ -21,7 +21,7 @@
#include <vector>
#include "../condition/ConditionTracker.h"
-#include "../external/StatsPullerManager.h"
+#include "../external/StatsPullerManagerImpl.h"
#include "../matchers/LogMatchingTracker.h"
namespace android {
diff --git a/cmds/statsd/src/packages/UidMap.cpp b/cmds/statsd/src/packages/UidMap.cpp
index d83c3a4..2398814 100644
--- a/cmds/statsd/src/packages/UidMap.cpp
+++ b/cmds/statsd/src/packages/UidMap.cpp
@@ -52,7 +52,7 @@
void UidMap::updateMap(const vector<int32_t>& uid, const vector<int32_t>& versionCode,
const vector<String16>& packageName) {
- updateMap(time(nullptr) * 1000000000, uid, versionCode, packageName);
+ updateMap(time(nullptr) * NS_PER_SEC, uid, versionCode, packageName);
}
void UidMap::updateMap(const int64_t& timestamp, const vector<int32_t>& uid,
@@ -76,7 +76,7 @@
}
void UidMap::updateApp(const String16& app_16, const int32_t& uid, const int32_t& versionCode) {
- updateApp(time(nullptr) * 1000000000, app_16, uid, versionCode);
+ updateApp(time(nullptr) * NS_PER_SEC, app_16, uid, versionCode);
}
void UidMap::updateApp(const int64_t& timestamp, const String16& app_16, const int32_t& uid,
@@ -112,7 +112,7 @@
}
void UidMap::removeApp(const String16& app_16, const int32_t& uid) {
- removeApp(time(nullptr) * 1000000000, app_16, uid);
+ removeApp(time(nullptr) * NS_PER_SEC, app_16, uid);
}
void UidMap::removeApp(const int64_t& timestamp, const String16& app_16, const int32_t& uid) {
lock_guard<mutex> lock(mMutex);
@@ -150,6 +150,31 @@
mSubscribers.erase(producer);
}
+void UidMap::assignIsolatedUid(int isolatedUid, int parentUid) {
+ lock_guard<mutex> lock(mIsolatedMutex);
+
+ mIsolatedUidMap[isolatedUid] = parentUid;
+}
+
+void UidMap::removeIsolatedUid(int isolatedUid, int parentUid) {
+ lock_guard<mutex> lock(mIsolatedMutex);
+
+ auto it = mIsolatedUidMap.find(isolatedUid);
+ if (it != mIsolatedUidMap.end()) {
+ mIsolatedUidMap.erase(it);
+ }
+}
+
+int UidMap::getParentUidOrSelf(int uid) {
+ lock_guard<mutex> lock(mIsolatedMutex);
+
+ auto it = mIsolatedUidMap.find(uid);
+ if (it != mIsolatedUidMap.end()) {
+ return it->second;
+ }
+ return uid;
+}
+
void UidMap::clearOutput() {
mOutput.Clear();
@@ -177,7 +202,7 @@
}
UidMapping UidMap::getOutput(const ConfigKey& key) {
- return getOutput(time(nullptr) * 1000000000, key);
+ return getOutput(time(nullptr) * NS_PER_SEC, key);
}
UidMapping UidMap::getOutput(const int64_t& timestamp, const ConfigKey& key) {
diff --git a/cmds/statsd/src/packages/UidMap.h b/cmds/statsd/src/packages/UidMap.h
index bf120e0..de68fbc 100644
--- a/cmds/statsd/src/packages/UidMap.h
+++ b/cmds/statsd/src/packages/UidMap.h
@@ -84,6 +84,12 @@
// Informs uid map that a config is removed. Used for keeping mConfigKeys up to date.
void OnConfigRemoved(const ConfigKey& key);
+ void assignIsolatedUid(int isolatedUid, int parentUid);
+ void removeIsolatedUid(int isolatedUid, int parentUid);
+
+ // Returns the parent uid if it exists. Otherwise, returns the same uid that was passed-in.
+ int getParentUidOrSelf(int uid);
+
// Gets the output. If every config key has received the output, then the output is cleared.
UidMapping getOutput(const ConfigKey& key);
@@ -105,11 +111,16 @@
// TODO: Use shared_mutex for improved read-locking if a library can be found in Android.
mutable mutex mMutex;
+ mutable mutex mIsolatedMutex;
// Maps uid to application data. This must be multimap since there is a feature in Android for
// multiple apps to share the same uid.
std::unordered_multimap<int, AppData> mMap;
+ // Maps isolated uid to the parent uid. Any metrics for an isolated uid will instead contribute
+ // to the parent uid.
+ std::unordered_map<int, int> mIsolatedUidMap;
+
// We prepare the output proto as apps are updated, so that we can grab the current output.
UidMapping mOutput;
diff --git a/cmds/statsd/src/stats_events.proto b/cmds/statsd/src/stats_events.proto
index 82d9759..9ab07de 100644
--- a/cmds/statsd/src/stats_events.proto
+++ b/cmds/statsd/src/stats_events.proto
@@ -72,7 +72,7 @@
PhoneSignalStrengthChanged phone_signal_strength_changed = 40;
SettingChanged setting_changed = 41;
ActivityForegroundStateChanged activity_foreground_state_changed = 42;
-
+ IsolatedUidChanged isolated_uid_changed = 43;
// TODO: Reorder the numbering so that the most frequent occur events occur in the first 15.
}
@@ -86,6 +86,9 @@
PowerStatePlatformSleepStatePulled power_state_platform_sleep_state_pulled = 1005;
PowerStateVoterPulled power_state_voter_pulled = 1006;
PowerStateSubsystemSleepStatePulled power_state_subsystem_sleep_state_pulled = 1007;
+ CpuTimePerFreqPulled cpu_time_per_freq_pulled = 1008;
+ CpuTimePerUidPulled cpu_time_per_uid_pulled = 1009;
+ CpuTimePerUidFreqPulled cpu_time_per_uid_freq_pulled = 1010;
}
}
@@ -695,7 +698,6 @@
optional int32 user = 7;
}
-
/*
* Logs activity going to foreground or background
*
@@ -848,3 +850,62 @@
optional uint64 last_entry_timestamp_ms = 5;
optional bool supported_only_in_suspend = 6;
}
+
+/**
+<<<<<<< HEAD
+ * Logs creation or removal of an isolated uid. Isolated uid's are temporary uid's to sandbox risky
+ * behavior in its own uid. However, the metrics of these isolated uid's almost always should be
+ * attributed back to the parent (host) uid. One example is Chrome.
+ *
+ * Logged from:
+ * frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java
+ */
+message IsolatedUidChanged {
+ // The host UID. Generally, we should attribute metrics from the isolated uid to the host uid.
+ optional int32 parent_uid = 1;
+
+ optional int32 isolated_uid = 2;
+
+ // 1 denotes we're creating an isolated uid and 0 denotes removal. We expect an isolated uid to
+ // be removed before if it's used for another parent uid.
+ optional int32 is_create = 3;
+}
+
+/*
+<<<<<<< HEAD
+ * Pulls Cpu time per frequency.
+ * Note: this should be pulled for gauge metric only, without condition.
+ * The puller keeps internal state of last values. It should not be pulled by
+ * different metrics.
+ * The pulled data is delta of cpu time from last pull, calculated as
+ * following:
+ * if current time is larger than last value, take delta between the two.
+ * if current time is smaller than last value, there must be a cpu
+ * hotplug event, and the current time is taken as delta.
+ */
+message CpuTimePerFreqPulled {
+ optional uint32 cluster = 1;
+ optional uint32 freq_index = 2;
+ optional uint64 time = 3;
+}
+
+/*
+ * Pulls Cpu Time Per Uid.
+ * Note that isolated process uid time should be attributed to host uids.
+ */
+message CpuTimePerUidPulled {
+ optional uint64 uid = 1;
+ optional uint64 user_time_ms = 2;
+ optional uint64 sys_time_ms = 3;
+}
+
+/**
+ * Pulls Cpu Time Per Uid per frequency.
+ * Note that isolated process uid time should be attributed to host uids.
+ * For each uid, we order the time by descending frequencies.
+ */
+message CpuTimePerUidFreqPulled {
+ optional uint64 uid = 1;
+ optional uint64 freq_idx = 2;
+ optional uint64 time_ms = 3;
+}
diff --git a/cmds/statsd/src/stats_util.h b/cmds/statsd/src/stats_util.h
index a9507bf..e1d0aceb 100644
--- a/cmds/statsd/src/stats_util.h
+++ b/cmds/statsd/src/stats_util.h
@@ -13,8 +13,8 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-#ifndef STATS_UTIL_H
-#define STATS_UTIL_H
+
+#pragma once
#include "logd/LogReader.h"
#include "storage/DropboxWriter.h"
@@ -22,6 +22,8 @@
#include <log/logprint.h>
#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
+#include <unordered_map>
+
namespace android {
namespace os {
namespace statsd {
@@ -33,6 +35,10 @@
typedef std::map<std::string, HashableDimensionKey> ConditionKey;
+// TODO: For P, change int to int64_t.
+// TODO: Should HashableDimensionKey be marked here as const?
+typedef std::unordered_map<HashableDimensionKey, int> DimToValMap;
+
EventMetricData parse(log_msg msg);
int getTagId(log_msg msg);
@@ -41,5 +47,3 @@
} // namespace statsd
} // namespace os
} // namespace android
-
-#endif // STATS_UTIL_H
diff --git a/cmds/statsd/src/statsd_config.proto b/cmds/statsd/src/statsd_config.proto
index f3e6894..7648a91 100644
--- a/cmds/statsd/src/statsd_config.proto
+++ b/cmds/statsd/src/statsd_config.proto
@@ -83,7 +83,13 @@
optional string stop_all = 4;
- repeated KeyMatcher dimension = 5;
+ enum InitialValue {
+ UNKNOWN = 0;
+ FALSE = 1;
+ }
+ optional InitialValue initial_value = 5 [default = UNKNOWN];
+
+ repeated KeyMatcher dimension = 6;
}
message Condition {
@@ -106,56 +112,59 @@
}
message Alert {
- message IncidentdDetails {
- optional string alert_name = 1;
- repeated int32 incidentd_sections = 2;
+ optional string name = 1;
+
+ optional string metric_name = 2;
+
+ message IncidentdDetails {
+ repeated int32 section = 1;
}
- optional IncidentdDetails incidentd_details = 1;
+ optional IncidentdDetails incidentd_details = 3;
- optional int32 number_of_buckets = 3;
+ optional int32 number_of_buckets = 4;
- optional int32 refractory_period_secs = 4;
+ optional int32 refractory_period_secs = 5;
- optional int64 trigger_if_sum_gt = 5;
+ optional int64 trigger_if_sum_gt = 6;
+
+ optional int32 refractory_period_in_buckets = 7;
}
message EventMetric {
- optional int64 metric_id = 1;
+ optional string name = 1;
- optional string what = 2;
+ optional string what = 2;
- optional string condition = 3;
+ optional string condition = 3;
- repeated EventConditionLink links = 4;
+ repeated EventConditionLink links = 4;
}
message CountMetric {
- optional int64 metric_id = 1;
+ optional string name = 1;
- optional string what = 2;
+ optional string what = 2;
- optional string condition = 3;
+ optional string condition = 3;
- repeated KeyMatcher dimension = 4;
+ repeated KeyMatcher dimension = 4;
- optional Bucket bucket = 5;
+ optional Bucket bucket = 5;
- repeated Alert alerts = 6;
+ optional bool include_in_output = 6;
- optional bool include_in_output = 7;
-
- repeated EventConditionLink links = 8;
+ repeated EventConditionLink links = 7;
}
message DurationMetric {
- optional int64 metric_id = 1;
+ optional string name = 1;
- optional string what = 2;
+ optional string what = 2;
- enum AggregationType {
- DURATION_SUM = 1;
+ enum AggregationType {
+ DURATION_SUM = 1;
- DURATION_MAX_SPARSE = 2;
+ DURATION_MAX_SPARSE = 2;
}
optional AggregationType type = 3;
@@ -165,51 +174,42 @@
optional Bucket bucket = 6;
- repeated Alert alerts = 7;
-
- repeated EventConditionLink links = 8;
-
+ repeated EventConditionLink links = 7;
}
message GaugeMetric {
- optional int64 metric_id = 1;
+ optional string name = 1;
- optional string what = 2;
+ optional string what = 2;
- optional int32 gauge_field = 3;
+ optional int32 gauge_field = 3;
- optional string condition = 4;
+ optional string condition = 4;
- repeated KeyMatcher dimension = 5;
+ repeated KeyMatcher dimension = 5;
- optional Bucket bucket = 6;
+ optional Bucket bucket = 6;
- repeated Alert alerts = 7;
-
- repeated EventConditionLink links = 8;
+ repeated EventConditionLink links = 7;
}
message ValueMetric {
- optional int64 metric_id = 1;
+ optional string name = 1;
- optional string what = 2;
+ optional string what = 2;
- optional int32 value_field = 3;
+ optional int32 value_field = 3;
- optional string condition = 4;
+ optional string condition = 4;
- repeated KeyMatcher dimension = 5;
+ repeated KeyMatcher dimension = 5;
- optional Bucket bucket = 6;
+ optional Bucket bucket = 6;
- repeated Alert alerts = 7;
+ repeated EventConditionLink links = 7;
- repeated EventConditionLink links = 8;
-
- enum Operation {
- SUM = 1;
- }
- optional Operation operation = 9 [default = SUM];
+ enum Operation { SUM = 1; }
+ optional Operation operation = 9 [default = SUM];
}
message EventConditionLink {
@@ -220,19 +220,21 @@
};
message StatsdConfig {
- optional int64 config_id = 1;
+ optional string name = 1;
- repeated EventMetric event_metric = 2;
+ repeated EventMetric event_metric = 2;
- repeated CountMetric count_metric = 3;
+ repeated CountMetric count_metric = 3;
- repeated ValueMetric value_metric = 4;
+ repeated ValueMetric value_metric = 4;
- repeated DurationMetric duration_metric = 5;
+ repeated GaugeMetric gauge_metric = 5;
- repeated GaugeMetric gauge_metric = 6;
+ repeated DurationMetric duration_metric = 6;
- repeated LogEntryMatcher log_entry_matcher = 7;
+ repeated LogEntryMatcher log_entry_matcher = 7;
- repeated Condition condition = 8;
+ repeated Condition condition = 8;
+
+ repeated Alert alerts = 9;
}
diff --git a/cmds/statsd/tests/ConfigManager_test.cpp b/cmds/statsd/tests/ConfigManager_test.cpp
index aa896ca..fad5de6 100644
--- a/cmds/statsd/tests/ConfigManager_test.cpp
+++ b/cmds/statsd/tests/ConfigManager_test.cpp
@@ -30,7 +30,7 @@
namespace statsd {
static ostream& operator<<(ostream& os, const StatsdConfig& config) {
- return os << "StatsdConfig{id=" << config.config_id() << "}";
+ return os << "StatsdConfig{name=" << config.name().c_str() << "}";
}
} // namespace statsd
@@ -56,8 +56,8 @@
/**
* Validate that the StatsdConfig is the one we wanted.
*/
-MATCHER_P(StatsdConfigEq, configId, "") {
- return arg.config_id() == configId;
+MATCHER_P(StatsdConfigEq, name, "") {
+ return arg.name() == name;
}
/**
@@ -70,13 +70,13 @@
manager->AddListener(listener);
StatsdConfig config91;
- config91.set_config_id(91);
+ config91.set_name("91");
StatsdConfig config92;
- config92.set_config_id(92);
+ config92.set_name("92");
StatsdConfig config93;
- config93.set_config_id(93);
+ config93.set_name("93");
StatsdConfig config94;
- config94.set_config_id(94);
+ config94.set_name("94");
{
InSequence s;
@@ -85,27 +85,27 @@
// TODO: Remove this when we get rid of the fake one, and make this
// test loading one from disk somewhere.
EXPECT_CALL(*(listener.get()),
- OnConfigUpdated(ConfigKeyEq(0, "fake"), StatsdConfigEq(12345)))
+ OnConfigUpdated(ConfigKeyEq(0, "fake"), StatsdConfigEq("12345")))
.RetiresOnSaturation();
manager->Startup();
// Add another one
- EXPECT_CALL(*(listener.get()), OnConfigUpdated(ConfigKeyEq(1, "zzz"), StatsdConfigEq(91)))
+ EXPECT_CALL(*(listener.get()), OnConfigUpdated(ConfigKeyEq(1, "zzz"), StatsdConfigEq("91")))
.RetiresOnSaturation();
manager->UpdateConfig(ConfigKey(1, "zzz"), config91);
// Update It
- EXPECT_CALL(*(listener.get()), OnConfigUpdated(ConfigKeyEq(1, "zzz"), StatsdConfigEq(92)))
+ EXPECT_CALL(*(listener.get()), OnConfigUpdated(ConfigKeyEq(1, "zzz"), StatsdConfigEq("92")))
.RetiresOnSaturation();
manager->UpdateConfig(ConfigKey(1, "zzz"), config92);
// Add one with the same uid but a different name
- EXPECT_CALL(*(listener.get()), OnConfigUpdated(ConfigKeyEq(1, "yyy"), StatsdConfigEq(93)))
+ EXPECT_CALL(*(listener.get()), OnConfigUpdated(ConfigKeyEq(1, "yyy"), StatsdConfigEq("93")))
.RetiresOnSaturation();
manager->UpdateConfig(ConfigKey(1, "yyy"), config93);
// Add one with the same name but a different uid
- EXPECT_CALL(*(listener.get()), OnConfigUpdated(ConfigKeyEq(2, "zzz"), StatsdConfigEq(94)))
+ EXPECT_CALL(*(listener.get()), OnConfigUpdated(ConfigKeyEq(2, "zzz"), StatsdConfigEq("94")))
.RetiresOnSaturation();
manager->UpdateConfig(ConfigKey(2, "zzz"), config94);
diff --git a/cmds/statsd/tests/MetricsManager_test.cpp b/cmds/statsd/tests/MetricsManager_test.cpp
index 7333785..caa1cf4 100644
--- a/cmds/statsd/tests/MetricsManager_test.cpp
+++ b/cmds/statsd/tests/MetricsManager_test.cpp
@@ -41,7 +41,7 @@
StatsdConfig buildGoodConfig() {
StatsdConfig config;
- config.set_config_id(12345L);
+ config.set_name("12345");
LogEntryMatcher* eventMatcher = config.add_log_entry_matcher();
eventMatcher->set_name("SCREEN_IS_ON");
@@ -76,7 +76,7 @@
StatsdConfig buildCircleMatchers() {
StatsdConfig config;
- config.set_config_id(12345L);
+ config.set_name("12345");
LogEntryMatcher* eventMatcher = config.add_log_entry_matcher();
eventMatcher->set_name("SCREEN_IS_ON");
@@ -102,7 +102,7 @@
StatsdConfig buildMissingMatchers() {
StatsdConfig config;
- config.set_config_id(12345L);
+ config.set_name("12345");
LogEntryMatcher* eventMatcher = config.add_log_entry_matcher();
eventMatcher->set_name("SCREEN_IS_ON");
@@ -128,7 +128,7 @@
StatsdConfig buildDimensionMetricsWithMultiTags() {
StatsdConfig config;
- config.set_config_id(12345L);
+ config.set_name("12345");
LogEntryMatcher* eventMatcher = config.add_log_entry_matcher();
eventMatcher->set_name("BATTERY_VERY_LOW");
@@ -150,7 +150,7 @@
// Count process state changes, slice by uid, while SCREEN_IS_OFF
CountMetric* metric = config.add_count_metric();
- metric->set_metric_id(3);
+ metric->set_name("3");
metric->set_what("BATTERY_LOW");
metric->mutable_bucket()->set_bucket_size_millis(30 * 1000L);
KeyMatcher* keyMatcher = metric->add_dimension();
@@ -161,7 +161,7 @@
StatsdConfig buildCircleConditions() {
StatsdConfig config;
- config.set_config_id(12345L);
+ config.set_name("12345");
LogEntryMatcher* eventMatcher = config.add_log_entry_matcher();
eventMatcher->set_name("SCREEN_IS_ON");
diff --git a/cmds/statsd/tests/UidMap_test.cpp b/cmds/statsd/tests/UidMap_test.cpp
index 671f6d4..c64719e 100644
--- a/cmds/statsd/tests/UidMap_test.cpp
+++ b/cmds/statsd/tests/UidMap_test.cpp
@@ -13,7 +13,10 @@
// limitations under the License.
#include "packages/UidMap.h"
+#include "StatsLogProcessor.h"
#include "config/ConfigKey.h"
+#include "logd/LogEvent.h"
+#include "statslog.h"
#include <gtest/gtest.h>
@@ -29,6 +32,31 @@
const string kApp1 = "app1.sharing.1";
const string kApp2 = "app2.sharing.1";
+TEST(UidMapTest, TestIsolatedUID) {
+ sp<UidMap> m = new UidMap();
+ StatsLogProcessor p(m, nullptr);
+ LogEvent addEvent(android::util::ISOLATED_UID_CHANGED, 1);
+ android_log_event_list* list = addEvent.GetAndroidLogEventList();
+ *list << 100; // parent UID
+ *list << 101; // isolated UID
+ *list << 1; // Indicates creation.
+ addEvent.init();
+
+ EXPECT_EQ(101, m->getParentUidOrSelf(101));
+
+ p.OnLogEvent(addEvent);
+ EXPECT_EQ(100, m->getParentUidOrSelf(101));
+
+ LogEvent removeEvent(android::util::ISOLATED_UID_CHANGED, 1);
+ list = removeEvent.GetAndroidLogEventList();
+ *list << 100; // parent UID
+ *list << 101; // isolated UID
+ *list << 0; // Indicates removal.
+ removeEvent.init();
+ p.OnLogEvent(removeEvent);
+ EXPECT_EQ(101, m->getParentUidOrSelf(101));
+}
+
TEST(UidMapTest, TestMatching) {
UidMap m;
vector<int32_t> uids;
diff --git a/cmds/statsd/tests/anomaly/AnomalyTracker_test.cpp b/cmds/statsd/tests/anomaly/AnomalyTracker_test.cpp
new file mode 100644
index 0000000..b8150d0
--- /dev/null
+++ b/cmds/statsd/tests/anomaly/AnomalyTracker_test.cpp
@@ -0,0 +1,239 @@
+// Copyright (C) 2017 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "src/anomaly/DiscreteAnomalyTracker.h"
+
+#include <gtest/gtest.h>
+#include <stdio.h>
+#include <vector>
+
+using namespace testing;
+using android::sp;
+using std::set;
+using std::unordered_map;
+using std::vector;
+
+#ifdef __ANDROID__
+
+namespace android {
+namespace os {
+namespace statsd {
+
+void AddValueToBucket(const std::vector<std::pair<string, long>>& key_value_pair_list,
+ std::shared_ptr<DimToValMap> bucket) {
+ for (auto itr = key_value_pair_list.begin(); itr != key_value_pair_list.end(); itr++) {
+ (*bucket)[itr->first] += itr->second;
+ }
+}
+
+std::shared_ptr<DimToValMap> MockeBucket(
+ const std::vector<std::pair<string, long>>& key_value_pair_list) {
+ std::shared_ptr<DimToValMap> bucket = std::make_shared<DimToValMap>();
+ AddValueToBucket(key_value_pair_list, bucket);
+ return bucket;
+}
+
+TEST(AnomalyTrackerTest, TestConsecutiveBuckets) {
+ Alert alert;
+ alert.set_number_of_buckets(3);
+ alert.set_refractory_period_in_buckets(3);
+ alert.set_trigger_if_sum_gt(2);
+
+ DiscreteAnomalyTracker anomaly_tracker(alert);
+
+ std::shared_ptr<DimToValMap> bucket0 = MockeBucket({{"a", 1}, {"b", 2}, {"c", 1}});
+ // Adds bucket #0
+ anomaly_tracker.addOrUpdateBucket(bucket0, 0);
+ EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.size(), 3UL);
+ EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("a")->second, 1);
+ EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("b")->second, 2);
+ EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("c")->second, 1);
+ EXPECT_FALSE(anomaly_tracker.detectAnomaly());
+ anomaly_tracker.declareAndDeclareAnomaly();
+ EXPECT_EQ(anomaly_tracker.mAnomalyDeclared, 0L);
+
+ // Adds bucket #0 again. The sum does not change.
+ anomaly_tracker.addOrUpdateBucket(bucket0, 0);
+ EXPECT_EQ(anomaly_tracker.mCurrentBucketIndex, 0L);
+ EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.size(), 3UL);
+ EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("a")->second, 1);
+ EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("b")->second, 2);
+ EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("c")->second, 1);
+ EXPECT_FALSE(anomaly_tracker.detectAnomaly());
+ anomaly_tracker.declareAndDeclareAnomaly();
+ EXPECT_EQ(anomaly_tracker.mAnomalyDeclared, 0L);
+ EXPECT_EQ(anomaly_tracker.mLastAlarmAtBucketIndex, -1L);
+
+ // Adds bucket #1.
+ std::shared_ptr<DimToValMap> bucket1 = MockeBucket({{"b", 2}});
+ anomaly_tracker.addOrUpdateBucket(bucket1, 1);
+ EXPECT_EQ(anomaly_tracker.mCurrentBucketIndex, 1L);
+ EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.size(), 3UL);
+ EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("a")->second, 1);
+ EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("b")->second, 4);
+ EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("c")->second, 1);
+ // Alarm.
+ EXPECT_TRUE(anomaly_tracker.detectAnomaly());
+ anomaly_tracker.declareAndDeclareAnomaly();
+ EXPECT_EQ(anomaly_tracker.mAnomalyDeclared, 1L);
+ EXPECT_EQ(anomaly_tracker.mLastAlarmAtBucketIndex, 1L);
+
+ // Adds bucket #1 again. The sum does not change.
+ anomaly_tracker.addOrUpdateBucket(bucket1, 1);
+ EXPECT_EQ(anomaly_tracker.mCurrentBucketIndex, 1L);
+ EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.size(), 3UL);
+ EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("a")->second, 1);
+ EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("b")->second, 4);
+ EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("c")->second, 1);
+ // Alarm.
+ EXPECT_TRUE(anomaly_tracker.detectAnomaly());
+ anomaly_tracker.declareAndDeclareAnomaly();
+ EXPECT_EQ(anomaly_tracker.mAnomalyDeclared, 1L);
+ EXPECT_EQ(anomaly_tracker.mLastAlarmAtBucketIndex, 1L);
+
+ // Adds bucket #2.
+ anomaly_tracker.addOrUpdateBucket(MockeBucket({{"a", 1}}), 2);
+ EXPECT_EQ(anomaly_tracker.mCurrentBucketIndex, 2L);
+ EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.size(), 3UL);
+ EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("a")->second, 2);
+ EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("b")->second, 4);
+ EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("c")->second, 1);
+ EXPECT_TRUE(anomaly_tracker.detectAnomaly());
+ // Within refractory period.
+ anomaly_tracker.declareAndDeclareAnomaly();
+ EXPECT_EQ(anomaly_tracker.mAnomalyDeclared, 1L);
+ EXPECT_EQ(anomaly_tracker.mLastAlarmAtBucketIndex, 1L);
+
+ // Adds bucket #3.
+ anomaly_tracker.addOrUpdateBucket(MockeBucket({{"a", 1}}), 3);
+ EXPECT_EQ(anomaly_tracker.mCurrentBucketIndex, 3L);
+ EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.size(), 2UL);
+ EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("a")->second, 2);
+ EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("b")->second, 2);
+ EXPECT_FALSE(anomaly_tracker.detectAnomaly());
+
+ // Adds bucket #3.
+ anomaly_tracker.addOrUpdateBucket(MockeBucket({{"a", 2}}), 4);
+ EXPECT_EQ(anomaly_tracker.mCurrentBucketIndex, 4L);
+ EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.size(), 1UL);
+ EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("a")->second, 4);
+ EXPECT_TRUE(anomaly_tracker.detectAnomaly());
+ // Within refractory period.
+ anomaly_tracker.declareAndDeclareAnomaly();
+ EXPECT_EQ(anomaly_tracker.mAnomalyDeclared, 1L);
+ EXPECT_EQ(anomaly_tracker.mLastAlarmAtBucketIndex, 1L);
+
+ anomaly_tracker.addOrUpdateBucket(MockeBucket({{"a", 1}}), 5);
+ EXPECT_EQ(anomaly_tracker.mCurrentBucketIndex, 5L);
+ EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.size(), 1UL);
+ EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("a")->second, 4);
+ EXPECT_TRUE(anomaly_tracker.detectAnomaly());
+ // Within refractory period.
+ anomaly_tracker.declareAndDeclareAnomaly();
+ EXPECT_EQ(anomaly_tracker.mAnomalyDeclared, 2L);
+ EXPECT_EQ(anomaly_tracker.mLastAlarmAtBucketIndex, 5L);
+}
+
+TEST(AnomalyTrackerTest, TestSparseBuckets) {
+ Alert alert;
+ alert.set_number_of_buckets(3);
+ alert.set_refractory_period_in_buckets(3);
+ alert.set_trigger_if_sum_gt(2);
+
+ DiscreteAnomalyTracker anomaly_tracker(alert);
+
+ // Add bucket #9
+ anomaly_tracker.addOrUpdateBucket(MockeBucket({{"a", 1}, {"b", 2}, {"c", 1}}), 9);
+ EXPECT_EQ(anomaly_tracker.mCurrentBucketIndex, 9L);
+ EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.size(), 3UL);
+ EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("a")->second, 1);
+ EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("b")->second, 2);
+ EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("c")->second, 1);
+ EXPECT_FALSE(anomaly_tracker.detectAnomaly());
+ anomaly_tracker.declareAndDeclareAnomaly();
+ EXPECT_EQ(anomaly_tracker.mAnomalyDeclared, 0L);
+ EXPECT_EQ(anomaly_tracker.mLastAlarmAtBucketIndex, -1L);
+
+ // Add bucket #16
+ anomaly_tracker.addOrUpdateBucket(MockeBucket({{"b", 4}}), 16);
+ EXPECT_EQ(anomaly_tracker.mCurrentBucketIndex, 16L);
+ EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.size(), 1UL);
+ EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("b")->second, 4);
+ EXPECT_TRUE(anomaly_tracker.detectAnomaly());
+ anomaly_tracker.declareAndDeclareAnomaly();
+ EXPECT_EQ(anomaly_tracker.mAnomalyDeclared, 1L);
+ EXPECT_EQ(anomaly_tracker.mLastAlarmAtBucketIndex, 16L);
+
+ // Add bucket #18
+ anomaly_tracker.addOrUpdateBucket(MockeBucket({{"b", 1}, {"c", 1}}), 18);
+ EXPECT_EQ(anomaly_tracker.mCurrentBucketIndex, 18L);
+ EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.size(), 2UL);
+ EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("b")->second, 5);
+ EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("c")->second, 1);
+ EXPECT_TRUE(anomaly_tracker.detectAnomaly());
+ anomaly_tracker.declareAndDeclareAnomaly();
+ // Within refractory period.
+ EXPECT_EQ(anomaly_tracker.mAnomalyDeclared, 1L);
+ EXPECT_EQ(anomaly_tracker.mLastAlarmAtBucketIndex, 16L);
+
+ // Add bucket #18 again.
+ anomaly_tracker.addOrUpdateBucket(MockeBucket({{"b", 1}, {"c", 1}}), 18);
+ EXPECT_EQ(anomaly_tracker.mCurrentBucketIndex, 18L);
+ EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.size(), 2UL);
+ EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("b")->second, 5);
+ EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("c")->second, 1);
+ EXPECT_TRUE(anomaly_tracker.detectAnomaly());
+ anomaly_tracker.declareAndDeclareAnomaly();
+ EXPECT_EQ(anomaly_tracker.mAnomalyDeclared, 1L);
+ EXPECT_EQ(anomaly_tracker.mLastAlarmAtBucketIndex, 16L);
+
+ // Add bucket #20
+ anomaly_tracker.addOrUpdateBucket(MockeBucket({{"b", 3}, {"d", 1}}), 20);
+ EXPECT_EQ(anomaly_tracker.mCurrentBucketIndex, 20L);
+ EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.size(), 3UL);
+ EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("b")->second, 4);
+ EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("c")->second, 1);
+ EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("d")->second, 1);
+ EXPECT_TRUE(anomaly_tracker.detectAnomaly());
+ anomaly_tracker.declareAndDeclareAnomaly();
+ EXPECT_EQ(anomaly_tracker.mAnomalyDeclared, 2L);
+ EXPECT_EQ(anomaly_tracker.mLastAlarmAtBucketIndex, 20L);
+
+ // Add bucket #25
+ anomaly_tracker.addOrUpdateBucket(MockeBucket({{"d", 1}}), 25);
+ EXPECT_EQ(anomaly_tracker.mCurrentBucketIndex, 25L);
+ EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.size(), 1UL);
+ EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("d")->second, 1L);
+ EXPECT_FALSE(anomaly_tracker.detectAnomaly());
+ anomaly_tracker.declareAndDeclareAnomaly();
+ EXPECT_EQ(anomaly_tracker.mAnomalyDeclared, 2L);
+ EXPECT_EQ(anomaly_tracker.mLastAlarmAtBucketIndex, 20L);
+
+ // Add bucket #28
+ anomaly_tracker.addOrUpdateBucket(MockeBucket({{"e", 5}}), 28);
+ EXPECT_EQ(anomaly_tracker.mCurrentBucketIndex, 28L);
+ EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.size(), 1UL);
+ EXPECT_EQ(anomaly_tracker.mSumOverPastBuckets.find("e")->second, 5L);
+ EXPECT_TRUE(anomaly_tracker.detectAnomaly());
+ anomaly_tracker.declareAndDeclareAnomaly();
+ EXPECT_EQ(anomaly_tracker.mAnomalyDeclared, 3L);
+ EXPECT_EQ(anomaly_tracker.mLastAlarmAtBucketIndex, 28L);
+}
+
+} // namespace statsd
+} // namespace os
+} // namespace android
+#else
+GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
diff --git a/cmds/statsd/tests/ConditionTracker_test.cpp b/cmds/statsd/tests/condition/CombinationConditionTracker_test.cpp
similarity index 98%
rename from cmds/statsd/tests/ConditionTracker_test.cpp
rename to cmds/statsd/tests/condition/CombinationConditionTracker_test.cpp
index 2935ac7..23d6926 100644
--- a/cmds/statsd/tests/ConditionTracker_test.cpp
+++ b/cmds/statsd/tests/condition/CombinationConditionTracker_test.cpp
@@ -23,7 +23,6 @@
using namespace android::os::statsd;
using std::vector;
-
#ifdef __ANDROID__
TEST(ConditionTrackerTest, TestUnknownCondition) {
LogicalOperation operation = LogicalOperation::AND;
@@ -39,7 +38,7 @@
conditionResults.push_back(ConditionState::kTrue);
EXPECT_EQ(evaluateCombinationCondition(children, operation, conditionResults),
- ConditionState::kUnknown);
+ ConditionState::kUnknown);
}
TEST(ConditionTrackerTest, TestAndCondition) {
// Set up the matcher
diff --git a/cmds/statsd/tests/condition/SimpleConditionTracker_test.cpp b/cmds/statsd/tests/condition/SimpleConditionTracker_test.cpp
new file mode 100644
index 0000000..05aad29
--- /dev/null
+++ b/cmds/statsd/tests/condition/SimpleConditionTracker_test.cpp
@@ -0,0 +1,469 @@
+// Copyright (C) 2017 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+#include "src/condition/SimpleConditionTracker.h"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <stdio.h>
+#include <vector>
+
+using std::map;
+using std::unordered_map;
+using std::vector;
+
+#ifdef __ANDROID__
+
+namespace android {
+namespace os {
+namespace statsd {
+
+SimpleCondition getWakeLockHeldCondition(bool countNesting, bool defaultFalse,
+ bool outputSlicedUid) {
+ SimpleCondition simpleCondition;
+ simpleCondition.set_start("WAKE_LOCK_ACQUIRE");
+ simpleCondition.set_stop("WAKE_LOCK_RELEASE");
+ simpleCondition.set_stop_all("RELEASE_ALL");
+ if (outputSlicedUid) {
+ KeyMatcher* keyMatcher = simpleCondition.add_dimension();
+ keyMatcher->set_key(1);
+ }
+
+ simpleCondition.set_count_nesting(countNesting);
+ simpleCondition.set_initial_value(defaultFalse ? SimpleCondition_InitialValue_FALSE
+ : SimpleCondition_InitialValue_UNKNOWN);
+ return simpleCondition;
+}
+
+void makeWakeLockEvent(LogEvent* event, int uid, const string& wl, int acquire) {
+ auto list = event->GetAndroidLogEventList();
+ *list << uid; // uid
+ *list << wl;
+ *list << acquire;
+ event->init();
+}
+
+map<string, HashableDimensionKey> getWakeLockQueryKey(int key, int uid,
+ const string& conditionName) {
+ // test query
+ KeyValuePair kv1;
+ kv1.set_key(key);
+ kv1.set_value_int(uid);
+ vector<KeyValuePair> kv_list;
+ kv_list.push_back(kv1);
+ map<string, HashableDimensionKey> queryKey;
+ queryKey[conditionName] = getHashableKey(kv_list);
+ return queryKey;
+}
+
+TEST(SimpleConditionTrackerTest, TestNonSlicedCondition) {
+ SimpleCondition simpleCondition;
+ simpleCondition.set_start("SCREEN_TURNED_ON");
+ simpleCondition.set_stop("SCREEN_TURNED_OFF");
+ simpleCondition.set_count_nesting(false);
+
+ unordered_map<string, int> trackerNameIndexMap;
+ trackerNameIndexMap["SCREEN_TURNED_ON"] = 0;
+ trackerNameIndexMap["SCREEN_TURNED_OFF"] = 1;
+
+ SimpleConditionTracker conditionTracker("SCREEN_IS_ON", 0 /*tracker index*/, simpleCondition,
+ trackerNameIndexMap);
+
+ LogEvent event(1 /*tagId*/, 0 /*timestamp*/);
+
+ vector<MatchingState> matcherState;
+ matcherState.push_back(MatchingState::kNotMatched);
+ matcherState.push_back(MatchingState::kNotMatched);
+
+ vector<sp<ConditionTracker>> allConditions;
+ vector<ConditionState> conditionCache(1, ConditionState::kNotEvaluated);
+ vector<bool> changedCache(1, false);
+
+ conditionTracker.evaluateCondition(event, matcherState, allConditions, conditionCache,
+ changedCache);
+ // not matched start or stop. condition doesn't change
+ EXPECT_EQ(ConditionState::kUnknown, conditionCache[0]);
+ EXPECT_FALSE(changedCache[0]);
+
+ // prepare a case for match start.
+ matcherState.clear();
+ matcherState.push_back(MatchingState::kMatched);
+ matcherState.push_back(MatchingState::kNotMatched);
+ conditionCache[0] = ConditionState::kNotEvaluated;
+ changedCache[0] = false;
+
+ conditionTracker.evaluateCondition(event, matcherState, allConditions, conditionCache,
+ changedCache);
+ // now condition should change to true.
+ EXPECT_EQ(ConditionState::kTrue, conditionCache[0]);
+ EXPECT_TRUE(changedCache[0]);
+
+ // the case for match stop.
+ matcherState.clear();
+ matcherState.push_back(MatchingState::kNotMatched);
+ matcherState.push_back(MatchingState::kMatched);
+ conditionCache[0] = ConditionState::kNotEvaluated;
+ changedCache[0] = false;
+
+ conditionTracker.evaluateCondition(event, matcherState, allConditions, conditionCache,
+ changedCache);
+
+ // condition changes to false.
+ EXPECT_EQ(ConditionState::kFalse, conditionCache[0]);
+ EXPECT_TRUE(changedCache[0]);
+
+ // match stop again.
+ matcherState.clear();
+ matcherState.push_back(MatchingState::kNotMatched);
+ matcherState.push_back(MatchingState::kMatched);
+ conditionCache[0] = ConditionState::kNotEvaluated;
+ changedCache[0] = false;
+
+ conditionTracker.evaluateCondition(event, matcherState, allConditions, conditionCache,
+ changedCache);
+ // condition should still be false. not changed.
+ EXPECT_EQ(ConditionState::kFalse, conditionCache[0]);
+ EXPECT_FALSE(changedCache[0]);
+}
+
+TEST(SimpleConditionTrackerTest, TestNonSlicedConditionNestCounting) {
+ SimpleCondition simpleCondition;
+ simpleCondition.set_start("SCREEN_TURNED_ON");
+ simpleCondition.set_stop("SCREEN_TURNED_OFF");
+ simpleCondition.set_count_nesting(true);
+
+ unordered_map<string, int> trackerNameIndexMap;
+ trackerNameIndexMap["SCREEN_TURNED_ON"] = 0;
+ trackerNameIndexMap["SCREEN_TURNED_OFF"] = 1;
+
+ SimpleConditionTracker conditionTracker("SCREEN_IS_ON", 0 /*condition tracker index*/,
+ simpleCondition, trackerNameIndexMap);
+
+ LogEvent event(1 /*tagId*/, 0 /*timestamp*/);
+
+ // one matched start
+ vector<MatchingState> matcherState;
+ matcherState.push_back(MatchingState::kMatched);
+ matcherState.push_back(MatchingState::kNotMatched);
+ vector<sp<ConditionTracker>> allConditions;
+ vector<ConditionState> conditionCache(1, ConditionState::kNotEvaluated);
+ vector<bool> changedCache(1, false);
+
+ conditionTracker.evaluateCondition(event, matcherState, allConditions, conditionCache,
+ changedCache);
+
+ EXPECT_EQ(ConditionState::kTrue, conditionCache[0]);
+ EXPECT_TRUE(changedCache[0]);
+
+ // prepare for another matched start.
+ matcherState.clear();
+ matcherState.push_back(MatchingState::kMatched);
+ matcherState.push_back(MatchingState::kNotMatched);
+ conditionCache[0] = ConditionState::kNotEvaluated;
+ changedCache[0] = false;
+
+ conditionTracker.evaluateCondition(event, matcherState, allConditions, conditionCache,
+ changedCache);
+
+ EXPECT_EQ(ConditionState::kTrue, conditionCache[0]);
+ EXPECT_FALSE(changedCache[0]);
+
+ // ONE MATCHED STOP
+ matcherState.clear();
+ matcherState.push_back(MatchingState::kNotMatched);
+ matcherState.push_back(MatchingState::kMatched);
+ conditionCache[0] = ConditionState::kNotEvaluated;
+ changedCache[0] = false;
+
+ conditionTracker.evaluateCondition(event, matcherState, allConditions, conditionCache,
+ changedCache);
+ // result should still be true
+ EXPECT_EQ(ConditionState::kTrue, conditionCache[0]);
+ EXPECT_FALSE(changedCache[0]);
+
+ // ANOTHER MATCHED STOP
+ matcherState.clear();
+ matcherState.push_back(MatchingState::kNotMatched);
+ matcherState.push_back(MatchingState::kMatched);
+ conditionCache[0] = ConditionState::kNotEvaluated;
+ changedCache[0] = false;
+
+ conditionTracker.evaluateCondition(event, matcherState, allConditions, conditionCache,
+ changedCache);
+ // result should still be true
+ EXPECT_EQ(ConditionState::kFalse, conditionCache[0]);
+ EXPECT_TRUE(changedCache[0]);
+}
+
+TEST(SimpleConditionTrackerTest, TestSlicedCondition) {
+ SimpleCondition simpleCondition = getWakeLockHeldCondition(
+ true /*nesting*/, true /*default to false*/, true /*output slice by uid*/);
+ string conditionName = "WL_HELD_BY_UID2";
+
+ unordered_map<string, int> trackerNameIndexMap;
+ trackerNameIndexMap["WAKE_LOCK_ACQUIRE"] = 0;
+ trackerNameIndexMap["WAKE_LOCK_RELEASE"] = 1;
+ trackerNameIndexMap["RELEASE_ALL"] = 2;
+
+ SimpleConditionTracker conditionTracker(conditionName, 0 /*condition tracker index*/,
+ simpleCondition, trackerNameIndexMap);
+ int uid = 111;
+
+ LogEvent event(1 /*tagId*/, 0 /*timestamp*/);
+ makeWakeLockEvent(&event, uid, "wl1", 1);
+
+ // one matched start
+ vector<MatchingState> matcherState;
+ matcherState.push_back(MatchingState::kMatched);
+ matcherState.push_back(MatchingState::kNotMatched);
+ vector<sp<ConditionTracker>> allConditions;
+ vector<ConditionState> conditionCache(1, ConditionState::kNotEvaluated);
+ vector<bool> changedCache(1, false);
+
+ conditionTracker.evaluateCondition(event, matcherState, allConditions, conditionCache,
+ changedCache);
+
+ EXPECT_EQ(1UL, conditionTracker.mSlicedConditionState.size());
+ EXPECT_TRUE(changedCache[0]);
+
+ // Now test query
+ const auto queryKey = getWakeLockQueryKey(1, uid, conditionName);
+ conditionCache[0] = ConditionState::kNotEvaluated;
+
+ conditionTracker.isConditionMet(queryKey, allConditions, conditionCache);
+ EXPECT_EQ(ConditionState::kTrue, conditionCache[0]);
+
+ // another wake lock acquired by this uid
+ LogEvent event2(1 /*tagId*/, 0 /*timestamp*/);
+ makeWakeLockEvent(&event2, uid, "wl2", 1);
+ matcherState.clear();
+ matcherState.push_back(MatchingState::kMatched);
+ matcherState.push_back(MatchingState::kNotMatched);
+ conditionCache[0] = ConditionState::kNotEvaluated;
+ changedCache[0] = false;
+ conditionTracker.evaluateCondition(event2, matcherState, allConditions, conditionCache,
+ changedCache);
+ EXPECT_FALSE(changedCache[0]);
+
+ // wake lock 1 release
+ LogEvent event3(1 /*tagId*/, 0 /*timestamp*/);
+ makeWakeLockEvent(&event3, uid, "wl1", 0); // now release it.
+ matcherState.clear();
+ matcherState.push_back(MatchingState::kNotMatched);
+ matcherState.push_back(MatchingState::kMatched);
+ conditionCache[0] = ConditionState::kNotEvaluated;
+ changedCache[0] = false;
+ conditionTracker.evaluateCondition(event3, matcherState, allConditions, conditionCache,
+ changedCache);
+ // nothing changes, because wake lock 2 is still held for this uid
+ EXPECT_FALSE(changedCache[0]);
+
+ LogEvent event4(1 /*tagId*/, 0 /*timestamp*/);
+ makeWakeLockEvent(&event4, uid, "wl2", 0); // now release it.
+ matcherState.clear();
+ matcherState.push_back(MatchingState::kNotMatched);
+ matcherState.push_back(MatchingState::kMatched);
+ conditionCache[0] = ConditionState::kNotEvaluated;
+ changedCache[0] = false;
+ conditionTracker.evaluateCondition(event4, matcherState, allConditions, conditionCache,
+ changedCache);
+ EXPECT_EQ(0UL, conditionTracker.mSlicedConditionState.size());
+ EXPECT_TRUE(changedCache[0]);
+
+ // query again
+ conditionCache[0] = ConditionState::kNotEvaluated;
+ conditionTracker.isConditionMet(queryKey, allConditions, conditionCache);
+ EXPECT_EQ(ConditionState::kFalse, conditionCache[0]);
+}
+
+TEST(SimpleConditionTrackerTest, TestSlicedWithNoOutputDim) {
+ SimpleCondition simpleCondition = getWakeLockHeldCondition(
+ true /*nesting*/, true /*default to false*/, false /*slice output by uid*/);
+ string conditionName = "WL_HELD";
+
+ unordered_map<string, int> trackerNameIndexMap;
+ trackerNameIndexMap["WAKE_LOCK_ACQUIRE"] = 0;
+ trackerNameIndexMap["WAKE_LOCK_RELEASE"] = 1;
+ trackerNameIndexMap["RELEASE_ALL"] = 2;
+
+ SimpleConditionTracker conditionTracker(conditionName, 0 /*condition tracker index*/,
+ simpleCondition, trackerNameIndexMap);
+ int uid1 = 111;
+ string uid1_wl1 = "wl1_1";
+ int uid2 = 222;
+ string uid2_wl1 = "wl2_1";
+
+ LogEvent event(1 /*tagId*/, 0 /*timestamp*/);
+ makeWakeLockEvent(&event, uid1, uid1_wl1, 1);
+
+ // one matched start for uid1
+ vector<MatchingState> matcherState;
+ matcherState.push_back(MatchingState::kMatched);
+ matcherState.push_back(MatchingState::kNotMatched);
+ vector<sp<ConditionTracker>> allConditions;
+ vector<ConditionState> conditionCache(1, ConditionState::kNotEvaluated);
+ vector<bool> changedCache(1, false);
+
+ conditionTracker.evaluateCondition(event, matcherState, allConditions, conditionCache,
+ changedCache);
+
+ EXPECT_EQ(1UL, conditionTracker.mSlicedConditionState.size());
+ EXPECT_TRUE(changedCache[0]);
+
+ // Now test query
+ map<string, HashableDimensionKey> queryKey;
+ conditionCache[0] = ConditionState::kNotEvaluated;
+
+ conditionTracker.isConditionMet(queryKey, allConditions, conditionCache);
+ EXPECT_EQ(ConditionState::kTrue, conditionCache[0]);
+
+ // another wake lock acquired by this uid
+ LogEvent event2(1 /*tagId*/, 0 /*timestamp*/);
+ makeWakeLockEvent(&event2, uid2, uid2_wl1, 1);
+ matcherState.clear();
+ matcherState.push_back(MatchingState::kMatched);
+ matcherState.push_back(MatchingState::kNotMatched);
+ conditionCache[0] = ConditionState::kNotEvaluated;
+ changedCache[0] = false;
+ conditionTracker.evaluateCondition(event2, matcherState, allConditions, conditionCache,
+ changedCache);
+ EXPECT_FALSE(changedCache[0]);
+
+ // uid1 wake lock 1 release
+ LogEvent event3(1 /*tagId*/, 0 /*timestamp*/);
+ makeWakeLockEvent(&event3, uid1, uid1_wl1, 0); // now release it.
+ matcherState.clear();
+ matcherState.push_back(MatchingState::kNotMatched);
+ matcherState.push_back(MatchingState::kMatched);
+ conditionCache[0] = ConditionState::kNotEvaluated;
+ changedCache[0] = false;
+ conditionTracker.evaluateCondition(event3, matcherState, allConditions, conditionCache,
+ changedCache);
+ // nothing changes, because uid2 is still holding wl.
+ EXPECT_FALSE(changedCache[0]);
+
+ LogEvent event4(1 /*tagId*/, 0 /*timestamp*/);
+ makeWakeLockEvent(&event4, uid2, uid2_wl1, 0); // now release it.
+ matcherState.clear();
+ matcherState.push_back(MatchingState::kNotMatched);
+ matcherState.push_back(MatchingState::kMatched);
+ conditionCache[0] = ConditionState::kNotEvaluated;
+ changedCache[0] = false;
+ conditionTracker.evaluateCondition(event4, matcherState, allConditions, conditionCache,
+ changedCache);
+ EXPECT_EQ(0UL, conditionTracker.mSlicedConditionState.size());
+ EXPECT_TRUE(changedCache[0]);
+
+ // query again
+ conditionCache[0] = ConditionState::kNotEvaluated;
+ conditionTracker.isConditionMet(queryKey, allConditions, conditionCache);
+ EXPECT_EQ(ConditionState::kFalse, conditionCache[0]);
+}
+
+TEST(SimpleConditionTrackerTest, TestStopAll) {
+ SimpleCondition simpleCondition = getWakeLockHeldCondition(
+ true /*nesting*/, true /*default to false*/, true /*output slice by uid*/);
+ string conditionName = "WL_HELD_BY_UID3";
+
+ unordered_map<string, int> trackerNameIndexMap;
+ trackerNameIndexMap["WAKE_LOCK_ACQUIRE"] = 0;
+ trackerNameIndexMap["WAKE_LOCK_RELEASE"] = 1;
+ trackerNameIndexMap["RELEASE_ALL"] = 2;
+
+ SimpleConditionTracker conditionTracker(conditionName, 0 /*condition tracker index*/,
+ simpleCondition, trackerNameIndexMap);
+ int uid1 = 111;
+ int uid2 = 222;
+
+ LogEvent event(1 /*tagId*/, 0 /*timestamp*/);
+ makeWakeLockEvent(&event, uid1, "wl1", 1);
+
+ // one matched start
+ vector<MatchingState> matcherState;
+ matcherState.push_back(MatchingState::kMatched);
+ matcherState.push_back(MatchingState::kNotMatched);
+ matcherState.push_back(MatchingState::kNotMatched);
+ vector<sp<ConditionTracker>> allConditions;
+ vector<ConditionState> conditionCache(1, ConditionState::kNotEvaluated);
+ vector<bool> changedCache(1, false);
+
+ conditionTracker.evaluateCondition(event, matcherState, allConditions, conditionCache,
+ changedCache);
+
+ EXPECT_EQ(1UL, conditionTracker.mSlicedConditionState.size());
+ EXPECT_TRUE(changedCache[0]);
+
+ // Now test query
+ const auto queryKey = getWakeLockQueryKey(1, uid1, conditionName);
+ conditionCache[0] = ConditionState::kNotEvaluated;
+
+ conditionTracker.isConditionMet(queryKey, allConditions, conditionCache);
+ EXPECT_EQ(ConditionState::kTrue, conditionCache[0]);
+
+ // another wake lock acquired by uid2
+ LogEvent event2(1 /*tagId*/, 0 /*timestamp*/);
+ makeWakeLockEvent(&event2, uid2, "wl2", 1);
+ matcherState.clear();
+ matcherState.push_back(MatchingState::kMatched);
+ matcherState.push_back(MatchingState::kNotMatched);
+ matcherState.push_back(MatchingState::kNotMatched);
+ conditionCache[0] = ConditionState::kNotEvaluated;
+ changedCache[0] = false;
+ conditionTracker.evaluateCondition(event2, matcherState, allConditions, conditionCache,
+ changedCache);
+ EXPECT_EQ(2UL, conditionTracker.mSlicedConditionState.size());
+ EXPECT_TRUE(changedCache[0]);
+
+ // TEST QUERY
+ const auto queryKey2 = getWakeLockQueryKey(1, uid2, conditionName);
+ conditionCache[0] = ConditionState::kNotEvaluated;
+
+ conditionTracker.isConditionMet(queryKey, allConditions, conditionCache);
+ EXPECT_EQ(ConditionState::kTrue, conditionCache[0]);
+
+
+ // stop all event
+ LogEvent event3(2 /*tagId*/, 0 /*timestamp*/);
+ matcherState.clear();
+ matcherState.push_back(MatchingState::kNotMatched);
+ matcherState.push_back(MatchingState::kNotMatched);
+ matcherState.push_back(MatchingState::kMatched);
+
+ conditionCache[0] = ConditionState::kNotEvaluated;
+ changedCache[0] = false;
+ conditionTracker.evaluateCondition(event3, matcherState, allConditions, conditionCache,
+ changedCache);
+ EXPECT_TRUE(changedCache[0]);
+ EXPECT_EQ(0UL, conditionTracker.mSlicedConditionState.size());
+
+ // TEST QUERY
+ const auto queryKey3 = getWakeLockQueryKey(1, uid1, conditionName);
+ conditionCache[0] = ConditionState::kNotEvaluated;
+
+ conditionTracker.isConditionMet(queryKey, allConditions, conditionCache);
+ EXPECT_EQ(ConditionState::kFalse, conditionCache[0]);
+
+ // TEST QUERY
+ const auto queryKey4 = getWakeLockQueryKey(1, uid2, conditionName);
+ conditionCache[0] = ConditionState::kNotEvaluated;
+
+ conditionTracker.isConditionMet(queryKey, allConditions, conditionCache);
+ EXPECT_EQ(ConditionState::kFalse, conditionCache[0]);
+}
+
+} // namespace statsd
+} // namespace os
+} // namespace android
+#else
+GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
diff --git a/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp b/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp
index 5a4ee73..d07a84d 100644
--- a/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp
@@ -40,7 +40,7 @@
int tagId = 1;
CountMetric metric;
- metric.set_metric_id(1);
+ metric.set_name("1");
metric.mutable_bucket()->set_bucket_size_millis(bucketSizeNs / 1000000);
LogEvent event1(tagId, bucketStartTimeNs + 1);
@@ -92,7 +92,7 @@
int64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
CountMetric metric;
- metric.set_metric_id(1);
+ metric.set_name("1");
metric.mutable_bucket()->set_bucket_size_millis(bucketSizeNs / 1000000);
metric.set_condition("SCREEN_ON");
@@ -129,7 +129,7 @@
int64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
CountMetric metric;
- metric.set_metric_id(1);
+ metric.set_name("1");
metric.mutable_bucket()->set_bucket_size_millis(bucketSizeNs / 1000000);
metric.set_condition("APP_IN_BACKGROUND_PER_UID_AND_SCREEN_ON");
EventConditionLink* link = metric.add_links();
diff --git a/cmds/statsd/tests/metrics/EventMetricProducer_test.cpp b/cmds/statsd/tests/metrics/EventMetricProducer_test.cpp
index 76dbc73..0971d26 100644
--- a/cmds/statsd/tests/metrics/EventMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/EventMetricProducer_test.cpp
@@ -38,7 +38,7 @@
uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
EventMetric metric;
- metric.set_metric_id(1);
+ metric.set_name("1");
LogEvent event1(1 /*tag id*/, bucketStartTimeNs + 1);
LogEvent event2(1 /*tag id*/, bucketStartTimeNs + 2);
@@ -61,7 +61,7 @@
uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
EventMetric metric;
- metric.set_metric_id(1);
+ metric.set_name("1");
metric.set_condition("SCREEN_ON");
LogEvent event1(1, bucketStartTimeNs + 1);
@@ -87,7 +87,7 @@
uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
EventMetric metric;
- metric.set_metric_id(1);
+ metric.set_name("1");
metric.set_condition("APP_IN_BACKGROUND_PER_UID_AND_SCREEN_ON");
EventConditionLink* link = metric.add_links();
link->set_condition("APP_IN_BACKGROUND_PER_UID");
diff --git a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
new file mode 100644
index 0000000..72b4194
--- /dev/null
+++ b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
@@ -0,0 +1,299 @@
+// Copyright (C) 2017 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "metrics_test_helper.h"
+#include "src/metrics/ValueMetricProducer.h"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <stdio.h>
+#include <vector>
+
+using namespace testing;
+using android::sp;
+using std::set;
+using std::unordered_map;
+using std::vector;
+using std::shared_ptr;
+using std::make_shared;
+
+#ifdef __ANDROID__
+
+namespace android {
+namespace os {
+namespace statsd {
+
+/*
+ * Tests pulled atoms with no conditions
+ */
+TEST(ValueMetricProducerTest, TestNonDimensionalEvents) {
+ int64_t bucketStartTimeNs = 10000000000;
+ int64_t bucketSizeNs = 60 * 1000 * 1000 * 1000LL;
+
+ int64_t bucket2StartTimeNs = bucketStartTimeNs + bucketSizeNs;
+ int64_t bucket3StartTimeNs = bucketStartTimeNs + 2*bucketSizeNs;
+
+ ValueMetric metric;
+ metric.set_name("1");
+ metric.mutable_bucket()->set_bucket_size_millis(bucketSizeNs / 1000000);
+ metric.set_value_field(2);
+
+ int tagId = 1;
+
+ sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+ // TODO: pending refactor of StatsPullerManager
+ // For now we still need this so that it doesn't do real pulling.
+ shared_ptr<MockStatsPullerManager> pullerManager = make_shared<StrictMock<MockStatsPullerManager>>();
+ EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _)).WillOnce(Return());
+ EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return());
+
+ ValueMetricProducer valueProducer(metric, -1 /*-1 meaning no condition*/, wizard,tagId,
+ bucketStartTimeNs, pullerManager);
+
+ vector<shared_ptr<LogEvent>> allData;
+ allData.clear();
+ shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 1);
+ auto list = event->GetAndroidLogEventList();
+ *list << 1;
+ *list << 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;
+ // has one raw pair
+ EXPECT_EQ(1UL, curInterval.raw.size());
+ // value is 11, 11
+ EXPECT_EQ(11, curInterval.raw.front().first);
+ EXPECT_EQ(11, curInterval.raw.front().second);
+ ValueMetricProducer::Interval nextInterval = valueProducer.mNextSlicedBucket.begin()->second;
+ // has one raw pair
+ EXPECT_EQ(1UL, nextInterval.raw.size());
+ // value is 11, 0
+ EXPECT_EQ(11, nextInterval.raw.front().first);
+ EXPECT_EQ(0, nextInterval.raw.front().second);
+ EXPECT_EQ(0UL, valueProducer.mPastBuckets.size());
+
+ allData.clear();
+ event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 1);
+ list = event->GetAndroidLogEventList();
+ *list << 1;
+ *list << 22;
+ event->init();
+ allData.push_back(event);
+ valueProducer.onDataPulled(allData);
+ // has one slice
+ EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
+ curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
+ // has one raw pair
+ EXPECT_EQ(1UL, curInterval.raw.size());
+ // value is 22, 0
+ EXPECT_EQ(22, curInterval.raw.front().first);
+ EXPECT_EQ(0, curInterval.raw.front().second);
+ EXPECT_EQ(0UL, valueProducer.mNextSlicedBucket.size());
+ EXPECT_EQ(1UL, valueProducer.mPastBuckets.size());
+ EXPECT_EQ(1UL, valueProducer.mPastBuckets.begin()->second.size());
+ EXPECT_EQ(11, valueProducer.mPastBuckets.begin()->second.back().mValue);
+
+ allData.clear();
+ event = make_shared<LogEvent>(tagId, bucket3StartTimeNs + 1);
+ list = event->GetAndroidLogEventList();
+ *list << 1;
+ *list << 33;
+ event->init();
+ allData.push_back(event);
+ valueProducer.onDataPulled(allData);
+ EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
+ curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
+ EXPECT_EQ(1UL, curInterval.raw.size());
+ // value is 33, 0
+ EXPECT_EQ(33, curInterval.raw.front().first);
+ EXPECT_EQ(0, curInterval.raw.front().second);
+ EXPECT_EQ(0UL, valueProducer.mNextSlicedBucket.size());
+ EXPECT_EQ(1UL, valueProducer.mPastBuckets.size());
+ EXPECT_EQ(2UL, valueProducer.mPastBuckets.begin()->second.size());
+ EXPECT_EQ(11, valueProducer.mPastBuckets.begin()->second.back().mValue);
+}
+
+/*
+ * Test pulled event with non sliced condition.
+ */
+TEST(ValueMetricProducerTest, TestEventsWithNonSlicedCondition) {
+ int64_t bucketStartTimeNs = 10000000000;
+ int64_t bucketSizeNs = 60 * 1000 * 1000 * 1000LL;
+
+ int64_t bucket2StartTimeNs = bucketStartTimeNs + bucketSizeNs;
+ int64_t bucket3StartTimeNs = bucketStartTimeNs + 2 * bucketSizeNs;
+
+ ValueMetric metric;
+ metric.set_name("1");
+ metric.mutable_bucket()->set_bucket_size_millis(bucketSizeNs / 1000000);
+ metric.set_value_field(2);
+ metric.set_condition("SCREEN_ON");
+
+ int tagId = 1;
+
+ sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+ shared_ptr<MockStatsPullerManager> pullerManager = make_shared<StrictMock<MockStatsPullerManager>>();
+ EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _)).WillOnce(Return());
+ EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillRepeatedly(Return());
+
+ EXPECT_CALL(*pullerManager, Pull(tagId, _)).WillOnce(Invoke([] (int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+ int64_t bucketStartTimeNs = 10000000000;
+ int64_t bucketSizeNs = 60 * 1000 * 1000 * 1000LL;
+
+ int64_t bucket2StartTimeNs = bucketStartTimeNs + bucketSizeNs;
+ int64_t bucket3StartTimeNs = bucketStartTimeNs + 2 * bucketSizeNs;
+ data->clear();
+ shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
+ auto list = event->GetAndroidLogEventList();
+ *list << 1;
+ *list << 100;
+ event->init();
+ data->push_back(event);
+ return true;
+ }))
+ .WillOnce(Invoke([] (int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+ int64_t bucketStartTimeNs = 10000000000;
+ int64_t bucketSizeNs = 60 * 1000 * 1000 * 1000LL;
+
+ int64_t bucket2StartTimeNs = bucketStartTimeNs + bucketSizeNs;
+ int64_t bucket3StartTimeNs = bucketStartTimeNs + 2 * bucketSizeNs;
+ data->clear();
+ shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 10);
+ auto list = event->GetAndroidLogEventList();
+ *list << 1;
+ *list << 120;
+ event->init();
+ data->push_back(event);
+ return true;
+ }));
+
+ ValueMetricProducer valueProducer(metric, 1, wizard,tagId,
+ bucketStartTimeNs, pullerManager);
+
+ valueProducer.onConditionChanged(true, bucketStartTimeNs + 10);
+
+ // has one slice
+ EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
+ ValueMetricProducer::Interval curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
+ // has one raw pair
+ EXPECT_EQ(1UL, curInterval.raw.size());
+ // value is 100, 0
+ EXPECT_EQ(100, curInterval.raw.front().first);
+ EXPECT_EQ(0, curInterval.raw.front().second);
+ EXPECT_EQ(0UL, valueProducer.mNextSlicedBucket.size());
+ EXPECT_EQ(0UL, valueProducer.mPastBuckets.size());
+
+ vector<shared_ptr<LogEvent>> allData;
+ allData.clear();
+ shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 1);
+ auto list = event->GetAndroidLogEventList();
+ *list << 1;
+ *list << 110;
+ event->init();
+ allData.push_back(event);
+ valueProducer.onDataPulled(allData);
+
+ // has one slice
+ EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
+ curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
+ // has one raw pair
+ EXPECT_EQ(1UL, curInterval.raw.size());
+ // value is 110, 0
+ EXPECT_EQ(110, curInterval.raw.front().first);
+ EXPECT_EQ(0, curInterval.raw.front().second);
+ EXPECT_EQ(1UL, valueProducer.mPastBuckets.size());
+ EXPECT_EQ(1UL, valueProducer.mPastBuckets.begin()->second.size());
+ EXPECT_EQ(10, valueProducer.mPastBuckets.begin()->second.back().mValue);
+
+ valueProducer.onConditionChanged(false, bucket2StartTimeNs + 1);
+
+ // has one slice
+ EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
+ curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
+ // has one raw pair
+ EXPECT_EQ(1UL, curInterval.raw.size());
+ // value is 110, 120
+ EXPECT_EQ(110, curInterval.raw.front().first);
+ EXPECT_EQ(120, curInterval.raw.front().second);
+}
+
+TEST(ValueMetricProducerTest, TestPushedEventsWithoutCondition) {
+ int64_t bucketStartTimeNs = 10000000000;
+ int64_t bucketSizeNs = 60 * 1000 * 1000 * 1000LL;
+
+ int64_t bucket2StartTimeNs = bucketStartTimeNs + bucketSizeNs;
+ int64_t bucket3StartTimeNs = bucketStartTimeNs + 2 * bucketSizeNs;
+
+ ValueMetric metric;
+ metric.set_name("1");
+ metric.mutable_bucket()->set_bucket_size_millis(bucketSizeNs / 1000000);
+ metric.set_value_field(2);
+
+ int tagId = 1;
+
+ sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+ shared_ptr<MockStatsPullerManager> pullerManager = make_shared<StrictMock<MockStatsPullerManager>>();
+
+ ValueMetricProducer valueProducer(metric, -1, wizard,-1,
+ bucketStartTimeNs, pullerManager);
+
+ shared_ptr<LogEvent> event1 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
+ auto list = event1->GetAndroidLogEventList();
+ *list << 1;
+ *list << 10;
+ event1->init();
+ shared_ptr<LogEvent> event2 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
+ auto list2 = event2->GetAndroidLogEventList();
+ *list2 << 1;
+ *list2 << 20;
+ event2->init();
+ valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event1, false);
+ // has one slice
+ EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
+ ValueMetricProducer::Interval curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
+ // has one raw pair
+ EXPECT_EQ(1UL, curInterval.raw.size());
+ // value is 10, 0
+ EXPECT_EQ(10, curInterval.raw.front().first);
+ EXPECT_EQ(0, curInterval.raw.front().second);
+ EXPECT_EQ(0UL, valueProducer.mNextSlicedBucket.size());
+
+ valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event2, false);
+
+ // has one slice
+ EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
+ curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
+ // has one raw pair
+ EXPECT_EQ(2UL, curInterval.raw.size());
+ // value is 10, 20
+ EXPECT_EQ(10, curInterval.raw.front().first);
+ EXPECT_EQ(20, curInterval.raw.back().first);
+ EXPECT_EQ(0UL, valueProducer.mNextSlicedBucket.size());
+
+ valueProducer.flush_if_needed(bucket3StartTimeNs);
+ EXPECT_EQ(1UL, valueProducer.mPastBuckets.size());
+ EXPECT_EQ(1UL, valueProducer.mPastBuckets.begin()->second.size());
+ EXPECT_EQ(30, valueProducer.mPastBuckets.begin()->second.back().mValue);
+}
+
+} // namespace statsd
+} // namespace os
+} // namespace android
+#else
+GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
diff --git a/cmds/statsd/tests/metrics/metrics_test_helper.h b/cmds/statsd/tests/metrics/metrics_test_helper.h
index 5fd7d62..fa221aa 100644
--- a/cmds/statsd/tests/metrics/metrics_test_helper.h
+++ b/cmds/statsd/tests/metrics/metrics_test_helper.h
@@ -14,6 +14,7 @@
#pragma once
#include "src/condition/ConditionWizard.h"
+#include "src/external/StatsPullerManager.h"
#include <gmock/gmock.h>
#include <gtest/gtest.h>
@@ -30,6 +31,13 @@
const std::map<std::string, HashableDimensionKey>& conditionParameters));
};
+class MockStatsPullerManager : public StatsPullerManager {
+public:
+ MOCK_METHOD3(RegisterReceiver, void(int tagId, wp<PullDataReceiver> receiver, long intervalMs));
+ MOCK_METHOD2(UnRegisterReceiver, void(int tagId, wp<PullDataReceiver> receiver));
+ MOCK_METHOD2(Pull, bool(const int pullCode, vector<std::shared_ptr<LogEvent>>* data));
+};
+
} // namespace statsd
} // namespace os
} // namespace android
diff --git a/config/compiled-classes-phone b/config/compiled-classes-phone
index 7703fdc..fb201ef 100644
--- a/config/compiled-classes-phone
+++ b/config/compiled-classes-phone
@@ -3943,9 +3943,6 @@
android.telephony.VoLteServiceState
android.telephony.VoLteServiceState$1
android.telephony.gsm.GsmCellLocation
-android.telephony.ims.ImsServiceProxy$INotifyStatusChanged
-android.telephony.ims.ImsServiceProxyCompat
-android.telephony.ims.feature.IMMTelFeature
android.telephony.ims.stub.ImsConfigImplBase
android.telephony.ims.stub.ImsEcbmImplBase
android.telephony.ims.stub.ImsUtImplBase
@@ -5208,6 +5205,8 @@
com.android.internal.app.AlertController$ButtonHandler
com.android.internal.app.AlertController$RecycleListView
com.android.internal.app.AssistUtils
+com.android.internal.app.ColorDisplayController
+com.android.internal.app.ColorDisplayController$Callback
com.android.internal.app.IAppOpsCallback
com.android.internal.app.IAppOpsCallback$Stub
com.android.internal.app.IAppOpsCallback$Stub$Proxy
@@ -5233,8 +5232,6 @@
com.android.internal.app.IVoiceInteractionSessionShowCallback$Stub
com.android.internal.app.IVoiceInteractor
com.android.internal.app.IVoiceInteractor$Stub
-com.android.internal.app.NightDisplayController
-com.android.internal.app.NightDisplayController$Callback
com.android.internal.app.ProcessMap
com.android.internal.app.ResolverActivity
com.android.internal.app.ToolbarActionBar
diff --git a/config/preloaded-classes b/config/preloaded-classes
index a9e90de..784c3f8 100644
--- a/config/preloaded-classes
+++ b/config/preloaded-classes
@@ -2768,6 +2768,7 @@
com.android.ims.ImsException
com.android.internal.R$styleable
com.android.internal.app.AlertController$AlertParams
+com.android.internal.app.ColorDisplayController
com.android.internal.app.IAppOpsCallback
com.android.internal.app.IAppOpsCallback$Stub
com.android.internal.app.IAppOpsService
@@ -2780,7 +2781,6 @@
com.android.internal.app.IVoiceInteractionManagerService$Stub
com.android.internal.app.IVoiceInteractor
com.android.internal.app.IVoiceInteractor$Stub
-com.android.internal.app.NightDisplayController
com.android.internal.appwidget.IAppWidgetService
com.android.internal.appwidget.IAppWidgetService$Stub
com.android.internal.appwidget.IAppWidgetService$Stub$Proxy
diff --git a/core/java/Android.bp b/core/java/Android.bp
index 42b0f6b..1503445 100644
--- a/core/java/Android.bp
+++ b/core/java/Android.bp
@@ -2,3 +2,8 @@
name: "IKeyAttestationApplicationIdProvider.aidl",
srcs: ["android/security/keymaster/IKeyAttestationApplicationIdProvider.aidl"],
}
+
+filegroup {
+ name: "IKeystoreService.aidl",
+ srcs: ["android/security/IKeystoreService.aidl"],
+}
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index a8863bf..99f3dee 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -193,10 +193,13 @@
* <a name="Fragments"></a>
* <h3>Fragments</h3>
*
- * <p>Starting with {@link android.os.Build.VERSION_CODES#HONEYCOMB}, Activity
- * implementations can make use of the {@link Fragment} class to better
+ * <p>The {@link android.support.v4.app.FragmentActivity} subclass
+ * can make use of the {@link android.support.v4.app.Fragment} class to better
* modularize their code, build more sophisticated user interfaces for larger
- * screens, and help scale their application between small and large screens.
+ * screens, and help scale their application between small and large screens.</p>
+ *
+ * <p>For more information about using fragments, read the
+ * <a href="{@docRoot}guide/components/fragments.html">Fragments</a> developer guide.</p>
*
* <a name="ActivityLifecycle"></a>
* <h3>Activity Lifecycle</h3>
@@ -915,7 +918,10 @@
/**
* Return the LoaderManager for this activity, creating it if needed.
+ *
+ * @deprecated Use {@link android.support.v4.app.FragmentActivity#getSupportLoaderManager()}
*/
+ @Deprecated
public LoaderManager getLoaderManager() {
return mFragments.getLoaderManager();
}
@@ -2395,7 +2401,10 @@
/**
* Return the FragmentManager for interacting with fragments associated
* with this activity.
+ *
+ * @deprecated Use {@link android.support.v4.app.FragmentActivity#getSupportFragmentManager()}
*/
+ @Deprecated
public FragmentManager getFragmentManager() {
return mFragments.getFragmentManager();
}
@@ -2404,7 +2413,11 @@
* Called when a Fragment is being attached to this activity, immediately
* after the call to its {@link Fragment#onAttach Fragment.onAttach()}
* method and before {@link Fragment#onCreate Fragment.onCreate()}.
+ *
+ * @deprecated Use {@link
+ * android.support.v4.app.FragmentActivity#onAttachFragment(android.support.v4.app.Fragment)}
*/
+ @Deprecated
public void onAttachFragment(Fragment fragment) {
}
@@ -5106,7 +5119,11 @@
*
* @see Fragment#startActivity
* @see Fragment#startActivityForResult
+ *
+ * @deprecated Use {@link android.support.v4.app.FragmentActivity#startActivityFromFragment(
+ * android.support.v4.app.Fragment,Intent,int)}
*/
+ @Deprecated
public void startActivityFromFragment(@NonNull Fragment fragment,
@RequiresPermission Intent intent, int requestCode) {
startActivityFromFragment(fragment, intent, requestCode, null);
@@ -5131,7 +5148,11 @@
*
* @see Fragment#startActivity
* @see Fragment#startActivityForResult
+ *
+ * @deprecated Use {@link android.support.v4.app.FragmentActivity#startActivityFromFragment(
+ * android.support.v4.app.Fragment,Intent,int,Bundle)}
*/
+ @Deprecated
public void startActivityFromFragment(@NonNull Fragment fragment,
@RequiresPermission Intent intent, int requestCode, @Nullable Bundle options) {
startActivityForResult(fragment.mWho, intent, requestCode, options);
diff --git a/core/java/android/app/DexLoadReporter.java b/core/java/android/app/DexLoadReporter.java
index f99d1a8..0643414 100644
--- a/core/java/android/app/DexLoadReporter.java
+++ b/core/java/android/app/DexLoadReporter.java
@@ -19,7 +19,6 @@
import android.os.FileUtils;
import android.os.RemoteException;
import android.os.SystemProperties;
-import android.system.ErrnoException;
import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
@@ -27,8 +26,6 @@
import dalvik.system.BaseDexClassLoader;
import dalvik.system.VMRuntime;
-import libcore.io.Libcore;
-
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
@@ -155,23 +152,12 @@
return;
}
- File realDexPath;
- try {
- // Secondary dex profiles are stored in the oat directory, next to the real dex file
- // and have the same name with 'cur.prof' appended. We use the realpath because that
- // is what installd is using when processing the dex file.
- // NOTE: Keep in sync with installd.
- realDexPath = new File(Libcore.os.realpath(dexPath));
- } catch (ErrnoException ex) {
- Slog.e(TAG, "Failed to get the real path of secondary dex " + dexPath
- + ":" + ex.getMessage());
- // Do not continue with registration if we could not retrieve the real path.
- return;
- }
-
+ // Secondary dex profiles are stored in the oat directory, next to dex file
+ // and have the same name with 'cur.prof' appended.
// NOTE: Keep this in sync with installd expectations.
- File secondaryProfileDir = new File(realDexPath.getParent(), "oat");
- File secondaryProfile = new File(secondaryProfileDir, realDexPath.getName() + ".cur.prof");
+ File dexPathFile = new File(dexPath);
+ File secondaryProfileDir = new File(dexPathFile.getParent(), "oat");
+ File secondaryProfile = new File(secondaryProfileDir, dexPathFile.getName() + ".cur.prof");
// Create the profile if not already there.
// Returns true if the file was created, false if the file already exists.
diff --git a/core/java/android/app/DialogFragment.java b/core/java/android/app/DialogFragment.java
index 7e0e4d8..a0fb6ee 100644
--- a/core/java/android/app/DialogFragment.java
+++ b/core/java/android/app/DialogFragment.java
@@ -136,7 +136,10 @@
*
* {@sample development/samples/ApiDemos/src/com/example/android/apis/app/FragmentDialogOrActivity.java
* embed}
+ *
+ * @deprecated Use {@link android.support.v4.app.DialogFragment}
*/
+@Deprecated
public class DialogFragment extends Fragment
implements DialogInterface.OnCancelListener, DialogInterface.OnDismissListener {
diff --git a/core/java/android/app/Fragment.java b/core/java/android/app/Fragment.java
index 9377345..a92684b 100644
--- a/core/java/android/app/Fragment.java
+++ b/core/java/android/app/Fragment.java
@@ -256,7 +256,10 @@
* <p>After each call to this function, a new entry is on the stack, and
* pressing back will pop it to return the user to whatever previous state
* the activity UI was in.
+ *
+ * @deprecated Use {@link android.support.v4.app.Fragment}
*/
+@Deprecated
public class Fragment implements ComponentCallbacks2, OnCreateContextMenuListener {
private static final ArrayMap<String, Class<?>> sClassMap =
new ArrayMap<String, Class<?>>();
@@ -414,7 +417,10 @@
* State information that has been retrieved from a fragment instance
* through {@link FragmentManager#saveFragmentInstanceState(Fragment)
* FragmentManager.saveFragmentInstanceState}.
+ *
+ * @deprecated Use {@link android.support.v4.app.Fragment.SavedState}
*/
+ @Deprecated
public static class SavedState implements Parcelable {
final Bundle mState;
@@ -458,7 +464,10 @@
/**
* Thrown by {@link Fragment#instantiate(Context, String, Bundle)} when
* there is an instantiation failure.
+ *
+ * @deprecated Use {@link android.support.v4.app.Fragment.InstantiationException}
*/
+ @Deprecated
static public class InstantiationException extends AndroidRuntimeException {
public InstantiationException(String msg, Exception cause) {
super(msg, cause);
@@ -1031,7 +1040,10 @@
/**
* Return the LoaderManager for this fragment, creating it if needed.
+ *
+ * @deprecated Use {@link android.support.v4.app.Fragment#getLoaderManager()}
*/
+ @Deprecated
public LoaderManager getLoaderManager() {
if (mLoaderManager != null) {
return mLoaderManager;
diff --git a/core/java/android/app/FragmentBreadCrumbs.java b/core/java/android/app/FragmentBreadCrumbs.java
index d0aa0fd..e3e47ae 100644
--- a/core/java/android/app/FragmentBreadCrumbs.java
+++ b/core/java/android/app/FragmentBreadCrumbs.java
@@ -65,7 +65,10 @@
/**
* Interface to intercept clicks on the bread crumbs.
+ *
+ * @deprecated This widget is no longer supported.
*/
+ @Deprecated
public interface OnBreadCrumbClickListener {
/**
* Called when a bread crumb is clicked.
diff --git a/core/java/android/app/FragmentContainer.java b/core/java/android/app/FragmentContainer.java
index f8836bc8..a1dd32f 100644
--- a/core/java/android/app/FragmentContainer.java
+++ b/core/java/android/app/FragmentContainer.java
@@ -24,7 +24,10 @@
/**
* Callbacks to a {@link Fragment}'s container.
+ *
+ * @deprecated Use {@link android.support.v4.app.FragmentContainer}
*/
+@Deprecated
public abstract class FragmentContainer {
/**
* Return the view with the given resource ID. May return {@code null} if the
diff --git a/core/java/android/app/FragmentController.java b/core/java/android/app/FragmentController.java
index cff94d8..cbb58d4 100644
--- a/core/java/android/app/FragmentController.java
+++ b/core/java/android/app/FragmentController.java
@@ -37,7 +37,10 @@
* <p>
* It is the responsibility of the host to take care of the Fragment's lifecycle.
* The methods provided by {@link FragmentController} are for that purpose.
+ *
+ * @deprecated Use {@link android.support.v4.app.FragmentController}
*/
+@Deprecated
public class FragmentController {
private final FragmentHostCallback<?> mHost;
diff --git a/core/java/android/app/FragmentHostCallback.java b/core/java/android/app/FragmentHostCallback.java
index 5ef23e6..1edc68e 100644
--- a/core/java/android/app/FragmentHostCallback.java
+++ b/core/java/android/app/FragmentHostCallback.java
@@ -37,7 +37,10 @@
* Fragments may be hosted by any object; such as an {@link Activity}. In order to
* host fragments, implement {@link FragmentHostCallback}, overriding the methods
* applicable to the host.
+ *
+ * @deprecated Use {@link android.support.v4.app.FragmentHostCallback}
*/
+@Deprecated
public abstract class FragmentHostCallback<E> extends FragmentContainer {
private final Activity mActivity;
final Context mContext;
diff --git a/core/java/android/app/FragmentManager.java b/core/java/android/app/FragmentManager.java
index 0d5cd02..12e60b8 100644
--- a/core/java/android/app/FragmentManager.java
+++ b/core/java/android/app/FragmentManager.java
@@ -74,7 +74,10 @@
* {@link android.support.v4.app.FragmentActivity}. See the blog post
* <a href="http://android-developers.blogspot.com/2011/03/fragments-for-all.html">
* Fragments For All</a> for more details.
+ *
+ * @deprecated Use {@link android.support.v4.app.FragmentManager}
*/
+@Deprecated
public abstract class FragmentManager {
/**
* Representation of an entry on the fragment back stack, as created
@@ -86,7 +89,10 @@
* <p>Note that you should never hold on to a BackStackEntry object;
* the identifier as returned by {@link #getId} is the only thing that
* will be persisted across activity instances.
+ *
+ * @deprecated Use {@link android.support.v4.app.FragmentManager.BackStackEntry}
*/
+ @Deprecated
public interface BackStackEntry {
/**
* Return the unique identifier for the entry. This is the only
@@ -129,7 +135,10 @@
/**
* Interface to watch for changes to the back stack.
+ *
+ * @deprecated Use {@link android.support.v4.app.FragmentManager.OnBackStackChangedListener}
*/
+ @Deprecated
public interface OnBackStackChangedListener {
/**
* Called whenever the contents of the back stack change.
@@ -428,7 +437,10 @@
/**
* Callback interface for listening to fragment state changes that happen
* within a given FragmentManager.
+ *
+ * @deprecated Use {@link android.support.v4.app.FragmentManager.FragmentLifecycleCallbacks}
*/
+ @Deprecated
public abstract static class FragmentLifecycleCallbacks {
/**
* Called right before the fragment's {@link Fragment#onAttach(Context)} method is called.
diff --git a/core/java/android/app/FragmentManagerNonConfig.java b/core/java/android/app/FragmentManagerNonConfig.java
index 50d3797..beb1a15 100644
--- a/core/java/android/app/FragmentManagerNonConfig.java
+++ b/core/java/android/app/FragmentManagerNonConfig.java
@@ -27,7 +27,10 @@
* and passed to the state save and restore process for fragments in
* {@link FragmentController#retainNonConfig()} and
* {@link FragmentController#restoreAllState(Parcelable, FragmentManagerNonConfig)}.</p>
+ *
+ * @deprecated Use {@link android.support.v4.app.FragmentManagerNonConfig}
*/
+@Deprecated
public class FragmentManagerNonConfig {
private final List<Fragment> mFragments;
private final List<FragmentManagerNonConfig> mChildNonConfigs;
diff --git a/core/java/android/app/FragmentTransaction.java b/core/java/android/app/FragmentTransaction.java
index c910e90..0f4a7fb 100644
--- a/core/java/android/app/FragmentTransaction.java
+++ b/core/java/android/app/FragmentTransaction.java
@@ -21,7 +21,10 @@
* <a href="{@docRoot}guide/components/fragments.html">Fragments</a> developer
* guide.</p>
* </div>
+ *
+ * @deprecated Use {@link android.support.v4.app.FragmentTransaction}
*/
+@Deprecated
public abstract class FragmentTransaction {
/**
* Calls {@link #add(int, Fragment, String)} with a 0 containerViewId.
diff --git a/core/java/android/app/Instrumentation.java b/core/java/android/app/Instrumentation.java
index e260967..d49e11f 100644
--- a/core/java/android/app/Instrumentation.java
+++ b/core/java/android/app/Instrumentation.java
@@ -17,6 +17,7 @@
package android.app;
import android.annotation.IntDef;
+import android.annotation.Nullable;
import android.content.ActivityNotFoundException;
import android.content.ComponentName;
import android.content.Context;
@@ -418,22 +419,51 @@
* different process. In addition, if the given Intent resolves to
* multiple activities, instead of displaying a dialog for the user to
* select an activity, an exception will be thrown.
- *
+ *
* <p>The function returns as soon as the activity goes idle following the
* call to its {@link Activity#onCreate}. Generally this means it has gone
* through the full initialization including {@link Activity#onResume} and
* drawn and displayed its initial window.
- *
+ *
* @param intent Description of the activity to start.
- *
+ *
* @see Context#startActivity
+ * @see #startActivitySync(Intent, Bundle)
*/
public Activity startActivitySync(Intent intent) {
+ return startActivitySync(intent, null /* options */);
+ }
+
+ /**
+ * Start a new activity and wait for it to begin running before returning.
+ * In addition to being synchronous, this method as some semantic
+ * differences from the standard {@link Context#startActivity} call: the
+ * activity component is resolved before talking with the activity manager
+ * (its class name is specified in the Intent that this method ultimately
+ * starts), and it does not allow you to start activities that run in a
+ * different process. In addition, if the given Intent resolves to
+ * multiple activities, instead of displaying a dialog for the user to
+ * select an activity, an exception will be thrown.
+ *
+ * <p>The function returns as soon as the activity goes idle following the
+ * call to its {@link Activity#onCreate}. Generally this means it has gone
+ * through the full initialization including {@link Activity#onResume} and
+ * drawn and displayed its initial window.
+ *
+ * @param intent Description of the activity to start.
+ * @param options Additional options for how the Activity should be started.
+ * May be null if there are no options. See {@link android.app.ActivityOptions}
+ * for how to build the Bundle supplied here; there are no supported definitions
+ * for building it manually.
+ *
+ * @see Context#startActivity(Intent, Bundle)
+ */
+ public Activity startActivitySync(Intent intent, @Nullable Bundle options) {
validateNotAppThread();
synchronized (mSync) {
intent = new Intent(intent);
-
+
ActivityInfo ai = intent.resolveActivityInfo(
getTargetContext().getPackageManager(), 0);
if (ai == null) {
@@ -447,7 +477,7 @@
+ myProc + " resolved to different process "
+ ai.processName + ": " + intent);
}
-
+
intent.setComponent(new ComponentName(
ai.applicationInfo.packageName, ai.name));
final ActivityWaiter aw = new ActivityWaiter(intent);
@@ -457,7 +487,7 @@
}
mWaitingActivities.add(aw);
- getTargetContext().startActivity(intent);
+ getTargetContext().startActivity(intent, options);
do {
try {
@@ -465,7 +495,7 @@
} catch (InterruptedException e) {
}
} while (mWaitingActivities.contains(aw));
-
+
return aw.activity;
}
}
diff --git a/core/java/android/app/ListFragment.java b/core/java/android/app/ListFragment.java
index 0b96d84..90b77b3 100644
--- a/core/java/android/app/ListFragment.java
+++ b/core/java/android/app/ListFragment.java
@@ -144,7 +144,10 @@
*
* @see #setListAdapter
* @see android.widget.ListView
+ *
+ * @deprecated Use {@link android.support.v4.app.ListFragment}
*/
+@Deprecated
public class ListFragment extends Fragment {
final private Handler mHandler = new Handler();
diff --git a/core/java/android/app/LoaderManager.java b/core/java/android/app/LoaderManager.java
index 56dfc58..7969684 100644
--- a/core/java/android/app/LoaderManager.java
+++ b/core/java/android/app/LoaderManager.java
@@ -54,11 +54,17 @@
* <p>For more information about using loaders, read the
* <a href="{@docRoot}guide/topics/fundamentals/loaders.html">Loaders</a> developer guide.</p>
* </div>
+ *
+ * @deprecated Use {@link android.support.v4.app.LoaderManager}
*/
+@Deprecated
public abstract class LoaderManager {
/**
* Callback interface for a client to interact with the manager.
+ *
+ * @deprecated Use {@link android.support.v4.app.LoaderManager.LoaderCallbacks}
*/
+ @Deprecated
public interface LoaderCallbacks<D> {
/**
* Instantiate and return a new Loader for the given ID.
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index 6989db6..d813d66 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -41,6 +41,8 @@
import android.content.pm.LauncherApps;
import android.content.pm.PackageManager;
import android.content.pm.ShortcutManager;
+import android.content.pm.crossprofile.CrossProfileApps;
+import android.content.pm.crossprofile.ICrossProfileApps;
import android.content.res.Resources;
import android.hardware.ConsumerIrManager;
import android.hardware.ISerialManager;
@@ -81,6 +83,7 @@
import android.net.IpSecManager;
import android.net.NetworkPolicyManager;
import android.net.NetworkScoreManager;
+import android.net.NetworkWatchlistManager;
import android.net.lowpan.ILowpanManager;
import android.net.lowpan.LowpanManager;
import android.net.nsd.INsdManager;
@@ -150,6 +153,7 @@
import com.android.internal.app.IBatteryStats;
import com.android.internal.app.ISoundTriggerService;
import com.android.internal.appwidget.IAppWidgetService;
+import com.android.internal.net.INetworkWatchlistManager;
import com.android.internal.os.IDropBoxManagerService;
import com.android.internal.policy.PhoneLayoutInflater;
@@ -862,6 +866,17 @@
return new ShortcutManager(ctx, IShortcutService.Stub.asInterface(b));
}});
+ registerService(Context.NETWORK_WATCHLIST_SERVICE, NetworkWatchlistManager.class,
+ new CachedServiceFetcher<NetworkWatchlistManager>() {
+ @Override
+ public NetworkWatchlistManager createService(ContextImpl ctx)
+ throws ServiceNotFoundException {
+ IBinder b =
+ ServiceManager.getServiceOrThrow(Context.NETWORK_WATCHLIST_SERVICE);
+ return new NetworkWatchlistManager(ctx,
+ INetworkWatchlistManager.Stub.asInterface(b));
+ }});
+
registerService(Context.SYSTEM_HEALTH_SERVICE, SystemHealthManager.class,
new CachedServiceFetcher<SystemHealthManager>() {
@Override
@@ -909,6 +924,18 @@
public RulesManager createService(ContextImpl ctx) {
return new RulesManager(ctx.getOuterContext());
}});
+
+ registerService(Context.CROSS_PROFILE_APPS_SERVICE, CrossProfileApps.class,
+ new CachedServiceFetcher<CrossProfileApps>() {
+ @Override
+ public CrossProfileApps createService(ContextImpl ctx)
+ throws ServiceNotFoundException {
+ IBinder b = ServiceManager.getServiceOrThrow(
+ Context.CROSS_PROFILE_APPS_SERVICE);
+ return new CrossProfileApps(ctx.getOuterContext(),
+ ICrossProfileApps.Stub.asInterface(b));
+ }
+ });
}
/**
diff --git a/core/java/android/app/WindowConfiguration.java b/core/java/android/app/WindowConfiguration.java
index de27b4f..2c1fad1 100644
--- a/core/java/android/app/WindowConfiguration.java
+++ b/core/java/android/app/WindowConfiguration.java
@@ -500,15 +500,12 @@
* @hide
*/
public boolean supportSplitScreenWindowingMode() {
- return supportSplitScreenWindowingMode(mWindowingMode, mActivityType);
+ return supportSplitScreenWindowingMode(mActivityType);
}
/** @hide */
- public static boolean supportSplitScreenWindowingMode(int windowingMode, int activityType) {
- if (activityType == ACTIVITY_TYPE_ASSISTANT) {
- return false;
- }
- return windowingMode != WINDOWING_MODE_FREEFORM && windowingMode != WINDOWING_MODE_PINNED;
+ public static boolean supportSplitScreenWindowingMode(int activityType) {
+ return activityType != ACTIVITY_TYPE_ASSISTANT;
}
/** @hide */
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index a2e5cc9..f0226b7 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -6097,8 +6097,8 @@
/**
* Flag used by {@link #createAndManageUser} to specify that the user should be created
- * ephemeral.
- * @hide
+ * ephemeral. Ephemeral users will be removed after switching to another user or rebooting the
+ * device.
*/
public static final int MAKE_USER_EPHEMERAL = 0x0002;
diff --git a/core/java/android/app/job/JobInfo.java b/core/java/android/app/job/JobInfo.java
index b640bd5..530d84b 100644
--- a/core/java/android/app/job/JobInfo.java
+++ b/core/java/android/app/job/JobInfo.java
@@ -16,6 +16,12 @@
package android.app.job;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED;
+import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
import static android.util.TimeUtils.formatDuration;
import android.annotation.BytesLong;
@@ -25,6 +31,8 @@
import android.annotation.RequiresPermission;
import android.content.ClipData;
import android.content.ComponentName;
+import android.net.NetworkRequest;
+import android.net.NetworkSpecifier;
import android.net.Uri;
import android.os.BaseBundle;
import android.os.Bundle;
@@ -56,6 +64,7 @@
NETWORK_TYPE_ANY,
NETWORK_TYPE_UNMETERED,
NETWORK_TYPE_NOT_ROAMING,
+ NETWORK_TYPE_CELLULAR,
NETWORK_TYPE_METERED,
})
@Retention(RetentionPolicy.SOURCE)
@@ -69,8 +78,21 @@
public static final int NETWORK_TYPE_UNMETERED = 2;
/** This job requires network connectivity that is not roaming. */
public static final int NETWORK_TYPE_NOT_ROAMING = 3;
- /** This job requires metered connectivity such as most cellular data networks. */
- public static final int NETWORK_TYPE_METERED = 4;
+ /** This job requires network connectivity that is a cellular network. */
+ public static final int NETWORK_TYPE_CELLULAR = 4;
+
+ /**
+ * This job requires metered connectivity such as most cellular data
+ * networks.
+ *
+ * @deprecated Cellular networks may be unmetered, or Wi-Fi networks may be
+ * metered, so this isn't a good way of selecting a specific
+ * transport. Instead, use {@link #NETWORK_TYPE_CELLULAR} or
+ * {@link android.net.NetworkRequest.Builder#addTransportType(int)}
+ * if your job requires a specific network transport.
+ */
+ @Deprecated
+ public static final int NETWORK_TYPE_METERED = NETWORK_TYPE_CELLULAR;
/** Sentinel value indicating that bytes are unknown. */
public static final int NETWORK_BYTES_UNKNOWN = -1;
@@ -253,7 +275,7 @@
private final long triggerContentMaxDelay;
private final boolean hasEarlyConstraint;
private final boolean hasLateConstraint;
- private final int networkType;
+ private final NetworkRequest networkRequest;
private final long networkBytes;
private final long minLatencyMillis;
private final long maxExecutionDelayMillis;
@@ -385,10 +407,37 @@
}
/**
- * The kind of connectivity requirements that the job has.
+ * Return the basic description of the kind of network this job requires.
+ *
+ * @deprecated This method attempts to map {@link #getRequiredNetwork()}
+ * into the set of simple constants, which results in a loss of
+ * fidelity. Callers should move to using
+ * {@link #getRequiredNetwork()} directly.
+ * @see Builder#setRequiredNetworkType(int)
*/
+ @Deprecated
public @NetworkType int getNetworkType() {
- return networkType;
+ if (networkRequest == null) {
+ return NETWORK_TYPE_NONE;
+ } else if (networkRequest.networkCapabilities.hasCapability(NET_CAPABILITY_NOT_METERED)) {
+ return NETWORK_TYPE_UNMETERED;
+ } else if (networkRequest.networkCapabilities.hasCapability(NET_CAPABILITY_NOT_ROAMING)) {
+ return NETWORK_TYPE_NOT_ROAMING;
+ } else if (networkRequest.networkCapabilities.hasTransport(TRANSPORT_CELLULAR)) {
+ return NETWORK_TYPE_CELLULAR;
+ } else {
+ return NETWORK_TYPE_ANY;
+ }
+ }
+
+ /**
+ * Return the detailed description of the kind of network this job requires,
+ * or {@code null} if no specific kind of network is required.
+ *
+ * @see Builder#setRequiredNetwork(NetworkRequest)
+ */
+ public @Nullable NetworkRequest getRequiredNetwork() {
+ return networkRequest;
}
/**
@@ -438,8 +487,7 @@
* job does not recur periodically.
*/
public long getIntervalMillis() {
- final long minInterval = getMinPeriodMillis();
- return intervalMillis >= minInterval ? intervalMillis : minInterval;
+ return intervalMillis;
}
/**
@@ -447,10 +495,7 @@
* execute at any time in a window of flex length at the end of the period.
*/
public long getFlexMillis() {
- long interval = getIntervalMillis();
- long percentClamp = 5 * interval / 100;
- long clampedFlex = Math.max(flexMillis, Math.max(percentClamp, getMinFlexMillis()));
- return clampedFlex <= interval ? clampedFlex : interval;
+ return flexMillis;
}
/**
@@ -459,8 +504,7 @@
* to 30 seconds, minimum is currently 10 seconds.
*/
public long getInitialBackoffMillis() {
- final long minBackoff = getMinBackoffMillis();
- return initialBackoffMillis >= minBackoff ? initialBackoffMillis : minBackoff;
+ return initialBackoffMillis;
}
/**
@@ -538,7 +582,7 @@
if (hasLateConstraint != j.hasLateConstraint) {
return false;
}
- if (networkType != j.networkType) {
+ if (!Objects.equals(networkRequest, j.networkRequest)) {
return false;
}
if (networkBytes != j.networkBytes) {
@@ -601,7 +645,9 @@
hashCode = 31 * hashCode + Long.hashCode(triggerContentMaxDelay);
hashCode = 31 * hashCode + Boolean.hashCode(hasEarlyConstraint);
hashCode = 31 * hashCode + Boolean.hashCode(hasLateConstraint);
- hashCode = 31 * hashCode + networkType;
+ if (networkRequest != null) {
+ hashCode = 31 * hashCode + networkRequest.hashCode();
+ }
hashCode = 31 * hashCode + Long.hashCode(networkBytes);
hashCode = 31 * hashCode + Long.hashCode(minLatencyMillis);
hashCode = 31 * hashCode + Long.hashCode(maxExecutionDelayMillis);
@@ -632,7 +678,11 @@
triggerContentUris = in.createTypedArray(TriggerContentUri.CREATOR);
triggerContentUpdateDelay = in.readLong();
triggerContentMaxDelay = in.readLong();
- networkType = in.readInt();
+ if (in.readInt() != 0) {
+ networkRequest = NetworkRequest.CREATOR.createFromParcel(in);
+ } else {
+ networkRequest = null;
+ }
networkBytes = in.readLong();
minLatencyMillis = in.readLong();
maxExecutionDelayMillis = in.readLong();
@@ -661,7 +711,7 @@
: null;
triggerContentUpdateDelay = b.mTriggerContentUpdateDelay;
triggerContentMaxDelay = b.mTriggerContentMaxDelay;
- networkType = b.mNetworkType;
+ networkRequest = b.mNetworkRequest;
networkBytes = b.mNetworkBytes;
minLatencyMillis = b.mMinLatencyMillis;
maxExecutionDelayMillis = b.mMaxExecutionDelayMillis;
@@ -699,7 +749,12 @@
out.writeTypedArray(triggerContentUris, flags);
out.writeLong(triggerContentUpdateDelay);
out.writeLong(triggerContentMaxDelay);
- out.writeInt(networkType);
+ if (networkRequest != null) {
+ out.writeInt(1);
+ networkRequest.writeToParcel(out, flags);
+ } else {
+ out.writeInt(0);
+ }
out.writeLong(networkBytes);
out.writeLong(minLatencyMillis);
out.writeLong(maxExecutionDelayMillis);
@@ -833,7 +888,7 @@
private int mFlags;
// Requirements.
private int mConstraintFlags;
- private int mNetworkType;
+ private NetworkRequest mNetworkRequest;
private long mNetworkBytes = NETWORK_BYTES_UNKNOWN;
private ArrayList<TriggerContentUri> mTriggerContentUris;
private long mTriggerContentUpdateDelay = -1;
@@ -934,24 +989,84 @@
}
/**
- * Set some description of the kind of network type your job needs to
- * have. Not calling this function means the network is not necessary,
- * as the default is {@link #NETWORK_TYPE_NONE}. Bear in mind that
- * calling this function defines network as a strict requirement for
- * your job. If the network requested is not available your job will
- * never run. See {@link #setOverrideDeadline(long)} to change this
- * behaviour.
+ * Set basic description of the kind of network your job requires. If
+ * you need more precise control over network capabilities, see
+ * {@link #setRequiredNetwork(NetworkRequest)}.
+ * <p>
+ * If your job doesn't need a network connection, you don't need to call
+ * this method, as the default value is {@link #NETWORK_TYPE_NONE}.
+ * <p>
+ * Calling this method defines network as a strict requirement for your
+ * job. If the network requested is not available your job will never
+ * run. See {@link #setOverrideDeadline(long)} to change this behavior.
+ * Calling this method will override any requirements previously defined
+ * by {@link #setRequiredNetwork(NetworkRequest)}; you typically only
+ * want to call one of these methods.
* <p class="note">
- * Note: When your job executes in
+ * When your job executes in
* {@link JobService#onStartJob(JobParameters)}, be sure to use the
* specific network returned by {@link JobParameters#getNetwork()},
* otherwise you'll use the default network which may not meet this
* constraint.
*
+ * @see #setRequiredNetwork(NetworkRequest)
+ * @see JobInfo#getNetworkType()
* @see JobParameters#getNetwork()
*/
public Builder setRequiredNetworkType(@NetworkType int networkType) {
- mNetworkType = networkType;
+ if (networkType == NETWORK_TYPE_NONE) {
+ return setRequiredNetwork(null);
+ } else {
+ final NetworkRequest.Builder builder = new NetworkRequest.Builder();
+
+ // All types require validated Internet
+ builder.addCapability(NET_CAPABILITY_INTERNET);
+ builder.addCapability(NET_CAPABILITY_VALIDATED);
+ builder.removeCapability(NET_CAPABILITY_NOT_VPN);
+
+ if (networkType == NETWORK_TYPE_ANY) {
+ // No other capabilities
+ } else if (networkType == NETWORK_TYPE_UNMETERED) {
+ builder.addCapability(NET_CAPABILITY_NOT_METERED);
+ } else if (networkType == NETWORK_TYPE_NOT_ROAMING) {
+ builder.addCapability(NET_CAPABILITY_NOT_ROAMING);
+ } else if (networkType == NETWORK_TYPE_CELLULAR) {
+ builder.addTransportType(TRANSPORT_CELLULAR);
+ }
+
+ return setRequiredNetwork(builder.build());
+ }
+ }
+
+ /**
+ * Set detailed description of the kind of network your job requires.
+ * <p>
+ * If your job doesn't need a network connection, you don't need to call
+ * this method, as the default is {@code null}.
+ * <p>
+ * Calling this method defines network as a strict requirement for your
+ * job. If the network requested is not available your job will never
+ * run. See {@link #setOverrideDeadline(long)} to change this behavior.
+ * Calling this method will override any requirements previously defined
+ * by {@link #setRequiredNetworkType(int)}; you typically only want to
+ * call one of these methods.
+ * <p class="note">
+ * When your job executes in
+ * {@link JobService#onStartJob(JobParameters)}, be sure to use the
+ * specific network returned by {@link JobParameters#getNetwork()},
+ * otherwise you'll use the default network which may not meet this
+ * constraint.
+ *
+ * @param networkRequest The detailed description of the kind of network
+ * this job requires, or {@code null} if no specific kind of
+ * network is required. Defining a {@link NetworkSpecifier}
+ * is only supported for jobs that aren't persisted.
+ * @see #setRequiredNetworkType(int)
+ * @see JobInfo#getRequiredNetwork()
+ * @see JobParameters#getNetwork()
+ */
+ public Builder setRequiredNetwork(@Nullable NetworkRequest networkRequest) {
+ mNetworkRequest = networkRequest;
return this;
}
@@ -1140,6 +1255,21 @@
* higher.
*/
public Builder setPeriodic(long intervalMillis, long flexMillis) {
+ final long minPeriod = getMinPeriodMillis();
+ if (intervalMillis < minPeriod) {
+ Log.w(TAG, "Requested interval " + formatDuration(intervalMillis) + " for job "
+ + mJobId + " is too small; raising to " + formatDuration(minPeriod));
+ intervalMillis = minPeriod;
+ }
+
+ final long percentClamp = 5 * intervalMillis / 100;
+ final long minFlex = Math.max(percentClamp, getMinFlexMillis());
+ if (flexMillis < minFlex) {
+ Log.w(TAG, "Requested flex " + formatDuration(flexMillis) + " for job " + mJobId
+ + " is too small; raising to " + formatDuration(minFlex));
+ flexMillis = minFlex;
+ }
+
mIsPeriodic = true;
mIntervalMillis = intervalMillis;
mFlexMillis = flexMillis;
@@ -1189,6 +1319,13 @@
*/
public Builder setBackoffCriteria(long initialBackoffMillis,
@BackoffPolicy int backoffPolicy) {
+ final long minBackoff = getMinBackoffMillis();
+ if (initialBackoffMillis < minBackoff) {
+ Log.w(TAG, "Requested backoff " + formatDuration(initialBackoffMillis) + " for job "
+ + mJobId + " is too small; raising to " + formatDuration(minBackoff));
+ initialBackoffMillis = minBackoff;
+ }
+
mBackoffPolicySet = true;
mInitialBackoffMillis = initialBackoffMillis;
mBackoffPolicy = backoffPolicy;
@@ -1213,16 +1350,22 @@
public JobInfo build() {
// Allow jobs with no constraints - What am I, a database?
if (!mHasEarlyConstraint && !mHasLateConstraint && mConstraintFlags == 0 &&
- mNetworkType == NETWORK_TYPE_NONE &&
+ mNetworkRequest == null &&
mTriggerContentUris == null) {
throw new IllegalArgumentException("You're trying to build a job with no " +
"constraints, this is not allowed.");
}
// Check that network estimates require network type
- if (mNetworkBytes > 0 && mNetworkType == NETWORK_TYPE_NONE) {
+ if (mNetworkBytes > 0 && mNetworkRequest == null) {
throw new IllegalArgumentException(
"Can't provide estimated network usage without requiring a network");
}
+ // We can't serialize network specifiers
+ if (mIsPersisted && mNetworkRequest != null
+ && mNetworkRequest.networkCapabilities.getNetworkSpecifier() != null) {
+ throw new IllegalArgumentException(
+ "Network specifiers aren't supported for persistent jobs");
+ }
// Check that a deadline was not set on a periodic job.
if (mIsPeriodic) {
if (mMaxExecutionDelayMillis != 0L) {
@@ -1257,31 +1400,7 @@
" back-off policy, so calling setBackoffCriteria with" +
" setRequiresDeviceIdle is an error.");
}
- JobInfo job = new JobInfo(this);
- if (job.isPeriodic()) {
- if (job.intervalMillis != job.getIntervalMillis()) {
- StringBuilder builder = new StringBuilder();
- builder.append("Specified interval for ")
- .append(String.valueOf(mJobId))
- .append(" is ");
- formatDuration(mIntervalMillis, builder);
- builder.append(". Clamped to ");
- formatDuration(job.getIntervalMillis(), builder);
- Log.w(TAG, builder.toString());
- }
- if (job.flexMillis != job.getFlexMillis()) {
- StringBuilder builder = new StringBuilder();
- builder.append("Specified flex for ")
- .append(String.valueOf(mJobId))
- .append(" is ");
- formatDuration(mFlexMillis, builder);
- builder.append(". Clamped to ");
- formatDuration(job.getFlexMillis(), builder);
- Log.w(TAG, builder.toString());
- }
- }
- return job;
+ return new JobInfo(this);
}
}
-
}
diff --git a/core/java/android/app/usage/UsageStatsManagerInternal.java b/core/java/android/app/usage/UsageStatsManagerInternal.java
index dbaace2..29e7439 100644
--- a/core/java/android/app/usage/UsageStatsManagerInternal.java
+++ b/core/java/android/app/usage/UsageStatsManagerInternal.java
@@ -118,7 +118,15 @@
AppIdleStateChangeListener listener);
public static abstract class AppIdleStateChangeListener {
- public abstract void onAppIdleStateChanged(String packageName, int userId, boolean idle);
+
+ /** Callback to inform listeners that the idle state has changed to a new bucket. */
+ public abstract void onAppIdleStateChanged(String packageName, int userId, boolean idle,
+ int bucket);
+
+ /**
+ * Callback to inform listeners that the parole state has changed. This means apps are
+ * allowed to do work even if they're idle or in a low bucket.
+ */
public abstract void onParoleStateChanged(boolean isParoleOn);
}
diff --git a/core/java/android/bluetooth/BluetoothSocket.java b/core/java/android/bluetooth/BluetoothSocket.java
index 4035ee1..0569913 100644
--- a/core/java/android/bluetooth/BluetoothSocket.java
+++ b/core/java/android/bluetooth/BluetoothSocket.java
@@ -375,7 +375,7 @@
IBluetooth bluetoothProxy =
BluetoothAdapter.getDefaultAdapter().getBluetoothService(null);
if (bluetoothProxy == null) throw new IOException("Bluetooth is off");
- mPfd = bluetoothProxy.connectSocket(mDevice, mType,
+ mPfd = bluetoothProxy.getSocketManager().connectSocket(mDevice, mType,
mUuid, mPort, getSecurityFlags());
synchronized (this) {
if (DBG) Log.d(TAG, "connect(), SocketState: " + mSocketState + ", mPfd: " + mPfd);
@@ -417,7 +417,7 @@
return -1;
}
try {
- mPfd = bluetoothProxy.createSocketChannel(mType, mServiceName,
+ mPfd = bluetoothProxy.getSocketManager().createSocketChannel(mType, mServiceName,
mUuid, mPort, getSecurityFlags());
} catch (RemoteException e) {
Log.e(TAG, Log.getStackTraceString(new Throwable()));
diff --git a/core/java/android/content/AsyncTaskLoader.java b/core/java/android/content/AsyncTaskLoader.java
index b7545bf..6e9f09c 100644
--- a/core/java/android/content/AsyncTaskLoader.java
+++ b/core/java/android/content/AsyncTaskLoader.java
@@ -49,7 +49,10 @@
* fragment}
*
* @param <D> the data type to be loaded.
+ *
+ * @deprecated Use {@link android.support.v4.content.AsyncTaskLoader}
*/
+@Deprecated
public abstract class AsyncTaskLoader<D> extends Loader<D> {
static final String TAG = "AsyncTaskLoader";
static final boolean DEBUG = false;
diff --git a/core/java/android/content/ContentProviderClient.java b/core/java/android/content/ContentProviderClient.java
index f8c139f..2d490a0 100644
--- a/core/java/android/content/ContentProviderClient.java
+++ b/core/java/android/content/ContentProviderClient.java
@@ -22,6 +22,7 @@
import android.database.CrossProcessCursorWrapper;
import android.database.Cursor;
import android.net.Uri;
+import android.os.Binder;
import android.os.Bundle;
import android.os.CancellationSignal;
import android.os.DeadObjectException;
@@ -102,8 +103,16 @@
if (sAnrHandler == null) {
sAnrHandler = new Handler(Looper.getMainLooper(), null, true /* async */);
}
+
+ // If the remote process hangs, we're going to kill it, so we're
+ // technically okay doing blocking calls.
+ Binder.allowBlocking(mContentProvider.asBinder());
} else {
mAnrRunnable = null;
+
+ // If we're no longer watching for hangs, revert back to default
+ // blocking behavior.
+ Binder.defaultBlocking(mContentProvider.asBinder());
}
}
}
@@ -511,6 +520,10 @@
private boolean closeInternal() {
mCloseGuard.close();
if (mClosed.compareAndSet(false, true)) {
+ // We can't do ANR checks after we cease to exist! Reset any
+ // blocking behavior changes we might have made.
+ setDetectNotResponding(0);
+
if (mStable) {
return mContentResolver.releaseProvider(mContentProvider);
} else {
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index c165fb3..72f75112 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -3413,6 +3413,8 @@
public static final String NETWORK_STATS_SERVICE = "netstats";
/** {@hide} */
public static final String NETWORK_POLICY_SERVICE = "netpolicy";
+ /** {@hide} */
+ public static final String NETWORK_WATCHLIST_SERVICE = "network_watchlist";
/**
* Use with {@link #getSystemService} to retrieve a {@link
@@ -4071,6 +4073,14 @@
public static final String TIME_ZONE_RULES_MANAGER_SERVICE = "timezone";
/**
+ * Use with {@link #getSystemService} to retrieve a
+ * {@link android.content.pm.crossprofile.CrossProfileApps} for cross profile operations.
+ *
+ * @see #getSystemService
+ */
+ public static final String CROSS_PROFILE_APPS_SERVICE = "crossprofileapps";
+
+ /**
* 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/CursorLoader.java b/core/java/android/content/CursorLoader.java
index c78871c..33386e5 100644
--- a/core/java/android/content/CursorLoader.java
+++ b/core/java/android/content/CursorLoader.java
@@ -38,7 +38,10 @@
* in the desired paramters with {@link #setUri(Uri)}, {@link #setSelection(String)},
* {@link #setSelectionArgs(String[])}, {@link #setSortOrder(String)},
* and {@link #setProjection(String[])}.
+ *
+ * @deprecated Use {@link android.support.v4.content.CursorLoader}
*/
+@Deprecated
public class CursorLoader extends AsyncTaskLoader<Cursor> {
final ForceLoadContentObserver mObserver;
diff --git a/core/java/android/content/Loader.java b/core/java/android/content/Loader.java
index 3faf13b..80f9a14 100644
--- a/core/java/android/content/Loader.java
+++ b/core/java/android/content/Loader.java
@@ -48,7 +48,10 @@
* </div>
*
* @param <D> The result returned when the load is complete
+ *
+ * @deprecated Use {@link android.support.v4.content.Loader}
*/
+@Deprecated
public class Loader<D> {
int mId;
OnLoadCompleteListener<D> mListener;
@@ -66,7 +69,10 @@
* is told it has changed. You do not normally need to use this yourself;
* it is used for you by {@link CursorLoader} to take care of executing
* an update when the cursor's backing data changes.
+ *
+ * @deprecated Use {@link android.support.v4.content.Loader.ForceLoadContentObserver}
*/
+ @Deprecated
public final class ForceLoadContentObserver extends ContentObserver {
public ForceLoadContentObserver() {
super(new Handler());
@@ -90,7 +96,10 @@
* to find out when a Loader it is managing has completed so that this can
* be reported to its client. This interface should only be used if a
* Loader is not being used in conjunction with LoaderManager.
+ *
+ * @deprecated Use {@link android.support.v4.content.Loader.OnLoadCompleteListener}
*/
+ @Deprecated
public interface OnLoadCompleteListener<D> {
/**
* Called on the thread that created the Loader when the load is complete.
@@ -108,7 +117,10 @@
* to find out when a Loader it is managing has been canceled so that it
* can schedule the next Loader. This interface should only be used if a
* Loader is not being used in conjunction with LoaderManager.
+ *
+ * @deprecated Use {@link android.support.v4.content.Loader.OnLoadCanceledListener}
*/
+ @Deprecated
public interface OnLoadCanceledListener<D> {
/**
* Called on the thread that created the Loader when the load is canceled.
diff --git a/core/java/android/content/pm/crossprofile/CrossProfileApps.java b/core/java/android/content/pm/crossprofile/CrossProfileApps.java
new file mode 100644
index 0000000..c441b5f
--- /dev/null
+++ b/core/java/android/content/pm/crossprofile/CrossProfileApps.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.content.pm.crossprofile;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.ComponentName;
+import android.content.Context;
+import android.graphics.Rect;
+import android.os.Bundle;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.os.UserManager;
+
+import java.util.List;
+
+/**
+ * Class for handling cross profile operations. Apps can use this class to interact with its
+ * instance in any profile that is in {@link #getTargetUserProfiles()}. For example, app can
+ * use this class to start its main activity in managed profile.
+ */
+public class CrossProfileApps {
+ private final Context mContext;
+ private final ICrossProfileApps mService;
+
+ /** @hide */
+ public CrossProfileApps(Context context, ICrossProfileApps service) {
+ mContext = context;
+ mService = service;
+ }
+
+ /**
+ * Starts the specified main activity of the caller package in the specified profile.
+ *
+ * @param component The ComponentName of the activity to launch, it must be exported and has
+ * action {@link android.content.Intent#ACTION_MAIN}, category
+ * {@link android.content.Intent#CATEGORY_LAUNCHER}. Otherwise, SecurityException will
+ * be thrown.
+ * @param user The UserHandle of the profile, must be one of the users returned by
+ * {@link #getTargetUserProfiles()}, otherwise a {@link SecurityException} will
+ * be thrown.
+ * @param sourceBounds The Rect containing the source bounds of the clicked icon, see
+ * {@link android.content.Intent#setSourceBounds(Rect)}.
+ * @param startActivityOptions Options to pass to startActivity
+ */
+ public void startMainActivity(@NonNull ComponentName component, @NonNull UserHandle user,
+ @Nullable Rect sourceBounds, @Nullable Bundle startActivityOptions) {
+ try {
+ mService.startActivityAsUser(mContext.getPackageName(),
+ component, sourceBounds, startActivityOptions, user);
+ } catch (RemoteException ex) {
+ throw ex.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Return a list of user profiles that that the caller can use when calling other APIs in this
+ * class.
+ * <p>
+ * A user profile would be considered as a valid target user profile, provided that:
+ * <ul>
+ * <li>It gets caller app installed</li>
+ * <li>It is not equal to the calling user</li>
+ * <li>It is in the same profile group of calling user profile</li>
+ * <li>It is enabled</li>
+ * </ul>
+ *
+ * @see UserManager#getUserProfiles()
+ */
+ public @NonNull List<UserHandle> getTargetUserProfiles() {
+ try {
+ return mService.getTargetUserProfiles(mContext.getPackageName());
+ } catch (RemoteException ex) {
+ throw ex.rethrowFromSystemServer();
+ }
+ }
+}
diff --git a/core/java/android/content/pm/crossprofile/ICrossProfileApps.aidl b/core/java/android/content/pm/crossprofile/ICrossProfileApps.aidl
new file mode 100644
index 0000000..dd8d04f
--- /dev/null
+++ b/core/java/android/content/pm/crossprofile/ICrossProfileApps.aidl
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content.pm.crossprofile;
+
+import android.content.ComponentName;
+import android.content.Intent;
+import android.graphics.Rect;
+import android.os.Bundle;
+import android.os.UserHandle;
+
+/**
+ * @hide
+ */
+interface ICrossProfileApps {
+ void startActivityAsUser(in String callingPackage, in ComponentName component, in Rect sourceBounds, in Bundle startActivityOptions, in UserHandle user);
+ List<UserHandle> getTargetUserProfiles(in String callingPackage);
+}
\ No newline at end of file
diff --git a/core/java/android/content/res/Configuration.java b/core/java/android/content/res/Configuration.java
index dfd3bbf..26efda1 100644
--- a/core/java/android/content/res/Configuration.java
+++ b/core/java/android/content/res/Configuration.java
@@ -2105,6 +2105,7 @@
break;
case DENSITY_DPI_NONE:
parts.add("nodpi");
+ break;
default:
parts.add(config.densityDpi + "dpi");
break;
diff --git a/core/java/android/database/sqlite/SQLiteConnection.java b/core/java/android/database/sqlite/SQLiteConnection.java
index 361b81b..b692039 100644
--- a/core/java/android/database/sqlite/SQLiteConnection.java
+++ b/core/java/android/database/sqlite/SQLiteConnection.java
@@ -416,7 +416,8 @@
boolean foreignKeyModeChanged = configuration.foreignKeyConstraintsEnabled
!= mConfiguration.foreignKeyConstraintsEnabled;
boolean walModeChanged = ((configuration.openFlags ^ mConfiguration.openFlags)
- & SQLiteDatabase.ENABLE_WRITE_AHEAD_LOGGING) != 0;
+ & SQLiteDatabase.ENABLE_WRITE_AHEAD_LOGGING) != 0
+ || configuration.useCompatibilityWal != mConfiguration.useCompatibilityWal;
boolean localeChanged = !configuration.locale.equals(mConfiguration.locale);
// Update configuration parameters.
diff --git a/core/java/android/database/sqlite/SQLiteGlobal.java b/core/java/android/database/sqlite/SQLiteGlobal.java
index bb2a517..d6d9764 100644
--- a/core/java/android/database/sqlite/SQLiteGlobal.java
+++ b/core/java/android/database/sqlite/SQLiteGlobal.java
@@ -83,7 +83,6 @@
/**
* Returns true if compatibility WAL mode is supported. In this mode, only
* database journal mode is changed. Connection pool will use at most one connection.
- * @hide
*/
public static boolean isCompatibilityWalSupported() {
return SystemProperties.getBoolean("debug.sqlite.compatibility_wal_supported",
diff --git a/core/java/android/database/sqlite/package.html b/core/java/android/database/sqlite/package.html
index 864a9bb..4d6ba28 100644
--- a/core/java/android/database/sqlite/package.html
+++ b/core/java/android/database/sqlite/package.html
@@ -20,6 +20,8 @@
<p>The version of SQLite depends on the version of Android. See the following table:
<table style="width:auto;">
<tr><th>Android API</th><th>SQLite Version</th></tr>
+ <tr><td>API 27</td><td>3.19</td></tr>
+ <tr><td>API 26</td><td>3.18</td></tr>
<tr><td>API 24</td><td>3.9</td></tr>
<tr><td>API 21</td><td>3.8</td></tr>
<tr><td>API 11</td><td>3.7</td></tr>
diff --git a/core/java/android/hardware/camera2/CameraCaptureSession.java b/core/java/android/hardware/camera2/CameraCaptureSession.java
index da771e4..ff69bd8 100644
--- a/core/java/android/hardware/camera2/CameraCaptureSession.java
+++ b/core/java/android/hardware/camera2/CameraCaptureSession.java
@@ -249,7 +249,7 @@
* <p>This function can also be called in case where multiple surfaces share the same
* OutputConfiguration, and one of the surfaces becomes available after the {@link
* CameraCaptureSession} is created. In that case, the application must first create the
- * OutputConfiguration with the available Surface, then enable furture surface sharing via
+ * OutputConfiguration with the available Surface, then enable further surface sharing via
* {@link OutputConfiguration#enableSurfaceSharing}, before creating the CameraCaptureSession.
* After the CameraCaptureSession is created, and once the extra Surface becomes available, the
* application must then call {@link OutputConfiguration#addSurface} before finalizing the
@@ -645,6 +645,44 @@
public abstract Surface getInputSurface();
/**
+ * Update {@link OutputConfiguration} after configuration finalization see
+ * {@link #finalizeOutputConfigurations}.
+ *
+ * <p>Any {@link OutputConfiguration} that has been modified via calls to
+ * {@link OutputConfiguration#addSurface} or {@link OutputConfiguration#removeSurface} must be
+ * updated. After the update call returns without throwing exceptions any newly added surfaces
+ * can be referenced in subsequent capture requests.</p>
+ *
+ * <p>Surfaces that get removed must not be part of any active repeating or single/burst
+ * request or have any pending results. Consider updating any repeating requests first via
+ * {@link #setRepeatingRequest} or {@link #setRepeatingBurst} and then wait for the last frame
+ * number when the sequence completes {@link CaptureCallback#onCaptureSequenceCompleted}
+ * before calling updateOutputConfiguration to remove a previously active Surface.</p>
+ *
+ * <p>Surfaces that get added must not be part of any other registered
+ * {@link OutputConfiguration}.</p>
+ *
+ * @param config Modified output configuration.
+ *
+ * @throws CameraAccessException if the camera device is no longer connected or has
+ * encountered a fatal error.
+ * @throws IllegalArgumentException if an attempt was made to add a {@link Surface} already
+ * in use by another buffer-producing API, such as MediaCodec or
+ * a different camera device or {@link OutputConfiguration}; or
+ * new surfaces are not compatible (see
+ * {@link OutputConfiguration#enableSurfaceSharing}); or a
+ * {@link Surface} that was removed from the modified
+ * {@link OutputConfiguration} still has pending requests.
+ * @throws IllegalStateException if this session is no longer active, either because the session
+ * was explicitly closed, a new session has been created
+ * or the camera device has been closed.
+ */
+ public void updateOutputConfiguration(OutputConfiguration config)
+ throws CameraAccessException {
+ throw new UnsupportedOperationException("Subclasses must override this method");
+ }
+
+ /**
* Close this capture session asynchronously.
*
* <p>Closing a session frees up the target output Surfaces of the session for reuse with either
diff --git a/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java
index c7654c9..374789c 100644
--- a/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java
@@ -314,6 +314,20 @@
}
@Override
+ public void updateOutputConfiguration(OutputConfiguration config)
+ throws CameraAccessException {
+ synchronized (mDeviceImpl.mInterfaceLock) {
+ checkNotClosed();
+
+ if (DEBUG) {
+ Log.v(TAG, mIdString + "updateOutputConfiguration");
+ }
+
+ mDeviceImpl.updateOutputConfiguration(config);
+ }
+ }
+
+ @Override
public boolean isReprocessable() {
return mInput != null;
}
diff --git a/core/java/android/hardware/camera2/impl/CameraConstrainedHighSpeedCaptureSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraConstrainedHighSpeedCaptureSessionImpl.java
index fec7fd9..8c4dbfa 100644
--- a/core/java/android/hardware/camera2/impl/CameraConstrainedHighSpeedCaptureSessionImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraConstrainedHighSpeedCaptureSessionImpl.java
@@ -235,6 +235,13 @@
}
@Override
+ public void updateOutputConfiguration(OutputConfiguration config)
+ throws CameraAccessException {
+ throw new UnsupportedOperationException("Constrained high speed session doesn't support"
+ + " this method");
+ }
+
+ @Override
public void close() {
mSessionImpl.close();
}
diff --git a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
index bfeb14d..6787d84 100644
--- a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
@@ -764,6 +764,24 @@
}
}
+ public void updateOutputConfiguration(OutputConfiguration config)
+ throws CameraAccessException {
+ synchronized(mInterfaceLock) {
+ int streamId = -1;
+ for (int i = 0; i < mConfiguredOutputs.size(); i++) {
+ if (config.getSurface() == mConfiguredOutputs.valueAt(i).getSurface()) {
+ streamId = mConfiguredOutputs.keyAt(i);
+ break;
+ }
+ }
+ if (streamId == -1) {
+ throw new IllegalArgumentException("Invalid output configuration");
+ }
+
+ mRemoteDevice.updateOutputConfiguration(streamId, config);
+ }
+ }
+
public void tearDown(Surface surface) throws CameraAccessException {
if (surface == null) throw new IllegalArgumentException("Surface is null");
diff --git a/core/java/android/hardware/camera2/impl/ICameraDeviceUserWrapper.java b/core/java/android/hardware/camera2/impl/ICameraDeviceUserWrapper.java
index 27087a2..0978ff8 100644
--- a/core/java/android/hardware/camera2/impl/ICameraDeviceUserWrapper.java
+++ b/core/java/android/hardware/camera2/impl/ICameraDeviceUserWrapper.java
@@ -215,6 +215,16 @@
}
}
+ public void updateOutputConfiguration(int streamId, OutputConfiguration config)
+ throws CameraAccessException {
+ try {
+ mRemoteDevice.updateOutputConfiguration(streamId, config);
+ } catch (Throwable t) {
+ CameraManager.throwAsPublicException(t);
+ throw new UnsupportedOperationException("Unexpected exception", t);
+ }
+ }
+
public void finalizeOutputConfigurations(int streamId, OutputConfiguration deferredConfig)
throws CameraAccessException {
try {
diff --git a/core/java/android/hardware/camera2/legacy/CameraDeviceUserShim.java b/core/java/android/hardware/camera2/legacy/CameraDeviceUserShim.java
index 49d4096..119cca8 100644
--- a/core/java/android/hardware/camera2/legacy/CameraDeviceUserShim.java
+++ b/core/java/android/hardware/camera2/legacy/CameraDeviceUserShim.java
@@ -646,6 +646,11 @@
}
@Override
+ public void updateOutputConfiguration(int streamId, OutputConfiguration config) {
+ // TODO: b/63912484 implement updateOutputConfiguration.
+ }
+
+ @Override
public void waitUntilIdle() throws RemoteException {
if (DEBUG) {
Log.d(TAG, "waitUntilIdle called.");
diff --git a/core/java/android/hardware/camera2/params/OutputConfiguration.java b/core/java/android/hardware/camera2/params/OutputConfiguration.java
index 2b317d6..7409671 100644
--- a/core/java/android/hardware/camera2/params/OutputConfiguration.java
+++ b/core/java/android/hardware/camera2/params/OutputConfiguration.java
@@ -42,6 +42,53 @@
* A class for describing camera output, which contains a {@link Surface} and its specific
* configuration for creating capture session.
*
+ * <p>There are several ways to instantiate, modify and use OutputConfigurations. The most common
+ * and recommended usage patterns are summarized in the following list:</p>
+ *<ul>
+ * <li>Passing a {@link Surface} to the constructor and using the OutputConfiguration instance as
+ * argument to {@link CameraDevice#createCaptureSessionByOutputConfigurations}. This is the most
+ * frequent usage and clients should consider it first before other more complicated alternatives.
+ * </li>
+ *
+ * <li>Passing only a surface source class as an argument to the constructor. This is usually
+ * followed by a call to create a capture session
+ * (see {@link CameraDevice#createCaptureSessionByOutputConfigurations} and a {@link Surface} add
+ * call {@link #addSurface} with a valid {@link Surface}. The sequence completes with
+ * {@link CameraCaptureSession#finalizeOutputConfigurations}. This is the deferred usage case which
+ * aims to enhance performance by allowing the resource-intensive capture session create call to
+ * execute in parallel with any {@link Surface} initialization, such as waiting for a
+ * {@link android.view.SurfaceView} to be ready as part of the UI initialization.</li>
+ *
+ * <li>The third and most complex usage pattern inlvolves surface sharing. Once instantiated an
+ * OutputConfiguration can be enabled for surface sharing via {@link #enableSurfaceSharing}. This
+ * must be done before creating a new capture session and enables calls to
+ * {@link CameraCaptureSession#updateOutputConfiguration}. An OutputConfiguration with enabled
+ * surface sharing can be modified via {@link #addSurface} or {@link #removeSurface}. The updates
+ * to this OutputConfiguration will only come into effect after
+ * {@link CameraCaptureSession#updateOutputConfiguration} returns without throwing exceptions.
+ * Such updates can be done as long as the session is active. Clients should always consider the
+ * additional requirements and limitations placed on the output surfaces (for more details see
+ * {@link #enableSurfaceSharing}, {@link #addSurface}, {@link #removeSurface},
+ * {@link CameraCaptureSession#updateOutputConfiguration}). A trade-off exists between additional
+ * complexity and flexibility. If exercised correctly surface sharing can switch between different
+ * output surfaces without interrupting any ongoing repeating capture requests. This saves time and
+ * can significantly improve the user experience.</li>
+ *
+ * <li>Surface sharing can be used in combination with deferred surfaces. The rules from both cases
+ * are combined and clients must call {@link #enableSurfaceSharing} before creating a capture
+ * session. Attach and/or remove output surfaces via {@link #addSurface}/{@link #removeSurface} and
+ * finalize the configuration using {@link CameraCaptureSession#finalizeOutputConfigurations}.
+ * {@link CameraCaptureSession#updateOutputConfiguration} can be called after the configuration
+ * finalize method returns without exceptions.</li>
+ *
+ * </ul>
+ *
+ * <p>Please note that surface sharing is currently only enabled for outputs that use the
+ * {@link ImageFormat#PRIVATE} format. This includes surface sources like
+ * {@link android.view.SurfaceView}, {@link android.media.MediaRecorder},
+ * {@link android.graphics.SurfaceTexture} and {@link android.media.ImageReader}, configured using
+ * the aforementioned format.</p>
+ *
* @see CameraDevice#createCaptureSessionByOutputConfigurations
*
*/
@@ -123,7 +170,7 @@
* {@link OutputConfiguration#addSurface} should not exceed this value.</p>
*
*/
- private static final int MAX_SURFACES_COUNT = 2;
+ private static final int MAX_SURFACES_COUNT = 4;
/**
* Create a new {@link OutputConfiguration} instance with a {@link Surface},
@@ -280,7 +327,10 @@
* <p>For advanced use cases, a camera application may require more streams than the combination
* guaranteed by {@link CameraDevice#createCaptureSession}. In this case, more than one
* compatible surface can be attached to an OutputConfiguration so that they map to one
- * camera stream, and the outputs share memory buffers when possible. </p>
+ * camera stream, and the outputs share memory buffers when possible. Due to buffer sharing
+ * clients should be careful when adding surface outputs that modify their input data. If such
+ * case exists, camera clients should have an additional mechanism to synchronize read and write
+ * access between individual consumers.</p>
*
* <p>Two surfaces are compatible in the below cases:</p>
*
@@ -301,9 +351,9 @@
* CameraDevice#createCaptureSessionByOutputConfigurations}. Calling this function after {@link
* CameraDevice#createCaptureSessionByOutputConfigurations} has no effect.</p>
*
- * <p>Up to 2 surfaces can be shared for an OutputConfiguration. The supported surfaces for
- * sharing must be of type SurfaceTexture, SurfaceView, MediaRecorder, MediaCodec, or
- * implementation defined ImageReader.</p>
+ * <p>Up to {@link #getMaxSharedSurfaceCount} surfaces can be shared for an OutputConfiguration.
+ * The supported surfaces for sharing must be of type SurfaceTexture, SurfaceView,
+ * MediaRecorder, MediaCodec, or implementation defined ImageReader.</p>
*/
public void enableSurfaceSharing() {
mIsShared = true;
@@ -329,8 +379,10 @@
* <p> This function can be called before or after {@link
* CameraDevice#createCaptureSessionByOutputConfigurations}. If it's called after,
* the application must finalize the capture session with
- * {@link CameraCaptureSession#finalizeOutputConfigurations}.
- * </p>
+ * {@link CameraCaptureSession#finalizeOutputConfigurations}. It is possible to call this method
+ * after the output configurations have been finalized only in cases of enabled surface sharing
+ * see {@link #enableSurfaceSharing}. The modified output configuration must be updated with
+ * {@link CameraCaptureSession#updateOutputConfiguration}.</p>
*
* <p> If the OutputConfiguration was constructed with a deferred surface by {@link
* OutputConfiguration#OutputConfiguration(Size, Class)}, the added surface must be obtained
@@ -388,6 +440,31 @@
}
/**
+ * Remove a surface from this OutputConfiguration.
+ *
+ * <p> Surfaces added via calls to {@link #addSurface} can also be removed from the
+ * OutputConfiguration. The only notable exception is the surface associated with
+ * the OutputConfigration see {@link #getSurface} which was passed as part of the constructor
+ * or was added first in the deferred case
+ * {@link OutputConfiguration#OutputConfiguration(Size, Class)}.</p>
+ *
+ * @param surface The surface to be removed.
+ *
+ * @throws IllegalArgumentException If the surface is associated with this OutputConfiguration
+ * (see {@link #getSurface}) or the surface didn't get added
+ * with {@link #addSurface}.
+ */
+ public void removeSurface(@NonNull Surface surface) {
+ if (getSurface() == surface) {
+ throw new IllegalArgumentException(
+ "Cannot remove surface associated with this output configuration");
+ }
+ if (!mSurfaces.remove(surface)) {
+ throw new IllegalArgumentException("Surface is not part of this output configuration");
+ }
+ }
+
+ /**
* Create a new {@link OutputConfiguration} instance with another {@link OutputConfiguration}
* instance.
*
@@ -447,6 +524,17 @@
}
/**
+ * Get the maximum supported shared {@link Surface} count.
+ *
+ * @return the maximum number of surfaces that can be added per each OutputConfiguration.
+ *
+ * @see #enableSurfaceSharing
+ */
+ public static int getMaxSharedSurfaceCount() {
+ return MAX_SURFACES_COUNT;
+ }
+
+ /**
* Get the {@link Surface} associated with this {@link OutputConfiguration}.
*
* If more than one surface is associated with this {@link OutputConfiguration}, return the
diff --git a/core/java/android/hardware/usb/AccessoryFilter.java b/core/java/android/hardware/usb/AccessoryFilter.java
new file mode 100644
index 0000000..d9b7c5b
--- /dev/null
+++ b/core/java/android/hardware/usb/AccessoryFilter.java
@@ -0,0 +1,145 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.usb;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
+import java.io.IOException;
+import java.util.Objects;
+
+/**
+ * This class is used to describe a USB accessory.
+ * When used in HashMaps all values must be specified,
+ * but wildcards can be used for any of the fields in
+ * the package meta-data.
+ *
+ * @hide
+ */
+public class AccessoryFilter {
+ // USB accessory manufacturer (or null for unspecified)
+ public final String mManufacturer;
+ // USB accessory model (or null for unspecified)
+ public final String mModel;
+ // USB accessory version (or null for unspecified)
+ public final String mVersion;
+
+ public AccessoryFilter(String manufacturer, String model, String version) {
+ mManufacturer = manufacturer;
+ mModel = model;
+ mVersion = version;
+ }
+
+ public AccessoryFilter(UsbAccessory accessory) {
+ mManufacturer = accessory.getManufacturer();
+ mModel = accessory.getModel();
+ mVersion = accessory.getVersion();
+ }
+
+ public static AccessoryFilter read(XmlPullParser parser)
+ throws XmlPullParserException, IOException {
+ String manufacturer = null;
+ String model = null;
+ String version = null;
+
+ int count = parser.getAttributeCount();
+ for (int i = 0; i < count; i++) {
+ String name = parser.getAttributeName(i);
+ String value = parser.getAttributeValue(i);
+
+ if ("manufacturer".equals(name)) {
+ manufacturer = value;
+ } else if ("model".equals(name)) {
+ model = value;
+ } else if ("version".equals(name)) {
+ version = value;
+ }
+ }
+ return new AccessoryFilter(manufacturer, model, version);
+ }
+
+ public void write(XmlSerializer serializer)throws IOException {
+ serializer.startTag(null, "usb-accessory");
+ if (mManufacturer != null) {
+ serializer.attribute(null, "manufacturer", mManufacturer);
+ }
+ if (mModel != null) {
+ serializer.attribute(null, "model", mModel);
+ }
+ if (mVersion != null) {
+ serializer.attribute(null, "version", mVersion);
+ }
+ serializer.endTag(null, "usb-accessory");
+ }
+
+ public boolean matches(UsbAccessory acc) {
+ if (mManufacturer != null && !acc.getManufacturer().equals(mManufacturer)) return false;
+ if (mModel != null && !acc.getModel().equals(mModel)) return false;
+ return !(mVersion != null && !acc.getVersion().equals(mVersion));
+ }
+
+ /**
+ * Is the accessories described {@code accessory} covered by this filter?
+ *
+ * @param accessory A filter describing the accessory
+ *
+ * @return {@code true} iff this the filter covers the accessory
+ */
+ public boolean contains(AccessoryFilter accessory) {
+ if (mManufacturer != null && !Objects.equals(accessory.mManufacturer, mManufacturer)) {
+ return false;
+ }
+ if (mModel != null && !Objects.equals(accessory.mModel, mModel)) return false;
+ return !(mVersion != null && !Objects.equals(accessory.mVersion, mVersion));
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ // can't compare if we have wildcard strings
+ if (mManufacturer == null || mModel == null || mVersion == null) {
+ return false;
+ }
+ if (obj instanceof AccessoryFilter) {
+ AccessoryFilter filter = (AccessoryFilter)obj;
+ return (mManufacturer.equals(filter.mManufacturer) &&
+ mModel.equals(filter.mModel) &&
+ mVersion.equals(filter.mVersion));
+ }
+ if (obj instanceof UsbAccessory) {
+ UsbAccessory accessory = (UsbAccessory)obj;
+ return (mManufacturer.equals(accessory.getManufacturer()) &&
+ mModel.equals(accessory.getModel()) &&
+ mVersion.equals(accessory.getVersion()));
+ }
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ return ((mManufacturer == null ? 0 : mManufacturer.hashCode()) ^
+ (mModel == null ? 0 : mModel.hashCode()) ^
+ (mVersion == null ? 0 : mVersion.hashCode()));
+ }
+
+ @Override
+ public String toString() {
+ return "AccessoryFilter[mManufacturer=\"" + mManufacturer +
+ "\", mModel=\"" + mModel +
+ "\", mVersion=\"" + mVersion + "\"]";
+ }
+}
diff --git a/core/java/android/hardware/usb/DeviceFilter.java b/core/java/android/hardware/usb/DeviceFilter.java
new file mode 100644
index 0000000..439c629
--- /dev/null
+++ b/core/java/android/hardware/usb/DeviceFilter.java
@@ -0,0 +1,313 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.usb;
+
+import android.util.Slog;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
+import java.io.IOException;
+import java.util.Objects;
+
+/**
+ * This class is used to describe a USB device.
+ * When used in HashMaps all values must be specified,
+ * but wildcards can be used for any of the fields in
+ * the package meta-data.
+ *
+ * @hide
+ */
+public class DeviceFilter {
+ private static final String TAG = DeviceFilter.class.getSimpleName();
+
+ // USB Vendor ID (or -1 for unspecified)
+ public final int mVendorId;
+ // USB Product ID (or -1 for unspecified)
+ public final int mProductId;
+ // USB device or interface class (or -1 for unspecified)
+ public final int mClass;
+ // USB device subclass (or -1 for unspecified)
+ public final int mSubclass;
+ // USB device protocol (or -1 for unspecified)
+ public final int mProtocol;
+ // USB device manufacturer name string (or null for unspecified)
+ public final String mManufacturerName;
+ // USB device product name string (or null for unspecified)
+ public final String mProductName;
+ // USB device serial number string (or null for unspecified)
+ public final String mSerialNumber;
+
+ public DeviceFilter(int vid, int pid, int clasz, int subclass, int protocol,
+ String manufacturer, String product, String serialnum) {
+ mVendorId = vid;
+ mProductId = pid;
+ mClass = clasz;
+ mSubclass = subclass;
+ mProtocol = protocol;
+ mManufacturerName = manufacturer;
+ mProductName = product;
+ mSerialNumber = serialnum;
+ }
+
+ public DeviceFilter(UsbDevice device) {
+ mVendorId = device.getVendorId();
+ mProductId = device.getProductId();
+ mClass = device.getDeviceClass();
+ mSubclass = device.getDeviceSubclass();
+ mProtocol = device.getDeviceProtocol();
+ mManufacturerName = device.getManufacturerName();
+ mProductName = device.getProductName();
+ mSerialNumber = device.getSerialNumber();
+ }
+
+ public static DeviceFilter read(XmlPullParser parser)
+ throws XmlPullParserException, IOException {
+ int vendorId = -1;
+ int productId = -1;
+ int deviceClass = -1;
+ int deviceSubclass = -1;
+ int deviceProtocol = -1;
+ String manufacturerName = null;
+ String productName = null;
+ String serialNumber = null;
+
+ int count = parser.getAttributeCount();
+ for (int i = 0; i < count; i++) {
+ String name = parser.getAttributeName(i);
+ String value = parser.getAttributeValue(i);
+ // Attribute values are ints or strings
+ if ("manufacturer-name".equals(name)) {
+ manufacturerName = value;
+ } else if ("product-name".equals(name)) {
+ productName = value;
+ } else if ("serial-number".equals(name)) {
+ serialNumber = value;
+ } else {
+ int intValue;
+ int radix = 10;
+ if (value != null && value.length() > 2 && value.charAt(0) == '0' &&
+ (value.charAt(1) == 'x' || value.charAt(1) == 'X')) {
+ // allow hex values starting with 0x or 0X
+ radix = 16;
+ value = value.substring(2);
+ }
+ try {
+ intValue = Integer.parseInt(value, radix);
+ } catch (NumberFormatException e) {
+ Slog.e(TAG, "invalid number for field " + name, e);
+ continue;
+ }
+ if ("vendor-id".equals(name)) {
+ vendorId = intValue;
+ } else if ("product-id".equals(name)) {
+ productId = intValue;
+ } else if ("class".equals(name)) {
+ deviceClass = intValue;
+ } else if ("subclass".equals(name)) {
+ deviceSubclass = intValue;
+ } else if ("protocol".equals(name)) {
+ deviceProtocol = intValue;
+ }
+ }
+ }
+ return new DeviceFilter(vendorId, productId,
+ deviceClass, deviceSubclass, deviceProtocol,
+ manufacturerName, productName, serialNumber);
+ }
+
+ public void write(XmlSerializer serializer) throws IOException {
+ serializer.startTag(null, "usb-device");
+ if (mVendorId != -1) {
+ serializer.attribute(null, "vendor-id", Integer.toString(mVendorId));
+ }
+ if (mProductId != -1) {
+ serializer.attribute(null, "product-id", Integer.toString(mProductId));
+ }
+ if (mClass != -1) {
+ serializer.attribute(null, "class", Integer.toString(mClass));
+ }
+ if (mSubclass != -1) {
+ serializer.attribute(null, "subclass", Integer.toString(mSubclass));
+ }
+ if (mProtocol != -1) {
+ serializer.attribute(null, "protocol", Integer.toString(mProtocol));
+ }
+ if (mManufacturerName != null) {
+ serializer.attribute(null, "manufacturer-name", mManufacturerName);
+ }
+ if (mProductName != null) {
+ serializer.attribute(null, "product-name", mProductName);
+ }
+ if (mSerialNumber != null) {
+ serializer.attribute(null, "serial-number", mSerialNumber);
+ }
+ serializer.endTag(null, "usb-device");
+ }
+
+ private boolean matches(int clasz, int subclass, int protocol) {
+ return ((mClass == -1 || clasz == mClass) &&
+ (mSubclass == -1 || subclass == mSubclass) &&
+ (mProtocol == -1 || protocol == mProtocol));
+ }
+
+ public boolean matches(UsbDevice device) {
+ if (mVendorId != -1 && device.getVendorId() != mVendorId) return false;
+ if (mProductId != -1 && device.getProductId() != mProductId) return false;
+ if (mManufacturerName != null && device.getManufacturerName() == null) return false;
+ if (mProductName != null && device.getProductName() == null) return false;
+ if (mSerialNumber != null && device.getSerialNumber() == null) return false;
+ if (mManufacturerName != null && device.getManufacturerName() != null &&
+ !mManufacturerName.equals(device.getManufacturerName())) return false;
+ if (mProductName != null && device.getProductName() != null &&
+ !mProductName.equals(device.getProductName())) return false;
+ if (mSerialNumber != null && device.getSerialNumber() != null &&
+ !mSerialNumber.equals(device.getSerialNumber())) return false;
+
+ // check device class/subclass/protocol
+ if (matches(device.getDeviceClass(), device.getDeviceSubclass(),
+ device.getDeviceProtocol())) return true;
+
+ // if device doesn't match, check the interfaces
+ int count = device.getInterfaceCount();
+ for (int i = 0; i < count; i++) {
+ UsbInterface intf = device.getInterface(i);
+ if (matches(intf.getInterfaceClass(), intf.getInterfaceSubclass(),
+ intf.getInterfaceProtocol())) return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * If the device described by {@code device} covered by this filter?
+ *
+ * @param device The device
+ *
+ * @return {@code true} iff this filter covers the {@code device}
+ */
+ public boolean contains(DeviceFilter device) {
+ // -1 and null means "match anything"
+
+ if (mVendorId != -1 && device.mVendorId != mVendorId) return false;
+ if (mProductId != -1 && device.mProductId != mProductId) return false;
+ if (mManufacturerName != null && !Objects.equals(mManufacturerName,
+ device.mManufacturerName)) {
+ return false;
+ }
+ if (mProductName != null && !Objects.equals(mProductName, device.mProductName)) {
+ return false;
+ }
+ if (mSerialNumber != null
+ && !Objects.equals(mSerialNumber, device.mSerialNumber)) {
+ return false;
+ }
+
+ // check device class/subclass/protocol
+ return matches(device.mClass, device.mSubclass, device.mProtocol);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ // can't compare if we have wildcard strings
+ if (mVendorId == -1 || mProductId == -1 ||
+ mClass == -1 || mSubclass == -1 || mProtocol == -1) {
+ return false;
+ }
+ if (obj instanceof DeviceFilter) {
+ DeviceFilter filter = (DeviceFilter)obj;
+
+ if (filter.mVendorId != mVendorId ||
+ filter.mProductId != mProductId ||
+ filter.mClass != mClass ||
+ filter.mSubclass != mSubclass ||
+ filter.mProtocol != mProtocol) {
+ return(false);
+ }
+ if ((filter.mManufacturerName != null &&
+ mManufacturerName == null) ||
+ (filter.mManufacturerName == null &&
+ mManufacturerName != null) ||
+ (filter.mProductName != null &&
+ mProductName == null) ||
+ (filter.mProductName == null &&
+ mProductName != null) ||
+ (filter.mSerialNumber != null &&
+ mSerialNumber == null) ||
+ (filter.mSerialNumber == null &&
+ mSerialNumber != null)) {
+ return(false);
+ }
+ if ((filter.mManufacturerName != null &&
+ mManufacturerName != null &&
+ !mManufacturerName.equals(filter.mManufacturerName)) ||
+ (filter.mProductName != null &&
+ mProductName != null &&
+ !mProductName.equals(filter.mProductName)) ||
+ (filter.mSerialNumber != null &&
+ mSerialNumber != null &&
+ !mSerialNumber.equals(filter.mSerialNumber))) {
+ return false;
+ }
+ return true;
+ }
+ if (obj instanceof UsbDevice) {
+ UsbDevice device = (UsbDevice)obj;
+ if (device.getVendorId() != mVendorId ||
+ device.getProductId() != mProductId ||
+ device.getDeviceClass() != mClass ||
+ device.getDeviceSubclass() != mSubclass ||
+ device.getDeviceProtocol() != mProtocol) {
+ return(false);
+ }
+ if ((mManufacturerName != null && device.getManufacturerName() == null) ||
+ (mManufacturerName == null && device.getManufacturerName() != null) ||
+ (mProductName != null && device.getProductName() == null) ||
+ (mProductName == null && device.getProductName() != null) ||
+ (mSerialNumber != null && device.getSerialNumber() == null) ||
+ (mSerialNumber == null && device.getSerialNumber() != null)) {
+ return(false);
+ }
+ if ((device.getManufacturerName() != null &&
+ !mManufacturerName.equals(device.getManufacturerName())) ||
+ (device.getProductName() != null &&
+ !mProductName.equals(device.getProductName())) ||
+ (device.getSerialNumber() != null &&
+ !mSerialNumber.equals(device.getSerialNumber()))) {
+ return false;
+ }
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ return (((mVendorId << 16) | mProductId) ^
+ ((mClass << 16) | (mSubclass << 8) | mProtocol));
+ }
+
+ @Override
+ public String toString() {
+ return "DeviceFilter[mVendorId=" + mVendorId + ",mProductId=" + mProductId +
+ ",mClass=" + mClass + ",mSubclass=" + mSubclass +
+ ",mProtocol=" + mProtocol + ",mManufacturerName=" + mManufacturerName +
+ ",mProductName=" + mProductName + ",mSerialNumber=" + mSerialNumber +
+ "]";
+ }
+}
diff --git a/core/java/android/net/IIpConnectivityMetrics.aidl b/core/java/android/net/IIpConnectivityMetrics.aidl
index 6f07b31..aeaf09d 100644
--- a/core/java/android/net/IIpConnectivityMetrics.aidl
+++ b/core/java/android/net/IIpConnectivityMetrics.aidl
@@ -30,11 +30,11 @@
int logEvent(in ConnectivityMetricsEvent event);
/**
- * At most one callback can be registered (by DevicePolicyManager).
+ * Callback can be registered by DevicePolicyManager or NetworkWatchlistService only.
* @return status {@code true} if registering/unregistering of the callback was successful,
* {@code false} otherwise (might happen if IIpConnectivityMetrics is not available,
* if it happens make sure you call it when the service is up in the caller)
*/
- boolean registerNetdEventCallback(in INetdEventCallback callback);
- boolean unregisterNetdEventCallback();
+ boolean addNetdEventCallback(in int callerType, in INetdEventCallback callback);
+ boolean removeNetdEventCallback(in int callerType);
}
diff --git a/core/java/android/net/INetdEventCallback.aidl b/core/java/android/net/INetdEventCallback.aidl
index 49436be..1fd9423 100644
--- a/core/java/android/net/INetdEventCallback.aidl
+++ b/core/java/android/net/INetdEventCallback.aidl
@@ -19,6 +19,10 @@
/** {@hide} */
oneway interface INetdEventCallback {
+ // Possible addNetdEventCallback callers.
+ const int CALLBACK_CALLER_DEVICE_POLICY = 0;
+ const int CALLBACK_CALLER_NETWORK_WATCHLIST = 1;
+
/**
* Reports a single DNS lookup function call.
* This method must not block or perform long-running operations.
diff --git a/core/java/android/net/IpSecAlgorithm.java b/core/java/android/net/IpSecAlgorithm.java
index 16b1452..64f8f39 100644
--- a/core/java/android/net/IpSecAlgorithm.java
+++ b/core/java/android/net/IpSecAlgorithm.java
@@ -78,7 +78,11 @@
/**
* AES-GCM Authentication/Integrity + Encryption/Ciphering Algorithm.
*
- * <p>Valid lengths for this key are {128, 192, 256}.
+ * <p>Valid lengths for keying material are {160, 224, 288}.
+ *
+ * <p>As per RFC4106 (Section 8.1), keying material consists of a 128, 192, or 256 bit AES key
+ * followed by a 32-bit salt. RFC compliance requires that the salt must be unique per
+ * invocation with the same key.
*
* <p>Valid ICV (truncation) lengths are {64, 96, 128}.
*/
diff --git a/core/java/android/net/IpSecManager.java b/core/java/android/net/IpSecManager.java
index d7b3256..eccd5f4 100644
--- a/core/java/android/net/IpSecManager.java
+++ b/core/java/android/net/IpSecManager.java
@@ -136,7 +136,7 @@
}
@Override
- protected void finalize() {
+ protected void finalize() throws Throwable {
if (mCloseGuard != null) {
mCloseGuard.warnIfOpen();
}
diff --git a/core/java/android/net/LocalSocketImpl.java b/core/java/android/net/LocalSocketImpl.java
index 05c8afb..6e4a231 100644
--- a/core/java/android/net/LocalSocketImpl.java
+++ b/core/java/android/net/LocalSocketImpl.java
@@ -16,18 +16,18 @@
package android.net;
-import java.io.IOException;
-import java.io.OutputStream;
-import java.io.InputStream;
-import java.io.FileDescriptor;
-import java.net.SocketOptions;
-
import android.system.ErrnoException;
+import android.system.Int32Ref;
import android.system.Os;
import android.system.OsConstants;
import android.system.StructLinger;
import android.system.StructTimeval;
-import android.util.MutableInt;
+
+import java.io.FileDescriptor;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.SocketOptions;
/**
* Socket implementation used for android.net.LocalSocket and
@@ -62,7 +62,7 @@
FileDescriptor myFd = fd;
if (myFd == null) throw new IOException("socket closed");
- MutableInt avail = new MutableInt(0);
+ Int32Ref avail = new Int32Ref(0);
try {
Os.ioctlInt(myFd, OsConstants.FIONREAD, avail);
} catch (ErrnoException e) {
@@ -167,7 +167,7 @@
if (myFd == null) throw new IOException("socket closed");
// Loop until the output buffer is empty.
- MutableInt pending = new MutableInt(0);
+ Int32Ref pending = new Int32Ref(0);
while (true) {
try {
// See linux/net/unix/af_unix.c
diff --git a/core/java/android/net/MacAddress.java b/core/java/android/net/MacAddress.java
index e76d17d..f6a69ba 100644
--- a/core/java/android/net/MacAddress.java
+++ b/core/java/android/net/MacAddress.java
@@ -16,29 +16,128 @@
package android.net;
-import com.android.internal.annotations.VisibleForTesting;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.util.BitUtils;
import java.util.Arrays;
+import java.util.Random;
+import java.util.StringJoiner;
/**
+ * Represents a mac address.
+ *
* @hide
*/
-public final class MacAddress {
-
- // TODO: add isLocallyAssigned().
- // TODO: add getRandomAddress() factory method.
+public final class MacAddress implements Parcelable {
private static final int ETHER_ADDR_LEN = 6;
- private static final byte FF = (byte) 0xff;
- @VisibleForTesting
- static final byte[] ETHER_ADDR_BROADCAST = { FF, FF, FF, FF, FF, FF };
+ private static final byte[] ETHER_ADDR_BROADCAST = addr(0xff, 0xff, 0xff, 0xff, 0xff, 0xff);
+ /** The broadcast mac address. */
+ public static final MacAddress BROADCAST_ADDRESS = new MacAddress(ETHER_ADDR_BROADCAST);
+
+ /** The zero mac address. */
+ public static final MacAddress ALL_ZEROS_ADDRESS = new MacAddress(0);
+
+ /** Represents categories of mac addresses. */
public enum MacAddressType {
UNICAST,
MULTICAST,
BROADCAST;
}
+ private static final long VALID_LONG_MASK = BROADCAST_ADDRESS.mAddr;
+ private static final long LOCALLY_ASSIGNED_MASK = new MacAddress("2:0:0:0:0:0").mAddr;
+ private static final long MULTICAST_MASK = new MacAddress("1:0:0:0:0:0").mAddr;
+ private static final long OUI_MASK = new MacAddress("ff:ff:ff:0:0:0").mAddr;
+ private static final long NIC_MASK = new MacAddress("0:0:0:ff:ff:ff").mAddr;
+ private static final MacAddress BASE_ANDROID_MAC = new MacAddress("da:a1:19:0:0:0");
+
+ // Internal representation of the mac address as a single 8 byte long.
+ // The encoding scheme sets the two most significant bytes to 0. The 6 bytes of the
+ // mac address are encoded in the 6 least significant bytes of the long, where the first
+ // byte of the array is mapped to the 3rd highest logical byte of the long, the second
+ // byte of the array is mapped to the 4th highest logical byte of the long, and so on.
+ private final long mAddr;
+
+ private MacAddress(long addr) {
+ mAddr = addr;
+ }
+
+ /** Creates a MacAddress for the given byte representation. */
+ public MacAddress(byte[] addr) {
+ this(longAddrFromByteAddr(addr));
+ }
+
+ /** Creates a MacAddress for the given string representation. */
+ public MacAddress(String addr) {
+ this(longAddrFromByteAddr(byteAddrFromStringAddr(addr)));
+ }
+
+ /** Returns the MacAddressType of this MacAddress. */
+ public MacAddressType addressType() {
+ if (equals(BROADCAST_ADDRESS)) {
+ return MacAddressType.BROADCAST;
+ }
+ if (isMulticastAddress()) {
+ return MacAddressType.MULTICAST;
+ }
+ return MacAddressType.UNICAST;
+ }
+
+ /** Returns true if this MacAddress corresponds to a multicast address. */
+ public boolean isMulticastAddress() {
+ return (mAddr & MULTICAST_MASK) != 0;
+ }
+
+ /** Returns true if this MacAddress corresponds to a locally assigned address. */
+ public boolean isLocallyAssigned() {
+ return (mAddr & LOCALLY_ASSIGNED_MASK) != 0;
+ }
+
+ /** Returns a byte array representation of this MacAddress. */
+ public byte[] toByteArray() {
+ return byteAddrFromLongAddr(mAddr);
+ }
+
+ @Override
+ public String toString() {
+ return stringAddrFromByteAddr(byteAddrFromLongAddr(mAddr));
+ }
+
+ @Override
+ public int hashCode() {
+ return (int) ((mAddr >> 32) ^ mAddr);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ return (o instanceof MacAddress) && ((MacAddress) o).mAddr == mAddr;
+ }
+
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeLong(mAddr);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ public static final Parcelable.Creator<MacAddress> CREATOR =
+ new Parcelable.Creator<MacAddress>() {
+ public MacAddress createFromParcel(Parcel in) {
+ return new MacAddress(in.readLong());
+ }
+
+ public MacAddress[] newArray(int size) {
+ return new MacAddress[size];
+ }
+ };
+
/** Return true if the given byte array is not null and has the length of a mac address. */
public static boolean isMacAddress(byte[] addr) {
return addr != null && addr.length == ETHER_ADDR_LEN;
@@ -46,17 +145,130 @@
/**
* Return the MacAddressType of the mac address represented by the given byte array,
- * or null if the given byte array does not represent an mac address. */
+ * or null if the given byte array does not represent an mac address.
+ */
public static MacAddressType macAddressType(byte[] addr) {
if (!isMacAddress(addr)) {
return null;
}
- if (Arrays.equals(addr, ETHER_ADDR_BROADCAST)) {
- return MacAddressType.BROADCAST;
+ return new MacAddress(addr).addressType();
+ }
+
+ /** DOCME */
+ public static byte[] byteAddrFromStringAddr(String addr) {
+ if (addr == null) {
+ throw new IllegalArgumentException("cannot convert the null String");
}
- if ((addr[0] & 0x01) == 1) {
- return MacAddressType.MULTICAST;
+ String[] parts = addr.split(":");
+ if (parts.length != ETHER_ADDR_LEN) {
+ throw new IllegalArgumentException(addr + " was not a valid MAC address");
}
- return MacAddressType.UNICAST;
+ byte[] bytes = new byte[ETHER_ADDR_LEN];
+ for (int i = 0; i < ETHER_ADDR_LEN; i++) {
+ int x = Integer.valueOf(parts[i], 16);
+ if (x < 0 || 0xff < x) {
+ throw new IllegalArgumentException(addr + "was not a valid MAC address");
+ }
+ bytes[i] = (byte) x;
+ }
+ return bytes;
+ }
+
+ /** DOCME */
+ public static String stringAddrFromByteAddr(byte[] addr) {
+ if (!isMacAddress(addr)) {
+ return null;
+ }
+ StringJoiner j = new StringJoiner(":");
+ for (byte b : addr) {
+ j.add(Integer.toHexString(BitUtils.uint8(b)));
+ }
+ return j.toString();
+ }
+
+ /** @hide */
+ public static byte[] byteAddrFromLongAddr(long addr) {
+ byte[] bytes = new byte[ETHER_ADDR_LEN];
+ int index = ETHER_ADDR_LEN;
+ while (index-- > 0) {
+ bytes[index] = (byte) addr;
+ addr = addr >> 8;
+ }
+ return bytes;
+ }
+
+ /** @hide */
+ public static long longAddrFromByteAddr(byte[] addr) {
+ if (!isMacAddress(addr)) {
+ throw new IllegalArgumentException(
+ Arrays.toString(addr) + " was not a valid MAC address");
+ }
+ long longAddr = 0;
+ for (byte b : addr) {
+ longAddr = (longAddr << 8) + BitUtils.uint8(b);
+ }
+ return longAddr;
+ }
+
+ /** @hide */
+ public static long longAddrFromStringAddr(String addr) {
+ if (addr == null) {
+ throw new IllegalArgumentException("cannot convert the null String");
+ }
+ String[] parts = addr.split(":");
+ if (parts.length != ETHER_ADDR_LEN) {
+ throw new IllegalArgumentException(addr + " was not a valid MAC address");
+ }
+ long longAddr = 0;
+ int index = ETHER_ADDR_LEN;
+ while (index-- > 0) {
+ int x = Integer.valueOf(parts[index], 16);
+ if (x < 0 || 0xff < x) {
+ throw new IllegalArgumentException(addr + "was not a valid MAC address");
+ }
+ longAddr = x + (longAddr << 8);
+ }
+ return longAddr;
+ }
+
+ /** @hide */
+ public static String stringAddrFromLongAddr(long addr) {
+ addr = Long.reverseBytes(addr) >> 16;
+ StringJoiner j = new StringJoiner(":");
+ for (int i = 0; i < ETHER_ADDR_LEN; i++) {
+ j.add(Integer.toHexString((byte) addr));
+ addr = addr >> 8;
+ }
+ return j.toString();
+ }
+
+ /**
+ * Returns a randomely generated mac address with the Android OUI value "DA-A1-19".
+ * The locally assigned bit is always set to 1.
+ */
+ public static MacAddress getRandomAddress() {
+ return getRandomAddress(BASE_ANDROID_MAC, new Random());
+ }
+
+ /**
+ * Returns a randomely generated mac address using the given Random object and the same
+ * OUI values as the given MacAddress. The locally assigned bit is always set to 1.
+ */
+ public static MacAddress getRandomAddress(MacAddress base, Random r) {
+ long longAddr = (base.mAddr & OUI_MASK) | (NIC_MASK & r.nextLong()) | LOCALLY_ASSIGNED_MASK;
+ return new MacAddress(longAddr);
+ }
+
+ // Convenience function for working around the lack of byte literals.
+ private static byte[] addr(int... in) {
+ if (in.length != ETHER_ADDR_LEN) {
+ throw new IllegalArgumentException(Arrays.toString(in)
+ + " was not an array with length equal to " + ETHER_ADDR_LEN);
+ }
+ byte[] out = new byte[ETHER_ADDR_LEN];
+ for (int i = 0; i < ETHER_ADDR_LEN; i++) {
+ out[i] = (byte) in[i];
+ }
+ return out;
}
}
diff --git a/core/java/android/net/NetworkWatchlistManager.java b/core/java/android/net/NetworkWatchlistManager.java
new file mode 100644
index 0000000..42e43c8
--- /dev/null
+++ b/core/java/android/net/NetworkWatchlistManager.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net;
+
+import android.annotation.SystemService;
+import android.content.Context;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.util.Log;
+
+import com.android.internal.net.INetworkWatchlistManager;
+import com.android.internal.util.Preconditions;
+
+/**
+ * Class that manage network watchlist in system.
+ * @hide
+ */
+@SystemService(Context.NETWORK_WATCHLIST_SERVICE)
+public class NetworkWatchlistManager {
+
+ private static final String TAG = "NetworkWatchlistManager";
+ private static final String SHARED_MEMORY_TAG = "NETWORK_WATCHLIST_SHARED_MEMORY";
+
+ private final Context mContext;
+ private final INetworkWatchlistManager mNetworkWatchlistManager;
+
+ /**
+ * @hide
+ */
+ public NetworkWatchlistManager(Context context, INetworkWatchlistManager manager) {
+ mContext = context;
+ mNetworkWatchlistManager = manager;
+ }
+
+ /**
+ * @hide
+ */
+ public NetworkWatchlistManager(Context context) {
+ mContext = Preconditions.checkNotNull(context, "missing context");
+ mNetworkWatchlistManager = (INetworkWatchlistManager)
+ INetworkWatchlistManager.Stub.asInterface(
+ ServiceManager.getService(Context.NETWORK_WATCHLIST_SERVICE));
+ }
+
+ /**
+ * Report network watchlist records if necessary.
+ *
+ * Watchlist report process will run summarize records into a single report, then the
+ * report will be processed by differential privacy framework and store it on disk.
+ *
+ * @hide
+ */
+ public void reportWatchlistIfNecessary() {
+ try {
+ mNetworkWatchlistManager.reportWatchlistIfNecessary();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Cannot report records", e);
+ e.rethrowFromSystemServer();
+ }
+ }
+}
diff --git a/core/java/android/net/SSLCertificateSocketFactory.java b/core/java/android/net/SSLCertificateSocketFactory.java
index 0b1569c..4817813 100644
--- a/core/java/android/net/SSLCertificateSocketFactory.java
+++ b/core/java/android/net/SSLCertificateSocketFactory.java
@@ -63,7 +63,12 @@
* This implementation does check the server's certificate hostname, but only
* for createSocket variants that specify a hostname. When using methods that
* use {@link InetAddress} or which return an unconnected socket, you MUST
- * verify the server's identity yourself to ensure a secure connection.</p>
+ * verify the server's identity yourself to ensure a secure connection.
+ *
+ * Refer to
+ * <a href="https://developer.android.com/training/articles/security-gms-provider.html">
+ * Updating Your Security Provider to Protect Against SSL Exploits</a>
+ * for further information.</p>
*
* <p>One way to verify the server's identity is to use
* {@link HttpsURLConnection#getDefaultHostnameVerifier()} to get a
diff --git a/core/java/android/net/metrics/NetworkEvent.java b/core/java/android/net/metrics/NetworkEvent.java
index 4df3bf0..1999e78 100644
--- a/core/java/android/net/metrics/NetworkEvent.java
+++ b/core/java/android/net/metrics/NetworkEvent.java
@@ -60,29 +60,25 @@
@Retention(RetentionPolicy.SOURCE)
public @interface EventType {}
- public final int netId;
public final @EventType int eventType;
public final long durationMs;
- public NetworkEvent(int netId, @EventType int eventType, long durationMs) {
- this.netId = netId;
+ public NetworkEvent(@EventType int eventType, long durationMs) {
this.eventType = eventType;
this.durationMs = durationMs;
}
- public NetworkEvent(int netId, @EventType int eventType) {
- this(netId, eventType, 0);
+ public NetworkEvent(@EventType int eventType) {
+ this(eventType, 0);
}
private NetworkEvent(Parcel in) {
- netId = in.readInt();
eventType = in.readInt();
durationMs = in.readLong();
}
@Override
public void writeToParcel(Parcel out, int flags) {
- out.writeInt(netId);
out.writeInt(eventType);
out.writeLong(durationMs);
}
@@ -105,8 +101,8 @@
@Override
public String toString() {
- return String.format("NetworkEvent(%d, %s, %dms)",
- netId, Decoder.constants.get(eventType), durationMs);
+ return String.format("NetworkEvent(%s, %dms)",
+ Decoder.constants.get(eventType), durationMs);
}
final static class Decoder {
diff --git a/core/java/android/net/metrics/WakeupEvent.java b/core/java/android/net/metrics/WakeupEvent.java
index cbf3fc8..8f1a5c4 100644
--- a/core/java/android/net/metrics/WakeupEvent.java
+++ b/core/java/android/net/metrics/WakeupEvent.java
@@ -16,6 +16,10 @@
package android.net.metrics;
+import android.net.MacAddress;
+
+import java.util.StringJoiner;
+
/**
* An event logged when NFLOG notifies userspace of a wakeup packet for
* watched interfaces.
@@ -23,12 +27,35 @@
*/
public class WakeupEvent {
public String iface;
- public long timestampMs;
public int uid;
+ public int ethertype;
+ public byte[] dstHwAddr;
+ public String srcIp;
+ public String dstIp;
+ public int ipNextHeader;
+ public int srcPort;
+ public int dstPort;
+ public long timestampMs;
@Override
public String toString() {
- return String.format("WakeupEvent(%tT.%tL, %s, uid: %d)",
- timestampMs, timestampMs, iface, uid);
+ StringJoiner j = new StringJoiner(", ", "WakeupEvent(", ")");
+ j.add(String.format("%tT.%tL", timestampMs, timestampMs));
+ j.add(iface);
+ j.add("uid: " + Integer.toString(uid));
+ j.add("eth=0x" + Integer.toHexString(ethertype));
+ j.add("dstHw=" + MacAddress.stringAddrFromByteAddr(dstHwAddr));
+ if (ipNextHeader > 0) {
+ j.add("ipNxtHdr=" + ipNextHeader);
+ j.add("srcIp=" + srcIp);
+ j.add("dstIp=" + dstIp);
+ if (srcPort > -1) {
+ j.add("srcPort=" + srcPort);
+ }
+ if (dstPort > -1) {
+ j.add("dstPort=" + dstPort);
+ }
+ }
+ return j.toString();
}
}
diff --git a/core/java/android/net/metrics/WakeupStats.java b/core/java/android/net/metrics/WakeupStats.java
index 97e83f9..1ba9777 100644
--- a/core/java/android/net/metrics/WakeupStats.java
+++ b/core/java/android/net/metrics/WakeupStats.java
@@ -16,8 +16,12 @@
package android.net.metrics;
+import android.net.MacAddress;
import android.os.Process;
import android.os.SystemClock;
+import android.util.SparseIntArray;
+
+import java.util.StringJoiner;
/**
* An event logged per interface and that aggregates WakeupEvents for that interface.
@@ -38,6 +42,13 @@
public long noUidWakeups = 0;
public long durationSec = 0;
+ public long l2UnicastCount = 0;
+ public long l2MulticastCount = 0;
+ public long l2BroadcastCount = 0;
+
+ public final SparseIntArray ethertypes = new SparseIntArray();
+ public final SparseIntArray ipNextHeaders = new SparseIntArray();
+
public WakeupStats(String iface) {
this.iface = iface;
}
@@ -68,20 +79,56 @@
}
break;
}
+
+ switch (MacAddress.macAddressType(ev.dstHwAddr)) {
+ case UNICAST:
+ l2UnicastCount++;
+ break;
+ case MULTICAST:
+ l2MulticastCount++;
+ break;
+ case BROADCAST:
+ l2BroadcastCount++;
+ break;
+ default:
+ break;
+ }
+
+ increment(ethertypes, ev.ethertype);
+ if (ev.ipNextHeader >= 0) {
+ increment(ipNextHeaders, ev.ipNextHeader);
+ }
}
@Override
public String toString() {
updateDuration();
- return new StringBuilder()
- .append("WakeupStats(").append(iface)
- .append(", total: ").append(totalWakeups)
- .append(", root: ").append(rootWakeups)
- .append(", system: ").append(systemWakeups)
- .append(", apps: ").append(applicationWakeups)
- .append(", non-apps: ").append(nonApplicationWakeups)
- .append(", no uid: ").append(noUidWakeups)
- .append(", ").append(durationSec).append("s)")
- .toString();
+ StringJoiner j = new StringJoiner(", ", "WakeupStats(", ")");
+ j.add(iface);
+ j.add("" + durationSec + "s");
+ j.add("total: " + totalWakeups);
+ j.add("root: " + rootWakeups);
+ j.add("system: " + systemWakeups);
+ j.add("apps: " + applicationWakeups);
+ j.add("non-apps: " + nonApplicationWakeups);
+ j.add("no uid: " + noUidWakeups);
+ j.add(String.format("l2 unicast/multicast/broadcast: %d/%d/%d",
+ l2UnicastCount, l2MulticastCount, l2BroadcastCount));
+ for (int i = 0; i < ethertypes.size(); i++) {
+ int eth = ethertypes.keyAt(i);
+ int count = ethertypes.valueAt(i);
+ j.add(String.format("ethertype 0x%x: %d", eth, count));
+ }
+ for (int i = 0; i < ipNextHeaders.size(); i++) {
+ int proto = ipNextHeaders.keyAt(i);
+ int count = ipNextHeaders.valueAt(i);
+ j.add(String.format("ipNxtHdr %d: %d", proto, count));
+ }
+ return j.toString();
+ }
+
+ private static void increment(SparseIntArray counters, int key) {
+ int newcount = counters.get(key, 0) + 1;
+ counters.put(key, newcount);
}
}
diff --git a/core/java/android/os/Binder.java b/core/java/android/os/Binder.java
index 2bfb013..b5bcd02 100644
--- a/core/java/android/os/Binder.java
+++ b/core/java/android/os/Binder.java
@@ -193,6 +193,19 @@
}
/**
+ * Reset the given interface back to the default blocking behavior,
+ * reverting any changes made by {@link #allowBlocking(IBinder)}.
+ *
+ * @hide
+ */
+ public static IBinder defaultBlocking(IBinder binder) {
+ if (binder instanceof BinderProxy) {
+ ((BinderProxy) binder).mWarnOnBlocking = sWarnOnBlocking;
+ }
+ return binder;
+ }
+
+ /**
* Inherit the current {@link #allowBlocking(IBinder)} value from one given
* interface to another.
*
diff --git a/core/java/android/os/IUserManager.aidl b/core/java/android/os/IUserManager.aidl
index e426356..5d96fd3 100644
--- a/core/java/android/os/IUserManager.aidl
+++ b/core/java/android/os/IUserManager.aidl
@@ -94,4 +94,5 @@
boolean isUserUnlocked(int userId);
boolean isUserRunning(int userId);
boolean isUserNameSet(int userHandle);
+ boolean hasRestrictedProfiles();
}
diff --git a/core/java/android/os/ParcelFileDescriptor.aidl b/core/java/android/os/ParcelFileDescriptor.aidl
index 5857aae..6bbd99e 100644
--- a/core/java/android/os/ParcelFileDescriptor.aidl
+++ b/core/java/android/os/ParcelFileDescriptor.aidl
@@ -17,4 +17,4 @@
package android.os;
-parcelable ParcelFileDescriptor;
+parcelable ParcelFileDescriptor cpp_header "android/os/parcel_file_descriptor.h";
diff --git a/core/java/android/os/ParcelUuid.aidl b/core/java/android/os/ParcelUuid.aidl
index f7e080a..6f36297 100644
--- a/core/java/android/os/ParcelUuid.aidl
+++ b/core/java/android/os/ParcelUuid.aidl
@@ -16,4 +16,4 @@
package android.os;
-parcelable ParcelUuid;
+parcelable ParcelUuid cpp_header "android/os/parcel_uuid.h";
diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java
index 01b6535..dd4825e 100644
--- a/core/java/android/os/PowerManager.java
+++ b/core/java/android/os/PowerManager.java
@@ -526,7 +526,11 @@
ServiceType.SCREEN_BRIGHTNESS,
ServiceType.SOUND,
ServiceType.BATTERY_STATS,
- ServiceType.DATA_SAVER})
+ ServiceType.DATA_SAVER,
+ ServiceType.FORCE_ALL_APPS_STANDBY_JOBS,
+ ServiceType.FORCE_ALL_APPS_STANDBY_ALARMS,
+ ServiceType.OPTIONAL_SENSORS,
+ })
public @interface ServiceType {
int NULL = 0;
int GPS = 1;
@@ -539,6 +543,21 @@
int SOUND = 8;
int BATTERY_STATS = 9;
int DATA_SAVER = 10;
+
+ /**
+ * Whether the job scheduler should force app standby on all apps on battery saver or not.
+ */
+ int FORCE_ALL_APPS_STANDBY_JOBS = 11;
+
+ /**
+ * Whether the alarm manager should force app standby on all apps on battery saver or not.
+ */
+ int FORCE_ALL_APPS_STANDBY_ALARMS = 12;
+
+ /**
+ * Whether to disable non-essential sensors. (e.g. edge sensors.)
+ */
+ int OPTIONAL_SENSORS = 13;
}
final Context mContext;
diff --git a/core/java/android/os/PowerManagerInternal.java b/core/java/android/os/PowerManagerInternal.java
index a01b8ed..77ac2651 100644
--- a/core/java/android/os/PowerManagerInternal.java
+++ b/core/java/android/os/PowerManagerInternal.java
@@ -18,6 +18,8 @@
import android.view.Display;
+import java.util.function.Consumer;
+
/**
* Power manager local system service interface.
*
@@ -125,6 +127,23 @@
public abstract void registerLowPowerModeObserver(LowPowerModeListener listener);
+ /**
+ * Same as {@link #registerLowPowerModeObserver} but can take a lambda.
+ */
+ public void registerLowPowerModeObserver(int serviceType, Consumer<PowerSaveState> listener) {
+ registerLowPowerModeObserver(new LowPowerModeListener() {
+ @Override
+ public int getServiceType() {
+ return serviceType;
+ }
+
+ @Override
+ public void onLowPowerModeChanged(PowerSaveState state) {
+ listener.accept(state);
+ }
+ });
+ }
+
public interface LowPowerModeListener {
int getServiceType();
void onLowPowerModeChanged(PowerSaveState state);
diff --git a/core/java/android/os/StrictMode.java b/core/java/android/os/StrictMode.java
index d5820b6..f90604a 100644
--- a/core/java/android/os/StrictMode.java
+++ b/core/java/android/os/StrictMode.java
@@ -16,11 +16,11 @@
package android.os;
import android.animation.ValueAnimator;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.TestApi;
import android.app.ActivityManager;
import android.app.ActivityThread;
-import android.app.ApplicationErrorReport;
import android.app.IActivityManager;
import android.content.BroadcastReceiver;
import android.content.Context;
@@ -29,8 +29,6 @@
import android.content.pm.ApplicationInfo;
import android.net.TrafficStats;
import android.net.Uri;
-import android.os.StrictMode.ThreadPolicy;
-import android.os.StrictMode.VmPolicy;
import android.os.strictmode.CleartextNetworkViolation;
import android.os.strictmode.ContentUriWithoutPermissionViolation;
import android.os.strictmode.CustomViolation;
@@ -44,7 +42,7 @@
import android.os.strictmode.ResourceMismatchViolation;
import android.os.strictmode.ServiceConnectionLeakedViolation;
import android.os.strictmode.SqliteObjectLeakedViolation;
-import android.os.strictmode.UnbufferedIOViolation;
+import android.os.strictmode.UnbufferedIoViolation;
import android.os.strictmode.UntaggedSocketViolation;
import android.os.strictmode.Violation;
import android.os.strictmode.WebViewMethodCalledOnWrongThreadViolation;
@@ -75,6 +73,8 @@
import java.util.Arrays;
import java.util.Deque;
import java.util.HashMap;
+import java.util.concurrent.Executor;
+import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.atomic.AtomicInteger;
/**
@@ -163,12 +163,14 @@
private static final String CLEARTEXT_PROPERTY = "persist.sys.strictmode.clear";
/**
- * Quick feature-flag that can be used to disable the defaults provided by
- * {@link #initThreadDefaults(ApplicationInfo)} and
- * {@link #initVmDefaults(ApplicationInfo)}.
+ * Quick feature-flag that can be used to disable the defaults provided by {@link
+ * #initThreadDefaults(ApplicationInfo)} and {@link #initVmDefaults(ApplicationInfo)}.
*/
private static final boolean DISABLE = false;
+ // Only apply VM penalties for the same violation at this interval.
+ private static final long MIN_VM_INTERVAL_MS = 1000;
+
// Only log a duplicate stack trace to the logs every second.
private static final long MIN_LOG_INTERVAL_MS = 1000;
@@ -374,6 +376,28 @@
private static volatile ViolationLogger sLogger = LOGCAT_LOGGER;
+ private static final ThreadLocal<OnThreadViolationListener> sThreadViolationListener =
+ new ThreadLocal<>();
+ private static final ThreadLocal<Executor> sThreadViolationExecutor = new ThreadLocal<>();
+
+ /**
+ * When #{@link ThreadPolicy.Builder#penaltyListener} is enabled, the listener is called on the
+ * provided executor when a Thread violation occurs.
+ */
+ public interface OnThreadViolationListener {
+ /** Called on a thread policy violation. */
+ void onThreadViolation(Violation v);
+ }
+
+ /**
+ * When #{@link VmPolicy.Builder#penaltyListener} is enabled, the listener is called on the
+ * provided executor when a VM violation occurs.
+ */
+ public interface OnVmViolationListener {
+ /** Called on a VM policy violation. */
+ void onVmViolation(Violation v);
+ }
+
/** {@hide} */
@TestApi
public static void setViolationLogger(ViolationLogger listener) {
@@ -403,12 +427,16 @@
*/
public static final class ThreadPolicy {
/** The default, lax policy which doesn't catch anything. */
- public static final ThreadPolicy LAX = new ThreadPolicy(0);
+ public static final ThreadPolicy LAX = new ThreadPolicy(0, null, null);
final int mask;
+ final OnThreadViolationListener mListener;
+ final Executor mCallbackExecutor;
- private ThreadPolicy(int mask) {
+ private ThreadPolicy(int mask, OnThreadViolationListener listener, Executor executor) {
this.mask = mask;
+ mListener = listener;
+ mCallbackExecutor = executor;
}
@Override
@@ -436,6 +464,8 @@
*/
public static final class Builder {
private int mMask = 0;
+ private OnThreadViolationListener mListener;
+ private Executor mExecutor;
/**
* Create a Builder that detects nothing and has no violations. (but note that {@link
@@ -601,6 +631,20 @@
return enable(PENALTY_DROPBOX);
}
+ /**
+ * Call #{@link OnThreadViolationListener#onThreadViolation(Violation)} on specified
+ * executor every violation.
+ */
+ public Builder penaltyListener(
+ @NonNull OnThreadViolationListener listener, @NonNull Executor executor) {
+ if (executor == null) {
+ throw new NullPointerException("executor must not be null");
+ }
+ mListener = listener;
+ mExecutor = executor;
+ return this;
+ }
+
private Builder enable(int bit) {
mMask |= bit;
return this;
@@ -620,7 +664,8 @@
public ThreadPolicy build() {
// If there are detection bits set but no violation bits
// set, enable simple logging.
- if (mMask != 0
+ if (mListener == null
+ && mMask != 0
&& (mMask
& (PENALTY_DEATH
| PENALTY_LOG
@@ -629,7 +674,7 @@
== 0) {
penaltyLog();
}
- return new ThreadPolicy(mMask);
+ return new ThreadPolicy(mMask, mListener, mExecutor);
}
}
}
@@ -641,19 +686,27 @@
*/
public static final class VmPolicy {
/** The default, lax policy which doesn't catch anything. */
- public static final VmPolicy LAX = new VmPolicy(0, EMPTY_CLASS_LIMIT_MAP);
+ public static final VmPolicy LAX = new VmPolicy(0, EMPTY_CLASS_LIMIT_MAP, null, null);
final int mask;
+ final OnVmViolationListener mListener;
+ final Executor mCallbackExecutor;
// Map from class to max number of allowed instances in memory.
final HashMap<Class, Integer> classInstanceLimit;
- private VmPolicy(int mask, HashMap<Class, Integer> classInstanceLimit) {
+ private VmPolicy(
+ int mask,
+ HashMap<Class, Integer> classInstanceLimit,
+ OnVmViolationListener listener,
+ Executor executor) {
if (classInstanceLimit == null) {
throw new NullPointerException("classInstanceLimit == null");
}
this.mask = mask;
this.classInstanceLimit = classInstanceLimit;
+ mListener = listener;
+ mCallbackExecutor = executor;
}
@Override
@@ -681,6 +734,8 @@
*/
public static final class Builder {
private int mMask;
+ private OnVmViolationListener mListener;
+ private Executor mExecutor;
private HashMap<Class, Integer> mClassInstanceLimit; // null until needed
private boolean mClassInstanceLimitNeedCow = false; // need copy-on-write
@@ -694,6 +749,8 @@
mMask = base.mask;
mClassInstanceLimitNeedCow = true;
mClassInstanceLimit = base.classInstanceLimit;
+ mListener = base.mListener;
+ mExecutor = base.mCallbackExecutor;
}
/**
@@ -910,6 +967,19 @@
return enable(PENALTY_DROPBOX);
}
+ /**
+ * Call #{@link OnVmViolationListener#onVmViolation(Violation)} on every violation.
+ */
+ public Builder penaltyListener(
+ @NonNull OnVmViolationListener listener, @NonNull Executor executor) {
+ if (executor == null) {
+ throw new NullPointerException("executor must not be null");
+ }
+ mListener = listener;
+ mExecutor = executor;
+ return this;
+ }
+
private Builder enable(int bit) {
mMask |= bit;
return this;
@@ -929,7 +999,8 @@
public VmPolicy build() {
// If there are detection bits set but no violation bits
// set, enable simple logging.
- if (mMask != 0
+ if (mListener == null
+ && mMask != 0
&& (mMask
& (PENALTY_DEATH
| PENALTY_LOG
@@ -940,7 +1011,9 @@
}
return new VmPolicy(
mMask,
- mClassInstanceLimit != null ? mClassInstanceLimit : EMPTY_CLASS_LIMIT_MAP);
+ mClassInstanceLimit != null ? mClassInstanceLimit : EMPTY_CLASS_LIMIT_MAP,
+ mListener,
+ mExecutor);
}
}
}
@@ -973,6 +1046,8 @@
*/
public static void setThreadPolicy(final ThreadPolicy policy) {
setThreadPolicyMask(policy.mask);
+ sThreadViolationListener.set(policy.mListener);
+ sThreadViolationExecutor.set(policy.mCallbackExecutor);
}
/** @hide */
@@ -1029,7 +1104,10 @@
// introduce VmPolicy cleanly) but this isn't particularly
// optimal for users who might call this method often. This
// should be in a thread-local and not allocate on each call.
- return new ThreadPolicy(getThreadPolicyMask());
+ return new ThreadPolicy(
+ getThreadPolicyMask(),
+ sThreadViolationListener.get(),
+ sThreadViolationExecutor.get());
}
/**
@@ -1042,7 +1120,10 @@
* end of a block
*/
public static ThreadPolicy allowThreadDiskWrites() {
- return new ThreadPolicy(allowThreadDiskWritesMask());
+ return new ThreadPolicy(
+ allowThreadDiskWritesMask(),
+ sThreadViolationListener.get(),
+ sThreadViolationExecutor.get());
}
/** @hide */
@@ -1063,7 +1144,10 @@
* @return the old policy, to be passed to setThreadPolicy to restore the policy.
*/
public static ThreadPolicy allowThreadDiskReads() {
- return new ThreadPolicy(allowThreadDiskReadsMask());
+ return new ThreadPolicy(
+ allowThreadDiskReadsMask(),
+ sThreadViolationListener.get(),
+ sThreadViolationExecutor.get());
}
/** @hide */
@@ -1076,16 +1160,27 @@
return oldPolicyMask;
}
+ private static ThreadPolicy allowThreadViolations() {
+ ThreadPolicy oldPolicy = getThreadPolicy();
+ setThreadPolicyMask(0);
+ return oldPolicy;
+ }
+
+ private static VmPolicy allowVmViolations() {
+ VmPolicy oldPolicy = getVmPolicy();
+ sVmPolicy = VmPolicy.LAX;
+ return oldPolicy;
+ }
+
/**
- * Determine if the given app is "bundled" as part of the system image.
- * These bundled apps are developed in lock-step with the OS, and they
- * aren't updated outside of an OTA, so we want to chase any
- * {@link StrictMode} regressions by enabling detection when running on
- * {@link Build#IS_USERDEBUG} or {@link Build#IS_ENG} builds.
- * <p>
- * Unbundled apps included in the system image are expected to detect and
- * triage their own {@link StrictMode} issues separate from the OS release
- * process, which is why we don't enable them here.
+ * Determine if the given app is "bundled" as part of the system image. These bundled apps are
+ * developed in lock-step with the OS, and they aren't updated outside of an OTA, so we want to
+ * chase any {@link StrictMode} regressions by enabling detection when running on {@link
+ * Build#IS_USERDEBUG} or {@link Build#IS_ENG} builds.
+ *
+ * <p>Unbundled apps included in the system image are expected to detect and triage their own
+ * {@link StrictMode} issues separate from the OS release process, which is why we don't enable
+ * them here.
*
* @hide
*/
@@ -1122,8 +1217,8 @@
*/
public static void initThreadDefaults(ApplicationInfo ai) {
final ThreadPolicy.Builder builder = new ThreadPolicy.Builder();
- final int targetSdkVersion = (ai != null) ? ai.targetSdkVersion
- : Build.VERSION_CODES.CUR_DEVELOPMENT;
+ final int targetSdkVersion =
+ (ai != null) ? ai.targetSdkVersion : Build.VERSION_CODES.CUR_DEVELOPMENT;
// Starting in HC, we don't allow network usage on the main thread
if (targetSdkVersion >= Build.VERSION_CODES.HONEYCOMB) {
@@ -1162,8 +1257,8 @@
*/
public static void initVmDefaults(ApplicationInfo ai) {
final VmPolicy.Builder builder = new VmPolicy.Builder();
- final int targetSdkVersion = (ai != null) ? ai.targetSdkVersion
- : Build.VERSION_CODES.CUR_DEVELOPMENT;
+ final int targetSdkVersion =
+ (ai != null) ? ai.targetSdkVersion : Build.VERSION_CODES.CUR_DEVELOPMENT;
// Starting in N, we don't allow file:// Uri exposure
if (targetSdkVersion >= Build.VERSION_CODES.N) {
@@ -1204,7 +1299,9 @@
sVmPolicy.mask
| DETECT_VM_FILE_URI_EXPOSURE
| PENALTY_DEATH_ON_FILE_URI_EXPOSURE,
- sVmPolicy.classInstanceLimit);
+ sVmPolicy.classInstanceLimit,
+ sVmPolicy.mListener,
+ sVmPolicy.mCallbackExecutor);
}
/**
@@ -1219,7 +1316,9 @@
sVmPolicy.mask
& ~(DETECT_VM_FILE_URI_EXPOSURE
| PENALTY_DEATH_ON_FILE_URI_EXPOSURE),
- sVmPolicy.classInstanceLimit);
+ sVmPolicy.classInstanceLimit,
+ sVmPolicy.mListener,
+ sVmPolicy.mCallbackExecutor);
}
/**
@@ -1340,7 +1439,7 @@
if (tooManyViolationsThisLoop()) {
return;
}
- startHandlingViolationException(new UnbufferedIOViolation());
+ startHandlingViolationException(new UnbufferedIoViolation());
}
// Part of BlockGuard.Policy interface:
@@ -1409,7 +1508,7 @@
// go into this immediate mode?
if (looper == null || (info.mPolicy & THREAD_PENALTY_MASK) == PENALTY_DEATH) {
info.durationMillis = -1; // unknown (redundant, already set)
- handleViolation(info);
+ onThreadPolicyViolation(info);
return;
}
@@ -1447,30 +1546,28 @@
THREAD_HANDLER
.get()
.postAtFrontOfQueue(
- new Runnable() {
- public void run() {
- long loopFinishTime = SystemClock.uptimeMillis();
+ () -> {
+ long loopFinishTime = SystemClock.uptimeMillis();
- // Note: we do this early, before handling the
- // violation below, as handling the violation
- // may include PENALTY_DEATH and we don't want
- // to keep the red border on.
- if (windowManager != null) {
- try {
- windowManager.showStrictModeViolation(false);
- } catch (RemoteException unused) {
- }
+ // Note: we do this early, before handling the
+ // violation below, as handling the violation
+ // may include PENALTY_DEATH and we don't want
+ // to keep the red border on.
+ if (windowManager != null) {
+ try {
+ windowManager.showStrictModeViolation(false);
+ } catch (RemoteException unused) {
}
-
- for (int n = 0; n < records.size(); ++n) {
- ViolationInfo v = records.get(n);
- v.violationNumThisLoop = n + 1;
- v.durationMillis =
- (int) (loopFinishTime - v.violationUptimeMillis);
- handleViolation(v);
- }
- records.clear();
}
+
+ for (int n = 0; n < records.size(); ++n) {
+ ViolationInfo v = records.get(n);
+ v.violationNumThisLoop = n + 1;
+ v.durationMillis =
+ (int) (loopFinishTime - v.violationUptimeMillis);
+ onThreadPolicyViolation(v);
+ }
+ records.clear();
});
}
@@ -1479,13 +1576,13 @@
// violation fired and now (after the violating code ran) due
// to people who push/pop temporary policy in regions of code,
// hence the policy being passed around.
- void handleViolation(final ViolationInfo info) {
- if (LOG_V) Log.d(TAG, "handleViolation; policy=" + info.mPolicy);
+ void onThreadPolicyViolation(final ViolationInfo info) {
+ if (LOG_V) Log.d(TAG, "onThreadPolicyViolation; policy=" + info.mPolicy);
if (info.penaltyEnabled(PENALTY_GATHER)) {
ArrayList<ViolationInfo> violations = gatheredViolations.get();
if (violations == null) {
- violations = new ArrayList<ViolationInfo>(1);
+ violations = new ArrayList<>(1);
gatheredViolations.set(violations);
}
for (ViolationInfo previous : violations) {
@@ -1519,6 +1616,8 @@
sLogger.log(info);
}
+ final Violation violation = info.mViolation;
+
// The violationMaskSubset, passed to ActivityManager, is a
// subset of the original StrictMode policy bitmask, with
// only the bit violated and penalty bits to be executed
@@ -1552,15 +1651,32 @@
}
if ((info.getPolicyMask() & PENALTY_DEATH) != 0) {
- executeDeathPenalty(info);
+ throw new RuntimeException("StrictMode ThreadPolicy violation", violation);
+ }
+
+ // penaltyDeath will cause penaltyCallback to no-op since we cannot guarantee the
+ // executor finishes before crashing.
+ final OnThreadViolationListener listener = sThreadViolationListener.get();
+ final Executor executor = sThreadViolationExecutor.get();
+ if (listener != null && executor != null) {
+ try {
+ executor.execute(
+ () -> {
+ // Lift violated policy to prevent infinite recursion.
+ ThreadPolicy oldPolicy = allowThreadViolations();
+ try {
+ listener.onThreadViolation(violation);
+ } finally {
+ setThreadPolicy(oldPolicy);
+ }
+ });
+ } catch (RejectedExecutionException e) {
+ Log.e(TAG, "ThreadPolicy penaltyCallback failed", e);
+ }
}
}
}
- private static void executeDeathPenalty(ViolationInfo info) {
- throw new RuntimeException("StrictMode death penalty", info.mViolation);
- }
-
/**
* In the common case, as set by conditionallyEnableDebugLogging, we're just dropboxing any
* violations but not showing a dialog, not loggging, and not killing the process. In these
@@ -1736,9 +1852,8 @@
* #setThreadPolicy}.
*/
public static void enableDefaults() {
- StrictMode.setThreadPolicy(
- new StrictMode.ThreadPolicy.Builder().detectAll().penaltyLog().build());
- StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder().detectAll().penaltyLog().build());
+ setThreadPolicy(new StrictMode.ThreadPolicy.Builder().detectAll().penaltyLog().build());
+ setVmPolicy(new StrictMode.VmPolicy.Builder().detectAll().penaltyLog().build());
}
/** @hide */
@@ -1857,11 +1972,11 @@
}
/** @hide */
- public static void onVmPolicyViolation(Violation originStack, boolean forceDeath) {
+ public static void onVmPolicyViolation(Violation violation, boolean forceDeath) {
final boolean penaltyDropbox = (sVmPolicy.mask & PENALTY_DROPBOX) != 0;
final boolean penaltyDeath = ((sVmPolicy.mask & PENALTY_DEATH) != 0) || forceDeath;
final boolean penaltyLog = (sVmPolicy.mask & PENALTY_LOG) != 0;
- final ViolationInfo info = new ViolationInfo(originStack, sVmPolicy.mask);
+ final ViolationInfo info = new ViolationInfo(violation, sVmPolicy.mask);
// Erase stuff not relevant for process-wide violations
info.numAnimationsRunning = 0;
@@ -1870,37 +1985,37 @@
final Integer fingerprint = info.hashCode();
final long now = SystemClock.uptimeMillis();
- long lastViolationTime = 0;
+ long lastViolationTime;
long timeSinceLastViolationMillis = Long.MAX_VALUE;
synchronized (sLastVmViolationTime) {
if (sLastVmViolationTime.containsKey(fingerprint)) {
lastViolationTime = sLastVmViolationTime.get(fingerprint);
timeSinceLastViolationMillis = now - lastViolationTime;
}
- if (timeSinceLastViolationMillis > MIN_LOG_INTERVAL_MS) {
+ if (timeSinceLastViolationMillis > MIN_VM_INTERVAL_MS) {
sLastVmViolationTime.put(fingerprint, now);
}
}
-
- if (penaltyLog && sLogger != null) {
- sLogger.log(info);
+ if (timeSinceLastViolationMillis <= MIN_VM_INTERVAL_MS) {
+ // Rate limit all penalties.
+ return;
}
- if (penaltyLog && timeSinceLastViolationMillis > MIN_LOG_INTERVAL_MS) {
+
+ if (penaltyLog && sLogger != null && timeSinceLastViolationMillis > MIN_LOG_INTERVAL_MS) {
sLogger.log(info);
}
int violationMaskSubset = PENALTY_DROPBOX | (ALL_VM_DETECT_BITS & sVmPolicy.mask);
- if (penaltyDropbox && !penaltyDeath) {
- // Common case for userdebug/eng builds. If no death and
- // just dropboxing, we can do the ActivityManager call
- // asynchronously.
- dropboxViolationAsync(violationMaskSubset, info);
- return;
- }
-
- if (penaltyDropbox && lastViolationTime == 0) {
- handleApplicationStrictModeViolation(violationMaskSubset, info);
+ if (penaltyDropbox) {
+ if (penaltyDeath) {
+ handleApplicationStrictModeViolation(violationMaskSubset, info);
+ } else {
+ // Common case for userdebug/eng builds. If no death and
+ // just dropboxing, we can do the ActivityManager call
+ // asynchronously.
+ dropboxViolationAsync(violationMaskSubset, info);
+ }
}
if (penaltyDeath) {
@@ -1908,6 +2023,26 @@
Process.killProcess(Process.myPid());
System.exit(10);
}
+
+ // If penaltyDeath, we can't guarantee this callback finishes before the process dies for
+ // all executors. penaltyDeath supersedes penaltyCallback.
+ if (sVmPolicy.mListener != null && sVmPolicy.mCallbackExecutor != null) {
+ final OnVmViolationListener listener = sVmPolicy.mListener;
+ try {
+ sVmPolicy.mCallbackExecutor.execute(
+ () -> {
+ // Lift violated policy to prevent infinite recursion.
+ VmPolicy oldPolicy = allowVmViolations();
+ try {
+ listener.onVmViolation(violation);
+ } finally {
+ setVmPolicy(oldPolicy);
+ }
+ });
+ } catch (RejectedExecutionException e) {
+ Log.e(TAG, "VmPolicy penaltyCallback failed", e);
+ }
+ }
}
/** Called from Parcel.writeNoException() */
@@ -2407,7 +2542,7 @@
return DETECT_CUSTOM;
} else if (mViolation instanceof ResourceMismatchViolation) {
return DETECT_RESOURCE_MISMATCH;
- } else if (mViolation instanceof UnbufferedIOViolation) {
+ } else if (mViolation instanceof UnbufferedIoViolation) {
return DETECT_UNBUFFERED_IO;
} else if (mViolation instanceof SqliteObjectLeakedViolation) {
return DETECT_VM_CURSOR_LEAKS;
diff --git a/core/java/android/os/SystemClock.java b/core/java/android/os/SystemClock.java
index b3d76d7..c52c22d 100644
--- a/core/java/android/os/SystemClock.java
+++ b/core/java/android/os/SystemClock.java
@@ -16,12 +16,18 @@
package android.os;
+import android.annotation.NonNull;
import android.app.IAlarmManager;
import android.content.Context;
import android.util.Slog;
import dalvik.annotation.optimization.CriticalNative;
+import java.time.Clock;
+import java.time.Instant;
+import java.time.ZoneId;
+import java.time.ZoneOffset;
+
/**
* Core timekeeping facilities.
*
@@ -168,6 +174,31 @@
native public static long uptimeMillis();
/**
+ * Return {@link Clock} that starts at system boot, not counting time spent
+ * in deep sleep.
+ */
+ public static @NonNull Clock uptimeMillisClock() {
+ return new Clock() {
+ @Override
+ public ZoneId getZone() {
+ return ZoneOffset.UTC;
+ }
+ @Override
+ public Clock withZone(ZoneId zone) {
+ throw new UnsupportedOperationException();
+ }
+ @Override
+ public long millis() {
+ return SystemClock.uptimeMillis();
+ }
+ @Override
+ public Instant instant() {
+ return Instant.ofEpochMilli(millis());
+ }
+ };
+ }
+
+ /**
* Returns milliseconds since boot, including time spent in sleep.
*
* @return elapsed milliseconds since boot.
@@ -176,6 +207,31 @@
native public static long elapsedRealtime();
/**
+ * Return {@link Clock} that starts at system boot, including time spent in
+ * sleep.
+ */
+ public static @NonNull Clock elapsedRealtimeClock() {
+ return new Clock() {
+ @Override
+ public ZoneId getZone() {
+ return ZoneOffset.UTC;
+ }
+ @Override
+ public Clock withZone(ZoneId zone) {
+ throw new UnsupportedOperationException();
+ }
+ @Override
+ public long millis() {
+ return SystemClock.elapsedRealtime();
+ }
+ @Override
+ public Instant instant() {
+ return Instant.ofEpochMilli(millis());
+ }
+ };
+ }
+
+ /**
* Returns nanoseconds since boot, including time spent in sleep.
*
* @return elapsed nanoseconds since boot.
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index c54b72d..22967af 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -140,6 +140,18 @@
public static final String DISALLOW_CONFIG_WIFI = "no_config_wifi";
/**
+ * Specifies if a user is disallowed from changing the device
+ * language. The default value is <code>false</code>.
+ *
+ * <p>Key for user restrictions.
+ * <p>Type: Boolean
+ * @see DevicePolicyManager#addUserRestriction(ComponentName, String)
+ * @see DevicePolicyManager#clearUserRestriction(ComponentName, String)
+ * @see #getUserRestrictions()
+ */
+ public static final String DISALLOW_CONFIG_LOCALE = "no_config_locale";
+
+ /**
* Specifies if a user is disallowed from installing applications.
* The default value is <code>false</code>.
*
@@ -792,6 +804,19 @@
public static final String DISALLOW_AUTOFILL = "no_autofill";
/**
+ * Specifies if user switching is blocked on the current user.
+ *
+ * <p> This restriction can only be set by the device owner, it will be applied to all users.
+ *
+ * <p>The default value is <code>false</code>.
+ *
+ * @see DevicePolicyManager#addUserRestriction(ComponentName, String)
+ * @see DevicePolicyManager#clearUserRestriction(ComponentName, String)
+ * @see #getUserRestrictions()
+ */
+ public static final String DISALLOW_USER_SWITCH = "no_user_switch";
+
+ /**
* Application restriction key that is used to indicate the pending arrival
* of real restrictions for the app.
*
@@ -917,7 +942,7 @@
/**
* Returns whether switching users is currently allowed.
* <p>For instance switching users is not allowed if the current user is in a phone call,
- * or system user hasn't been unlocked yet
+ * system user hasn't been unlocked yet, or {@link #DISALLOW_USER_SWITCH} is set.
* @hide
*/
public boolean canSwitchUsers() {
@@ -927,7 +952,9 @@
boolean isSystemUserUnlocked = isUserUnlocked(UserHandle.SYSTEM);
boolean inCall = TelephonyManager.getDefault().getCallState()
!= TelephonyManager.CALL_STATE_IDLE;
- return (allowUserSwitchingWhenSystemUserLocked || isSystemUserUnlocked) && !inCall;
+ boolean isUserSwitchDisallowed = hasUserRestriction(DISALLOW_USER_SWITCH);
+ return (allowUserSwitchingWhenSystemUserLocked || isSystemUserUnlocked) && !inCall
+ && !isUserSwitchDisallowed;
}
/**
@@ -1022,12 +1049,22 @@
}
/**
- * Used to check if the user making this call is linked to another user. Linked users may have
+ * @hide
+ * @deprecated Use {@link #isRestrictedProfile()}
+ */
+ @Deprecated
+ public boolean isLinkedUser() {
+ return isRestrictedProfile();
+ }
+
+ /**
+ * Returns whether the caller is running as restricted profile. Restricted profile may have
* a reduced number of available apps, app restrictions and account restrictions.
* @return whether the user making this call is a linked user
* @hide
*/
- public boolean isLinkedUser() {
+ @SystemApi
+ public boolean isRestrictedProfile() {
try {
return mService.isRestricted();
} catch (RemoteException re) {
@@ -1048,6 +1085,20 @@
}
/**
+ * Returns whether the calling user has at least one restricted profile associated with it.
+ * @return
+ * @hide
+ */
+ @SystemApi
+ public boolean hasRestrictedProfiles() {
+ try {
+ return mService.hasRestrictedProfiles();
+ } catch (RemoteException re) {
+ throw re.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Checks if a user is a guest user.
* @return whether user is a guest user.
* @hide
@@ -1067,6 +1118,7 @@
return user != null && user.isGuest();
}
+
/**
* Checks if the calling app is running in a demo user. When running in a demo user,
* apps can be more helpful to the user, or explain their features in more detail.
@@ -2298,6 +2350,9 @@
if (!supportsMultipleUsers()) {
return false;
}
+ if (hasUserRestriction(DISALLOW_USER_SWITCH)) {
+ return false;
+ }
// If Demo Mode is on, don't show user switcher
if (isDeviceInDemoMode(mContext)) {
return false;
diff --git a/core/java/android/os/strictmode/CleartextNetworkViolation.java b/core/java/android/os/strictmode/CleartextNetworkViolation.java
index 893780d..6a0d381 100644
--- a/core/java/android/os/strictmode/CleartextNetworkViolation.java
+++ b/core/java/android/os/strictmode/CleartextNetworkViolation.java
@@ -15,8 +15,8 @@
*/
package android.os.strictmode;
-/** @hide */
public final class CleartextNetworkViolation extends Violation {
+ /** @hide */
public CleartextNetworkViolation(String msg) {
super(msg);
}
diff --git a/core/java/android/os/strictmode/ContentUriWithoutPermissionViolation.java b/core/java/android/os/strictmode/ContentUriWithoutPermissionViolation.java
index 017c4f9..e78dc79 100644
--- a/core/java/android/os/strictmode/ContentUriWithoutPermissionViolation.java
+++ b/core/java/android/os/strictmode/ContentUriWithoutPermissionViolation.java
@@ -17,8 +17,8 @@
import android.net.Uri;
-/** @hide */
public final class ContentUriWithoutPermissionViolation extends Violation {
+ /** @hide */
public ContentUriWithoutPermissionViolation(Uri uri, String location) {
super(
uri
diff --git a/core/java/android/os/strictmode/CustomViolation.java b/core/java/android/os/strictmode/CustomViolation.java
index bc1656d..d4ad067 100644
--- a/core/java/android/os/strictmode/CustomViolation.java
+++ b/core/java/android/os/strictmode/CustomViolation.java
@@ -15,8 +15,8 @@
*/
package android.os.strictmode;
-/** @hide */
public final class CustomViolation extends Violation {
+ /** @hide */
public CustomViolation(String name) {
super(name);
}
diff --git a/core/java/android/os/strictmode/DiskReadViolation.java b/core/java/android/os/strictmode/DiskReadViolation.java
index 2edd33e..fad32db 100644
--- a/core/java/android/os/strictmode/DiskReadViolation.java
+++ b/core/java/android/os/strictmode/DiskReadViolation.java
@@ -15,8 +15,8 @@
*/
package android.os.strictmode;
-/** @hide */
public final class DiskReadViolation extends Violation {
+ /** @hide */
public DiskReadViolation() {
super(null);
}
diff --git a/core/java/android/os/strictmode/DiskWriteViolation.java b/core/java/android/os/strictmode/DiskWriteViolation.java
index 6465718..cb9ca38 100644
--- a/core/java/android/os/strictmode/DiskWriteViolation.java
+++ b/core/java/android/os/strictmode/DiskWriteViolation.java
@@ -15,8 +15,8 @@
*/
package android.os.strictmode;
-/** @hide */
public final class DiskWriteViolation extends Violation {
+ /** @hide */
public DiskWriteViolation() {
super(null);
}
diff --git a/core/java/android/os/strictmode/FileUriExposedViolation.java b/core/java/android/os/strictmode/FileUriExposedViolation.java
index 5f71ee5..e3e6f83 100644
--- a/core/java/android/os/strictmode/FileUriExposedViolation.java
+++ b/core/java/android/os/strictmode/FileUriExposedViolation.java
@@ -15,8 +15,8 @@
*/
package android.os.strictmode;
-/** @hide */
public final class FileUriExposedViolation extends Violation {
+ /** @hide */
public FileUriExposedViolation(String msg) {
super(msg);
}
diff --git a/core/java/android/os/strictmode/InstanceCountViolation.java b/core/java/android/os/strictmode/InstanceCountViolation.java
index d3da800..9ee2c8e 100644
--- a/core/java/android/os/strictmode/InstanceCountViolation.java
+++ b/core/java/android/os/strictmode/InstanceCountViolation.java
@@ -15,7 +15,6 @@
*/
package android.os.strictmode;
-/** @hide */
public class InstanceCountViolation extends Violation {
private final long mInstances;
@@ -24,6 +23,7 @@
"android.os.StrictMode", "setClassInstanceLimit", "StrictMode.java", 1)
};
+ /** @hide */
public InstanceCountViolation(Class klass, long instances, int limit) {
super(klass.toString() + "; instances=" + instances + "; limit=" + limit);
setStackTrace(FAKE_STACK);
diff --git a/core/java/android/os/strictmode/IntentReceiverLeakedViolation.java b/core/java/android/os/strictmode/IntentReceiverLeakedViolation.java
index 1d1dfc0..f416c94 100644
--- a/core/java/android/os/strictmode/IntentReceiverLeakedViolation.java
+++ b/core/java/android/os/strictmode/IntentReceiverLeakedViolation.java
@@ -15,8 +15,8 @@
*/
package android.os.strictmode;
-/** @hide */
public final class IntentReceiverLeakedViolation extends Violation {
+ /** @hide */
public IntentReceiverLeakedViolation(Throwable originStack) {
super(null);
setStackTrace(originStack.getStackTrace());
diff --git a/core/java/android/os/strictmode/LeakedClosableViolation.java b/core/java/android/os/strictmode/LeakedClosableViolation.java
index de12533..c795a6b 100644
--- a/core/java/android/os/strictmode/LeakedClosableViolation.java
+++ b/core/java/android/os/strictmode/LeakedClosableViolation.java
@@ -15,8 +15,8 @@
*/
package android.os.strictmode;
-/** @hide */
public final class LeakedClosableViolation extends Violation {
+ /** @hide */
public LeakedClosableViolation(String message, Throwable allocationSite) {
super(message);
initCause(allocationSite);
diff --git a/core/java/android/os/strictmode/NetworkViolation.java b/core/java/android/os/strictmode/NetworkViolation.java
index 637d0b6..abcf009 100644
--- a/core/java/android/os/strictmode/NetworkViolation.java
+++ b/core/java/android/os/strictmode/NetworkViolation.java
@@ -15,8 +15,8 @@
*/
package android.os.strictmode;
-/** @hide */
public final class NetworkViolation extends Violation {
+ /** @hide */
public NetworkViolation() {
super(null);
}
diff --git a/core/java/android/os/strictmode/ResourceMismatchViolation.java b/core/java/android/os/strictmode/ResourceMismatchViolation.java
index 01ae7f8..97c4499 100644
--- a/core/java/android/os/strictmode/ResourceMismatchViolation.java
+++ b/core/java/android/os/strictmode/ResourceMismatchViolation.java
@@ -15,8 +15,8 @@
*/
package android.os.strictmode;
-/** @hide */
public final class ResourceMismatchViolation extends Violation {
+ /** @hide */
public ResourceMismatchViolation(Object tag) {
super(tag.toString());
}
diff --git a/core/java/android/os/strictmode/ServiceConnectionLeakedViolation.java b/core/java/android/os/strictmode/ServiceConnectionLeakedViolation.java
index 5bab3bd..2d6b58f 100644
--- a/core/java/android/os/strictmode/ServiceConnectionLeakedViolation.java
+++ b/core/java/android/os/strictmode/ServiceConnectionLeakedViolation.java
@@ -15,8 +15,8 @@
*/
package android.os.strictmode;
-/** @hide */
public final class ServiceConnectionLeakedViolation extends Violation {
+ /** @hide */
public ServiceConnectionLeakedViolation(Throwable originStack) {
super(null);
setStackTrace(originStack.getStackTrace());
diff --git a/core/java/android/os/strictmode/SqliteObjectLeakedViolation.java b/core/java/android/os/strictmode/SqliteObjectLeakedViolation.java
index 8360088..0200220 100644
--- a/core/java/android/os/strictmode/SqliteObjectLeakedViolation.java
+++ b/core/java/android/os/strictmode/SqliteObjectLeakedViolation.java
@@ -15,9 +15,9 @@
*/
package android.os.strictmode;
-/** @hide */
public final class SqliteObjectLeakedViolation extends Violation {
+ /** @hide */
public SqliteObjectLeakedViolation(String message, Throwable originStack) {
super(message);
initCause(originStack);
diff --git a/core/java/android/os/strictmode/UnbufferedIOViolation.java b/core/java/android/os/strictmode/UnbufferedIoViolation.java
similarity index 69%
rename from core/java/android/os/strictmode/UnbufferedIOViolation.java
rename to core/java/android/os/strictmode/UnbufferedIoViolation.java
index 571ba50..a5c326d 100644
--- a/core/java/android/os/strictmode/UnbufferedIOViolation.java
+++ b/core/java/android/os/strictmode/UnbufferedIoViolation.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2017 The Android Open Source Project
+ * Copyright 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,9 +15,14 @@
*/
package android.os.strictmode;
-/** @hide */
-public final class UnbufferedIOViolation extends Violation {
- public UnbufferedIOViolation() {
+import android.os.StrictMode.ThreadPolicy.Builder;
+
+/**
+ * See #{@link Builder#detectUnbufferedIo()}
+ */
+public final class UnbufferedIoViolation extends Violation {
+ /** @hide */
+ public UnbufferedIoViolation() {
super(null);
}
}
diff --git a/core/java/android/os/strictmode/UntaggedSocketViolation.java b/core/java/android/os/strictmode/UntaggedSocketViolation.java
index 9752753..836a8b9 100644
--- a/core/java/android/os/strictmode/UntaggedSocketViolation.java
+++ b/core/java/android/os/strictmode/UntaggedSocketViolation.java
@@ -15,13 +15,13 @@
*/
package android.os.strictmode;
-/** @hide */
public final class UntaggedSocketViolation extends Violation {
/** @hide */
public static final String MESSAGE =
"Untagged socket detected; use"
+ " TrafficStats.setThreadSocketTag() to track all network usage";
+ /** @hide */
public UntaggedSocketViolation() {
super(MESSAGE);
}
diff --git a/core/java/android/os/strictmode/Violation.java b/core/java/android/os/strictmode/Violation.java
index ebae7fc..31c7d58 100644
--- a/core/java/android/os/strictmode/Violation.java
+++ b/core/java/android/os/strictmode/Violation.java
@@ -16,13 +16,9 @@
package android.os.strictmode;
-/**
- * Root class for all StrictMode violations.
- *
- * @hide
- */
+/** Root class for all StrictMode violations. */
public abstract class Violation extends Throwable {
- protected Violation(String message) {
+ Violation(String message) {
super(message);
}
}
diff --git a/core/java/android/os/strictmode/WebViewMethodCalledOnWrongThreadViolation.java b/core/java/android/os/strictmode/WebViewMethodCalledOnWrongThreadViolation.java
index d4c557a..c328d14 100644
--- a/core/java/android/os/strictmode/WebViewMethodCalledOnWrongThreadViolation.java
+++ b/core/java/android/os/strictmode/WebViewMethodCalledOnWrongThreadViolation.java
@@ -15,8 +15,8 @@
*/
package android.os.strictmode;
-/** @hide */
public final class WebViewMethodCalledOnWrongThreadViolation extends Violation {
+ /** @hide */
public WebViewMethodCalledOnWrongThreadViolation(Throwable originStack) {
super(null);
setStackTrace(originStack.getStackTrace());
diff --git a/core/java/android/preference/PreferenceFragment.java b/core/java/android/preference/PreferenceFragment.java
index 73fa01e..4c556ef 100644
--- a/core/java/android/preference/PreferenceFragment.java
+++ b/core/java/android/preference/PreferenceFragment.java
@@ -23,7 +23,6 @@
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.res.TypedArray;
-import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
@@ -105,7 +104,10 @@
*
* @see Preference
* @see PreferenceScreen
+ *
+ * @deprecated Use {@link android.support.v7.preference.PreferenceFragmentCompat}
*/
+@Deprecated
public abstract class PreferenceFragment extends Fragment implements
PreferenceManager.OnPreferenceTreeClickListener {
@@ -146,7 +148,11 @@
* Interface that PreferenceFragment's containing activity should
* implement to be able to process preference items that wish to
* switch to a new fragment.
+ *
+ * @deprecated Use {@link
+ * android.support.v7.preference.PreferenceFragmentCompat.OnPreferenceStartFragmentCallback}
*/
+ @Deprecated
public interface OnPreferenceStartFragmentCallback {
/**
* Called when the user has clicked on a Preference that has
diff --git a/core/java/android/provider/MediaStore.java b/core/java/android/provider/MediaStore.java
index 13e1e26..32d68cd 100644
--- a/core/java/android/provider/MediaStore.java
+++ b/core/java/android/provider/MediaStore.java
@@ -81,6 +81,13 @@
public static final String UNHIDE_CALL = "unhide";
/**
+ * The method name used by the media scanner service to reload all localized ringtone titles due
+ * to a locale change.
+ * @hide
+ */
+ public static final String RETRANSLATE_CALL = "update_titles";
+
+ /**
* This is for internal use by the media scanner only.
* Name of the (optional) Uri parameter that determines whether to skip deleting
* the file pointed to by the _data column, when deleting the database entry.
@@ -1358,6 +1365,18 @@
* @hide
*/
public static final String GENRE = "genre";
+
+ /**
+ * The resource URI of a localized title, if any
+ * <P>Type: TEXT</P>
+ * Conforms to this pattern:
+ * Scheme: {@link ContentResolver.SCHEME_ANDROID_RESOURCE}
+ * Authority: Package Name of ringtone title provider
+ * First Path Segment: Type of resource (must be "string")
+ * Second Path Segment: Resource ID of title
+ * @hide
+ */
+ public static final String TITLE_RESOURCE_URI = "title_resource_uri";
}
/**
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 0a20c43..d4d24de 100755
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -66,6 +66,7 @@
import android.os.ServiceManager;
import android.os.UserHandle;
import android.speech.tts.TextToSpeech;
+import android.telephony.SubscriptionManager;
import android.text.TextUtils;
import android.util.AndroidException;
import android.util.ArrayMap;
@@ -2113,6 +2114,9 @@
* functions for accessing individual settings entries.
*/
public static final class System extends NameValueTable {
+ // NOTE: If you add new settings here, be sure to add them to
+ // com.android.providers.settings.SettingsProtoDumpUtil#dumpProtoSystemSettingsLocked.
+
private static final float DEFAULT_FONT_SCALE = 1.0f;
/** @hide */
@@ -4562,6 +4566,9 @@
* APIs for those values, not modified directly by applications.
*/
public static final class Secure extends NameValueTable {
+ // NOTE: If you add new settings here, be sure to add them to
+ // com.android.providers.settings.SettingsProtoDumpUtil#dumpProtoSecureSettingsLocked.
+
/**
* The content:// style URL for this table
*/
@@ -5317,6 +5324,15 @@
public static final String AUTOFILL_SERVICE = "autofill_service";
/**
+ * Experimental autofill feature.
+ *
+ * <p>TODO(b/67867469): remove once feature is finished
+ * @hide
+ */
+ @TestApi
+ public static final String AUTOFILL_FEATURE_FIELD_DETECTION = "autofill_field_detection";
+
+ /**
* @deprecated Use {@link android.provider.Settings.Global#DEVICE_PROVISIONED} instead
*/
@Deprecated
@@ -7555,6 +7571,9 @@
* explicitly modify through the system UI or specialized APIs for those values.
*/
public static final class Global extends NameValueTable {
+ // NOTE: If you add new settings here, be sure to add them to
+ // com.android.providers.settings.SettingsProtoDumpUtil#dumpProtoGlobalSettingsLocked.
+
/**
* The content:// style URL for global secure settings items. Not public.
*/
@@ -8513,6 +8532,13 @@
public static final String NETWORK_METERED_MULTIPATH_PREFERENCE =
"network_metered_multipath_preference";
+ /**
+ * Network watchlist last report time.
+ * @hide
+ */
+ public static final String NETWORK_WATCHLIST_LAST_REPORT_TIME =
+ "network_watchlist_last_report_time";
+
/**
* The thresholds of the wifi throughput badging (SD, HD etc.) as a comma-delimited list of
* colon-delimited key-value pairs. The key is the badging enum value defined in
@@ -10132,8 +10158,12 @@
* <p>
* Type: int (0 for false, 1 for true)
* @hide
+ * @deprecated Use {@link android.telephony.SubscriptionManager#ENHANCED_4G_MODE_ENABLED}
+ * instead.
*/
- public static final String ENHANCED_4G_MODE_ENABLED = "volte_vt_enabled";
+ @Deprecated
+ public static final String ENHANCED_4G_MODE_ENABLED =
+ SubscriptionManager.ENHANCED_4G_MODE_ENABLED;
/**
* Whether VT (Video Telephony over IMS) is enabled
@@ -10141,8 +10171,10 @@
* Type: int (0 for false, 1 for true)
*
* @hide
+ * @deprecated Use {@link android.telephony.SubscriptionManager#VT_IMS_ENABLED} instead.
*/
- public static final String VT_IMS_ENABLED = "vt_ims_enabled";
+ @Deprecated
+ public static final String VT_IMS_ENABLED = SubscriptionManager.VT_IMS_ENABLED;
/**
* Whether WFC is enabled
@@ -10150,8 +10182,10 @@
* Type: int (0 for false, 1 for true)
*
* @hide
+ * @deprecated Use {@link android.telephony.SubscriptionManager#WFC_IMS_ENABLED} instead.
*/
- public static final String WFC_IMS_ENABLED = "wfc_ims_enabled";
+ @Deprecated
+ public static final String WFC_IMS_ENABLED = SubscriptionManager.WFC_IMS_ENABLED;
/**
* WFC mode on home/non-roaming network.
@@ -10159,8 +10193,10 @@
* Type: int - 2=Wi-Fi preferred, 1=Cellular preferred, 0=Wi-Fi only
*
* @hide
+ * @deprecated Use {@link android.telephony.SubscriptionManager#WFC_IMS_MODE} instead.
*/
- public static final String WFC_IMS_MODE = "wfc_ims_mode";
+ @Deprecated
+ public static final String WFC_IMS_MODE = SubscriptionManager.WFC_IMS_MODE;
/**
* WFC mode on roaming network.
@@ -10168,8 +10204,11 @@
* Type: int - see {@link #WFC_IMS_MODE} for values
*
* @hide
+ * @deprecated Use {@link android.telephony.SubscriptionManager#WFC_IMS_ROAMING_MODE}
+ * instead.
*/
- public static final String WFC_IMS_ROAMING_MODE = "wfc_ims_roaming_mode";
+ @Deprecated
+ public static final String WFC_IMS_ROAMING_MODE = SubscriptionManager.WFC_IMS_ROAMING_MODE;
/**
* Whether WFC roaming is enabled
@@ -10177,8 +10216,12 @@
* Type: int (0 for false, 1 for true)
*
* @hide
+ * @deprecated Use {@link android.telephony.SubscriptionManager#WFC_IMS_ROAMING_ENABLED}
+ * instead
*/
- public static final String WFC_IMS_ROAMING_ENABLED = "wfc_ims_roaming_enabled";
+ @Deprecated
+ public static final String WFC_IMS_ROAMING_ENABLED =
+ SubscriptionManager.WFC_IMS_ROAMING_ENABLED;
/**
* Whether user can enable/disable LTE as a preferred network. A carrier might control
@@ -10859,7 +10902,7 @@
/** User preferred subscriptions setting.
* This holds the details of the user selected subscription from the card and
- * the activation status. Each settings string have the coma separated values
+ * the activation status. Each settings string have the comma separated values
* iccId,appType,appId,activationStatus,3gppIndex,3gpp2Index
* @hide
*/
diff --git a/core/java/android/security/KeystoreArguments.aidl b/core/java/android/security/KeystoreArguments.aidl
index d636414..dc8ed50 100644
--- a/core/java/android/security/KeystoreArguments.aidl
+++ b/core/java/android/security/KeystoreArguments.aidl
@@ -17,4 +17,4 @@
package android.security;
/* @hide */
-parcelable KeystoreArguments;
+parcelable KeystoreArguments cpp_header "keystore/KeystoreArguments.h";
diff --git a/core/java/android/security/keymaster/ExportResult.aidl b/core/java/android/security/keymaster/ExportResult.aidl
index 4d9b2de..1748653 100644
--- a/core/java/android/security/keymaster/ExportResult.aidl
+++ b/core/java/android/security/keymaster/ExportResult.aidl
@@ -17,4 +17,4 @@
package android.security.keymaster;
/* @hide */
-parcelable ExportResult;
+parcelable ExportResult cpp_header "keystore/ExportResult.h";
diff --git a/core/java/android/security/keymaster/KeyCharacteristics.aidl b/core/java/android/security/keymaster/KeyCharacteristics.aidl
index be739d3..32e75ad 100644
--- a/core/java/android/security/keymaster/KeyCharacteristics.aidl
+++ b/core/java/android/security/keymaster/KeyCharacteristics.aidl
@@ -17,4 +17,4 @@
package android.security.keymaster;
/* @hide */
-parcelable KeyCharacteristics;
+parcelable KeyCharacteristics cpp_header "keystore/KeyCharacteristics.h";
diff --git a/core/java/android/security/keymaster/KeymasterArguments.aidl b/core/java/android/security/keymaster/KeymasterArguments.aidl
index 1a73206..44d9f09 100644
--- a/core/java/android/security/keymaster/KeymasterArguments.aidl
+++ b/core/java/android/security/keymaster/KeymasterArguments.aidl
@@ -17,4 +17,4 @@
package android.security.keymaster;
/* @hide */
-parcelable KeymasterArguments;
+parcelable KeymasterArguments cpp_header "keystore/KeymasterArguments.h";
diff --git a/core/java/android/security/keymaster/KeymasterBlob.aidl b/core/java/android/security/keymaster/KeymasterBlob.aidl
index b7cd1c9..5c5db9e 100644
--- a/core/java/android/security/keymaster/KeymasterBlob.aidl
+++ b/core/java/android/security/keymaster/KeymasterBlob.aidl
@@ -17,4 +17,4 @@
package android.security.keymaster;
/* @hide */
-parcelable KeymasterBlob;
+parcelable KeymasterBlob cpp_header "keystore/KeymasterBlob.h";
diff --git a/core/java/android/security/keymaster/KeymasterCertificateChain.aidl b/core/java/android/security/keymaster/KeymasterCertificateChain.aidl
index dc1876a..ddb5cae 100644
--- a/core/java/android/security/keymaster/KeymasterCertificateChain.aidl
+++ b/core/java/android/security/keymaster/KeymasterCertificateChain.aidl
@@ -17,4 +17,4 @@
package android.security.keymaster;
/* @hide */
-parcelable KeymasterCertificateChain;
+parcelable KeymasterCertificateChain cpp_header "keystore/KeymasterCertificateChain.h";
diff --git a/core/java/android/security/keymaster/OperationResult.aidl b/core/java/android/security/keymaster/OperationResult.aidl
index ed26c8d..db689d4 100644
--- a/core/java/android/security/keymaster/OperationResult.aidl
+++ b/core/java/android/security/keymaster/OperationResult.aidl
@@ -17,4 +17,4 @@
package android.security.keymaster;
/* @hide */
-parcelable OperationResult;
+parcelable OperationResult cpp_header "keystore/OperationResult.h";
diff --git a/core/java/android/service/autofill/FieldsDetection.java b/core/java/android/service/autofill/FieldsDetection.java
new file mode 100644
index 0000000..550ecf6
--- /dev/null
+++ b/core/java/android/service/autofill/FieldsDetection.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.service.autofill;
+
+import android.annotation.TestApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.view.autofill.AutofillId;
+
+/**
+ * Class by service to improve autofillable fields detection by tracking the meaning of fields
+ * manually edited by the user (when they match values provided by the service).
+ *
+ * TODO(b/67867469):
+ * - proper javadoc
+ * - unhide / remove testApi
+ * - add FieldsDetection management so service can set it just once and reference it in further
+ * calls to improve performance (and also API to refresh it)
+ * - rename to FieldsDetectionInfo or FieldClassification? (same for CTS tests)
+ * - add FieldsDetectionUnitTest once API is well-defined
+ * @hide
+ */
+@TestApi
+public final class FieldsDetection implements Parcelable {
+
+ private final AutofillId mFieldId;
+ private final String mRemoteId;
+ private final String mValue;
+
+ /**
+ * Creates a field detection for just one field / value pair.
+ *
+ * @param fieldId autofill id of the field in the screen.
+ * @param remoteId id used by the service to identify the field later.
+ * @param value field value known to the service.
+ *
+ * TODO(b/67867469):
+ * - proper javadoc
+ * - change signature to allow more fields / values / match methods
+ * - might also need to use a builder, where the constructor is the id for the fieldsdetector
+ * - might need id for values as well
+ * - add @NonNull / check it / add unit tests
+ * - make 'value' input more generic so it can accept distance-based match and other matches
+ * - throw exception if field value is less than X characters (somewhere between 7-10)
+ * - make sure to limit total number of fields to around 10 or so
+ * - use AutofillValue instead of String (so it can compare dates, for example)
+ */
+ public FieldsDetection(AutofillId fieldId, String remoteId, String value) {
+ mFieldId = fieldId;
+ mRemoteId = remoteId;
+ mValue = value;
+ }
+
+ /** @hide */
+ public AutofillId getFieldId() {
+ return mFieldId;
+ }
+
+ /** @hide */
+ public String getRemoteId() {
+ return mRemoteId;
+ }
+
+ /** @hide */
+ public String getValue() {
+ return mValue;
+ }
+
+ /////////////////////////////////////
+ // Object "contract" methods. //
+ /////////////////////////////////////
+ @Override
+ public String toString() {
+ // Cannot disclose remoteId or value because they could contain PII
+ return new StringBuilder("FieldsDetection: [field=").append(mFieldId)
+ .append(", remoteId_length=").append(mRemoteId.length())
+ .append(", value_length=").append(mValue.length())
+ .append("]").toString();
+ }
+
+ /////////////////////////////////////
+ // Parcelable "contract" methods. //
+ /////////////////////////////////////
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel parcel, int flags) {
+ parcel.writeParcelable(mFieldId, flags);
+ parcel.writeString(mRemoteId);
+ parcel.writeString(mValue);
+ }
+
+ public static final Parcelable.Creator<FieldsDetection> CREATOR =
+ new Parcelable.Creator<FieldsDetection>() {
+ @Override
+ public FieldsDetection createFromParcel(Parcel parcel) {
+ // TODO(b/67867469): remove comment below if it does not use a builder at the end
+ // Always go through the builder to ensure the data ingested by
+ // the system obeys the contract of the builder to avoid attacks
+ // using specially crafted parcels.
+ return new FieldsDetection(parcel.readParcelable(null), parcel.readString(),
+ parcel.readString());
+ }
+
+ @Override
+ public FieldsDetection[] newArray(int size) {
+ return new FieldsDetection[size];
+ }
+ };
+}
diff --git a/core/java/android/service/autofill/FillEventHistory.java b/core/java/android/service/autofill/FillEventHistory.java
index b1857b3..736d9ef 100644
--- a/core/java/android/service/autofill/FillEventHistory.java
+++ b/core/java/android/service/autofill/FillEventHistory.java
@@ -19,6 +19,7 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.TestApi;
import android.content.IntentSender;
import android.os.Bundle;
import android.os.Parcel;
@@ -164,6 +165,10 @@
dest.writeStringList(event.mManuallyFilledDatasetIds.get(j));
}
}
+ dest.writeString(event.mDetectedRemoteId);
+ if (event.mDetectedRemoteId != null) {
+ dest.writeInt(event.mDetectedFieldScore);
+ }
}
}
}
@@ -226,6 +231,7 @@
* <p>See {@link android.view.autofill.AutofillManager} for more information about autofill
* contexts.
*/
+ // TODO(b/67867469): update with field detection behavior
public static final int TYPE_CONTEXT_COMMITTED = 4;
/** @hide */
@@ -253,6 +259,9 @@
@Nullable private final ArrayList<AutofillId> mManuallyFilledFieldIds;
@Nullable private final ArrayList<ArrayList<String>> mManuallyFilledDatasetIds;
+ @Nullable private final String mDetectedRemoteId;
+ private final int mDetectedFieldScore;
+
/**
* Returns the type of the event.
*
@@ -355,6 +364,39 @@
}
/**
+ * Gets the results of the last {@link FieldsDetection} request.
+ *
+ * @return map of edit-distance match ({@code 0} means full match,
+ * {@code 1} means 1 character different, etc...) by remote id (as set in the
+ * {@link FieldsDetection} constructor), or {@code null} if none of the user-input values
+ * matched the requested detection.
+ *
+ * <p><b>Note: </b>Only set on events of type {@link #TYPE_CONTEXT_COMMITTED}, when the
+ * service requested {@link FillResponse.Builder#setFieldsDetection(FieldsDetection) fields
+ * detection}.
+ *
+ * TODO(b/67867469):
+ * - improve javadoc
+ * - refine score meaning (for example, should 1 be different of -1?)
+ * - mention when it's set
+ * - unhide
+ * - unhide / remove testApi
+ * - add @NonNull / check it / add unit tests
+ *
+ * @hide
+ */
+ @TestApi
+ @NonNull public Map<String, Integer> getDetectedFields() {
+ if (mDetectedRemoteId == null || mDetectedFieldScore == -1) {
+ return Collections.emptyMap();
+ }
+
+ final ArrayMap<String, Integer> map = new ArrayMap<>(1);
+ map.put(mDetectedRemoteId, mDetectedFieldScore);
+ return map;
+ }
+
+ /**
* Returns which fields were available on datasets provided by the service but manually
* entered by the user.
*
@@ -430,7 +472,6 @@
* and belonged to datasets.
* @param manuallyFilledDatasetIds The ids of datasets that had values matching the
* respective entry on {@code manuallyFilledFieldIds}.
- *
* @throws IllegalArgumentException If the length of {@code changedFieldIds} and
* {@code changedDatasetIds} doesn't match.
* @throws IllegalArgumentException If the length of {@code manuallyFilledFieldIds} and
@@ -438,13 +479,15 @@
*
* @hide
*/
+ // TODO(b/67867469): document detection field parameters once stable
public Event(int eventType, @Nullable String datasetId, @Nullable Bundle clientState,
@Nullable List<String> selectedDatasetIds,
@Nullable ArraySet<String> ignoredDatasetIds,
@Nullable ArrayList<AutofillId> changedFieldIds,
@Nullable ArrayList<String> changedDatasetIds,
@Nullable ArrayList<AutofillId> manuallyFilledFieldIds,
- @Nullable ArrayList<ArrayList<String>> manuallyFilledDatasetIds) {
+ @Nullable ArrayList<ArrayList<String>> manuallyFilledDatasetIds,
+ @Nullable String detectedRemoteId, int detectedFieldScore) {
mEventType = Preconditions.checkArgumentInRange(eventType, 0, TYPE_CONTEXT_COMMITTED,
"eventType");
mDatasetId = datasetId;
@@ -467,6 +510,8 @@
}
mManuallyFilledFieldIds = manuallyFilledFieldIds;
mManuallyFilledDatasetIds = manuallyFilledDatasetIds;
+ mDetectedRemoteId = detectedRemoteId;
+ mDetectedFieldScore = detectedFieldScore;
}
@Override
@@ -479,6 +524,8 @@
+ ", changedDatasetsIds=" + mChangedDatasetIds
+ ", manuallyFilledFieldIds=" + mManuallyFilledFieldIds
+ ", manuallyFilledDatasetIds=" + mManuallyFilledDatasetIds
+ + ", detectedRemoteId=" + mDetectedRemoteId
+ + ", detectedFieldScore=" + mDetectedFieldScore
+ "]";
}
}
@@ -514,11 +561,15 @@
} else {
manuallyFilledDatasetIds = null;
}
+ final String detectedRemoteId = parcel.readString();
+ final int detectedFieldScore = detectedRemoteId == null ? -1
+ : parcel.readInt();
selection.addEvent(new Event(eventType, datasetId, clientState,
selectedDatasetIds, ignoredDatasets,
changedFieldIds, changedDatasetIds,
- manuallyFilledFieldIds, manuallyFilledDatasetIds));
+ manuallyFilledFieldIds, manuallyFilledDatasetIds,
+ detectedRemoteId, detectedFieldScore));
}
return selection;
}
diff --git a/core/java/android/service/autofill/FillResponse.java b/core/java/android/service/autofill/FillResponse.java
index 2f6342a..4e6a884 100644
--- a/core/java/android/service/autofill/FillResponse.java
+++ b/core/java/android/service/autofill/FillResponse.java
@@ -22,6 +22,7 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.TestApi;
import android.app.Activity;
import android.content.IntentSender;
import android.content.pm.ParceledListSlice;
@@ -75,6 +76,7 @@
private final @Nullable AutofillId[] mAuthenticationIds;
private final @Nullable AutofillId[] mIgnoredIds;
private final long mDisableDuration;
+ private final @Nullable FieldsDetection mFieldsDetection;
private final int mFlags;
private int mRequestId;
@@ -87,6 +89,7 @@
mAuthenticationIds = builder.mAuthenticationIds;
mIgnoredIds = builder.mIgnoredIds;
mDisableDuration = builder.mDisableDuration;
+ mFieldsDetection = builder.mFieldsDetection;
mFlags = builder.mFlags;
mRequestId = INVALID_REQUEST_ID;
}
@@ -132,6 +135,11 @@
}
/** @hide */
+ public @Nullable FieldsDetection getFieldsDetection() {
+ return mFieldsDetection;
+ }
+
+ /** @hide */
public int getFlags() {
return mFlags;
}
@@ -167,6 +175,7 @@
private AutofillId[] mAuthenticationIds;
private AutofillId[] mIgnoredIds;
private long mDisableDuration;
+ private FieldsDetection mFieldsDetection;
private int mFlags;
private boolean mDestroyed;
@@ -315,6 +324,25 @@
}
/**
+ * TODO(b/67867469):
+ * - javadoc it
+ * - javadoc how to check results
+ * - unhide
+ * - unhide / remove testApi
+ * - throw exception (and document) if response has datasets or saveinfo
+ * - throw exception (and document) if id on fieldsDetection is ignored
+ *
+ * @hide
+ */
+ @TestApi
+ public Builder setFieldsDetection(@NonNull FieldsDetection fieldsDetection) {
+ throwIfDestroyed();
+ throwIfDisableAutofillCalled();
+ mFieldsDetection = Preconditions.checkNotNull(fieldsDetection);
+ return this;
+ }
+
+ /**
* Sets flags changing the response behavior.
*
* @param flags a combination of {@link #FLAG_TRACK_CONTEXT_COMMITED} and
@@ -365,7 +393,8 @@
if (duration <= 0) {
throw new IllegalArgumentException("duration must be greater than 0");
}
- if (mAuthentication != null || mDatasets != null || mSaveInfo != null) {
+ if (mAuthentication != null || mDatasets != null || mSaveInfo != null
+ || mFieldsDetection != null) {
throw new IllegalStateException("disableAutofill() must be the only method called");
}
@@ -388,11 +417,11 @@
*/
public FillResponse build() {
throwIfDestroyed();
-
if (mAuthentication == null && mDatasets == null && mSaveInfo == null
- && mDisableDuration == 0) {
- throw new IllegalStateException("need to provide at least one DataSet or a "
- + "SaveInfo or an authentication with a presentation or disable autofill");
+ && mDisableDuration == 0 && mFieldsDetection == null) {
+ throw new IllegalStateException("need to provide: at least one DataSet, or a "
+ + "SaveInfo, or an authentication with a presentation, "
+ + "or a FieldsDetection, or disable autofill");
}
mDestroyed = true;
return new FillResponse(this);
@@ -430,6 +459,7 @@
.append(", ignoredIds=").append(Arrays.toString(mIgnoredIds))
.append(", disableDuration=").append(mDisableDuration)
.append(", flags=").append(mFlags)
+ .append(", fieldDetection=").append(mFieldsDetection)
.append("]")
.toString();
}
@@ -453,6 +483,7 @@
parcel.writeParcelable(mPresentation, flags);
parcel.writeParcelableArray(mIgnoredIds, flags);
parcel.writeLong(mDisableDuration);
+ parcel.writeParcelable(mFieldsDetection, flags);
parcel.writeInt(mFlags);
parcel.writeInt(mRequestId);
}
@@ -488,6 +519,10 @@
if (disableDuration > 0) {
builder.disableAutofill(disableDuration);
}
+ final FieldsDetection fieldsDetection = parcel.readParcelable(null);
+ if (fieldsDetection != null) {
+ builder.setFieldsDetection(fieldsDetection);
+ }
builder.setFlags(parcel.readInt());
final FillResponse response = builder.build();
diff --git a/core/java/android/service/euicc/EuiccService.java b/core/java/android/service/euicc/EuiccService.java
index 0c2e4b7..cd233b8 100644
--- a/core/java/android/service/euicc/EuiccService.java
+++ b/core/java/android/service/euicc/EuiccService.java
@@ -97,6 +97,10 @@
public static final String ACTION_RESOLVE_NO_PRIVILEGES =
"android.service.euicc.action.RESOLVE_NO_PRIVILEGES";
+ /** Ask the user to input carrier confirmation code. */
+ public static final String ACTION_RESOLVE_CONFIRMATION_CODE =
+ "android.service.euicc.action.RESOLVE_CONFIRMATION_CODE";
+
/** Intent extra set for resolution requests containing the package name of the calling app. */
public static final String EXTRA_RESOLUTION_CALLING_PACKAGE =
"android.service.euicc.extra.RESOLUTION_CALLING_PACKAGE";
@@ -105,6 +109,8 @@
public static final int RESULT_OK = 0;
/** Result code indicating that an active SIM must be deactivated to perform the operation. */
public static final int RESULT_MUST_DEACTIVATE_SIM = -1;
+ /** Result code indicating that the user must input a carrier confirmation code. */
+ public static final int RESULT_NEED_CONFIRMATION_CODE = -2;
// New predefined codes should have negative values.
/** Start of implementation-specific error results. */
@@ -119,10 +125,13 @@
RESOLUTION_ACTIONS = new ArraySet<>();
RESOLUTION_ACTIONS.add(EuiccService.ACTION_RESOLVE_DEACTIVATE_SIM);
RESOLUTION_ACTIONS.add(EuiccService.ACTION_RESOLVE_NO_PRIVILEGES);
+ RESOLUTION_ACTIONS.add(EuiccService.ACTION_RESOLVE_CONFIRMATION_CODE);
}
/** Boolean extra for resolution actions indicating whether the user granted consent. */
public static final String RESOLUTION_EXTRA_CONSENT = "consent";
+ /** String extra for resolution actions indicating the carrier confirmation code. */
+ public static final String RESOLUTION_EXTRA_CONFIRMATION_CODE = "confirmation_code";
private final IEuiccService.Stub mStubWrapper;
diff --git a/core/java/android/service/notification/ConditionProviderService.java b/core/java/android/service/notification/ConditionProviderService.java
index 3e992ec..080482b 100644
--- a/core/java/android/service/notification/ConditionProviderService.java
+++ b/core/java/android/service/notification/ConditionProviderService.java
@@ -18,6 +18,7 @@
import android.annotation.SdkConstant;
import android.annotation.SystemApi;
+import android.app.ActivityManager;
import android.app.INotificationManager;
import android.app.Service;
import android.content.ComponentName;
@@ -56,6 +57,8 @@
* </meta-data>
* </service></pre>
*
+ * <p> Condition providers cannot be bound by the system on
+ * {@link ActivityManager#isLowRamDevice() low ram} devices</p>
*/
public abstract class ConditionProviderService extends Service {
private final String TAG = ConditionProviderService.class.getSimpleName()
diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java
index 08d3118..dac663e7 100644
--- a/core/java/android/service/notification/NotificationListenerService.java
+++ b/core/java/android/service/notification/NotificationListenerService.java
@@ -21,6 +21,7 @@
import android.annotation.SdkConstant;
import android.annotation.SystemApi;
import android.annotation.TestApi;
+import android.app.ActivityManager;
import android.app.INotificationManager;
import android.app.Notification;
import android.app.Notification.Builder;
@@ -82,6 +83,8 @@
* method is the <i>only</i> one that is safe to call before {@link #onListenerConnected()}
* or after {@link #onListenerDisconnected()}.
* </p>
+ * <p> Notification listeners cannot get notification access or be bound by the system on
+ * {@link ActivityManager#isLowRamDevice() low ram} devices</p>
*/
public abstract class NotificationListenerService extends Service {
diff --git a/core/java/android/util/TimeUtils.java b/core/java/android/util/TimeUtils.java
index 2b03ed6..cc4a0b6 100644
--- a/core/java/android/util/TimeUtils.java
+++ b/core/java/android/util/TimeUtils.java
@@ -340,6 +340,14 @@
}
/** @hide Just for debugging; not internationalized. */
+ public static String formatDuration(long duration) {
+ synchronized (sFormatSync) {
+ int len = formatDurationLocked(duration, 0);
+ return new String(sFormatStr, 0, len);
+ }
+ }
+
+ /** @hide Just for debugging; not internationalized. */
public static void formatDuration(long duration, PrintWriter pw) {
formatDuration(duration, pw, 0);
}
diff --git a/core/java/android/util/apk/ApkVerityBuilder.java b/core/java/android/util/apk/ApkVerityBuilder.java
index 0b9552e..7412ef4 100644
--- a/core/java/android/util/apk/ApkVerityBuilder.java
+++ b/core/java/android/util/apk/ApkVerityBuilder.java
@@ -20,6 +20,7 @@
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
+import java.security.DigestException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
@@ -66,7 +67,7 @@
*/
static ApkVerityResult generateApkVerity(RandomAccessFile apk,
SignatureInfo signatureInfo, ByteBufferFactory bufferFactory)
- throws IOException, SecurityException, NoSuchAlgorithmException {
+ throws IOException, SecurityException, NoSuchAlgorithmException, DigestException {
assertSigningBlockAlignedAndHasFullPages(signatureInfo);
long signingBlockSize =
@@ -112,6 +113,7 @@
private final ByteBuffer mOutput;
private final MessageDigest mMd;
+ private final byte[] mDigestBuffer = new byte[DIGEST_SIZE_BYTES];
private final byte[] mSalt;
private BufferedDigester(byte[] salt, ByteBuffer output) throws NoSuchAlgorithmException {
@@ -129,21 +131,22 @@
* consumption will continuous from there.
*/
@Override
- public void consume(ByteBuffer buffer) {
+ public void consume(ByteBuffer buffer) throws DigestException {
int offset = buffer.position();
int remaining = buffer.remaining();
while (remaining > 0) {
int allowance = (int) Math.min(remaining, BUFFER_SIZE - mBytesDigestedSinceReset);
- mMd.update(slice(buffer, offset, offset + allowance));
+ // Optimization: set the buffer limit to avoid allocating a new ByteBuffer object.
+ buffer.limit(buffer.position() + allowance);
+ mMd.update(buffer);
offset += allowance;
remaining -= allowance;
mBytesDigestedSinceReset += allowance;
if (mBytesDigestedSinceReset == BUFFER_SIZE) {
- byte[] digest = mMd.digest();
- mOutput.put(digest);
-
- mMd.reset();
+ mMd.digest(mDigestBuffer, 0, mDigestBuffer.length);
+ mOutput.put(mDigestBuffer);
+ // After digest, MessageDigest resets automatically, so no need to reset again.
mMd.update(mSalt);
mBytesDigestedSinceReset = 0;
}
@@ -152,12 +155,12 @@
/** Finish the current digestion if any. */
@Override
- public void finish() {
+ public void finish() throws DigestException {
if (mBytesDigestedSinceReset == 0) {
return;
}
- byte[] digest = mMd.digest();
- mOutput.put(digest);
+ mMd.digest(mDigestBuffer, 0, mDigestBuffer.length);
+ mOutput.put(mDigestBuffer);
}
private void fillUpLastOutputChunk() {
@@ -174,7 +177,7 @@
* digest the remaining.
*/
private static void consumeByChunk(DataDigester digester, DataSource source, int chunkSize)
- throws IOException {
+ throws IOException, DigestException {
long inputRemaining = source.size();
long inputOffset = 0;
while (inputRemaining > 0) {
@@ -191,7 +194,7 @@
private static void generateApkVerityDigestAtLeafLevel(RandomAccessFile apk,
SignatureInfo signatureInfo, byte[] salt, ByteBuffer output)
- throws IOException, NoSuchAlgorithmException {
+ throws IOException, NoSuchAlgorithmException, DigestException {
BufferedDigester digester = new BufferedDigester(salt, output);
// 1. Digest from the beginning of the file, until APK Signing Block is reached.
@@ -230,7 +233,7 @@
private static byte[] generateApkVerityTree(RandomAccessFile apk, SignatureInfo signatureInfo,
byte[] salt, int[] levelOffset, ByteBuffer output)
- throws IOException, NoSuchAlgorithmException {
+ throws IOException, NoSuchAlgorithmException, DigestException {
// 1. Digest the apk to generate the leaf level hashes.
generateApkVerityDigestAtLeafLevel(apk, signatureInfo, salt, slice(output,
levelOffset[levelOffset.length - 2], levelOffset[levelOffset.length - 1]));
@@ -252,6 +255,7 @@
byte[] rootHash = new byte[DIGEST_SIZE_BYTES];
BufferedDigester digester = new BufferedDigester(salt, ByteBuffer.wrap(rootHash));
digester.consume(slice(output, 0, CHUNK_SIZE_BYTES));
+ digester.finish();
return rootHash;
}
diff --git a/core/java/android/util/apk/ByteBufferDataSource.java b/core/java/android/util/apk/ByteBufferDataSource.java
index c2b9eff..3976568 100644
--- a/core/java/android/util/apk/ByteBufferDataSource.java
+++ b/core/java/android/util/apk/ByteBufferDataSource.java
@@ -18,6 +18,7 @@
import java.io.IOException;
import java.nio.ByteBuffer;
+import java.security.DigestException;
/**
* {@link DataSource} which provides data from a {@link ByteBuffer}.
@@ -42,7 +43,7 @@
@Override
public void feedIntoDataDigester(DataDigester md, long offset, int size)
- throws IOException {
+ throws IOException, DigestException {
// There's no way to tell MessageDigest to read data from ByteBuffer from a position
// other than the buffer's current position. We thus need to change the buffer's
// position to match the requested offset.
diff --git a/core/java/android/util/apk/DataDigester.java b/core/java/android/util/apk/DataDigester.java
index 74dce7e..278be80 100644
--- a/core/java/android/util/apk/DataDigester.java
+++ b/core/java/android/util/apk/DataDigester.java
@@ -17,11 +17,12 @@
package android.util.apk;
import java.nio.ByteBuffer;
+import java.security.DigestException;
interface DataDigester {
/** Consumes the {@link ByteBuffer}. */
- void consume(ByteBuffer buffer);
+ void consume(ByteBuffer buffer) throws DigestException;
/** Finishes the digestion. Must be called after the last {@link #consume(ByteBuffer)}. */
- void finish();
+ void finish() throws DigestException;
}
diff --git a/core/java/android/util/apk/DataSource.java b/core/java/android/util/apk/DataSource.java
index 2b39f49..82f3800 100644
--- a/core/java/android/util/apk/DataSource.java
+++ b/core/java/android/util/apk/DataSource.java
@@ -17,6 +17,7 @@
package android.util.apk;
import java.io.IOException;
+import java.security.DigestException;
/** Source of data to be digested. */
interface DataSource {
@@ -32,5 +33,6 @@
* @param offset offset of the region inside this data source.
* @param size size (in bytes) of the region.
*/
- void feedIntoDataDigester(DataDigester md, long offset, int size) throws IOException;
+ void feedIntoDataDigester(DataDigester md, long offset, int size)
+ throws IOException, DigestException;
}
diff --git a/core/java/android/util/apk/MemoryMappedFileDataSource.java b/core/java/android/util/apk/MemoryMappedFileDataSource.java
index 04a1c6b..8d2b1e32 100644
--- a/core/java/android/util/apk/MemoryMappedFileDataSource.java
+++ b/core/java/android/util/apk/MemoryMappedFileDataSource.java
@@ -24,6 +24,7 @@
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.DirectByteBuffer;
+import java.security.DigestException;
/**
* {@link DataSource} which provides data from a file descriptor by memory-mapping the sections
@@ -55,7 +56,7 @@
@Override
public void feedIntoDataDigester(DataDigester md, long offset, int size)
- throws IOException {
+ throws IOException, DigestException {
// IMPLEMENTATION NOTE: After a lot of experimentation, the implementation of this
// method was settled on a straightforward mmap with prefaulting.
//
diff --git a/core/java/android/view/DisplayFrames.java b/core/java/android/view/DisplayFrames.java
new file mode 100644
index 0000000..e6861d8
--- /dev/null
+++ b/core/java/android/view/DisplayFrames.java
@@ -0,0 +1,189 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.view;
+
+import static android.view.Surface.ROTATION_180;
+import static android.view.Surface.ROTATION_270;
+import static android.view.Surface.ROTATION_90;
+import static com.android.server.wm.proto.DisplayFramesProto.STABLE_BOUNDS;
+
+import android.graphics.Rect;
+import android.util.proto.ProtoOutputStream;
+
+import java.io.PrintWriter;
+
+/**
+ * Container class for all the display frames that affect how we do window layout on a display.
+ * @hide
+ */
+public class DisplayFrames {
+ public final int mDisplayId;
+
+ /**
+ * The current size of the screen; really; extends into the overscan area of the screen and
+ * doesn't account for any system elements like the status bar.
+ */
+ public final Rect mOverscan = new Rect();
+
+ /**
+ * The current visible size of the screen; really; (ir)regardless of whether the status bar can
+ * be hidden but not extending into the overscan area.
+ */
+ public final Rect mUnrestricted = new Rect();
+
+ /** Like mOverscan*, but allowed to move into the overscan region where appropriate. */
+ public final Rect mRestrictedOverscan = new Rect();
+
+ /**
+ * The current size of the screen; these may be different than (0,0)-(dw,dh) if the status bar
+ * can't be hidden; in that case it effectively carves out that area of the display from all
+ * other windows.
+ */
+ public final Rect mRestricted = new Rect();
+
+ /**
+ * During layout, the current screen borders accounting for any currently visible system UI
+ * elements.
+ */
+ public final Rect mSystem = new Rect();
+
+ /** For applications requesting stable content insets, these are them. */
+ public final Rect mStable = new Rect();
+
+ /**
+ * For applications requesting stable content insets but have also set the fullscreen window
+ * flag, these are the stable dimensions without the status bar.
+ */
+ public final Rect mStableFullscreen = new Rect();
+
+ /**
+ * During layout, the current screen borders with all outer decoration (status bar, input method
+ * dock) accounted for.
+ */
+ public final Rect mCurrent = new Rect();
+
+ /**
+ * During layout, the frame in which content should be displayed to the user, accounting for all
+ * screen decoration except for any space they deem as available for other content. This is
+ * usually the same as mCurrent*, but may be larger if the screen decor has supplied content
+ * insets.
+ */
+ public final Rect mContent = new Rect();
+
+ /**
+ * During layout, the frame in which voice content should be displayed to the user, accounting
+ * for all screen decoration except for any space they deem as available for other content.
+ */
+ public final Rect mVoiceContent = new Rect();
+
+ /** During layout, the current screen borders along which input method windows are placed. */
+ public final Rect mDock = new Rect();
+
+ private final Rect mDisplayInfoOverscan = new Rect();
+ private final Rect mRotatedDisplayInfoOverscan = new Rect();
+ public int mDisplayWidth;
+ public int mDisplayHeight;
+
+ public int mRotation;
+
+ public DisplayFrames(int displayId, DisplayInfo info) {
+ mDisplayId = displayId;
+ onDisplayInfoUpdated(info);
+ }
+
+ public void onDisplayInfoUpdated(DisplayInfo info) {
+ mDisplayWidth = info.logicalWidth;
+ mDisplayHeight = info.logicalHeight;
+ mRotation = info.rotation;
+ mDisplayInfoOverscan.set(
+ info.overscanLeft, info.overscanTop, info.overscanRight, info.overscanBottom);
+ }
+
+ public void onBeginLayout() {
+ switch (mRotation) {
+ case ROTATION_90:
+ mRotatedDisplayInfoOverscan.left = mDisplayInfoOverscan.top;
+ mRotatedDisplayInfoOverscan.top = mDisplayInfoOverscan.right;
+ mRotatedDisplayInfoOverscan.right = mDisplayInfoOverscan.bottom;
+ mRotatedDisplayInfoOverscan.bottom = mDisplayInfoOverscan.left;
+ break;
+ case ROTATION_180:
+ mRotatedDisplayInfoOverscan.left = mDisplayInfoOverscan.right;
+ mRotatedDisplayInfoOverscan.top = mDisplayInfoOverscan.bottom;
+ mRotatedDisplayInfoOverscan.right = mDisplayInfoOverscan.left;
+ mRotatedDisplayInfoOverscan.bottom = mDisplayInfoOverscan.top;
+ break;
+ case ROTATION_270:
+ mRotatedDisplayInfoOverscan.left = mDisplayInfoOverscan.bottom;
+ mRotatedDisplayInfoOverscan.top = mDisplayInfoOverscan.left;
+ mRotatedDisplayInfoOverscan.right = mDisplayInfoOverscan.top;
+ mRotatedDisplayInfoOverscan.bottom = mDisplayInfoOverscan.right;
+ break;
+ default:
+ mRotatedDisplayInfoOverscan.set(mDisplayInfoOverscan);
+ break;
+ }
+
+ mRestrictedOverscan.set(0, 0, mDisplayWidth, mDisplayHeight);
+ mOverscan.set(mRestrictedOverscan);
+ mSystem.set(mRestrictedOverscan);
+ mUnrestricted.set(mRotatedDisplayInfoOverscan);
+ mUnrestricted.right = mDisplayWidth - mUnrestricted.right;
+ mUnrestricted.bottom = mDisplayHeight - mUnrestricted.bottom;
+ mRestricted.set(mUnrestricted);
+ mDock.set(mUnrestricted);
+ mContent.set(mUnrestricted);
+ mVoiceContent.set(mUnrestricted);
+ mStable.set(mUnrestricted);
+ mStableFullscreen.set(mUnrestricted);
+ mCurrent.set(mUnrestricted);
+
+ }
+
+ public int getInputMethodWindowVisibleHeight() {
+ return mDock.bottom - mCurrent.bottom;
+ }
+
+ public void writeToProto(ProtoOutputStream proto, long fieldId) {
+ final long token = proto.start(fieldId);
+ mStable.writeToProto(proto, STABLE_BOUNDS);
+ proto.end(token);
+ }
+
+ public void dump(String prefix, PrintWriter pw) {
+ pw.println(prefix + "DisplayFrames w=" + mDisplayWidth + " h=" + mDisplayHeight
+ + " r=" + mRotation);
+ final String myPrefix = prefix + " ";
+ dumpFrame(mStable, "mStable", myPrefix, pw);
+ dumpFrame(mStableFullscreen, "mStableFullscreen", myPrefix, pw);
+ dumpFrame(mDock, "mDock", myPrefix, pw);
+ dumpFrame(mCurrent, "mCurrent", myPrefix, pw);
+ dumpFrame(mSystem, "mSystem", myPrefix, pw);
+ dumpFrame(mContent, "mContent", myPrefix, pw);
+ dumpFrame(mVoiceContent, "mVoiceContent", myPrefix, pw);
+ dumpFrame(mOverscan, "mOverscan", myPrefix, pw);
+ dumpFrame(mRestrictedOverscan, "mRestrictedOverscan", myPrefix, pw);
+ dumpFrame(mRestricted, "mRestricted", myPrefix, pw);
+ dumpFrame(mUnrestricted, "mUnrestricted", myPrefix, pw);
+ dumpFrame(mDisplayInfoOverscan, "mDisplayInfoOverscan", myPrefix, pw);
+ dumpFrame(mRotatedDisplayInfoOverscan, "mRotatedDisplayInfoOverscan", myPrefix, pw);
+ }
+
+ private void dumpFrame(Rect frame, String name, String prefix, PrintWriter pw) {
+ pw.print(prefix + name + "="); frame.printShortString(pw); pw.println();
+ }
+}
diff --git a/core/java/android/view/ThreadedRenderer.java b/core/java/android/view/ThreadedRenderer.java
index 2166f6e..7c76bab 100644
--- a/core/java/android/view/ThreadedRenderer.java
+++ b/core/java/android/view/ThreadedRenderer.java
@@ -70,6 +70,7 @@
* Name of the file that holds the shaders cache.
*/
private static final String CACHE_PATH_SHADERS = "com.android.opengl.shaders_cache";
+ private static final String CACHE_PATH_SKIASHADERS = "com.android.skia.shaders_cache";
/**
* System property used to enable or disable threaded rendering profiling.
@@ -272,7 +273,9 @@
* @hide
*/
public static void setupDiskCache(File cacheDir) {
- ThreadedRenderer.setupShadersDiskCache(new File(cacheDir, CACHE_PATH_SHADERS).getAbsolutePath());
+ ThreadedRenderer.setupShadersDiskCache(
+ new File(cacheDir, CACHE_PATH_SHADERS).getAbsolutePath(),
+ new File(cacheDir, CACHE_PATH_SKIASHADERS).getAbsolutePath());
}
/**
@@ -1007,7 +1010,7 @@
/** Not actually public - internal use only. This doc to make lint happy */
public static native void disableVsync();
- static native void setupShadersDiskCache(String cacheFile);
+ static native void setupShadersDiskCache(String cacheFile, String skiaCacheFile);
private static native void nRotateProcessStatsBuffer();
private static native void nSetProcessStatsBuffer(int fd);
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index c043dca..e12c0b0 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -14218,6 +14218,7 @@
*/
public void setScaleX(float scaleX) {
if (scaleX != getScaleX()) {
+ requireIsFinite(scaleX, "scaleX");
invalidateViewProperty(true, false);
mRenderNode.setScaleX(scaleX);
invalidateViewProperty(false, true);
@@ -14254,6 +14255,7 @@
*/
public void setScaleY(float scaleY) {
if (scaleY != getScaleY()) {
+ requireIsFinite(scaleY, "scaleY");
invalidateViewProperty(true, false);
mRenderNode.setScaleY(scaleY);
invalidateViewProperty(false, true);
@@ -14803,6 +14805,15 @@
}
}
+ private static void requireIsFinite(float transform, String propertyName) {
+ if (Float.isNaN(transform)) {
+ throw new IllegalArgumentException("Cannot set '" + propertyName + "' to Float.NaN");
+ }
+ if (Float.isInfinite(transform)) {
+ throw new IllegalArgumentException("Cannot set '" + propertyName + "' to infinity");
+ }
+ }
+
/**
* The visual x position of this view, in pixels. This is equivalent to the
* {@link #setTranslationX(float) translationX} property plus the current
@@ -14889,6 +14900,7 @@
*/
public void setElevation(float elevation) {
if (elevation != getElevation()) {
+ requireIsFinite(elevation, "elevation");
invalidateViewProperty(true, false);
mRenderNode.setElevation(elevation);
invalidateViewProperty(false, true);
@@ -14981,6 +14993,7 @@
*/
public void setTranslationZ(float translationZ) {
if (translationZ != getTranslationZ()) {
+ requireIsFinite(translationZ, "translationZ");
invalidateViewProperty(true, false);
mRenderNode.setTranslationZ(translationZ);
invalidateViewProperty(false, true);
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 37829f0..e30496f 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -513,7 +513,7 @@
mDisplayManager = (DisplayManager)context.getSystemService(Context.DISPLAY_SERVICE);
if (!sCompatibilityDone) {
- sAlwaysAssignFocus = mTargetSdkVersion < Build.VERSION_CODES.P;
+ sAlwaysAssignFocus = true;
sCompatibilityDone = true;
}
diff --git a/core/java/android/view/WindowManagerGlobal.java b/core/java/android/view/WindowManagerGlobal.java
index c7e8dee..cca66d6 100644
--- a/core/java/android/view/WindowManagerGlobal.java
+++ b/core/java/android/view/WindowManagerGlobal.java
@@ -605,9 +605,10 @@
public void setStoppedState(IBinder token, boolean stopped) {
synchronized (mLock) {
int count = mViews.size();
- for (int i = 0; i < count; i++) {
+ for (int i = count - 1; i >= 0; i--) {
if (token == null || mParams.get(i).token == token) {
ViewRootImpl root = mRoots.get(i);
+ // Client might remove the view by "stopped" event.
root.setWindowStopped(stopped);
}
}
diff --git a/core/java/android/view/WindowManagerInternal.java b/core/java/android/view/WindowManagerInternal.java
index 69cc100..cd1b190 100644
--- a/core/java/android/view/WindowManagerInternal.java
+++ b/core/java/android/view/WindowManagerInternal.java
@@ -18,6 +18,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.content.ClipData;
import android.graphics.Rect;
import android.graphics.Region;
import android.hardware.display.DisplayManagerInternal;
@@ -140,6 +141,30 @@
}
/**
+ * An interface to customize drag and drop behaviors.
+ */
+ public interface IDragDropCallback {
+ /**
+ * Called when drag operation is started.
+ */
+ default boolean performDrag(IWindow window, IBinder dragToken,
+ int touchSource, float touchX, float touchY, float thumbCenterX, float thumbCenterY,
+ ClipData data) {
+ return true;
+ }
+
+ /**
+ * Called when drop result is reported.
+ */
+ default void reportDropResult(IWindow window, boolean consumed) {}
+
+ /**
+ * Called when drag operation is cancelled.
+ */
+ default void cancelDragAndDrop(IBinder dragToken) {}
+ }
+
+ /**
* Request that the window manager call
* {@link DisplayManagerInternal#performTraversalInTransactionFromWindowManager}
* within a surface transaction at a later time.
@@ -225,9 +250,6 @@
*/
public abstract boolean isKeyguardLocked();
- /** @return {@code true} if the keyguard is going away. */
- public abstract boolean isKeyguardGoingAway();
-
/**
* @return Whether the keyguard is showing and not occluded.
*/
@@ -354,4 +376,9 @@
* {@param vr2dDisplayId}.
*/
public abstract void setVr2dDisplayId(int vr2dDisplayId);
+
+ /**
+ * Sets callback to DragDropController.
+ */
+ public abstract void registerDragDropControllerCallback(IDragDropCallback callback);
}
diff --git a/core/java/android/view/WindowManagerPolicy.java b/core/java/android/view/WindowManagerPolicy.java
index ebe3633..534335b 100644
--- a/core/java/android/view/WindowManagerPolicy.java
+++ b/core/java/android/view/WindowManagerPolicy.java
@@ -66,7 +66,6 @@
import android.annotation.IntDef;
import android.annotation.Nullable;
import android.annotation.SystemApi;
-import android.app.ActivityManager.StackId;
import android.content.Context;
import android.content.pm.ActivityInfo;
import android.content.res.CompatibilityInfo;
@@ -722,12 +721,6 @@
public void setInitialDisplaySize(Display display, int width, int height, int density);
/**
- * Called by window manager to set the overscan region that should be used for the
- * given display.
- */
- public void setDisplayOverscan(Display display, int left, int top, int right, int bottom);
-
- /**
* Check permissions when adding a window.
*
* @param attrs The window's LayoutParams.
@@ -1173,14 +1166,10 @@
/**
* Called when layout of the windows is about to start.
*
- * @param displayId Id of the display we are doing layout on.
- * @param displayWidth The current full width of the screen.
- * @param displayHeight The current full height of the screen.
- * @param displayRotation The current rotation being applied to the base window.
+ * @param displayFrames frames of the display we are doing layout on.
* @param uiMode The current uiMode in configuration.
*/
- public void beginLayoutLw(int displayId, int displayWidth, int displayHeight,
- int displayRotation, int uiMode);
+ default void beginLayoutLw(DisplayFrames displayFrames, int uiMode) {}
/**
* Returns the bottom-most layer of the system decor, above which no policy decor should
@@ -1189,37 +1178,28 @@
public int getSystemDecorLayerLw();
/**
- * Return the rectangle of the screen that is available for applications to run in.
- * This will be called immediately after {@link #beginLayoutLw}.
- *
- * @param r The rectangle to be filled with the boundaries available to applications.
- */
- public void getContentRectLw(Rect r);
-
- /**
- * Called for each window attached to the window manager as layout is
- * proceeding. The implementation of this function must take care of
- * setting the window's frame, either here or in finishLayout().
+ * Called for each window attached to the window manager as layout is proceeding. The
+ * implementation of this function must take care of setting the window's frame, either here or
+ * in finishLayout().
*
* @param win The window being positioned.
* @param attached For sub-windows, the window it is attached to; this
* window will already have had layoutWindow() called on it
* so you can use its Rect. Otherwise null.
+ * @param displayFrames The display frames.
*/
- public void layoutWindowLw(WindowState win, WindowState attached);
+ default void layoutWindowLw(
+ WindowState win, WindowState attached, DisplayFrames displayFrames) {}
/**
- * Return the insets for the areas covered by system windows. These values
- * are computed on the most recent layout, so they are not guaranteed to
- * be correct.
+ * Return the insets for the areas covered by system windows. These values are computed on the
+ * most recent layout, so they are not guaranteed to be correct.
*
* @param attrs The LayoutParams of the window.
* @param taskBounds The bounds of the task this window is on or {@code null} if no task is
* associated with the window.
- * @param displayRotation Rotation of the display.
- * @param displayWidth The width of the display.
- * @param displayHeight The height of the display.
+ * @param displayFrames display frames.
* @param outContentInsets The areas covered by system windows, expressed as positive insets.
* @param outStableInsets The areas covered by stable system windows irrespective of their
* current visibility. Expressed as positive insets.
@@ -1227,16 +1207,11 @@
* @return Whether to always consume the navigation bar.
* See {@link #isNavBarForcedShownLw(WindowState)}.
*/
- public boolean getInsetHintLw(WindowManager.LayoutParams attrs, Rect taskBounds,
- int displayRotation, int displayWidth, int displayHeight, Rect outContentInsets,
- Rect outStableInsets, Rect outOutsets);
-
- /**
- * Called when layout of the windows is finished. After this function has
- * returned, all windows given to layoutWindow() <em>must</em> have had a
- * frame assigned.
- */
- public void finishLayoutLw();
+ default boolean getInsetHintLw(WindowManager.LayoutParams attrs, Rect taskBounds,
+ DisplayFrames displayFrames, Rect outContentInsets, Rect outStableInsets,
+ Rect outOutsets) {
+ return false;
+ }
/** Layout state may have changed (so another layout will be performed) */
static final int FINISH_LAYOUT_REDO_LAYOUT = 0x0001;
@@ -1653,11 +1628,6 @@
public void showGlobalActions();
/**
- * @return The current height of the input method window.
- */
- public int getInputMethodWindowVisibleHeightLw();
-
- /**
* Called when the current user changes. Guaranteed to be called before the broadcast
* of the new user id is made to all listeners.
*
diff --git a/core/java/android/view/textclassifier/TextClassifier.java b/core/java/android/view/textclassifier/TextClassifier.java
index e35bb92..aeb8489 100644
--- a/core/java/android/view/textclassifier/TextClassifier.java
+++ b/core/java/android/view/textclassifier/TextClassifier.java
@@ -133,27 +133,6 @@
}
/**
- * Returns a {@link LinksInfo} that may be applied to the text to annotate it with links
- * information.
- *
- * @param text the text to generate annotations for
- * @param linkMask See {@link android.text.util.Linkify} for a list of linkMasks that may be
- * specified. Subclasses of this interface may specify additional linkMasks
- * @param defaultLocales ordered list of locale preferences that can be used to disambiguate
- * the provided text. If no locale preferences exist, set this to null or an empty locale
- * list in which case the classifier will decide whether to use no locale information, use
- * a default locale, or use the system default.
- *
- * @throws IllegalArgumentException if text is null
- * @hide
- */
- @WorkerThread
- default LinksInfo getLinks(
- @NonNull CharSequence text, int linkMask, @Nullable LocaleList defaultLocales) {
- return LinksInfo.NO_OP;
- }
-
- /**
* Returns a {@link TextLinks} that may be applied to the text to annotate it with links
* information.
*
diff --git a/core/java/android/view/textclassifier/TextClassifierImpl.java b/core/java/android/view/textclassifier/TextClassifierImpl.java
index 9c4fd57..2ad6e02 100644
--- a/core/java/android/view/textclassifier/TextClassifierImpl.java
+++ b/core/java/android/view/textclassifier/TextClassifierImpl.java
@@ -30,13 +30,8 @@
import android.provider.Browser;
import android.provider.ContactsContract;
import android.provider.Settings;
-import android.text.Spannable;
-import android.text.TextUtils;
-import android.text.method.WordIterator;
-import android.text.style.ClickableSpan;
import android.text.util.Linkify;
import android.util.Patterns;
-import android.view.View;
import android.widget.TextViewMetrics;
import com.android.internal.annotations.GuardedBy;
@@ -46,13 +41,8 @@
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
-import java.text.BreakIterator;
import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
import java.util.HashMap;
-import java.util.LinkedHashMap;
-import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
import java.util.Map;
@@ -194,21 +184,6 @@
}
@Override
- public LinksInfo getLinks(
- @NonNull CharSequence text, int linkMask, @Nullable LocaleList defaultLocales) {
- Preconditions.checkArgument(text != null);
- try {
- return LinksInfoFactory.create(
- mContext, getSmartSelection(defaultLocales), text.toString(), linkMask);
- } catch (Throwable t) {
- // Avoid throwing from this method. Log the error.
- Log.e(LOG_TAG, "Error getting links info.", t);
- }
- // Getting here means something went wrong, return a NO_OP result.
- return TextClassifier.NO_OP.getLinks(text, linkMask, defaultLocales);
- }
-
- @Override
public TextLinks generateLinks(
@NonNull CharSequence text, @Nullable TextLinks.Options options) {
Preconditions.checkNotNull(text);
@@ -260,7 +235,9 @@
if (mSmartSelection == null || !Objects.equals(mLocale, locale)) {
destroySmartSelectionIfExistsLocked();
final ParcelFileDescriptor fd = getFdLocked(locale);
- mSmartSelection = new SmartSelection(fd.getFd());
+ final int modelFd = fd.getFd();
+ mVersion = SmartSelection.getVersion(modelFd);
+ mSmartSelection = new SmartSelection(modelFd);
closeAndLogError(fd);
mLocale = locale;
}
@@ -281,18 +258,26 @@
@GuardedBy("mSmartSelectionLock") // Do not call outside this lock.
private ParcelFileDescriptor getFdLocked(Locale locale) throws FileNotFoundException {
ParcelFileDescriptor updateFd;
+ int updateVersion = -1;
try {
updateFd = ParcelFileDescriptor.open(
new File(UPDATED_MODEL_FILE_PATH), ParcelFileDescriptor.MODE_READ_ONLY);
+ if (updateFd != null) {
+ updateVersion = SmartSelection.getVersion(updateFd.getFd());
+ }
} catch (FileNotFoundException e) {
updateFd = null;
}
ParcelFileDescriptor factoryFd;
+ int factoryVersion = -1;
try {
final String factoryModelFilePath = getFactoryModelFilePathsLocked().get(locale);
if (factoryModelFilePath != null) {
factoryFd = ParcelFileDescriptor.open(
new File(factoryModelFilePath), ParcelFileDescriptor.MODE_READ_ONLY);
+ if (factoryFd != null) {
+ factoryVersion = SmartSelection.getVersion(factoryFd.getFd());
+ }
} else {
factoryFd = null;
}
@@ -328,15 +313,11 @@
return factoryFd;
}
- final int updateVersion = SmartSelection.getVersion(updateFdInt);
- final int factoryVersion = SmartSelection.getVersion(factoryFd.getFd());
if (updateVersion > factoryVersion) {
closeAndLogError(factoryFd);
- mVersion = updateVersion;
return updateFd;
} else {
closeAndLogError(updateFd);
- mVersion = factoryVersion;
return factoryFd;
}
}
@@ -516,180 +497,6 @@
}
/**
- * Detects and creates links for specified text.
- */
- private static final class LinksInfoFactory {
-
- private LinksInfoFactory() {}
-
- public static LinksInfo create(
- Context context, SmartSelection smartSelection, String text, int linkMask) {
- final WordIterator wordIterator = new WordIterator();
- wordIterator.setCharSequence(text, 0, text.length());
- final List<SpanSpec> spans = new ArrayList<>();
- int start = 0;
- int end;
- while ((end = wordIterator.nextBoundary(start)) != BreakIterator.DONE) {
- final String token = text.substring(start, end);
- if (TextUtils.isEmpty(token)) {
- continue;
- }
-
- final int[] selection = smartSelection.suggest(text, start, end);
- final int selectionStart = selection[0];
- final int selectionEnd = selection[1];
- if (selectionStart >= 0 && selectionEnd <= text.length()
- && selectionStart <= selectionEnd) {
- final SmartSelection.ClassificationResult[] results =
- smartSelection.classifyText(
- text, selectionStart, selectionEnd,
- getHintFlags(text, selectionStart, selectionEnd));
- if (results.length > 0) {
- final String type = getHighestScoringType(results);
- if (matches(type, linkMask)) {
- // For links without disambiguation, we simply use the default intent.
- final List<Intent> intents = IntentFactory.create(
- context, type, text.substring(selectionStart, selectionEnd));
- if (!intents.isEmpty() && hasActivityHandler(context, intents.get(0))) {
- final ClickableSpan span = createSpan(context, intents.get(0));
- spans.add(new SpanSpec(selectionStart, selectionEnd, span));
- }
- }
- }
- }
- start = end;
- }
- return new LinksInfoImpl(text, avoidOverlaps(spans, text));
- }
-
- /**
- * Returns true if the classification type matches the specified linkMask.
- */
- private static boolean matches(String type, int linkMask) {
- type = type.trim().toLowerCase(Locale.ENGLISH);
- if ((linkMask & Linkify.PHONE_NUMBERS) != 0
- && TextClassifier.TYPE_PHONE.equals(type)) {
- return true;
- }
- if ((linkMask & Linkify.EMAIL_ADDRESSES) != 0
- && TextClassifier.TYPE_EMAIL.equals(type)) {
- return true;
- }
- if ((linkMask & Linkify.MAP_ADDRESSES) != 0
- && TextClassifier.TYPE_ADDRESS.equals(type)) {
- return true;
- }
- if ((linkMask & Linkify.WEB_URLS) != 0
- && TextClassifier.TYPE_URL.equals(type)) {
- return true;
- }
- return false;
- }
-
- /**
- * Trim the number of spans so that no two spans overlap.
- *
- * This algorithm first ensures that there is only one span per start index, then it
- * makes sure that no two spans overlap.
- */
- private static List<SpanSpec> avoidOverlaps(List<SpanSpec> spans, String text) {
- Collections.sort(spans, Comparator.comparingInt(span -> span.mStart));
- // Group spans by start index. Take the longest span.
- final Map<Integer, SpanSpec> reps = new LinkedHashMap<>(); // order matters.
- final int size = spans.size();
- for (int i = 0; i < size; i++) {
- final SpanSpec span = spans.get(i);
- final LinksInfoFactory.SpanSpec rep = reps.get(span.mStart);
- if (rep == null || rep.mEnd < span.mEnd) {
- reps.put(span.mStart, span);
- }
- }
- // Avoid span intersections. Take the longer span.
- final LinkedList<SpanSpec> result = new LinkedList<>();
- for (SpanSpec rep : reps.values()) {
- if (result.isEmpty()) {
- result.add(rep);
- continue;
- }
-
- final SpanSpec last = result.getLast();
- if (rep.mStart < last.mEnd) {
- // Spans intersect. Use the one with characters.
- if ((rep.mEnd - rep.mStart) > (last.mEnd - last.mStart)) {
- result.set(result.size() - 1, rep);
- }
- } else {
- result.add(rep);
- }
- }
- return result;
- }
-
- private static ClickableSpan createSpan(final Context context, final Intent intent) {
- return new ClickableSpan() {
- // TODO: Style this span.
- @Override
- public void onClick(View widget) {
- context.startActivity(intent);
- }
- };
- }
-
- private static boolean hasActivityHandler(Context context, Intent intent) {
- if (intent == null) {
- return false;
- }
- final ResolveInfo resolveInfo = context.getPackageManager().resolveActivity(intent, 0);
- return resolveInfo != null && resolveInfo.activityInfo != null;
- }
-
- /**
- * Implementation of LinksInfo that adds ClickableSpans to the specified text.
- */
- private static final class LinksInfoImpl implements LinksInfo {
-
- private final CharSequence mOriginalText;
- private final List<SpanSpec> mSpans;
-
- LinksInfoImpl(CharSequence originalText, List<SpanSpec> spans) {
- mOriginalText = originalText;
- mSpans = spans;
- }
-
- @Override
- public boolean apply(@NonNull CharSequence text) {
- Preconditions.checkArgument(text != null);
- if (text instanceof Spannable && mOriginalText.toString().equals(text.toString())) {
- Spannable spannable = (Spannable) text;
- final int size = mSpans.size();
- for (int i = 0; i < size; i++) {
- final SpanSpec span = mSpans.get(i);
- spannable.setSpan(span.mSpan, span.mStart, span.mEnd, 0);
- }
- return true;
- }
- return false;
- }
- }
-
- /**
- * Span plus its start and end index.
- */
- private static final class SpanSpec {
-
- private final int mStart;
- private final int mEnd;
- private final ClickableSpan mSpan;
-
- SpanSpec(int start, int end, ClickableSpan span) {
- mStart = start;
- mEnd = end;
- mSpan = span;
- }
- }
- }
-
- /**
* Creates intents based on the classification type.
*/
private static final class IntentFactory {
diff --git a/core/java/android/view/textclassifier/logging/SmartSelectionEventTracker.java b/core/java/android/view/textclassifier/logging/SmartSelectionEventTracker.java
index 83af19b..fbf6535 100644
--- a/core/java/android/view/textclassifier/logging/SmartSelectionEventTracker.java
+++ b/core/java/android/view/textclassifier/logging/SmartSelectionEventTracker.java
@@ -48,9 +48,13 @@
private static final int START_EVENT_DELTA = MetricsEvent.FIELD_SELECTION_SINCE_START;
private static final int PREV_EVENT_DELTA = MetricsEvent.FIELD_SELECTION_SINCE_PREVIOUS;
private static final int INDEX = MetricsEvent.FIELD_SELECTION_SESSION_INDEX;
- private static final int VERSION_TAG = MetricsEvent.FIELD_SELECTION_VERSION_TAG;
- private static final int SMART_INDICES = MetricsEvent.FIELD_SELECTION_SMART_RANGE;
- private static final int EVENT_INDICES = MetricsEvent.FIELD_SELECTION_RANGE;
+ private static final int WIDGET_TYPE = MetricsEvent.FIELD_SELECTION_WIDGET_TYPE;
+ private static final int MODEL_NAME = MetricsEvent.FIELD_TEXTCLASSIFIER_MODEL;
+ private static final int ENTITY_TYPE = MetricsEvent.FIELD_SELECTION_ENTITY_TYPE;
+ private static final int SMART_START = MetricsEvent.FIELD_SELECTION_SMART_RANGE_START;
+ private static final int SMART_END = MetricsEvent.FIELD_SELECTION_SMART_RANGE_END;
+ private static final int EVENT_START = MetricsEvent.FIELD_SELECTION_RANGE_START;
+ private static final int EVENT_END = MetricsEvent.FIELD_SELECTION_RANGE_END;
private static final int SESSION_ID = MetricsEvent.FIELD_SELECTION_SESSION_ID;
private static final String ZERO = "0";
@@ -83,7 +87,7 @@
private long mSessionStartTime;
private long mLastEventTime;
private boolean mSmartSelectionTriggered;
- private String mVersionTag;
+ private String mModelName;
public SmartSelectionEventTracker(@NonNull Context context, @WidgetType int widgetType) {
mWidgetType = widgetType;
@@ -115,7 +119,7 @@
case SelectionEvent.EventType.SMART_SELECTION_SINGLE: // fall through
case SelectionEvent.EventType.SMART_SELECTION_MULTI:
mSmartSelectionTriggered = true;
- mVersionTag = getVersionTag(event);
+ mModelName = getModelName(event);
mSmartIndices[0] = event.mStart;
mSmartIndices[1] = event.mEnd;
break;
@@ -137,14 +141,18 @@
final long prevEventDelta = mLastEventTime == 0 ? 0 : now - mLastEventTime;
final LogMaker log = new LogMaker(MetricsEvent.TEXT_SELECTION_SESSION)
.setType(getLogType(event))
- .setSubtype(getLogSubType(event))
+ .setSubtype(MetricsEvent.TEXT_SELECTION_INVOCATION_MANUAL)
.setPackageName(mContext.getPackageName())
.addTaggedData(START_EVENT_DELTA, now - mSessionStartTime)
.addTaggedData(PREV_EVENT_DELTA, prevEventDelta)
.addTaggedData(INDEX, mIndex)
- .addTaggedData(VERSION_TAG, mVersionTag)
- .addTaggedData(SMART_INDICES, getSmartDelta())
- .addTaggedData(EVENT_INDICES, getEventDelta(event))
+ .addTaggedData(WIDGET_TYPE, getWidgetTypeName())
+ .addTaggedData(MODEL_NAME, mModelName)
+ .addTaggedData(ENTITY_TYPE, event.mEntityType)
+ .addTaggedData(SMART_START, getSmartRangeDelta(mSmartIndices[0]))
+ .addTaggedData(SMART_END, getSmartRangeDelta(mSmartIndices[1]))
+ .addTaggedData(EVENT_START, getRangeDelta(event.mStart))
+ .addTaggedData(EVENT_END, getRangeDelta(event.mEnd))
.addTaggedData(SESSION_ID, mSessionId);
mMetricsLogger.write(log);
debugLog(log);
@@ -169,7 +177,7 @@
mSessionStartTime = 0;
mLastEventTime = 0;
mSmartSelectionTriggered = false;
- mVersionTag = getVersionTag(null);
+ mModelName = getModelName(null);
mSessionId = null;
}
@@ -251,113 +259,64 @@
}
}
- private static int getLogSubType(SelectionEvent event) {
- switch (event.mEntityType) {
- case TextClassifier.TYPE_OTHER:
- return MetricsEvent.TEXT_CLASSIFIER_TYPE_OTHER;
- case TextClassifier.TYPE_EMAIL:
- return MetricsEvent.TEXT_CLASSIFIER_TYPE_EMAIL;
- case TextClassifier.TYPE_PHONE:
- return MetricsEvent.TEXT_CLASSIFIER_TYPE_PHONE;
- case TextClassifier.TYPE_ADDRESS:
- return MetricsEvent.TEXT_CLASSIFIER_TYPE_ADDRESS;
- case TextClassifier.TYPE_URL:
- return MetricsEvent.TEXT_CLASSIFIER_TYPE_URL;
- default:
- return MetricsEvent.TEXT_CLASSIFIER_TYPE_UNKNOWN;
- }
+ private int getRangeDelta(int offset) {
+ return offset - mOrigStart;
}
- private static String getLogSubTypeString(int logSubType) {
- switch (logSubType) {
- case MetricsEvent.TEXT_CLASSIFIER_TYPE_OTHER:
- return TextClassifier.TYPE_OTHER;
- case MetricsEvent.TEXT_CLASSIFIER_TYPE_EMAIL:
- return TextClassifier.TYPE_EMAIL;
- case MetricsEvent.TEXT_CLASSIFIER_TYPE_PHONE:
- return TextClassifier.TYPE_PHONE;
- case MetricsEvent.TEXT_CLASSIFIER_TYPE_ADDRESS:
- return TextClassifier.TYPE_ADDRESS;
- case MetricsEvent.TEXT_CLASSIFIER_TYPE_URL:
- return TextClassifier.TYPE_URL;
- default:
- return TextClassifier.TYPE_UNKNOWN;
- }
+ private int getSmartRangeDelta(int offset) {
+ return mSmartSelectionTriggered ? getRangeDelta(offset) : 0;
}
- private int getSmartDelta() {
- if (mSmartSelectionTriggered) {
- return (clamp(mSmartIndices[0] - mOrigStart) << 16)
- | (clamp(mSmartIndices[1] - mOrigStart) & 0xffff);
- }
- // If the smart selection model was not run, return invalid selection indices [0,0]. This
- // allows us to tell from the terminal event alone whether the model was run.
- return 0;
- }
-
- private int getEventDelta(SelectionEvent event) {
- return (clamp(event.mStart - mOrigStart) << 16)
- | (clamp(event.mEnd - mOrigStart) & 0xffff);
- }
-
- private String getVersionTag(@Nullable SelectionEvent event) {
- final String widgetType;
+ private String getWidgetTypeName() {
switch (mWidgetType) {
case WidgetType.TEXTVIEW:
- widgetType = TEXTVIEW;
- break;
+ return TEXTVIEW;
case WidgetType.WEBVIEW:
- widgetType = WEBVIEW;
- break;
+ return WEBVIEW;
case WidgetType.EDITTEXT:
- widgetType = EDITTEXT;
- break;
+ return EDITTEXT;
case WidgetType.EDIT_WEBVIEW:
- widgetType = EDIT_WEBVIEW;
- break;
+ return EDIT_WEBVIEW;
default:
- widgetType = UNKNOWN;
+ return UNKNOWN;
}
- final String version = event == null
+ }
+
+ private String getModelName(@Nullable SelectionEvent event) {
+ return event == null
? SelectionEvent.NO_VERSION_TAG
: Objects.toString(event.mVersionTag, SelectionEvent.NO_VERSION_TAG);
- return String.format("%s/%s", widgetType, version);
}
private static String createSessionId() {
return UUID.randomUUID().toString();
}
- private static int clamp(int val) {
- return Math.max(Math.min(val, Short.MAX_VALUE), Short.MIN_VALUE);
- }
-
private static void debugLog(LogMaker log) {
if (!DEBUG_LOG_ENABLED) return;
- final String tag = Objects.toString(log.getTaggedData(VERSION_TAG), "tag");
+ final String widget = Objects.toString(log.getTaggedData(WIDGET_TYPE), UNKNOWN);
final int index = Integer.parseInt(Objects.toString(log.getTaggedData(INDEX), ZERO));
if (log.getType() == MetricsEvent.ACTION_TEXT_SELECTION_START) {
String sessionId = Objects.toString(log.getTaggedData(SESSION_ID), "");
sessionId = sessionId.substring(sessionId.lastIndexOf("-") + 1);
- Log.d(LOG_TAG, String.format("New selection session: %s(%s)", tag, sessionId));
+ Log.d(LOG_TAG, String.format("New selection session: %s (%s)", widget, sessionId));
}
+ final String model = Objects.toString(log.getTaggedData(MODEL_NAME), UNKNOWN);
+ final String entity = Objects.toString(log.getTaggedData(ENTITY_TYPE), UNKNOWN);
final String type = getLogTypeString(log.getType());
- final String subType = getLogSubTypeString(log.getSubtype());
+ final int smartStart = Integer.parseInt(
+ Objects.toString(log.getTaggedData(SMART_START), ZERO));
+ final int smartEnd = Integer.parseInt(
+ Objects.toString(log.getTaggedData(SMART_END), ZERO));
+ final int eventStart = Integer.parseInt(
+ Objects.toString(log.getTaggedData(EVENT_START), ZERO));
+ final int eventEnd = Integer.parseInt(
+ Objects.toString(log.getTaggedData(EVENT_END), ZERO));
- final int smartIndices = Integer.parseInt(
- Objects.toString(log.getTaggedData(SMART_INDICES), ZERO));
- final int smartStart = (short) ((smartIndices & 0xffff0000) >> 16);
- final int smartEnd = (short) (smartIndices & 0xffff);
-
- final int eventIndices = Integer.parseInt(
- Objects.toString(log.getTaggedData(EVENT_INDICES), ZERO));
- final int eventStart = (short) ((eventIndices & 0xffff0000) >> 16);
- final int eventEnd = (short) (eventIndices & 0xffff);
-
- Log.d(LOG_TAG, String.format("%2d: %s/%s, context=%d,%d - old=%d,%d (%s)",
- index, type, subType, eventStart, eventEnd, smartStart, smartEnd, tag));
+ Log.d(LOG_TAG, String.format("%2d: %s/%s, range=%d,%d - smart_range=%d,%d (%s/%s)",
+ index, type, entity, eventStart, eventEnd, smartStart, smartEnd, widget, model));
}
/**
@@ -369,12 +328,12 @@
/**
* Use this to specify an indeterminate positive index.
*/
- public static final int OUT_OF_BOUNDS = Short.MAX_VALUE;
+ public static final int OUT_OF_BOUNDS = Integer.MAX_VALUE;
/**
* Use this to specify an indeterminate negative index.
*/
- public static final int OUT_OF_BOUNDS_NEGATIVE = Short.MIN_VALUE;
+ public static final int OUT_OF_BOUNDS_NEGATIVE = Integer.MIN_VALUE;
private static final String NO_VERSION_TAG = "";
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index 9295f5c..665d694 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -308,10 +308,15 @@
* WebView may upload anonymous diagnostic data to Google when the user has consented. This data
* helps Google improve WebView. Data is collected on a per-app basis for each app which has
* instantiated a WebView. An individual app can opt out of this feature by putting the following
- * tag in its manifest:
+ * tag in its manifest's {@code <application>} element:
* <pre>
- * <meta-data android:name="android.webkit.WebView.MetricsOptOut"
- * android:value="true" />
+ * <manifest>
+ * <application>
+ * ...
+ * <meta-data android:name="android.webkit.WebView.MetricsOptOut"
+ * android:value="true" />
+ * </application>
+ * </manifest>
* </pre>
* <p>
* Data will only be uploaded for a given app if the user has consented AND the app has not opted
@@ -323,11 +328,17 @@
* If Safe Browsing is enabled, WebView will block malicious URLs and present a warning UI to the
* user to allow them to navigate back safely or proceed to the malicious page.
* <p>
- * The recommended way for apps to enable the feature is putting the following tag in the manifest:
+ * The recommended way for apps to enable the feature is putting the following tag in the manifest's
+ * {@code <application>} element:
* <p>
* <pre>
- * <meta-data android:name="android.webkit.WebView.EnableSafeBrowsing"
- * android:value="true" />
+ * <manifest>
+ * <application>
+ * ...
+ * <meta-data android:name="android.webkit.WebView.EnableSafeBrowsing"
+ * android:value="true" />
+ * </application>
+ * </manifest>
* </pre>
*
*/
diff --git a/core/java/android/webkit/WebViewFragment.java b/core/java/android/webkit/WebViewFragment.java
index d803f62d..e5b7c8d 100644
--- a/core/java/android/webkit/WebViewFragment.java
+++ b/core/java/android/webkit/WebViewFragment.java
@@ -27,7 +27,10 @@
* A fragment that displays a WebView.
* <p>
* The WebView is automically paused or resumed when the Fragment is paused or resumed.
+ *
+ * @deprecated Manually call {@link WebView#onPause()} and {@link WebView#onResume()}
*/
+@Deprecated
public class WebViewFragment extends Fragment {
private WebView mWebView;
private boolean mIsWebViewAvailable;
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index 57aa119..d477ffd 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -6553,12 +6553,12 @@
* Adds "PROCESS_TEXT" menu items to the specified menu.
*/
public void onInitializeMenu(Menu menu) {
- final int size = mSupportedActivities.size();
loadSupportedActivities();
+ final int size = mSupportedActivities.size();
for (int i = 0; i < size; i++) {
final ResolveInfo resolveInfo = mSupportedActivities.get(i);
menu.add(Menu.NONE, Menu.NONE,
- Editor.MENU_ITEM_ORDER_PROCESS_TEXT_INTENT_ACTIONS_START + i++,
+ Editor.MENU_ITEM_ORDER_PROCESS_TEXT_INTENT_ACTIONS_START + i,
getLabel(resolveInfo))
.setIntent(createProcessTextIntentForResolveInfo(resolveInfo))
.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
diff --git a/core/java/android/widget/PopupWindow.java b/core/java/android/widget/PopupWindow.java
index 8dc8cab..e91db13 100644
--- a/core/java/android/widget/PopupWindow.java
+++ b/core/java/android/widget/PopupWindow.java
@@ -1354,6 +1354,7 @@
}
mDecorView = createDecorView(mBackgroundView);
+ mDecorView.setIsRootNamespace(true);
// The background owner should be elevated so that it casts a shadow.
mBackgroundView.setElevation(mElevation);
diff --git a/core/java/com/android/internal/app/NightDisplayController.java b/core/java/com/android/internal/app/ColorDisplayController.java
similarity index 98%
rename from core/java/com/android/internal/app/NightDisplayController.java
rename to core/java/com/android/internal/app/ColorDisplayController.java
index b8bfc64..b8682a8 100644
--- a/core/java/com/android/internal/app/NightDisplayController.java
+++ b/core/java/com/android/internal/app/ColorDisplayController.java
@@ -42,14 +42,14 @@
import java.time.format.DateTimeParseException;
/**
- * Controller for managing Night display settings.
+ * Controller for managing night display and color mode settings.
* <p/>
* Night display tints your screen red at night. This makes it easier to look at your screen in
* dim light and may help you fall asleep more easily.
*/
-public final class NightDisplayController {
+public final class ColorDisplayController {
- private static final String TAG = "NightDisplayController";
+ private static final String TAG = "ColorDisplayController";
private static final boolean DEBUG = false;
@Retention(RetentionPolicy.SOURCE)
@@ -114,11 +114,11 @@
private Callback mCallback;
- public NightDisplayController(@NonNull Context context) {
+ public ColorDisplayController(@NonNull Context context) {
this(context, ActivityManager.getCurrentUser());
}
- public NightDisplayController(@NonNull Context context, int userId) {
+ public ColorDisplayController(@NonNull Context context, int userId) {
mContext = context.getApplicationContext();
mUserId = userId;
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/IOverviewProxy.aidl b/core/java/com/android/internal/net/INetworkWatchlistManager.aidl
similarity index 72%
copy from packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/IOverviewProxy.aidl
copy to core/java/com/android/internal/net/INetworkWatchlistManager.aidl
index 8cf3be8..7e88369 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/IOverviewProxy.aidl
+++ b/core/java/com/android/internal/net/INetworkWatchlistManager.aidl
@@ -14,10 +14,13 @@
* limitations under the License.
*/
-package com.android.systemui.shared.recents.model;
+package com.android.internal.net;
-import android.view.MotionEvent;
+import android.os.SharedMemory;
-oneway interface IOverviewProxy {
- void onMotionEvent(in MotionEvent event);
+/** {@hide} */
+interface INetworkWatchlistManager {
+ boolean startWatchlistLogging();
+ boolean stopWatchlistLogging();
+ void reportWatchlistIfNecessary();
}
diff --git a/core/java/com/android/internal/net/NetworkStatsFactory.java b/core/java/com/android/internal/net/NetworkStatsFactory.java
index 3d3e148..5eda81b 100644
--- a/core/java/com/android/internal/net/NetworkStatsFactory.java
+++ b/core/java/com/android/internal/net/NetworkStatsFactory.java
@@ -43,6 +43,8 @@
/**
* Creates {@link NetworkStats} instances by parsing various {@code /proc/}
* files as needed.
+ *
+ * @hide
*/
public class NetworkStatsFactory {
private static final String TAG = "NetworkStatsFactory";
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 2ec64a5..f2483c0 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -3639,6 +3639,7 @@
public void addIsolatedUidLocked(int isolatedUid, int appUid) {
mIsolatedUids.put(isolatedUid, appUid);
+ StatsLog.write(StatsLog.ISOLATED_UID_CHANGED, appUid, isolatedUid, 1);
}
/**
@@ -3659,9 +3660,11 @@
* @see #scheduleRemoveIsolatedUidLocked(int, int)
*/
public void removeIsolatedUidLocked(int isolatedUid) {
- mIsolatedUids.delete(isolatedUid);
- mKernelUidCpuTimeReader.removeUid(isolatedUid);
- mKernelUidCpuFreqTimeReader.removeUid(isolatedUid);
+ StatsLog.write(
+ StatsLog.ISOLATED_UID_CHANGED, mIsolatedUids.get(isolatedUid, -1), isolatedUid, 0);
+ mIsolatedUids.delete(isolatedUid);
+ mKernelUidCpuTimeReader.removeUid(isolatedUid);
+ mKernelUidCpuFreqTimeReader.removeUid(isolatedUid);
}
public int mapUid(int uid) {
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index fb8b9f7..3552e43 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -28,10 +28,6 @@
"-Wunused",
"-Wunreachable-code",
- // necessary for Clang as the GL bindings need to turn
- // off a GCC warning that Clang doesn't know.
- "-Wno-unknown-pragmas",
-
// TODO: Linear blending should be enabled by default, but we are
// TODO: making it an opt-in while it's a work in progress
//"-DANDROID_ENABLE_LINEAR_BLENDING",
diff --git a/core/jni/android_opengl_EGL14.cpp b/core/jni/android_opengl_EGL14.cpp
index 6163588..a9d75fd 100644
--- a/core/jni/android_opengl_EGL14.cpp
+++ b/core/jni/android_opengl_EGL14.cpp
@@ -17,7 +17,6 @@
// This source file is automatically generated
#pragma GCC diagnostic ignored "-Wunused-variable"
-#pragma GCC diagnostic ignored "-Wunused-but-set-variable"
#pragma GCC diagnostic ignored "-Wunused-function"
#include "jni.h"
diff --git a/core/jni/android_opengl_EGLExt.cpp b/core/jni/android_opengl_EGLExt.cpp
index df1aa20..75a25fe 100644
--- a/core/jni/android_opengl_EGLExt.cpp
+++ b/core/jni/android_opengl_EGLExt.cpp
@@ -17,7 +17,6 @@
// This source file is automatically generated
#pragma GCC diagnostic ignored "-Wunused-variable"
-#pragma GCC diagnostic ignored "-Wunused-but-set-variable"
#pragma GCC diagnostic ignored "-Wunused-function"
#include "jni.h"
diff --git a/core/jni/android_opengl_GLES10.cpp b/core/jni/android_opengl_GLES10.cpp
index 6d4f6ec..ee5b594 100644
--- a/core/jni/android_opengl_GLES10.cpp
+++ b/core/jni/android_opengl_GLES10.cpp
@@ -18,7 +18,6 @@
// This source file is automatically generated
#pragma GCC diagnostic ignored "-Wunused-variable"
-#pragma GCC diagnostic ignored "-Wunused-but-set-variable"
#pragma GCC diagnostic ignored "-Wunused-function"
#include <GLES/gl.h>
diff --git a/core/jni/android_opengl_GLES10Ext.cpp b/core/jni/android_opengl_GLES10Ext.cpp
index e630cfca..da7d0f0 100644
--- a/core/jni/android_opengl_GLES10Ext.cpp
+++ b/core/jni/android_opengl_GLES10Ext.cpp
@@ -18,7 +18,6 @@
// This source file is automatically generated
#pragma GCC diagnostic ignored "-Wunused-variable"
-#pragma GCC diagnostic ignored "-Wunused-but-set-variable"
#pragma GCC diagnostic ignored "-Wunused-function"
#include <GLES/gl.h>
diff --git a/core/jni/android_opengl_GLES11.cpp b/core/jni/android_opengl_GLES11.cpp
index ab9cbb1..391ae53 100644
--- a/core/jni/android_opengl_GLES11.cpp
+++ b/core/jni/android_opengl_GLES11.cpp
@@ -18,7 +18,6 @@
// This source file is automatically generated
#pragma GCC diagnostic ignored "-Wunused-variable"
-#pragma GCC diagnostic ignored "-Wunused-but-set-variable"
#pragma GCC diagnostic ignored "-Wunused-function"
#include <GLES/gl.h>
diff --git a/core/jni/android_opengl_GLES11Ext.cpp b/core/jni/android_opengl_GLES11Ext.cpp
index 8f71a6d..09dce32 100644
--- a/core/jni/android_opengl_GLES11Ext.cpp
+++ b/core/jni/android_opengl_GLES11Ext.cpp
@@ -18,7 +18,6 @@
// This source file is automatically generated
#pragma GCC diagnostic ignored "-Wunused-variable"
-#pragma GCC diagnostic ignored "-Wunused-but-set-variable"
#pragma GCC diagnostic ignored "-Wunused-function"
#include <GLES/gl.h>
diff --git a/core/jni/android_opengl_GLES20.cpp b/core/jni/android_opengl_GLES20.cpp
index f83d204..99922cf 100644
--- a/core/jni/android_opengl_GLES20.cpp
+++ b/core/jni/android_opengl_GLES20.cpp
@@ -18,7 +18,6 @@
// This source file is automatically generated
#pragma GCC diagnostic ignored "-Wunused-variable"
-#pragma GCC diagnostic ignored "-Wunused-but-set-variable"
#pragma GCC diagnostic ignored "-Wunused-function"
#include <GLES2/gl2.h>
diff --git a/core/jni/android_opengl_GLES30.cpp b/core/jni/android_opengl_GLES30.cpp
index b649daf..adc635e 100644
--- a/core/jni/android_opengl_GLES30.cpp
+++ b/core/jni/android_opengl_GLES30.cpp
@@ -18,7 +18,6 @@
// This source file is automatically generated
#pragma GCC diagnostic ignored "-Wunused-variable"
-#pragma GCC diagnostic ignored "-Wunused-but-set-variable"
#pragma GCC diagnostic ignored "-Wunused-function"
#include <GLES3/gl3.h>
diff --git a/core/jni/android_opengl_GLES31.cpp b/core/jni/android_opengl_GLES31.cpp
index 07d920f..512f562 100644
--- a/core/jni/android_opengl_GLES31.cpp
+++ b/core/jni/android_opengl_GLES31.cpp
@@ -17,7 +17,6 @@
// This source file is automatically generated
#pragma GCC diagnostic ignored "-Wunused-variable"
-#pragma GCC diagnostic ignored "-Wunused-but-set-variable"
#pragma GCC diagnostic ignored "-Wunused-function"
#include <stdint.h>
diff --git a/core/jni/android_opengl_GLES31Ext.cpp b/core/jni/android_opengl_GLES31Ext.cpp
index 723dd4c..5543fca 100644
--- a/core/jni/android_opengl_GLES31Ext.cpp
+++ b/core/jni/android_opengl_GLES31Ext.cpp
@@ -17,7 +17,6 @@
// This source file is automatically generated
#pragma GCC diagnostic ignored "-Wunused-variable"
-#pragma GCC diagnostic ignored "-Wunused-but-set-variable"
#pragma GCC diagnostic ignored "-Wunused-function"
#include <GLES3/gl31.h>
diff --git a/core/jni/android_opengl_GLES32.cpp b/core/jni/android_opengl_GLES32.cpp
index 62a6e6c..2f1e31e 100644
--- a/core/jni/android_opengl_GLES32.cpp
+++ b/core/jni/android_opengl_GLES32.cpp
@@ -17,7 +17,6 @@
// This source file is automatically generated
#pragma GCC diagnostic ignored "-Wunused-variable"
-#pragma GCC diagnostic ignored "-Wunused-but-set-variable"
#pragma GCC diagnostic ignored "-Wunused-function"
#include <stdint.h>
diff --git a/core/jni/android_view_ThreadedRenderer.cpp b/core/jni/android_view_ThreadedRenderer.cpp
index 57263b1..870a0c2 100644
--- a/core/jni/android_view_ThreadedRenderer.cpp
+++ b/core/jni/android_view_ThreadedRenderer.cpp
@@ -54,6 +54,7 @@
#include <renderthread/RenderProxy.h>
#include <renderthread/RenderTask.h>
#include <renderthread/RenderThread.h>
+#include <pipeline/skia/ShaderCache.h>
namespace android {
@@ -970,10 +971,14 @@
// ----------------------------------------------------------------------------
static void android_view_ThreadedRenderer_setupShadersDiskCache(JNIEnv* env, jobject clazz,
- jstring diskCachePath) {
+ jstring diskCachePath, jstring skiaDiskCachePath) {
const char* cacheArray = env->GetStringUTFChars(diskCachePath, NULL);
android::egl_set_cache_filename(cacheArray);
env->ReleaseStringUTFChars(diskCachePath, cacheArray);
+
+ const char* skiaCacheArray = env->GetStringUTFChars(skiaDiskCachePath, NULL);
+ uirenderer::skiapipeline::ShaderCache::get().setFilename(skiaCacheArray);
+ env->ReleaseStringUTFChars(skiaDiskCachePath, skiaCacheArray);
}
// ----------------------------------------------------------------------------
@@ -1018,7 +1023,7 @@
{ "nNotifyFramePending", "(J)V", (void*) android_view_ThreadedRenderer_notifyFramePending },
{ "nSerializeDisplayListTree", "(J)V", (void*) android_view_ThreadedRenderer_serializeDisplayListTree },
{ "nDumpProfileInfo", "(JLjava/io/FileDescriptor;I)V", (void*) android_view_ThreadedRenderer_dumpProfileInfo },
- { "setupShadersDiskCache", "(Ljava/lang/String;)V",
+ { "setupShadersDiskCache", "(Ljava/lang/String;Ljava/lang/String;)V",
(void*) android_view_ThreadedRenderer_setupShadersDiskCache },
{ "nAddRenderNode", "(JJZ)V", (void*) android_view_ThreadedRenderer_addRenderNode},
{ "nRemoveRenderNode", "(JJ)V", (void*) android_view_ThreadedRenderer_removeRenderNode},
diff --git a/core/jni/com_google_android_gles_jni_GLImpl.cpp b/core/jni/com_google_android_gles_jni_GLImpl.cpp
index ac23eca..40ff7e4 100644
--- a/core/jni/com_google_android_gles_jni_GLImpl.cpp
+++ b/core/jni/com_google_android_gles_jni_GLImpl.cpp
@@ -18,7 +18,6 @@
// This source file is automatically generated
#pragma GCC diagnostic ignored "-Wunused-variable"
-#pragma GCC diagnostic ignored "-Wunused-but-set-variable"
#pragma GCC diagnostic ignored "-Wunused-function"
#include "jni.h"
diff --git a/core/jni/hwbinder/EphemeralStorage.cpp b/core/jni/hwbinder/EphemeralStorage.cpp
index 4996bc8..3b18f2b 100644
--- a/core/jni/hwbinder/EphemeralStorage.cpp
+++ b/core/jni/hwbinder/EphemeralStorage.cpp
@@ -111,6 +111,7 @@
break; \
}
+__attribute__((no_sanitize("unsigned-integer-overflow")))
void EphemeralStorage::release(JNIEnv *env) {
for (size_t i = mItems.size(); i--;) {
const Item &item = mItems[i];
diff --git a/core/proto/android/os/cpuinfo.proto b/core/proto/android/os/cpuinfo.proto
new file mode 100644
index 0000000..a95fa57
--- /dev/null
+++ b/core/proto/android/os/cpuinfo.proto
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+syntax = "proto2";
+
+option java_multiple_files = true;
+option java_outer_classname = "CpuInfoProto";
+
+import "frameworks/base/tools/streaming_proto/stream.proto";
+
+package android.os;
+
+/**
+ * Data structure of the linux command
+ * 'top -b -n 1 -H -s 6 -o pid,tid,user,pr,ni,%cpu,s,virt,res,pcy,cmd,name'
+ *
+ * Next Tag: 6
+ */
+message CpuInfo {
+
+ message TaskStats {
+ option (stream_proto.stream_msg).enable_fields_mapping = true;
+
+ optional int32 total = 1; // total number of cpu tasks
+ optional int32 running = 2; // number of running tasks
+ optional int32 sleeping = 3; // number of sleeping tasks
+ optional int32 stopped = 4; // number of stopped tasks
+ optional int32 zombie = 5; // number of zombie tasks
+ }
+ optional TaskStats task_stats = 1;
+
+ message MemStats { // unit in kB
+ option (stream_proto.stream_msg).enable_fields_mapping = true;
+
+ optional int32 total = 1;
+ optional int32 used = 2;
+ optional int32 free = 3;
+ optional int32 buffers = 4;
+ optional int32 cached = 5;
+ }
+ optional MemStats mem = 2;
+ optional MemStats swap = 3;
+
+ message CpuUsage { // unit is percentage %
+ option (stream_proto.stream_msg).enable_fields_mapping = true;
+
+ optional int32 cpu = 1; // 400% cpu indicates 4 cores
+ optional int32 user = 2;
+ optional int32 nice = 3;
+ optional int32 sys = 4;
+ optional int32 idle = 5;
+ optional int32 iow = 6;
+ optional int32 irq = 7;
+ optional int32 sirq = 8;
+ optional int32 host = 9;
+ }
+ optional CpuUsage cpu_usage = 4;
+
+ // Next Tag: 13
+ message Task {
+ option (stream_proto.stream_msg).enable_fields_mapping = true;
+
+ optional int32 pid = 1;
+ optional int32 tid = 2;
+ optional string user = 3;
+ optional string pr = 4; // priority of each task, using string type is because special value RT (real time)
+ optional sint32 ni = 5; // niceness value
+ optional float cpu = 6; // precentage of cpu usage of the task
+
+ enum Status {
+ option (stream_proto.stream_enum).enable_enums_mapping = true;
+
+ STATUS_UNKNOWN = 0;
+ STATUS_D = 1; // uninterruptible sleep
+ STATUS_R = 2; // running
+ STATUS_S = 3; // sleeping
+ STATUS_T = 4; // traced or stopped
+ STATUS_Z = 5; // zombie
+ }
+ optional Status s = 7; // process status
+ optional string virt = 8; // virtual memory size, i.e. 14.0G, 13.5M
+ optional string res = 9; // Resident size, i.e. 0, 3.1G
+
+ // How Android memory manager will treat the task
+ enum Policy {
+ option (stream_proto.stream_enum).enable_enums_mapping = true;
+
+ POLICY_UNKNOWN = 0;
+ POLICY_fg = 1; // foreground, the name is lower case for parsing the value
+ POLICY_bg = 2; // background, the name is lower case for parsing the value
+ POLICY_ta = 3; // TODO: figure out what is this value
+ }
+ optional Policy pcy = 10; // Policy of the task
+ optional string cmd = 11; // thread name
+ optional string name = 12; // program name
+ }
+ repeated Task tasks = 5;
+}
diff --git a/core/proto/android/os/incident.proto b/core/proto/android/os/incident.proto
index e998b09..4c3e937 100644
--- a/core/proto/android/os/incident.proto
+++ b/core/proto/android/os/incident.proto
@@ -20,11 +20,12 @@
import "frameworks/base/libs/incident/proto/android/privacy.proto";
import "frameworks/base/libs/incident/proto/android/section.proto";
-import "frameworks/base/core/proto/android/providers/settings.proto";
+import "frameworks/base/core/proto/android/os/cpuinfo.proto";
import "frameworks/base/core/proto/android/os/incidentheader.proto";
import "frameworks/base/core/proto/android/os/kernelwake.proto";
import "frameworks/base/core/proto/android/os/pagetypeinfo.proto";
import "frameworks/base/core/proto/android/os/procrank.proto";
+import "frameworks/base/core/proto/android/providers/settings.proto";
import "frameworks/base/core/proto/android/server/activitymanagerservice.proto";
import "frameworks/base/core/proto/android/server/alarmmanagerservice.proto";
import "frameworks/base/core/proto/android/server/windowmanagerservice.proto";
@@ -68,6 +69,11 @@
(section).args = "/d/wakeup_sources"
];
+ optional CpuInfo cpu_info = 2003 [
+ (section).type = SECTION_COMMAND,
+ (section).args = "/system/bin/top -b -n 1 -H -s 6 -o pid,tid,user,pr,ni,%cpu,s,virt,res,pcy,cmd,name"
+ ];
+
// System Services
optional com.android.server.fingerprint.FingerprintServiceDumpProto fingerprint = 3000 [
@@ -80,7 +86,11 @@
(section).args = "netstats --proto"
];
- optional android.providers.settings.SettingsServiceDumpProto settings = 3002;
+ optional android.providers.settings.SettingsServiceDumpProto settings = 3002 [
+ (section).type = SECTION_DUMPSYS,
+ (section).args = "settings --proto"
+ ];
+
optional android.service.appwidget.AppWidgetServiceDumpProto appwidget = 3003;
optional android.service.notification.NotificationServiceDumpProto notification = 3004 [
(section).type = SECTION_DUMPSYS,
diff --git a/core/proto/android/os/kernelwake.proto b/core/proto/android/os/kernelwake.proto
index d032a45..eaad37a 100644
--- a/core/proto/android/os/kernelwake.proto
+++ b/core/proto/android/os/kernelwake.proto
@@ -29,7 +29,7 @@
// Next Tag: 11
message WakeupSourceProto {
- option (stream_proto.stream).enable_fields_mapping = true;
+ option (stream_proto.stream_msg).enable_fields_mapping = true;
// Name of the event which triggers application processor
optional string name = 1;
diff --git a/core/proto/android/os/pagetypeinfo.proto b/core/proto/android/os/pagetypeinfo.proto
index 22b3d73..b86ee01 100644
--- a/core/proto/android/os/pagetypeinfo.proto
+++ b/core/proto/android/os/pagetypeinfo.proto
@@ -63,7 +63,7 @@
// Next tag: 9
message BlockProto {
- option (stream_proto.stream).enable_fields_mapping = true;
+ option (stream_proto.stream_msg).enable_fields_mapping = true;
optional int32 node = 1;
diff --git a/core/proto/android/os/procrank.proto b/core/proto/android/os/procrank.proto
index 4d62a60..9945f2e 100644
--- a/core/proto/android/os/procrank.proto
+++ b/core/proto/android/os/procrank.proto
@@ -33,7 +33,7 @@
// Next Tag: 11
message ProcessProto {
- option (stream_proto.stream).enable_fields_mapping = true;
+ option (stream_proto.stream_msg).enable_fields_mapping = true;
// ID of the process
optional int32 pid = 1;
diff --git a/core/proto/android/providers/settings.proto b/core/proto/android/providers/settings.proto
index 3411c6a..764288c 100644
--- a/core/proto/android/providers/settings.proto
+++ b/core/proto/android/providers/settings.proto
@@ -39,9 +39,10 @@
optional SystemSettingsProto system_settings = 3;
}
+// Note: it's a conscious decision to add each setting as a separate field. This
+// allows annotating each setting with its own privacy tag.
message GlobalSettingsProto {
- // Historical operations
- repeated SettingsOperationProto historical_op = 1;
+ repeated SettingsOperationProto historical_operations = 1;
optional SettingProto add_users_when_locked = 2;
optional SettingProto enable_accessibility_global_gesture_enabled = 3;
@@ -54,6 +55,7 @@
optional SettingProto radio_nfc = 10;
optional SettingProto airplane_mode_radios = 11;
optional SettingProto airplane_mode_toggleable_radios = 12;
+ optional SettingProto bluetooth_class_of_device = 293;
optional SettingProto bluetooth_disabled_profiles = 13;
optional SettingProto bluetooth_interoperability_list = 14;
optional SettingProto wifi_sleep_policy = 15;
@@ -86,6 +88,7 @@
optional SettingProto data_roaming = 42;
optional SettingProto mdc_initial_max_retry = 43;
optional SettingProto force_allow_on_external = 44;
+ optional SettingProto euicc_provisioned = 294;
optional SettingProto development_force_resizable_activities = 45;
optional SettingProto development_enable_freeform_windows_support = 46;
optional SettingProto development_settings_enabled = 47;
@@ -99,6 +102,11 @@
optional SettingProto hdmi_system_audio_control_enabled = 55;
optional SettingProto hdmi_control_auto_wakeup_enabled = 56;
optional SettingProto hdmi_control_auto_device_off_enabled = 57;
+ optional SettingProto location_background_throttle_interval_ms = 295;
+ optional SettingProto location_background_throttle_proximity_alert_interval_ms = 296;
+ optional SettingProto location_background_throttle_package_whitelist = 297;
+ optional SettingProto wifi_scan_background_throttle_interval_ms = 298;
+ optional SettingProto wifi_scan_background_throttle_package_whitelist = 299;
optional SettingProto mhl_input_switching_enabled = 58;
optional SettingProto mhl_power_charge_enabled = 59;
optional SettingProto mobile_data = 60;
@@ -109,6 +117,7 @@
optional SettingProto netstats_time_cache_max_age = 65;
optional SettingProto netstats_global_alert_bytes = 66;
optional SettingProto netstats_sample_enabled = 67;
+ optional SettingProto netstats_augment_enabled = 300;
optional SettingProto netstats_dev_bucket_duration = 68;
optional SettingProto netstats_dev_persist_bytes = 69;
optional SettingProto netstats_dev_rotate_age = 70;
@@ -156,6 +165,7 @@
optional SettingProto tether_supported = 113;
optional SettingProto tether_dun_required = 114;
optional SettingProto tether_dun_apn = 115;
+ optional SettingProto tether_offload_disabled = 301;
optional SettingProto carrier_app_whitelist = 116;
optional SettingProto usb_mass_storage_enabled = 117;
optional SettingProto use_google_mail = 118;
@@ -166,6 +176,9 @@
optional SettingProto network_switch_notification_daily_limit = 123;
optional SettingProto network_switch_notification_rate_limit_millis = 124;
optional SettingProto network_avoid_bad_wifi = 125;
+ optional SettingProto network_metered_multipath_preference = 302;
+ optional SettingProto network_watchlist_last_report_time = 303;
+ optional SettingProto wifi_badging_thresholds = 304;
optional SettingProto wifi_display_on = 126;
optional SettingProto wifi_display_certification_on = 127;
optional SettingProto wifi_display_wps_config = 128;
@@ -179,7 +192,14 @@
optional SettingProto wifi_on = 136;
optional SettingProto wifi_scan_always_available = 137;
optional SettingProto wifi_wakeup_enabled = 138;
+ optional SettingProto wifi_wakeup_available = 305;
+ optional SettingProto network_scoring_ui_enabled = 306;
+ optional SettingProto speed_label_cache_eviction_age_millis = 307;
+ optional SettingProto recommended_network_evaluator_cache_expiry_ms = 308;
optional SettingProto network_recommendations_enabled = 139;
+ optional SettingProto network_recommendations_package = 286;
+ optional SettingProto use_open_wifi_package = 309;
+ optional SettingProto network_recommendation_request_timeout_ms = 310;
optional SettingProto ble_scan_always_available = 140;
optional SettingProto wifi_saved_state = 141;
optional SettingProto wifi_supplicant_scan_interval_ms = 142;
@@ -219,15 +239,19 @@
optional SettingProto sys_storage_threshold_percentage = 176;
optional SettingProto sys_storage_threshold_max_bytes = 177;
optional SettingProto sys_storage_full_threshold_bytes = 178;
+ optional SettingProto sys_storage_cache_percentage = 311;
+ optional SettingProto sys_storage_cache_max_bytes = 312;
optional SettingProto sync_max_retry_delay_in_seconds = 179;
optional SettingProto connectivity_change_delay = 180;
optional SettingProto connectivity_sampling_interval_in_seconds = 181;
optional SettingProto pac_change_delay = 182;
optional SettingProto captive_portal_mode = 183;
+ optional SettingProto captive_portal_detection_enabled = 313;
optional SettingProto captive_portal_server = 184;
optional SettingProto captive_portal_https_url = 185;
optional SettingProto captive_portal_http_url = 186;
optional SettingProto captive_portal_fallback_url = 187;
+ optional SettingProto captive_portal_other_fallback_urls = 314;
optional SettingProto captive_portal_use_https = 188;
optional SettingProto captive_portal_user_agent = 189;
optional SettingProto nsd_on = 190;
@@ -243,21 +267,33 @@
optional SettingProto global_http_proxy_pac = 200;
optional SettingProto set_global_http_proxy = 201;
optional SettingProto default_dns_server = 202;
+ // The requested Private DNS mode and an accompanying specifier.
+ optional SettingProto private_dns_mode = 315;
+ optional SettingProto private_dns_specifier = 316;
optional SettingProto bluetooth_headset_priority_prefix = 203;
optional SettingProto bluetooth_a2dp_sink_priority_prefix = 204;
optional SettingProto bluetooth_a2dp_src_priority_prefix = 205;
+ optional SettingProto bluetooth_a2dp_supports_optional_codecs_prefix = 287;
+ optional SettingProto bluetooth_a2dp_optional_codecs_enabled_prefix = 288;
optional SettingProto bluetooth_input_device_priority_prefix = 206;
optional SettingProto bluetooth_map_priority_prefix = 207;
optional SettingProto bluetooth_map_client_priority_prefix = 208;
optional SettingProto bluetooth_pbap_client_priority_prefix = 209;
optional SettingProto bluetooth_sap_priority_prefix = 210;
optional SettingProto bluetooth_pan_priority_prefix = 211;
+ optional SettingProto activity_manager_constants = 317;
optional SettingProto device_idle_constants = 212;
optional SettingProto device_idle_constants_watch = 213;
+ optional SettingProto battery_saver_constants = 318;
+ optional SettingProto anomaly_detection_constants = 319;
+ optional SettingProto always_on_display_constants = 320;
optional SettingProto app_idle_constants = 214;
+ optional SettingProto power_manager_constants = 321;
optional SettingProto alarm_manager_constants = 215;
optional SettingProto job_scheduler_constants = 216;
optional SettingProto shortcut_manager_constants = 217;
+ optional SettingProto device_policy_constants = 322;
+ optional SettingProto text_classifier_constants = 323;
optional SettingProto window_animation_scale = 218;
optional SettingProto transition_animation_scale = 219;
optional SettingProto animator_duration_scale = 220;
@@ -287,6 +323,10 @@
optional SettingProto cert_pin_update_metadata_url = 244;
optional SettingProto intent_firewall_update_content_url = 245;
optional SettingProto intent_firewall_update_metadata_url = 246;
+ optional SettingProto lang_id_update_content_url = 324;
+ optional SettingProto lang_id_update_metadata_url = 325;
+ optional SettingProto smart_selection_update_content_url = 326;
+ optional SettingProto smart_selection_update_metadata_url = 327;
optional SettingProto selinux_status = 247;
optional SettingProto development_force_rtl = 248;
optional SettingProto low_battery_sound_timeout = 249;
@@ -308,13 +348,24 @@
optional SettingProto lte_service_forced = 265;
optional SettingProto ephemeral_cookie_max_size_bytes = 266;
optional SettingProto enable_ephemeral_feature = 267;
+ optional SettingProto instant_app_dexopt_enabled = 328;
optional SettingProto installed_instant_app_min_cache_period = 268;
+ optional SettingProto installed_instant_app_max_cache_period = 289;
+ optional SettingProto uninstalled_instant_app_min_cache_period = 290;
+ optional SettingProto uninstalled_instant_app_max_cache_period = 291;
+ optional SettingProto unused_static_shared_lib_min_cache_period = 292;
optional SettingProto allow_user_switching_when_system_user_locked = 269;
optional SettingProto boot_count = 270;
optional SettingProto safe_boot_disallowed = 271;
optional SettingProto device_demo_mode = 272;
+ optional SettingProto network_access_timeout_ms = 329;
optional SettingProto database_downgrade_reason = 274;
+ optional SettingProto database_creation_buildid = 330;
optional SettingProto contacts_database_wal_enabled = 275;
+ optional SettingProto location_settings_link_to_permissions_enabled = 331;
+ optional SettingProto backup_refactored_service_disabled = 332;
+ optional SettingProto euicc_factory_reset_timeout_millis = 333;
+ optional SettingProto storage_settings_clobber_threshold = 334;
optional SettingProto multi_sim_voice_call_subscription = 276;
optional SettingProto multi_sim_voice_prompt = 277;
optional SettingProto multi_sim_data_call_subscription = 278;
@@ -324,19 +375,20 @@
optional SettingProto contact_metadata_sync_enabled = 282;
optional SettingProto enable_cellular_on_boot = 283;
optional SettingProto max_notification_enqueue_rate = 284;
+ optional SettingProto show_notification_channel_warnings = 335;
optional SettingProto cell_on = 285;
- optional SettingProto network_recommendations_package = 286;
- optional SettingProto bluetooth_a2dp_supports_optional_codecs_prefix = 287;
- optional SettingProto bluetooth_a2dp_optional_codecs_enabled_prefix = 288;
- optional SettingProto installed_instant_app_max_cache_period = 289;
- optional SettingProto uninstalled_instant_app_min_cache_period = 290;
- optional SettingProto uninstalled_instant_app_max_cache_period = 291;
- optional SettingProto unused_static_shared_lib_min_cache_period = 292;
+ optional SettingProto show_temperature_warning = 336;
+ optional SettingProto warning_temperature = 337;
+ optional SettingProto enable_diskstats_logging = 338;
+ optional SettingProto enable_cache_quota_calculation = 339;
+ optional SettingProto enable_deletion_helper_no_threshold_toggle = 340;
+ optional SettingProto notification_snooze_options = 341;
+
+ // Next tag = 342;
}
message SecureSettingsProto {
- // Historical operations
- repeated SettingsOperationProto historical_op = 1;
+ repeated SettingsOperationProto historical_operations = 1;
optional SettingProto android_id = 2;
optional SettingProto default_input_method = 3;
@@ -347,6 +399,10 @@
optional SettingProto autofill_service = 8;
optional SettingProto bluetooth_hci_log = 9;
optional SettingProto user_setup_complete = 10;
+ // Whether the current user has been set up via setup wizard (0 = false,
+ // 1 = true). This value differs from USER_SETUP_COMPLETE in that it can be
+ // reset back to 0 in case SetupWizard has been re-enabled on TV devices.
+ optional SettingProto tv_user_setup_complete = 170;
optional SettingProto completed_category_prefix = 11;
optional SettingProto enabled_input_methods = 12;
optional SettingProto disabled_system_input_methods = 13;
@@ -354,10 +410,12 @@
optional SettingProto always_on_vpn_app = 15;
optional SettingProto always_on_vpn_lockdown = 16;
optional SettingProto install_non_market_apps = 17;
+ optional SettingProto unknown_sources_default_reversed = 171;
optional SettingProto location_mode = 18;
optional SettingProto location_previous_mode = 19;
optional SettingProto lock_to_app_exit_locked = 20;
optional SettingProto lock_screen_lock_after_timeout = 21;
+ optional SettingProto lock_screen_allow_private_notifications = 172;
optional SettingProto lock_screen_allow_remote_input = 22;
optional SettingProto show_note_about_notification_hiding = 23;
optional SettingProto trust_agents_initialized = 24;
@@ -366,6 +424,11 @@
optional SettingProto parental_control_redirect_url = 27;
optional SettingProto settings_classname = 28;
optional SettingProto accessibility_enabled = 29;
+ optional SettingProto accessibility_shortcut_enabled = 173;
+ optional SettingProto accessibility_shortcut_on_lock_screen = 174;
+ optional SettingProto accessibility_shortcut_dialog_shown = 175;
+ optional SettingProto accessibility_shortcut_target_service = 176;
+ optional SettingProto accessibility_button_target_component = 177;
optional SettingProto touch_exploration_enabled = 30;
optional SettingProto enabled_accessibility_services = 31;
optional SettingProto touch_exploration_granted_accessibility_services = 32;
@@ -375,7 +438,9 @@
optional SettingProto accessibility_screen_reader_url = 36;
optional SettingProto accessibility_web_content_key_bindings = 37;
optional SettingProto accessibility_display_magnification_enabled = 38;
+ optional SettingProto accessibility_display_magnification_navbar_enabled = 178;
optional SettingProto accessibility_display_magnification_scale = 39;
+ optional SettingProto accessibility_display_magnification_auto_update = 179;
optional SettingProto accessibility_soft_keyboard_mode = 40;
optional SettingProto accessibility_captioning_enabled = 41;
optional SettingProto accessibility_captioning_locale = 42;
@@ -448,6 +513,7 @@
optional SettingProto doze_enabled = 109;
optional SettingProto doze_always_on = 110;
optional SettingProto doze_pulse_on_pick_up = 111;
+ optional SettingProto doze_pulse_on_long_press = 180;
optional SettingProto doze_pulse_on_double_tap = 112;
optional SettingProto ui_night_mode = 113;
optional SettingProto screensaver_enabled = 114;
@@ -470,6 +536,7 @@
optional SettingProto immersive_mode_confirmations = 131;
optional SettingProto print_service_search_uri = 132;
optional SettingProto payment_service_search_uri = 133;
+ optional SettingProto autofill_service_search_uri = 181;
optional SettingProto skip_first_use_hints = 134;
optional SettingProto unsafe_volume_music_active_ms = 135;
optional SettingProto lock_screen_show_notifications = 136;
@@ -482,10 +549,18 @@
optional SettingProto camera_gesture_disabled = 143;
optional SettingProto camera_double_tap_power_gesture_disabled = 144;
optional SettingProto camera_double_twist_to_flip_enabled = 145;
+ optional SettingProto camera_lift_trigger_enabled = 182;
+ optional SettingProto assist_gesture_enabled = 183;
+ optional SettingProto assist_gesture_sensitivity = 184;
+ optional SettingProto assist_gesture_silence_alerts_enabled = 185;
+ optional SettingProto assist_gesture_wake_enabled = 186;
+ optional SettingProto assist_gesture_setup_complete = 187;
optional SettingProto night_display_activated = 146;
optional SettingProto night_display_auto_mode = 147;
+ optional SettingProto night_display_color_temperature = 188;
optional SettingProto night_display_custom_start_time = 148;
optional SettingProto night_display_custom_end_time = 149;
+ optional SettingProto night_display_last_activated_time = 189;
optional SettingProto brightness_use_twilight = 150;
optional SettingProto enabled_vr_listeners = 151;
optional SettingProto vr_display_mode = 152;
@@ -495,6 +570,7 @@
optional SettingProto automatic_storage_manager_days_to_retain = 156;
optional SettingProto automatic_storage_manager_bytes_cleared = 157;
optional SettingProto automatic_storage_manager_last_run = 158;
+ optional SettingProto automatic_storage_manager_turned_off_by_policy = 190;
optional SettingProto system_navigation_keys_enabled = 159;
optional SettingProto downloads_backup_enabled = 160;
optional SettingProto downloads_backup_allow_metered = 161;
@@ -504,13 +580,18 @@
optional SettingProto demo_user_setup_complete = 165;
optional SettingProto instant_apps_enabled = 166;
optional SettingProto device_paired = 167;
+ optional SettingProto package_verifier_state = 191;
+ optional SettingProto cmas_additional_broadcast_pkg = 192;
optional SettingProto notification_badging = 168;
+ optional SettingProto qs_auto_added_tiles = 193;
+ optional SettingProto lockdown_in_power_menu = 194;
optional SettingProto backup_manager_constants = 169;
+
+ // Next tag = 195
}
message SystemSettingsProto {
- // Historical operations
- repeated SettingsOperationProto historical_op = 1;
+ repeated SettingsOperationProto historical_operations = 1;
optional SettingProto end_button_behavior = 2;
optional SettingProto advanced_settings = 3;
@@ -518,6 +599,7 @@
optional SettingProto bluetooth_discoverability_timeout = 5;
optional SettingProto font_scale = 6;
optional SettingProto system_locales = 7;
+ optional SettingProto display_color_mode = 67;
optional SettingProto screen_off_timeout = 8;
optional SettingProto screen_brightness = 9;
optional SettingProto screen_brightness_for_vr = 10;
@@ -534,8 +616,17 @@
optional SettingProto volume_alarm = 21;
optional SettingProto volume_notification = 22;
optional SettingProto volume_bluetooth_sco = 23;
+ optional SettingProto volume_accessibility = 68;
optional SettingProto volume_master = 24;
optional SettingProto master_mono = 25;
+ // Whether silent mode should allow vibration feedback. This is used
+ // internally in AudioService and the Sound settings activity to coordinate
+ // decoupling of vibrate and silent modes. This setting will likely be
+ // removed in a future release with support for audio/vibe feedback
+ // profiles.
+ // Not used anymore. On devices with vibrator, the user explicitly selects
+ // silent or vibrate mode. Kept for use by legacy database upgrade code in
+ // DatabaseHelper.
optional SettingProto vibrate_in_silent = 26;
optional SettingProto append_for_last_audible = 27;
optional SettingProto ringtone = 28;
@@ -566,6 +657,10 @@
optional SettingProto notification_light_pulse = 53;
optional SettingProto pointer_location = 54;
optional SettingProto show_touches = 55;
+ // Log raw orientation data from {@link
+ // com.android.server.policy.WindowOrientationListener} for use with the
+ // orientationplot.py tool.
+ // 0 = no, 1 = yes
optional SettingProto window_orientation_listener_log = 56;
optional SettingProto lockscreen_sounds_enabled = 57;
optional SettingProto lockscreen_disabled = 58;
@@ -576,7 +671,10 @@
optional SettingProto pointer_speed = 63;
optional SettingProto lock_to_app_enabled = 64;
optional SettingProto egg_mode = 65;
+ optional SettingProto show_battery_percent = 69;
optional SettingProto when_to_make_wifi_calls = 66;
+
+ // Next tag = 70;
}
message SettingProto {
diff --git a/core/proto/android/server/powermanagerservice.proto b/core/proto/android/server/powermanagerservice.proto
index d442acf..d3b2cde 100644
--- a/core/proto/android/server/powermanagerservice.proto
+++ b/core/proto/android/server/powermanagerservice.proto
@@ -145,7 +145,7 @@
optional bool is_holding_display_suspend_blocker = 39;
// Settings and configuration
optional PowerServiceSettingsAndConfigurationDumpProto settings_and_configuration = 40;
- // Sleep timeout in ms
+ // Sleep timeout in ms. This can be -1.
optional sint32 sleep_timeout_ms = 41;
// Screen off timeout in ms
optional int32 screen_off_timeout_ms = 42;
@@ -263,7 +263,7 @@
optional float maximum_screen_dim_ratio_config = 24;
// The screen off timeout setting value in milliseconds.
optional int32 screen_off_timeout_setting_ms = 25;
- // The sleep timeout setting value in milliseconds.
+ // The sleep timeout setting value in milliseconds. Default value is -1.
optional sint32 sleep_timeout_setting_ms = 26;
// The maximum allowable screen off timeout according to the device administration policy.
optional int32 maximum_screen_off_timeout_from_device_admin_ms = 27;
diff --git a/core/proto/android/server/windowmanagerservice.proto b/core/proto/android/server/windowmanagerservice.proto
index 4d48a42..0228edb 100644
--- a/core/proto/android/server/windowmanagerservice.proto
+++ b/core/proto/android/server/windowmanagerservice.proto
@@ -48,7 +48,6 @@
/* represents PhoneWindowManager */
message WindowManagerPolicyProto {
- optional .android.graphics.RectProto stable_bounds = 1;
}
/* represents AppTransition */
@@ -101,8 +100,13 @@
optional .android.view.DisplayInfoProto display_info = 10;
optional int32 rotation = 11;
optional ScreenRotationAnimationProto screen_rotation_animation = 12;
+ optional DisplayFramesProto display_frames = 13;
}
+/* represents DisplayFrames */
+message DisplayFramesProto {
+ optional .android.graphics.RectProto stable_bounds = 1;
+}
/* represents DockedStackDividerController */
message DockedStackDividerControllerProto {
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 49de135..7af1b46 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -3657,7 +3657,7 @@
</activity-alias>
<activity-alias android:name="com.android.internal.app.ForwardIntentToManagedProfile"
android:targetActivity="com.android.internal.app.IntentForwarderActivity"
- android:icon="@drawable/ic_corp_icon"
+ android:icon="@drawable/ic_corp_badge"
android:exported="true"
android:label="@string/managed_profile_label">
</activity-alias>
@@ -3944,6 +3944,10 @@
<service android:name="com.android.server.timezone.TimeZoneUpdateIdler"
android:permission="android.permission.BIND_JOB_SERVICE" >
</service>
+
+ <service android:name="com.android.server.net.watchlist.ReportWatchlistJobService"
+ android:permission="android.permission.BIND_JOB_SERVICE" >
+ </service>
</application>
</manifest>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 1c2e5a4..1879ab7 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -123,6 +123,9 @@
be sent during a change to the audio output device. -->
<bool name="config_sendAudioBecomingNoisy">true</bool>
+ <!-- Flag to disable all transition animations -->
+ <bool name="config_disableTransitionAnimation">false</bool>
+
<!-- The duration (in milliseconds) of a short animation. -->
<integer name="config_shortAnimTime">200</integer>
@@ -876,7 +879,7 @@
<bool name="config_nightDisplayAvailable">@bool/config_setColorTransformAccelerated</bool>
<!-- Default mode to control how Night display is automatically activated.
- One of the following values (see NightDisplayController.java):
+ One of the following values (see ColorDisplayController.java):
0 - AUTO_MODE_DISABLED
1 - AUTO_MODE_CUSTOM
2 - AUTO_MODE_TWILIGHT
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 78a8e2a..9e0722b 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -4744,4 +4744,7 @@
A toast message shown when an app shortcut that wasn't restored due to an unknown issue is clicked,
-->
<string name="shortcut_restore_unknown_issue">Couldn\u2019t restore shortcut</string>
+
+ <!--Battery saver warning. STOPSHIP: Remove it eventually. -->
+ <string name="battery_saver_warning" translatable="false">Battery saver activated.\n\nSee go/extreme-battery-saver.\n\nThis contains aggressive experimental changes for P and may affect background app behavior.\n</string>
</resources>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index fd0012d..e476d89 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -252,6 +252,7 @@
<java-symbol type="bool" name="config_bluetooth_hfp_inband_ringing_support" />
<java-symbol type="bool" name="config_cellBroadcastAppLinks" />
<java-symbol type="bool" name="config_duplicate_port_omadm_wappush" />
+ <java-symbol type="bool" name="config_disableTransitionAnimation" />
<java-symbol type="bool" name="config_enableAutoPowerModes" />
<java-symbol type="integer" name="config_autoPowerModeThresholdAngle" />
<java-symbol type="integer" name="config_autoPowerModeAnyMotionSensor" />
@@ -1324,6 +1325,7 @@
<java-symbol type="drawable" name="cling_button" />
<java-symbol type="drawable" name="cling_arrow_up" />
<java-symbol type="drawable" name="cling_bg" />
+ <java-symbol type="drawable" name="ic_corp_badge" />
<java-symbol type="drawable" name="ic_corp_badge_color" />
<java-symbol type="drawable" name="ic_corp_badge_case" />
<java-symbol type="drawable" name="ic_corp_icon" />
@@ -3144,6 +3146,7 @@
<java-symbol type="string" name="shortcut_restore_not_supported" />
<java-symbol type="string" name="shortcut_restore_signature_mismatch" />
<java-symbol type="string" name="shortcut_restore_unknown_issue" />
+ <java-symbol type="string" name="battery_saver_warning" />
<!-- From media projection -->
<java-symbol type="string" name="config_mediaProjectionPermissionDialogComponent" />
diff --git a/core/tests/coretests/res/layout/add_column_in_table.xml b/core/tests/coretests/res/layout/add_column_in_table.xml
index 05f55a8..d929b02 100644
--- a/core/tests/coretests/res/layout/add_column_in_table.xml
+++ b/core/tests/coretests/res/layout/add_column_in_table.xml
@@ -18,6 +18,7 @@
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
+ <requestFocus />
<TableLayout android:id="@+id/table"
android:layout_width="match_parent"
diff --git a/core/tests/coretests/res/layout/baseline_0width_and_weight.xml b/core/tests/coretests/res/layout/baseline_0width_and_weight.xml
index acbb10b..eac9b9d 100644
--- a/core/tests/coretests/res/layout/baseline_0width_and_weight.xml
+++ b/core/tests/coretests/res/layout/baseline_0width_and_weight.xml
@@ -21,6 +21,7 @@
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
+ <requestFocus />
<LinearLayout android:id="@+id/layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
diff --git a/core/tests/coretests/res/layout/focus_after_removal.xml b/core/tests/coretests/res/layout/focus_after_removal.xml
index f4e388d..84449d1 100644
--- a/core/tests/coretests/res/layout/focus_after_removal.xml
+++ b/core/tests/coretests/res/layout/focus_after_removal.xml
@@ -22,6 +22,7 @@
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content">
+ <requestFocus />
<LinearLayout android:id="@+id/leftLayout"
android:orientation="vertical"
diff --git a/core/tests/coretests/res/layout/linear_layout_edittext_then_button.xml b/core/tests/coretests/res/layout/linear_layout_edittext_then_button.xml
index ab76e29..6b3b5a7 100644
--- a/core/tests/coretests/res/layout/linear_layout_edittext_then_button.xml
+++ b/core/tests/coretests/res/layout/linear_layout_edittext_then_button.xml
@@ -22,6 +22,7 @@
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
+ <requestFocus />
<EditText
android:id="@+id/editText"
diff --git a/core/tests/coretests/res/layout/visibility_callback.xml b/core/tests/coretests/res/layout/visibility_callback.xml
index 9034b3f..ff918f5 100644
--- a/core/tests/coretests/res/layout/visibility_callback.xml
+++ b/core/tests/coretests/res/layout/visibility_callback.xml
@@ -24,6 +24,7 @@
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
+ <requestFocus />
<LinearLayout
android:orientation="vertical"
diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
index 1002939..d36ed63 100644
--- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
@@ -26,7 +26,6 @@
import static java.lang.reflect.Modifier.isPublic;
import static java.lang.reflect.Modifier.isStatic;
-import android.platform.test.annotations.Presubmit;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
@@ -264,6 +263,7 @@
Settings.Global.NETSTATS_UID_TAG_ROTATE_AGE,
Settings.Global.NETWORK_AVOID_BAD_WIFI,
Settings.Global.NETWORK_METERED_MULTIPATH_PREFERENCE,
+ Settings.Global.NETWORK_WATCHLIST_LAST_REPORT_TIME,
Settings.Global.NETWORK_PREFERENCE,
Settings.Global.NETWORK_RECOMMENDATIONS_PACKAGE,
Settings.Global.NETWORK_RECOMMENDATION_REQUEST_TIMEOUT_MS,
diff --git a/core/tests/coretests/src/android/util/ListScenario.java b/core/tests/coretests/src/android/util/ListScenario.java
index fa088a3..129484a 100644
--- a/core/tests/coretests/src/android/util/ListScenario.java
+++ b/core/tests/coretests/src/android/util/ListScenario.java
@@ -28,6 +28,7 @@
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.TextView;
+
import com.google.android.collect.Maps;
import java.util.ArrayList;
@@ -60,18 +61,18 @@
// separators
private Set<Integer> mUnselectableItems = new HashSet<Integer>();
-
+
private boolean mStackFromBottom;
private int mClickedPosition = -1;
-
+
private int mLongClickedPosition = -1;
-
+
private int mConvertMisses = 0;
-
+
private int mHeaderViewCount;
private boolean mHeadersFocusable;
-
+
private int mFooterViewCount;
private LinearLayout mLinearLayout;
@@ -193,7 +194,7 @@
mIncludeHeader = includeHeader;
return this;
}
-
+
/**
* Sets the stacking direction
* @param stackFromBottom
@@ -203,7 +204,7 @@
mStackFromBottom = stackFromBottom;
return this;
}
-
+
/**
* Sets whether the sum of the height of the list items must be at least the
* height of the list view.
@@ -220,7 +221,7 @@
mFadingEdgeScreenSizeFactor = fadingEdgeScreenSizeFactor;
return this;
}
-
+
/**
* Set the number of header views to appear within the list
*/
@@ -246,7 +247,7 @@
mFooterViewCount = footerViewCount;
return this;
}
-
+
/**
* Sets whether the {@link ListScenario} will automatically set the
* adapter on the list view. If this is false, the client MUST set it
@@ -278,7 +279,7 @@
*/
protected void nothingSelected() {
}
-
+
/**
* Override this if you want to know when something has been clicked (perhaps
* more importantly, that {@link android.widget.AdapterView.OnItemClickListener} has
@@ -287,7 +288,7 @@
protected void positionClicked(int position) {
setClickedPosition(position);
}
-
+
/**
* Override this if you want to know when something has been long clicked (perhaps
* more importantly, that {@link android.widget.AdapterView.OnItemLongClickListener} has
@@ -303,7 +304,7 @@
// for test stability, turn off title bar
requestWindowFeature(Window.FEATURE_NO_TITLE);
-
+
mScreenHeight = getWindowManager().getDefaultDisplay().getHeight();
@@ -326,7 +327,7 @@
header.setText("Header: " + i);
mListView.addHeaderView(header);
}
-
+
for (int i=0; i<mFooterViewCount; i++) {
TextView header = new TextView(this);
header.setText("Footer: " + i);
@@ -336,7 +337,7 @@
if (params.mConnectAdapter) {
setAdapter(mListView);
}
-
+
mListView.setItemsCanFocus(mItemsFocusable);
if (mStartingSelectionPosition >= 0) {
mListView.setSelection(mStartingSelectionPosition);
@@ -360,11 +361,12 @@
positionClicked(position);
}
});
-
+
// set the fading edge length porportionally to the screen
// height for test stability
if (params.mFadingEdgeScreenSizeFactor != null) {
- mListView.setFadingEdgeLength((int) (params.mFadingEdgeScreenSizeFactor * mScreenHeight));
+ mListView.setFadingEdgeLength(
+ (int) (params.mFadingEdgeScreenSizeFactor * mScreenHeight));
} else {
mListView.setFadingEdgeLength((int) ((64.0 / 480) * mScreenHeight));
}
@@ -403,6 +405,7 @@
mLinearLayout.addView(mListView);
setContentView(mLinearLayout);
}
+ mLinearLayout.restoreDefaultFocus();
}
/**
@@ -426,7 +429,7 @@
}
});
}
-
+
/**
* @return The newly created ListView widget.
*/
@@ -440,16 +443,16 @@
protected Params createParams() {
return new Params();
}
-
+
/**
* Sets an adapter on a ListView.
- *
+ *
* @param listView The ListView to set the adapter on.
*/
protected void setAdapter(ListView listView) {
listView.setAdapter(new MyAdapter());
}
-
+
/**
* Read in and validate all of the params passed in by the scenario.
* @param params
@@ -525,7 +528,7 @@
if (!mIncludeHeader) {
throw new IllegalArgumentException("no header above list");
}
- mHeaderTextView.setText(value);
+ mHeaderTextView.setText(value);
}
/**
@@ -543,12 +546,12 @@
}
/**
- * Convert a non-null view.
+ * Convert a non-null view.
*/
public View convertView(int position, View convertView, ViewGroup parent) {
return ListItemFactory.convertText(convertView, getValueAtPosition(position), position);
}
-
+
public void setClickedPosition(int clickedPosition) {
mClickedPosition = clickedPosition;
}
@@ -580,7 +583,7 @@
}
});
}
-
+
/**
* Return an item type for the specified position in the adapter. Override if your
* adapter creates more than one type.
@@ -596,7 +599,7 @@
public int getViewTypeCount() {
return 1;
}
-
+
/**
* @return The number of times convertView failed
*/
@@ -647,7 +650,7 @@
}
return result;
}
-
+
@Override
public int getItemViewType(int position) {
return ListScenario.this.getItemViewType(position);
diff --git a/core/tests/coretests/src/android/widget/espresso/FloatingToolbarEspressoUtils.java b/core/tests/coretests/src/android/widget/espresso/FloatingToolbarEspressoUtils.java
index b6986d5..0355f82 100644
--- a/core/tests/coretests/src/android/widget/espresso/FloatingToolbarEspressoUtils.java
+++ b/core/tests/coretests/src/android/widget/espresso/FloatingToolbarEspressoUtils.java
@@ -48,6 +48,7 @@
import java.util.ArrayList;
import java.util.List;
+import java.util.function.Predicate;
/**
* Espresso utility methods for the floating toolbar.
@@ -175,31 +176,10 @@
* @throws AssertionError if the assertion fails
*/
public static void assertFloatingToolbarDoesNotContainItem(String itemLabel) {
- onFloatingToolBar().check(matches(new TypeSafeMatcher<View>() {
- @Override
- public boolean matchesSafely(View view) {
- return doesNotContainItem(view);
- }
-
- @Override
- public void describeTo(Description description) {}
-
- private boolean doesNotContainItem(View view) {
- if (view.getTag() instanceof MenuItem) {
- if (itemLabel.equals(((MenuItem) view.getTag()).getTitle().toString())) {
- return false;
- }
- } else if (view instanceof ViewGroup) {
- ViewGroup viewGroup = (ViewGroup) view;
- for (int i = 0; i < viewGroup.getChildCount(); i++) {
- if (doesNotContainItem(viewGroup.getChildAt(i))) {
- return false;
- }
- }
- }
- return true;
- }
- }));
+ final Predicate<View> hasMenuItemLabel = view ->
+ view.getTag() instanceof MenuItem
+ && itemLabel.equals(((MenuItem) view.getTag()).getTitle().toString());
+ assertFloatingToolbarMenuItem(hasMenuItemLabel, false);
}
/**
@@ -209,23 +189,30 @@
* @throws AssertionError if the assertion fails
*/
public static void assertFloatingToolbarDoesNotContainItem(final int menuItemId) {
+ final Predicate<View> hasMenuItemId = view ->
+ view.getTag() instanceof MenuItem
+ && ((MenuItem) view.getTag()).getItemId() == menuItemId;
+ assertFloatingToolbarMenuItem(hasMenuItemId, false);
+ }
+
+ private static void assertFloatingToolbarMenuItem(
+ final Predicate<View> predicate, final boolean positiveAssertion) {
onFloatingToolBar().check(matches(new TypeSafeMatcher<View>() {
@Override
public boolean matchesSafely(View view) {
- return !hasMenuItemWithSpecifiedId(view);
+ return positiveAssertion == containsItem(view);
}
@Override
public void describeTo(Description description) {}
- private boolean hasMenuItemWithSpecifiedId(View view) {
- if (view.getTag() instanceof MenuItem
- && ((MenuItem) view.getTag()).getItemId() == menuItemId) {
+ private boolean containsItem(View view) {
+ if (predicate.test(view)) {
return true;
} else if (view instanceof ViewGroup) {
ViewGroup viewGroup = (ViewGroup) view;
for (int i = 0; i < viewGroup.getChildCount(); i++) {
- if (hasMenuItemWithSpecifiedId(viewGroup.getChildAt(i))) {
+ if (containsItem(viewGroup.getChildAt(i))) {
return true;
}
}
diff --git a/core/tests/coretests/src/android/widget/layout/linear/LLOfTwoFocusableInTouchMode.java b/core/tests/coretests/src/android/widget/layout/linear/LLOfTwoFocusableInTouchMode.java
index 1ba56ba..0d8d834 100644
--- a/core/tests/coretests/src/android/widget/layout/linear/LLOfTwoFocusableInTouchMode.java
+++ b/core/tests/coretests/src/android/widget/layout/linear/LLOfTwoFocusableInTouchMode.java
@@ -16,12 +16,12 @@
package android.widget.layout.linear;
-import com.android.frameworks.coretests.R;
-
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
+import com.android.frameworks.coretests.R;
+
public class LLOfTwoFocusableInTouchMode extends Activity {
private View mButton1;
@@ -63,6 +63,7 @@
mB3Fired = true;
}
});
+ getWindow().getDecorView().restoreDefaultFocus();
}
public View getButton1() {
diff --git a/core/tests/coretests/src/android/widget/listview/AdjacentListsWithAdjacentISVsInside.java b/core/tests/coretests/src/android/widget/listview/AdjacentListsWithAdjacentISVsInside.java
index 98fbed3..9e49719 100644
--- a/core/tests/coretests/src/android/widget/listview/AdjacentListsWithAdjacentISVsInside.java
+++ b/core/tests/coretests/src/android/widget/listview/AdjacentListsWithAdjacentISVsInside.java
@@ -16,10 +16,9 @@
package android.widget.listview;
-import android.util.InternalSelectionView;
-
import android.app.Activity;
import android.os.Bundle;
+import android.util.InternalSelectionView;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
@@ -80,6 +79,7 @@
setContentView(combineAdjacent(mLeftListView, mRightListView));
+ getWindow().getDecorView().restoreDefaultFocus();
}
private static View combineAdjacent(View... views) {
diff --git a/core/tests/coretests/src/android/widget/listview/focus/ListHorizontalFocusWithinItemWinsTest.java b/core/tests/coretests/src/android/widget/listview/focus/ListHorizontalFocusWithinItemWinsTest.java
index 8f971bb..edc60b5 100644
--- a/core/tests/coretests/src/android/widget/listview/focus/ListHorizontalFocusWithinItemWinsTest.java
+++ b/core/tests/coretests/src/android/widget/listview/focus/ListHorizontalFocusWithinItemWinsTest.java
@@ -16,15 +16,12 @@
package android.widget.listview.focus;
-import android.widget.listview.ListHorizontalFocusWithinItemWins;
-
import android.test.ActivityInstrumentationTestCase;
import android.test.suitebuilder.annotation.MediumTest;
-import android.view.FocusFinder;
import android.view.KeyEvent;
-import android.view.View;
import android.widget.Button;
import android.widget.ListView;
+import android.widget.listview.ListHorizontalFocusWithinItemWins;
public class ListHorizontalFocusWithinItemWinsTest extends ActivityInstrumentationTestCase<ListHorizontalFocusWithinItemWins> {
@@ -51,12 +48,6 @@
public void testPreconditions() {
assertEquals("list position", 0, mListView.getSelectedItemPosition());
assertTrue("mTopLeftButton.isFocused()", mTopLeftButton.isFocused());
- assertEquals("global focus search to right from top left is bottom middle",
- mBottomMiddleButton,
- FocusFinder.getInstance().findNextFocus(mListView, mTopLeftButton, View.FOCUS_RIGHT));
- assertEquals("global focus search to left from top right is bottom middle",
- mBottomMiddleButton,
- FocusFinder.getInstance().findNextFocus(mListView, mTopRightButton, View.FOCUS_LEFT));
}
@MediumTest
diff --git a/core/tests/coretests/src/android/widget/touchmode/TouchModeFocusChangeTest.java b/core/tests/coretests/src/android/widget/touchmode/TouchModeFocusChangeTest.java
index bd6977e..5a6110c 100644
--- a/core/tests/coretests/src/android/widget/touchmode/TouchModeFocusChangeTest.java
+++ b/core/tests/coretests/src/android/widget/touchmode/TouchModeFocusChangeTest.java
@@ -16,7 +16,6 @@
package android.widget.touchmode;
-import android.widget.layout.linear.LLOfButtons1;
import static android.util.TouchModeFlexibleAsserts.assertInTouchModeAfterClick;
import static android.util.TouchModeFlexibleAsserts.assertInTouchModeAfterTap;
import static android.util.TouchModeFlexibleAsserts.assertNotInTouchModeAfterKey;
@@ -25,6 +24,8 @@
import android.test.suitebuilder.annotation.MediumTest;
import android.view.KeyEvent;
import android.widget.Button;
+import android.widget.layout.linear.LLOfButtons1;
+
/**
* Make sure focus isn't kept by buttons when entering touch mode.
@@ -52,7 +53,6 @@
@MediumTest
public void testPreconditions() {
assertFalse("we should not be in touch mode", mActivity.isInTouchMode());
- assertTrue("top button should have focus", mFirstButton.isFocused());
}
@MediumTest
diff --git a/core/tests/webkit/apk_with_native_libs/Android.mk b/core/tests/webkit/apk_with_native_libs/Android.mk
index 7c6c36e..c51de6a 100644
--- a/core/tests/webkit/apk_with_native_libs/Android.mk
+++ b/core/tests/webkit/apk_with_native_libs/Android.mk
@@ -20,6 +20,7 @@
MY_JNI_SHARED_LIBRARIES := libwebviewtest_jni
MY_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
MY_SRC_FILES := $(call all-java-files-under, src)
+MY_CFLAGS := -Wall -Werror
MY_SDK_VERSION := system_current
MY_PROGUARD_ENABLED := disabled
MY_MULTILIB := both
@@ -40,6 +41,7 @@
LOCAL_JNI_SHARED_LIBRARIES := $(MY_JNI_SHARED_LIBRARIES)
LOCAL_MODULE_PATH := $(MY_MODULE_PATH)
LOCAL_SRC_FILES := $(MY_SRC_FILES)
+LOCAL_CFLAGS := $(MY_CFLAGS)
LOCAL_SDK_VERSION := $(MY_SDK_VERSION)
LOCAL_PROGUARD_ENABLED := $(MY_PROGUARD_ENABLED)
LOCAL_MULTILIB := $(MY_MULTILIB)
@@ -59,6 +61,7 @@
LOCAL_JNI_SHARED_LIBRARIES := $(MY_JNI_SHARED_LIBRARIES)
LOCAL_MODULE_PATH := $(MY_MODULE_PATH)
LOCAL_SRC_FILES := $(MY_SRC_FILES)
+LOCAL_CFLAGS := $(MY_CFLAGS)
LOCAL_SDK_VERSION := $(MY_SDK_VERSION)
LOCAL_PROGUARD_ENABLED := $(MY_PROGUARD_ENABLED)
LOCAL_MULTILIB := $(MY_MULTILIB)
diff --git a/core/tests/webkit/apk_with_native_libs/jni/WebViewTestJniOnLoad.cpp b/core/tests/webkit/apk_with_native_libs/jni/WebViewTestJniOnLoad.cpp
index 9b0502f..0ced4ee 100644
--- a/core/tests/webkit/apk_with_native_libs/jni/WebViewTestJniOnLoad.cpp
+++ b/core/tests/webkit/apk_with_native_libs/jni/WebViewTestJniOnLoad.cpp
@@ -16,6 +16,6 @@
#include <jni.h>
-jint JNI_OnLoad(JavaVM *vm, void *reserved) {
+jint JNI_OnLoad(JavaVM * /*vm*/, void * /*reserved*/) {
return JNI_VERSION_1_4;
}
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index f7e0b46..161a0c2 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -258,6 +258,11 @@
<permission name="android.permission.WRITE_SECURE_SETTINGS"/>
</privapp-permissions>
+ <privapp-permissions package="com.android.settings.intelligence">
+ <permission name="android.permission.READ_SEARCH_INDEXABLES"/>
+ <permission name="android.permission.MODIFY_PHONE_STATE"/>
+ </privapp-permissions>
+
<privapp-permissions package="com.android.sharedstoragebackup">
<permission name="android.permission.WRITE_MEDIA_STORAGE"/>
</privapp-permissions>
diff --git a/graphics/java/android/graphics/Rect.java b/graphics/java/android/graphics/Rect.java
index 3dc928d..aff942d 100644
--- a/graphics/java/android/graphics/Rect.java
+++ b/graphics/java/android/graphics/Rect.java
@@ -475,6 +475,19 @@
}
/**
+ * If the specified rectangle intersects this rectangle, set this rectangle to that
+ * intersection, otherwise set this rectangle to the empty rectangle.
+ * @see #inset(int, int, int, int) but without checking if the rects overlap.
+ * @hide
+ */
+ public void intersectUnchecked(Rect other) {
+ left = Math.max(left, other.left);
+ top = Math.max(top, other.top);
+ right = Math.min(right, other.right);
+ bottom = Math.min(bottom, other.bottom);
+ }
+
+ /**
* If rectangles a and b intersect, return true and set this rectangle to
* that intersection, otherwise return false and do not change this
* rectangle. No check is performed to see if either rectangle is empty.
diff --git a/keystore/java/android/security/IKeyChainService.aidl b/keystore/java/android/security/IKeyChainService.aidl
index c4bb72c..635432d 100644
--- a/keystore/java/android/security/IKeyChainService.aidl
+++ b/keystore/java/android/security/IKeyChainService.aidl
@@ -28,6 +28,8 @@
String requestPrivateKey(String alias);
byte[] getCertificate(String alias);
byte[] getCaCertificates(String alias);
+ boolean isUserSelectable(String alias);
+ void setUserSelectable(String alias, boolean isUserSelectable);
// APIs used by CertInstaller and DevicePolicyManager
String installCaCertificate(in byte[] caCertificate);
diff --git a/keystore/java/android/security/KeyStore.java b/keystore/java/android/security/KeyStore.java
index a7469cd..399dddd 100644
--- a/keystore/java/android/security/KeyStore.java
+++ b/keystore/java/android/security/KeyStore.java
@@ -54,7 +54,7 @@
public class KeyStore {
private static final String TAG = "KeyStore";
- // ResponseCodes
+ // ResponseCodes - see system/security/keystore/include/keystore/keystore.h
public static final int NO_ERROR = 1;
public static final int LOCKED = 2;
public static final int UNINITIALIZED = 3;
@@ -168,10 +168,14 @@
public byte[] get(String key, int uid) {
try {
+ key = key != null ? key : "";
return mBinder.get(key, uid);
} catch (RemoteException e) {
Log.w(TAG, "Cannot connect to keystore", e);
return null;
+ } catch (android.os.ServiceSpecificException e) {
+ Log.w(TAG, "KeyStore exception", e);
+ return null;
}
}
@@ -185,6 +189,9 @@
public int insert(String key, byte[] value, int uid, int flags) {
try {
+ if (value == null) {
+ value = new byte[0];
+ }
return mBinder.insert(key, value, uid, flags);
} catch (RemoteException e) {
Log.w(TAG, "Cannot connect to keystore", e);
@@ -228,6 +235,9 @@
} catch (RemoteException e) {
Log.w(TAG, "Cannot connect to keystore", e);
return null;
+ } catch (android.os.ServiceSpecificException e) {
+ Log.w(TAG, "KeyStore exception", e);
+ return null;
}
}
@@ -276,6 +286,7 @@
*/
public boolean unlock(int userId, String password) {
try {
+ password = password != null ? password : "";
mError = mBinder.unlock(userId, password);
return mError == NO_ERROR;
} catch (RemoteException e) {
@@ -330,16 +341,25 @@
} catch (RemoteException e) {
Log.w(TAG, "Cannot connect to keystore", e);
return null;
+ } catch (android.os.ServiceSpecificException e) {
+ Log.w(TAG, "KeyStore exception", e);
+ return null;
}
+
}
public boolean verify(String key, byte[] data, byte[] signature) {
try {
+ signature = signature != null ? signature : new byte[0];
return mBinder.verify(key, data, signature) == NO_ERROR;
} catch (RemoteException e) {
Log.w(TAG, "Cannot connect to keystore", e);
return false;
+ } catch (android.os.ServiceSpecificException e) {
+ Log.w(TAG, "KeyStore exception", e);
+ return false;
}
+
}
public String grant(String key, int uid) {
@@ -432,6 +452,8 @@
public int generateKey(String alias, KeymasterArguments args, byte[] entropy, int uid,
int flags, KeyCharacteristics outCharacteristics) {
try {
+ entropy = entropy != null ? entropy : new byte[0];
+ args = args != null ? args : new KeymasterArguments();
return mBinder.generateKey(alias, args, entropy, uid, flags, outCharacteristics);
} catch (RemoteException e) {
Log.w(TAG, "Cannot connect to keystore", e);
@@ -447,6 +469,8 @@
public int getKeyCharacteristics(String alias, KeymasterBlob clientId, KeymasterBlob appId,
int uid, KeyCharacteristics outCharacteristics) {
try {
+ clientId = clientId != null ? clientId : new KeymasterBlob(new byte[0]);
+ appId = appId != null ? appId : new KeymasterBlob(new byte[0]);
return mBinder.getKeyCharacteristics(alias, clientId, appId, uid, outCharacteristics);
} catch (RemoteException e) {
Log.w(TAG, "Cannot connect to keystore", e);
@@ -478,6 +502,8 @@
public ExportResult exportKey(String alias, int format, KeymasterBlob clientId,
KeymasterBlob appId, int uid) {
try {
+ clientId = clientId != null ? clientId : new KeymasterBlob(new byte[0]);
+ appId = appId != null ? appId : new KeymasterBlob(new byte[0]);
return mBinder.exportKey(alias, format, clientId, appId, uid);
} catch (RemoteException e) {
Log.w(TAG, "Cannot connect to keystore", e);
@@ -492,6 +518,8 @@
public OperationResult begin(String alias, int purpose, boolean pruneable,
KeymasterArguments args, byte[] entropy, int uid) {
try {
+ args = args != null ? args : new KeymasterArguments();
+ entropy = entropy != null ? entropy : new byte[0];
return mBinder.begin(getToken(), alias, purpose, pruneable, args, entropy, uid);
} catch (RemoteException e) {
Log.w(TAG, "Cannot connect to keystore", e);
@@ -501,11 +529,15 @@
public OperationResult begin(String alias, int purpose, boolean pruneable,
KeymasterArguments args, byte[] entropy) {
+ entropy = entropy != null ? entropy : new byte[0];
+ args = args != null ? args : new KeymasterArguments();
return begin(alias, purpose, pruneable, args, entropy, UID_SELF);
}
public OperationResult update(IBinder token, KeymasterArguments arguments, byte[] input) {
try {
+ arguments = arguments != null ? arguments : new KeymasterArguments();
+ input = input != null ? input : new byte[0];
return mBinder.update(token, arguments, input);
} catch (RemoteException e) {
Log.w(TAG, "Cannot connect to keystore", e);
@@ -516,6 +548,9 @@
public OperationResult finish(IBinder token, KeymasterArguments arguments, byte[] signature,
byte[] entropy) {
try {
+ arguments = arguments != null ? arguments : new KeymasterArguments();
+ entropy = entropy != null ? entropy : new byte[0];
+ signature = signature != null ? signature : new byte[0];
return mBinder.finish(token, arguments, signature, entropy);
} catch (RemoteException e) {
Log.w(TAG, "Cannot connect to keystore", e);
@@ -632,6 +667,12 @@
public int attestKey(
String alias, KeymasterArguments params, KeymasterCertificateChain outChain) {
try {
+ if (params == null) {
+ params = new KeymasterArguments();
+ }
+ if (outChain == null) {
+ outChain = new KeymasterCertificateChain();
+ }
return mBinder.attestKey(alias, params, outChain);
} catch (RemoteException e) {
Log.w(TAG, "Cannot connect to keystore", e);
@@ -641,6 +682,12 @@
public int attestDeviceIds(KeymasterArguments params, KeymasterCertificateChain outChain) {
try {
+ if (params == null) {
+ params = new KeymasterArguments();
+ }
+ if (outChain == null) {
+ outChain = new KeymasterCertificateChain();
+ }
return mBinder.attestDeviceIds(params, outChain);
} catch (RemoteException e) {
Log.w(TAG, "Cannot connect to keystore", e);
diff --git a/libs/androidfw/Android.bp b/libs/androidfw/Android.bp
index 5484cf0..251b2e7 100644
--- a/libs/androidfw/Android.bp
+++ b/libs/androidfw/Android.bp
@@ -98,6 +98,9 @@
enabled: true,
},
},
+ sanitize: {
+ blacklist: "libandroidfw_blacklist.txt",
+ },
}
common_test_libs = [
diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp
index 5d248c2..3fe75cf 100644
--- a/libs/androidfw/ResourceTypes.cpp
+++ b/libs/androidfw/ResourceTypes.cpp
@@ -812,7 +812,13 @@
*outLen = encLen;
if ((uint32_t)(str+encLen-strings) < mStringPoolSize) {
- return (const char*)str;
+ // Reject malformed (non null-terminated) strings
+ if (str[encLen] != 0x00) {
+ ALOGW("Bad string block: string #%d is not null-terminated",
+ (int)idx);
+ return NULL;
+ }
+ return (const char*)str;
} else {
ALOGW("Bad string block: string #%d extends to %d, past end at %d\n",
(int)idx, (int)(str+encLen-strings), (int)mStringPoolSize);
diff --git a/libs/androidfw/libandroidfw_blacklist.txt b/libs/androidfw/libandroidfw_blacklist.txt
new file mode 100644
index 0000000..dd17e4d
--- /dev/null
+++ b/libs/androidfw/libandroidfw_blacklist.txt
@@ -0,0 +1 @@
+src:*/ResourceTypes.cpp
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp
index 2644292..24ce1e4 100644
--- a/libs/hwui/Android.bp
+++ b/libs/hwui/Android.bp
@@ -68,6 +68,7 @@
],
static_libs: [
"libplatformprotos",
+ "libEGL_blobCache",
],
}
@@ -131,6 +132,7 @@
"pipeline/skia/LayerDrawable.cpp",
"pipeline/skia/RenderNodeDrawable.cpp",
"pipeline/skia/ReorderBarrierDrawables.cpp",
+ "pipeline/skia/ShaderCache.cpp",
"pipeline/skia/SkiaDisplayList.cpp",
"pipeline/skia/SkiaOpenGLPipeline.cpp",
"pipeline/skia/SkiaOpenGLReadback.cpp",
@@ -346,6 +348,7 @@
"tests/unit/RecordingCanvasTests.cpp",
"tests/unit/RenderNodeTests.cpp",
"tests/unit/RenderPropertiesTests.cpp",
+ "tests/unit/ShaderCacheTests.cpp",
"tests/unit/SkiaBehaviorTests.cpp",
"tests/unit/SkiaDisplayListTests.cpp",
"tests/unit/SkiaPipelineTests.cpp",
diff --git a/libs/hwui/Extensions.cpp b/libs/hwui/Extensions.cpp
index b7f1fb2..530e82e 100644
--- a/libs/hwui/Extensions.cpp
+++ b/libs/hwui/Extensions.cpp
@@ -31,11 +31,7 @@
namespace uirenderer {
Extensions::Extensions() {
- if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaVulkan) {
- // Extensions class is used only by OpenGL and SkiaGL pipelines
- // The code below will crash for SkiaVulkan, because OpenGL is not initialized
- // TODO: instantiate Extensions class only for OpenGL pipeline
- // TODO: remove the only usage of Extensions by SkiaGL in SkiaOpenGLReadback::copyImageInto
+ if (Properties::isSkiaEnabled()) {
return;
}
const char* version = (const char*)glGetString(GL_VERSION);
diff --git a/libs/hwui/FrameInfoVisualizer.cpp b/libs/hwui/FrameInfoVisualizer.cpp
index 91756e7..5aea04d 100644
--- a/libs/hwui/FrameInfoVisualizer.cpp
+++ b/libs/hwui/FrameInfoVisualizer.cpp
@@ -245,22 +245,19 @@
// last call to dumpData(). In other words if there's a dumpData(), draw frame,
// dumpData(), the last dumpData() should only log 1 frame.
- FILE* file = fdopen(fd, "a");
- fprintf(file, "\n\tDraw\tPrepare\tProcess\tExecute\n");
+ dprintf(fd, "\n\tDraw\tPrepare\tProcess\tExecute\n");
for (size_t i = 0; i < mFrameSource.size(); i++) {
if (mFrameSource[i][FrameInfoIndex::IntendedVsync] <= mLastFrameLogged) {
continue;
}
mLastFrameLogged = mFrameSource[i][FrameInfoIndex::IntendedVsync];
- fprintf(file, "\t%3.2f\t%3.2f\t%3.2f\t%3.2f\n",
+ dprintf(fd, "\t%3.2f\t%3.2f\t%3.2f\t%3.2f\n",
durationMS(i, FrameInfoIndex::IntendedVsync, FrameInfoIndex::SyncStart),
durationMS(i, FrameInfoIndex::SyncStart, FrameInfoIndex::IssueDrawCommandsStart),
durationMS(i, FrameInfoIndex::IssueDrawCommandsStart, FrameInfoIndex::SwapBuffers),
durationMS(i, FrameInfoIndex::SwapBuffers, FrameInfoIndex::FrameCompleted));
}
-
- fflush(file);
}
} /* namespace uirenderer */
diff --git a/libs/hwui/JankTracker.cpp b/libs/hwui/JankTracker.cpp
index 5b67693..afdd339 100644
--- a/libs/hwui/JankTracker.cpp
+++ b/libs/hwui/JankTracker.cpp
@@ -174,24 +174,22 @@
}
void JankTracker::dumpFrames(int fd) {
- FILE* file = fdopen(fd, "a");
- fprintf(file, "\n\n---PROFILEDATA---\n");
+ dprintf(fd, "\n\n---PROFILEDATA---\n");
for (size_t i = 0; i < static_cast<size_t>(FrameInfoIndex::NumIndexes); i++) {
- fprintf(file, "%s", FrameInfoNames[i].c_str());
- fprintf(file, ",");
+ dprintf(fd, "%s", FrameInfoNames[i].c_str());
+ dprintf(fd, ",");
}
for (size_t i = 0; i < mFrames.size(); i++) {
FrameInfo& frame = mFrames[i];
if (frame[FrameInfoIndex::SyncStart] == 0) {
continue;
}
- fprintf(file, "\n");
+ dprintf(fd, "\n");
for (int i = 0; i < static_cast<int>(FrameInfoIndex::NumIndexes); i++) {
- fprintf(file, "%" PRId64 ",", frame[i]);
+ dprintf(fd, "%" PRId64 ",", frame[i]);
}
}
- fprintf(file, "\n---PROFILEDATA---\n\n");
- fflush(file);
+ dprintf(fd, "\n---PROFILEDATA---\n\n");
}
void JankTracker::reset() {
diff --git a/libs/hwui/Properties.cpp b/libs/hwui/Properties.cpp
index d90caf9..d41db63 100644
--- a/libs/hwui/Properties.cpp
+++ b/libs/hwui/Properties.cpp
@@ -178,7 +178,7 @@
}
RenderPipelineType Properties::getRenderPipelineType() {
- if (RenderPipelineType::NotInitialized != sRenderPipelineType) {
+ if (sRenderPipelineType != RenderPipelineType::NotInitialized) {
return sRenderPipelineType;
}
char prop[PROPERTY_VALUE_MAX];
@@ -196,11 +196,17 @@
return sRenderPipelineType;
}
-#ifdef HWUI_GLES_WRAP_ENABLED
void Properties::overrideRenderPipelineType(RenderPipelineType type) {
+#if !defined(HWUI_GLES_WRAP_ENABLED)
+ // If we're doing actual rendering then we can't change the renderer after it's been set.
+ // Unit tests can freely change this as often as it wants, though, as there's no actual
+ // GL rendering happening
+ if (sRenderPipelineType != RenderPipelineType::NotInitialized) {
+ return;
+ }
+#endif
sRenderPipelineType = type;
}
-#endif
bool Properties::isSkiaEnabled() {
auto renderType = getRenderPipelineType();
diff --git a/libs/hwui/Properties.h b/libs/hwui/Properties.h
index fbd9b7a..9c30e4a 100644
--- a/libs/hwui/Properties.h
+++ b/libs/hwui/Properties.h
@@ -255,10 +255,8 @@
static bool skpCaptureEnabled;
-// Used for testing only to change the render pipeline.
-#ifdef HWUI_GLES_WRAP_ENABLED
+ // Used for testing only to change the render pipeline.
static void overrideRenderPipelineType(RenderPipelineType);
-#endif
private:
static ProfileType sProfileType;
diff --git a/libs/hwui/pipeline/skia/ShaderCache.cpp b/libs/hwui/pipeline/skia/ShaderCache.cpp
new file mode 100644
index 0000000..87edd69
--- /dev/null
+++ b/libs/hwui/pipeline/skia/ShaderCache.cpp
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "ShaderCache.h"
+#include <algorithm>
+#include <log/log.h>
+#include <thread>
+#include "FileBlobCache.h"
+#include "utils/TraceUtils.h"
+
+namespace android {
+namespace uirenderer {
+namespace skiapipeline {
+
+// Cache size limits.
+static const size_t maxKeySize = 1024;
+static const size_t maxValueSize = 64 * 1024;
+static const size_t maxTotalSize = 512 * 1024;
+
+ShaderCache::ShaderCache() {
+ // There is an "incomplete FileBlobCache type" compilation error, if ctor is moved to header.
+}
+
+ShaderCache ShaderCache::sCache;
+
+ShaderCache& ShaderCache::get() {
+ return sCache;
+}
+
+void ShaderCache::initShaderDiskCache() {
+ ATRACE_NAME("initShaderDiskCache");
+ std::lock_guard<std::mutex> lock(mMutex);
+ if (mFilename.length() > 0) {
+ mBlobCache.reset(new FileBlobCache(maxKeySize, maxValueSize, maxTotalSize, mFilename));
+ mInitialized = true;
+ }
+}
+
+void ShaderCache::setFilename(const char* filename) {
+ std::lock_guard<std::mutex> lock(mMutex);
+ mFilename = filename;
+}
+
+BlobCache* ShaderCache::getBlobCacheLocked() {
+ LOG_ALWAYS_FATAL_IF(!mInitialized, "ShaderCache has not been initialized");
+ return mBlobCache.get();
+}
+
+sk_sp<SkData> ShaderCache::load(const SkData& key) {
+ ATRACE_NAME("ShaderCache::load");
+ size_t keySize = key.size();
+ std::lock_guard<std::mutex> lock(mMutex);
+ if (!mInitialized) {
+ ALOGE("ShaderCache::load not initialized");
+ return nullptr;
+ }
+
+ // mObservedBlobValueSize is reasonably big to avoid memory reallocation
+ // Allocate a buffer with malloc. SkData takes ownership of that allocation and will call free.
+ void* valueBuffer = malloc(mObservedBlobValueSize);
+ if (!valueBuffer) {
+ return nullptr;
+ }
+ BlobCache* bc = getBlobCacheLocked();
+ size_t valueSize = bc->get(key.data(), keySize, valueBuffer, mObservedBlobValueSize);
+ int maxTries = 3;
+ while (valueSize > mObservedBlobValueSize && maxTries > 0) {
+ mObservedBlobValueSize = std::min(valueSize, maxValueSize);
+ valueBuffer = realloc(valueBuffer, mObservedBlobValueSize);
+ if (!valueBuffer) {
+ return nullptr;
+ }
+ valueSize = bc->get(key.data(), keySize, valueBuffer, mObservedBlobValueSize);
+ maxTries--;
+ }
+ if (!valueSize) {
+ free(valueBuffer);
+ return nullptr;
+ }
+ if (valueSize > mObservedBlobValueSize) {
+ ALOGE("ShaderCache::load value size is too big %d", (int) valueSize);
+ free(valueBuffer);
+ return nullptr;
+ }
+ return SkData::MakeFromMalloc(valueBuffer, valueSize);
+}
+
+void ShaderCache::store(const SkData& key, const SkData& data) {
+ ATRACE_NAME("ShaderCache::store");
+ std::lock_guard<std::mutex> lock(mMutex);
+
+ if (!mInitialized) {
+ ALOGE("ShaderCache::store not initialized");
+ return;
+ }
+
+ size_t valueSize = data.size();
+ size_t keySize = key.size();
+ if (keySize == 0 || valueSize == 0 || valueSize >= maxValueSize) {
+ ALOGW("ShaderCache::store: sizes %d %d not allowed", (int)keySize, (int)valueSize);
+ return;
+ }
+
+ const void* value = data.data();
+
+ BlobCache* bc = getBlobCacheLocked();
+ bc->set(key.data(), keySize, value, valueSize);
+
+ if (!mSavePending && mDeferredSaveDelay > 0) {
+ mSavePending = true;
+ std::thread deferredSaveThread([this]() {
+ sleep(mDeferredSaveDelay);
+ std::lock_guard<std::mutex> lock(mMutex);
+ ATRACE_NAME("ShaderCache::saveToDisk");
+ if (mInitialized && mBlobCache) {
+ mBlobCache->writeToFile();
+ }
+ mSavePending = false;
+ });
+ deferredSaveThread.detach();
+ }
+}
+
+} /* namespace skiapipeline */
+} /* namespace uirenderer */
+} /* namespace android */
diff --git a/libs/hwui/pipeline/skia/ShaderCache.h b/libs/hwui/pipeline/skia/ShaderCache.h
new file mode 100644
index 0000000..27473d6
--- /dev/null
+++ b/libs/hwui/pipeline/skia/ShaderCache.h
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <cutils/compiler.h>
+#include <memory>
+#include <mutex>
+#include <string>
+#include <vector>
+#include <GrContextOptions.h>
+
+namespace android {
+
+class BlobCache;
+class FileBlobCache;
+
+namespace uirenderer {
+namespace skiapipeline {
+
+class ShaderCache : public GrContextOptions::PersistentCache {
+public:
+ /**
+ * "get" returns a pointer to the singleton ShaderCache object. This
+ * singleton object will never be destroyed.
+ */
+ ANDROID_API static ShaderCache& get();
+
+ /**
+ * "initShaderDiskCache" loads the serialized cache contents from disk and puts the ShaderCache
+ * into an initialized state, such that it is able to insert and retrieve entries from the
+ * cache. This should be called when HWUI pipeline is initialized. When not in the initialized
+ * state the load and store methods will return without performing any cache operations.
+ */
+ virtual void initShaderDiskCache();
+
+ /**
+ * "setFilename" sets the name of the file that should be used to store
+ * cache contents from one program invocation to another. This function does not perform any
+ * disk operation and it should be invoked before "initShaderCache".
+ */
+ virtual void setFilename(const char* filename);
+
+ /**
+ * "load" attempts to retrieve the value blob associated with a given key
+ * blob from cache. This will be called by Skia, when it needs to compile a new SKSL shader.
+ */
+ sk_sp<SkData> load(const SkData& key) override;
+
+ /**
+ * "store" attempts to insert a new key/value blob pair into the cache.
+ * This will be called by Skia after it compiled a new SKSL shader
+ */
+ void store(const SkData& key, const SkData& data) override;
+
+private:
+ // Creation and (the lack of) destruction is handled internally.
+ ShaderCache();
+
+ // Copying is disallowed.
+ ShaderCache(const ShaderCache&) = delete;
+ void operator=(const ShaderCache&) = delete;
+
+ /**
+ * "getBlobCacheLocked" returns the BlobCache object being used to store the
+ * key/value blob pairs. If the BlobCache object has not yet been created,
+ * this will do so, loading the serialized cache contents from disk if
+ * possible.
+ */
+ BlobCache* getBlobCacheLocked();
+
+ /**
+ * "mInitialized" indicates whether the ShaderCache is in the initialized
+ * state. It is initialized to false at construction time, and gets set to
+ * true when initialize is called.
+ * When in this state, the cache behaves as normal. When not,
+ * the load and store methods will return without performing any cache
+ * operations.
+ */
+ bool mInitialized = false;
+
+ /**
+ * "mBlobCache" is the cache in which the key/value blob pairs are stored. It
+ * is initially NULL, and will be initialized by getBlobCacheLocked the
+ * first time it's needed.
+ * The blob cache contains the Android build number. We treat version mismatches as an empty
+ * cache (logic implemented in BlobCache::unflatten).
+ */
+ std::unique_ptr<FileBlobCache> mBlobCache;
+
+ /**
+ * "mFilename" is the name of the file for storing cache contents in between
+ * program invocations. It is initialized to an empty string at
+ * construction time, and can be set with the setCacheFilename method. An
+ * empty string indicates that the cache should not be saved to or restored
+ * from disk.
+ */
+ std::string mFilename;
+
+ /**
+ * "mSavePending" indicates whether or not a deferred save operation is
+ * pending. Each time a key/value pair is inserted into the cache via
+ * load, a deferred save is initiated if one is not already pending.
+ * This will wait some amount of time and then trigger a save of the cache
+ * contents to disk.
+ */
+ bool mSavePending = false;
+
+ /**
+ * "mObservedBlobValueSize" is the maximum value size observed by the cache reading function.
+ */
+ size_t mObservedBlobValueSize = 20*1024;
+
+ /**
+ * The time in seconds to wait before saving newly inserted cache entries.
+ */
+ unsigned int mDeferredSaveDelay = 4;
+
+ /**
+ * "mMutex" is the mutex used to prevent concurrent access to the member
+ * variables. It must be locked whenever the member variables are accessed.
+ */
+ mutable std::mutex mMutex;
+
+ /**
+ * "sCache" is the singleton ShaderCache object.
+ */
+ static ShaderCache sCache;
+
+ friend class ShaderCacheTestUtils; //used for unit testing
+};
+
+} /* namespace skiapipeline */
+} /* namespace uirenderer */
+} /* namespace android */
diff --git a/libs/hwui/pipeline/skia/SkiaOpenGLReadback.cpp b/libs/hwui/pipeline/skia/SkiaOpenGLReadback.cpp
index 6db57ca..288d039 100644
--- a/libs/hwui/pipeline/skia/SkiaOpenGLReadback.cpp
+++ b/libs/hwui/pipeline/skia/SkiaOpenGLReadback.cpp
@@ -66,14 +66,8 @@
break;
}
- /* Ideally, we would call grContext->caps()->isConfigRenderable(...). We
- * currently can't do that since some devices (i.e. SwiftShader) supports all
- * the appropriate half float extensions, but only allow the buffer to be read
- * back as full floats. We can relax this extension if Skia implements support
- * for reading back float buffers (skbug.com/6945).
- */
if (pixelConfig == kRGBA_half_GrPixelConfig &&
- !DeviceInfo::get()->extensions().hasRenderableFloatTextures()) {
+ !grContext->caps()->isConfigRenderable(kRGBA_half_GrPixelConfig, false)) {
ALOGW("Can't copy surface into bitmap, RGBA_F16 config is not supported");
return CopyResult::DestinationInvalid;
}
diff --git a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
index 6657fe8..91b35c2 100644
--- a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
+++ b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
@@ -144,22 +144,25 @@
// ----------------------------------------------------------------------------
inline static const SkPaint* bitmapPaint(const SkPaint* origPaint, SkPaint* tmpPaint,
- sk_sp<SkColorFilter> colorFilter) {
- if ((origPaint && origPaint->isAntiAlias()) || colorFilter) {
+ sk_sp<SkColorFilter> colorSpaceFilter) {
+ if ((origPaint && origPaint->isAntiAlias()) || colorSpaceFilter) {
if (origPaint) {
*tmpPaint = *origPaint;
}
- sk_sp<SkColorFilter> filter;
- if (colorFilter && tmpPaint->getColorFilter()) {
- filter = SkColorFilter::MakeComposeFilter(tmpPaint->refColorFilter(), colorFilter);
- LOG_ALWAYS_FATAL_IF(!filter);
- } else {
- filter = colorFilter;
+ if (colorSpaceFilter) {
+ if (tmpPaint->getColorFilter()) {
+ tmpPaint->setColorFilter(
+ SkColorFilter::MakeComposeFilter(tmpPaint->refColorFilter(), colorSpaceFilter));
+ } else {
+ tmpPaint->setColorFilter(colorSpaceFilter);
+ }
+ LOG_ALWAYS_FATAL_IF(!tmpPaint->getColorFilter());
}
+
+ // disabling AA on bitmap draws matches legacy HWUI behavior
tmpPaint->setAntiAlias(false);
- tmpPaint->setColorFilter(filter);
return tmpPaint;
} else {
return origPaint;
diff --git a/libs/hwui/renderthread/CacheManager.cpp b/libs/hwui/renderthread/CacheManager.cpp
index a33b287..c22364b 100644
--- a/libs/hwui/renderthread/CacheManager.cpp
+++ b/libs/hwui/renderthread/CacheManager.cpp
@@ -18,6 +18,7 @@
#include "Layer.h"
#include "RenderThread.h"
+#include "pipeline/skia/ShaderCache.h"
#include "renderstate/RenderState.h"
#include <GrContextOptions.h>
@@ -127,6 +128,8 @@
}
contextOptions->fExecutor = mTaskProcessor.get();
}
+
+ contextOptions->fPersistentCache = &skiapipeline::ShaderCache::get();
}
void CacheManager::trimMemory(TrimMemoryMode mode) {
diff --git a/libs/hwui/renderthread/RenderThread.cpp b/libs/hwui/renderthread/RenderThread.cpp
index c117cb8..3e2eeee 100644
--- a/libs/hwui/renderthread/RenderThread.cpp
+++ b/libs/hwui/renderthread/RenderThread.cpp
@@ -16,6 +16,7 @@
#include "RenderThread.h"
+#include "pipeline/skia/ShaderCache.h"
#include "CanvasContext.h"
#include "EglManager.h"
#include "OpenGLReadback.h"
@@ -105,6 +106,7 @@
mRenderState = new RenderState(*this);
mVkManager = new VulkanManager(*this);
mCacheManager = new CacheManager(mDisplayInfo);
+ uirenderer::skiapipeline::ShaderCache::get().initShaderDiskCache();
}
void RenderThread::dumpGraphicsMemory(int fd) {
@@ -139,10 +141,8 @@
break;
}
- FILE* file = fdopen(fd, "a");
- fprintf(file, "\n%s\n", cachesOutput.string());
- fprintf(file, "\nPipeline=%s\n", pipeline.string());
- fflush(file);
+ dprintf(fd, "\n%s\n", cachesOutput.string());
+ dprintf(fd, "\nPipeline=%s\n", pipeline.string());
}
Readback& RenderThread::readback() {
diff --git a/libs/hwui/renderthread/VulkanManager.cpp b/libs/hwui/renderthread/VulkanManager.cpp
index 3272d69..a693e68 100644
--- a/libs/hwui/renderthread/VulkanManager.cpp
+++ b/libs/hwui/renderthread/VulkanManager.cpp
@@ -107,8 +107,11 @@
mGetDeviceQueue(mBackendContext->fDevice, mPresentQueueIndex, 0, &mPresentQueue);
+ GrContextOptions options;
+ options.fDisableDistanceFieldPaths = true;
+ mRenderThread.cacheManager().configureContext(&options);
mRenderThread.setGrContext(
- GrContext::Create(kVulkan_GrBackend, (GrBackendContext)mBackendContext.get()));
+ GrContext::Create(kVulkan_GrBackend, (GrBackendContext)mBackendContext.get(), options));
DeviceInfo::initialize(mRenderThread.getGrContext()->caps()->maxRenderTargetSize());
if (Properties::enablePartialUpdates && Properties::useBufferAge) {
@@ -599,6 +602,7 @@
mDeviceWaitIdle(mBackendContext->fDevice);
}
+ SkASSERT(surface->mBackbuffers);
VulkanSurface::BackbufferInfo* backbuffer =
surface->mBackbuffers + surface->mCurrentBackbufferIndex;
GrVkImageInfo* imageInfo;
@@ -680,6 +684,7 @@
}
int VulkanManager::getAge(VulkanSurface* surface) {
+ SkASSERT(surface->mBackbuffers);
VulkanSurface::BackbufferInfo* backbuffer =
surface->mBackbuffers + surface->mCurrentBackbufferIndex;
if (mSwapBehavior == SwapBehavior::Discard ||
diff --git a/libs/hwui/renderthread/VulkanManager.h b/libs/hwui/renderthread/VulkanManager.h
index b9898a7..c319c9e 100644
--- a/libs/hwui/renderthread/VulkanManager.h
+++ b/libs/hwui/renderthread/VulkanManager.h
@@ -59,11 +59,11 @@
VkSurfaceKHR mVkSurface = VK_NULL_HANDLE;
VkSwapchainKHR mSwapchain = VK_NULL_HANDLE;
- BackbufferInfo* mBackbuffers;
+ BackbufferInfo* mBackbuffers = nullptr;
uint32_t mCurrentBackbufferIndex;
uint32_t mImageCount;
- VkImage* mImages;
+ VkImage* mImages = nullptr;
ImageInfo* mImageInfos;
uint16_t mCurrentTime = 0;
};
diff --git a/libs/hwui/tests/common/TestScene.h b/libs/hwui/tests/common/TestScene.h
index 5cd90c5..91022cf 100644
--- a/libs/hwui/tests/common/TestScene.h
+++ b/libs/hwui/tests/common/TestScene.h
@@ -16,6 +16,9 @@
#pragma once
+#include <gui/Surface.h>
+#include <utils/StrongPointer.h>
+
#include <string>
#include <unordered_map>
@@ -66,6 +69,8 @@
static std::unordered_map<std::string, Info>& testMap();
static void registerScene(const Info& info);
+
+ sp<Surface> renderTarget;
};
} // namespace test
diff --git a/libs/hwui/tests/common/scenes/MagnifierAnimation.cpp b/libs/hwui/tests/common/scenes/MagnifierAnimation.cpp
new file mode 100644
index 0000000..9eddc20
--- /dev/null
+++ b/libs/hwui/tests/common/scenes/MagnifierAnimation.cpp
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "TestSceneBase.h"
+#include "renderthread/RenderProxy.h"
+#include "utils/Color.h"
+
+class MagnifierAnimation;
+
+static TestScene::Registrar _Magnifier(TestScene::Info{
+ "magnifier", "A sample magnifier using Readback",
+ TestScene::simpleCreateScene<MagnifierAnimation>});
+
+class MagnifierAnimation : public TestScene {
+public:
+ sp<RenderNode> card;
+ sp<RenderNode> zoomImageView;
+
+ void createContent(int width, int height, Canvas& canvas) override {
+ magnifier = TestUtils::createBitmap(200, 100);
+ SkBitmap temp;
+ magnifier->getSkBitmap(&temp);
+ temp.eraseColor(Color::White);
+ canvas.drawColor(Color::White, SkBlendMode::kSrcOver);
+ card = TestUtils::createNode(
+ 0, 0, width, height, [&](RenderProperties& props, Canvas& canvas) {
+ SkPaint paint;
+ paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
+ paint.setAntiAlias(true);
+ paint.setTextSize(50);
+
+ paint.setColor(Color::Black);
+ TestUtils::drawUtf8ToCanvas(&canvas, "Test string", paint, 10, 400);
+ });
+ canvas.drawRenderNode(card.get());
+ zoomImageView = TestUtils::createNode(
+ 100, 100, 500, 300, [&](RenderProperties& props, Canvas& canvas) {
+ props.setElevation(dp(16));
+ props.mutableOutline().setRoundRect(0, 0, props.getWidth(), props.getHeight(),
+ dp(6), 1);
+ props.mutableOutline().setShouldClip(true);
+ canvas.drawBitmap(*magnifier, 0.0f, 0.0f, (float)magnifier->width(),
+ (float)magnifier->height(), 0, 0, (float)props.getWidth(),
+ (float)props.getHeight(), nullptr);
+ });
+ canvas.insertReorderBarrier(true);
+ canvas.drawRenderNode(zoomImageView.get());
+ canvas.insertReorderBarrier(false);
+ }
+
+ void doFrame(int frameNr) override {
+ int curFrame = frameNr % 150;
+ card->mutateStagingProperties().setTranslationX(curFrame);
+ card->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
+ if (renderTarget) {
+ SkBitmap temp;
+ magnifier->getSkBitmap(&temp);
+ constexpr int x = 90;
+ constexpr int y = 325;
+ RenderProxy::copySurfaceInto(renderTarget, x, y, x + magnifier->width(),
+ y + magnifier->height(), &temp);
+ }
+ }
+
+ sk_sp<Bitmap> magnifier;
+};
diff --git a/libs/hwui/tests/common/scenes/TextAnimation.cpp b/libs/hwui/tests/common/scenes/TextAnimation.cpp
index b9ce24b..a502116 100644
--- a/libs/hwui/tests/common/scenes/TextAnimation.cpp
+++ b/libs/hwui/tests/common/scenes/TextAnimation.cpp
@@ -15,7 +15,6 @@
*/
#include "TestSceneBase.h"
-#include "utils/Color.h"
class TextAnimation;
diff --git a/libs/hwui/tests/macrobench/TestSceneRunner.cpp b/libs/hwui/tests/macrobench/TestSceneRunner.cpp
index 3eb58a9..9428f53 100644
--- a/libs/hwui/tests/macrobench/TestSceneRunner.cpp
+++ b/libs/hwui/tests/macrobench/TestSceneRunner.cpp
@@ -111,8 +111,6 @@
// Switch to the real display
gDisplay = getBuiltInDisplay();
- std::unique_ptr<TestScene> scene(info.createScene(opts));
-
Properties::forceDrawFrame = true;
TestContext testContext;
testContext.setRenderOffscreen(opts.renderOffscreen);
@@ -122,6 +120,9 @@
const int height = gDisplay.h;
sp<Surface> surface = testContext.surface();
+ std::unique_ptr<TestScene> scene(info.createScene(opts));
+ scene->renderTarget = surface;
+
sp<RenderNode> rootNode = TestUtils::createNode(
0, 0, width, height, [&scene, width, height](RenderProperties& props, Canvas& canvas) {
props.setClipToBounds(false);
diff --git a/libs/hwui/tests/macrobench/main.cpp b/libs/hwui/tests/macrobench/main.cpp
index f1ff939..8c0ca5c 100644
--- a/libs/hwui/tests/macrobench/main.cpp
+++ b/libs/hwui/tests/macrobench/main.cpp
@@ -68,6 +68,7 @@
--onscreen Render tests on device screen. By default tests
are offscreen rendered
--benchmark_format Set output format. Possible values are tabular, json, csv
+ --renderer=TYPE Sets the render pipeline to use. May be opengl, skiagl, or skiavk
)");
}
@@ -146,6 +147,20 @@
return true;
}
+static bool setRenderer(const char* renderer) {
+ if (!strcmp(renderer, "opengl")) {
+ Properties::overrideRenderPipelineType(RenderPipelineType::OpenGL);
+ } else if (!strcmp(renderer, "skiagl")) {
+ Properties::overrideRenderPipelineType(RenderPipelineType::SkiaGL);
+ } else if (!strcmp(renderer, "skiavk")) {
+ Properties::overrideRenderPipelineType(RenderPipelineType::SkiaVulkan);
+ } else {
+ fprintf(stderr, "Unknown format '%s'", renderer);
+ return false;
+ }
+ return true;
+}
+
// For options that only exist in long-form. Anything in the
// 0-255 range is reserved for short options (which just use their ASCII value)
namespace LongOpts {
@@ -158,6 +173,7 @@
BenchmarkFormat,
Onscreen,
Offscreen,
+ Renderer,
};
}
@@ -172,6 +188,7 @@
{"benchmark_format", required_argument, nullptr, LongOpts::BenchmarkFormat},
{"onscreen", no_argument, nullptr, LongOpts::Onscreen},
{"offscreen", no_argument, nullptr, LongOpts::Offscreen},
+ {"renderer", required_argument, nullptr, LongOpts::Renderer},
{0, 0, 0, 0}};
static const char* SHORT_OPTIONS = "c:r:h";
@@ -252,6 +269,16 @@
}
break;
+ case LongOpts::Renderer:
+ if (!optarg) {
+ error = true;
+ break;
+ }
+ if (!setRenderer(optarg)) {
+ error = true;
+ }
+ break;
+
case LongOpts::Onscreen:
gOpts.renderOffscreen = false;
break;
diff --git a/libs/hwui/tests/unit/ShaderCacheTests.cpp b/libs/hwui/tests/unit/ShaderCacheTests.cpp
new file mode 100644
index 0000000..43080a9
--- /dev/null
+++ b/libs/hwui/tests/unit/ShaderCacheTests.cpp
@@ -0,0 +1,176 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gtest/gtest.h>
+#include <dirent.h>
+#include <cutils/properties.h>
+#include <cstdint>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <utils/Log.h>
+#include "pipeline/skia/ShaderCache.h"
+#include "FileBlobCache.h"
+
+using namespace android::uirenderer::skiapipeline;
+
+namespace android {
+namespace uirenderer {
+namespace skiapipeline {
+
+class ShaderCacheTestUtils {
+public:
+ /**
+ * "setSaveDelay" sets the time in seconds to wait before saving newly inserted cache entries.
+ * If set to 0, then deferred save is disabled.
+ */
+ static void setSaveDelay(ShaderCache& cache, unsigned int saveDelay) {
+ cache.mDeferredSaveDelay = saveDelay;
+ }
+
+ /**
+ * "terminate" optionally stores the BlobCache on disk and release all in-memory cache.
+ * Next call to "initShaderDiskCache" will load again the in-memory cache from disk.
+ */
+ static void terminate(ShaderCache& cache, bool saveContent) {
+ std::lock_guard<std::mutex> lock(cache.mMutex);
+ if (cache.mInitialized && cache.mBlobCache && saveContent) {
+ cache.mBlobCache->writeToFile();
+ }
+ cache.mBlobCache = NULL;
+ }
+};
+
+} /* namespace skiapipeline */
+} /* namespace uirenderer */
+} /* namespace android */
+
+
+namespace {
+
+std::string getExternalStorageFolder() {
+ return getenv("EXTERNAL_STORAGE");
+}
+
+bool folderExist(const std::string& folderName) {
+ DIR* dir = opendir(folderName.c_str());
+ if (dir) {
+ closedir(dir);
+ return true;
+ }
+ return false;
+}
+
+bool checkShader(const sk_sp<SkData>& shader, const char* program) {
+ sk_sp<SkData> shader2 = SkData::MakeWithCString(program);
+ return shader->size() == shader2->size()
+ && 0 == memcmp(shader->data(), shader2->data(), shader->size());
+}
+
+bool checkShader(const sk_sp<SkData>& shader, std::vector<char>& program) {
+ sk_sp<SkData> shader2 = SkData::MakeWithCopy(program.data(), program.size());
+ return shader->size() == shader2->size()
+ && 0 == memcmp(shader->data(), shader2->data(), shader->size());
+}
+
+void setShader(sk_sp<SkData>& shader, const char* program) {
+ shader = SkData::MakeWithCString(program);
+}
+
+void setShader(sk_sp<SkData>& shader, std::vector<char>& program) {
+ shader = SkData::MakeWithCopy(program.data(), program.size());
+}
+
+
+
+#define GrProgramDescTest(a) (*SkData::MakeWithCString(#a).get())
+
+TEST(ShaderCacheTest, testWriteAndRead) {
+ if (!folderExist(getExternalStorageFolder())) {
+ //don't run the test if external storage folder is not available
+ return;
+ }
+ std::string cacheFile1 = getExternalStorageFolder() + "/shaderCacheTest1";
+ std::string cacheFile2 = getExternalStorageFolder() + "/shaderCacheTest2";
+
+ //remove any test files from previous test run
+ int deleteFile = remove(cacheFile1.c_str());
+ ASSERT_TRUE(0 == deleteFile || ENOENT == errno);
+
+ //read the cache from a file that does not exist
+ ShaderCache::get().setFilename(cacheFile1.c_str());
+ ShaderCacheTestUtils::setSaveDelay(ShaderCache::get(), 0); //disable deferred save
+ ShaderCache::get().initShaderDiskCache();
+
+ //read a key - should not be found since the cache is empty
+ sk_sp<SkData> outVS;
+ ASSERT_EQ(ShaderCache::get().load(GrProgramDescTest(432)), sk_sp<SkData>());
+
+ //write to the in-memory cache without storing on disk and verify we read the same values
+ sk_sp<SkData> inVS;
+ setShader(inVS, "sassas");
+ ShaderCache::get().store(GrProgramDescTest(100), *inVS.get());
+ setShader(inVS, "someVS");
+ ShaderCache::get().store(GrProgramDescTest(432), *inVS.get());
+ ASSERT_NE((outVS = ShaderCache::get().load(GrProgramDescTest(100))), sk_sp<SkData>());
+ ASSERT_TRUE(checkShader(outVS, "sassas"));
+ ASSERT_NE((outVS = ShaderCache::get().load(GrProgramDescTest(432))), sk_sp<SkData>());
+ ASSERT_TRUE(checkShader(outVS, "someVS"));
+
+ //store content to disk and release in-memory cache
+ ShaderCacheTestUtils::terminate(ShaderCache::get(), true);
+
+ //change to a file that does not exist and verify load fails
+ ShaderCache::get().setFilename(cacheFile2.c_str());
+ ShaderCache::get().initShaderDiskCache();
+ ASSERT_EQ(ShaderCache::get().load(GrProgramDescTest(432)), sk_sp<SkData>());
+ ShaderCacheTestUtils::terminate(ShaderCache::get(), false);
+
+ //load again content from disk from an existing file and check the data is read correctly
+ ShaderCache::get().setFilename(cacheFile1.c_str());
+ ShaderCache::get().initShaderDiskCache();
+ sk_sp<SkData> outVS2;
+ ASSERT_NE((outVS2 = ShaderCache::get().load(GrProgramDescTest(432))), sk_sp<SkData>());
+ ASSERT_TRUE(checkShader(outVS2, "someVS"));
+
+ //change data, store to disk, read back again and verify data has been changed
+ setShader(inVS, "ewData1");
+ ShaderCache::get().store(GrProgramDescTest(432), *inVS.get());
+ ShaderCacheTestUtils::terminate(ShaderCache::get(), true);
+ ShaderCache::get().initShaderDiskCache();
+ ASSERT_NE((outVS2 = ShaderCache::get().load(GrProgramDescTest(432))), sk_sp<SkData>());
+ ASSERT_TRUE(checkShader(outVS2, "ewData1"));
+
+
+ //write and read big data chunk (50K)
+ size_t dataSize = 50*1024;
+ std::vector<char> dataBuffer(dataSize);
+ for (size_t i = 0; i < dataSize; i++) {
+ dataBuffer[0] = dataSize % 256;
+ }
+ setShader(inVS, dataBuffer);
+ ShaderCache::get().store(GrProgramDescTest(432), *inVS.get());
+ ShaderCacheTestUtils::terminate(ShaderCache::get(), true);
+ ShaderCache::get().initShaderDiskCache();
+ ASSERT_NE((outVS2 = ShaderCache::get().load(GrProgramDescTest(432))), sk_sp<SkData>());
+ ASSERT_TRUE(checkShader(outVS2, dataBuffer));
+
+ ShaderCacheTestUtils::terminate(ShaderCache::get(), false);
+ remove(cacheFile1.c_str());
+}
+
+} // namespace
diff --git a/media/java/android/media/AudioAttributes.java b/media/java/android/media/AudioAttributes.java
index 20405d3..7afe267 100644
--- a/media/java/android/media/AudioAttributes.java
+++ b/media/java/android/media/AudioAttributes.java
@@ -244,6 +244,7 @@
SUPPRESSIBLE_USAGES.put(USAGE_GAME, SUPPRESSIBLE_MEDIA_SYSTEM_OTHER);
SUPPRESSIBLE_USAGES.put(USAGE_VOICE_COMMUNICATION_SIGNALLING, SUPPRESSIBLE_MEDIA_SYSTEM_OTHER);
SUPPRESSIBLE_USAGES.put(USAGE_ASSISTANT, SUPPRESSIBLE_MEDIA_SYSTEM_OTHER);
+ SUPPRESSIBLE_USAGES.put(USAGE_UNKNOWN, SUPPRESSIBLE_MEDIA_SYSTEM_OTHER);
}
/**
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index dab7632a..58976ca 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -43,17 +43,16 @@
import android.os.Message;
import android.os.Process;
import android.os.RemoteException;
-import android.os.SystemClock;
import android.os.ServiceManager;
+import android.os.SystemClock;
import android.os.UserHandle;
import android.provider.Settings;
import android.util.ArrayMap;
import android.util.Log;
-import android.util.Pair;
+import android.util.Slog;
import android.view.KeyEvent;
import java.util.ArrayList;
-import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
@@ -1966,9 +1965,28 @@
*/
private boolean querySoundEffectsEnabled(int user) {
return Settings.System.getIntForUser(getContext().getContentResolver(),
- Settings.System.SOUND_EFFECTS_ENABLED, 0, user) != 0;
+ Settings.System.SOUND_EFFECTS_ENABLED, 0, user) != 0
+ && !areSystemSoundsZenModeBlocked(getContext());
}
+ private boolean areSystemSoundsZenModeBlocked(Context context) {
+ int zenMode = Settings.Global.getInt(context.getContentResolver(),
+ Settings.Global.ZEN_MODE, 0);
+
+ switch (zenMode) {
+ case Settings.Global.ZEN_MODE_NO_INTERRUPTIONS:
+ case Settings.Global.ZEN_MODE_ALARMS:
+ return true;
+ case Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS:
+ final NotificationManager noMan = (NotificationManager) context
+ .getSystemService(Context.NOTIFICATION_SERVICE);
+ return (noMan.getNotificationPolicy().priorityCategories
+ & NotificationManager.Policy.PRIORITY_CATEGORY_MEDIA_SYSTEM_OTHER) == 0;
+ case Settings.Global.ZEN_MODE_OFF:
+ default:
+ return false;
+ }
+ }
/**
* Load Sound effects.
diff --git a/media/java/android/media/BufferingParams.java b/media/java/android/media/BufferingParams.java
index 681271b..521e897 100644
--- a/media/java/android/media/BufferingParams.java
+++ b/media/java/android/media/BufferingParams.java
@@ -26,170 +26,68 @@
/**
* Structure for source buffering management params.
*
- * Used by {@link MediaPlayer#getDefaultBufferingParams()},
- * {@link MediaPlayer#getBufferingParams()} and
+ * Used by {@link MediaPlayer#getBufferingParams()} and
* {@link MediaPlayer#setBufferingParams(BufferingParams)}
* to control source buffering behavior.
*
* <p>There are two stages of source buffering in {@link MediaPlayer}: initial buffering
* (when {@link MediaPlayer} is being prepared) and rebuffering (when {@link MediaPlayer}
- * is playing back source). {@link BufferingParams} includes mode and corresponding
- * watermarks for each stage of source buffering. The watermarks could be either size
- * based (in milliseconds), or time based (in kilobytes) or both, depending on the mode.
+ * is playing back source). {@link BufferingParams} includes corresponding marks for each
+ * stage of source buffering. The marks are time based (in milliseconds).
*
- * <p>There are 4 buffering modes: {@link #BUFFERING_MODE_NONE},
- * {@link #BUFFERING_MODE_TIME_ONLY}, {@link #BUFFERING_MODE_SIZE_ONLY} and
- * {@link #BUFFERING_MODE_TIME_THEN_SIZE}.
- * {@link MediaPlayer} source component has default buffering modes which can be queried
- * by calling {@link MediaPlayer#getDefaultBufferingParams()}.
- * Users should always use those default modes or their downsized version when trying to
- * change buffering params. For example, {@link #BUFFERING_MODE_TIME_THEN_SIZE} can be
- * downsized to {@link #BUFFERING_MODE_NONE}, {@link #BUFFERING_MODE_TIME_ONLY} or
- * {@link #BUFFERING_MODE_SIZE_ONLY}. But {@link #BUFFERING_MODE_TIME_ONLY} can not be
- * downsized to {@link #BUFFERING_MODE_SIZE_ONLY}.
+ * <p>{@link MediaPlayer} source component has default marks which can be queried by
+ * calling {@link MediaPlayer#getBufferingParams()} before any change is made by
+ * {@link MediaPlayer#setBufferingParams()}.
* <ul>
- * <li><strong>initial buffering stage:</strong> has one watermark which is used when
- * {@link MediaPlayer} is being prepared. When cached data amount exceeds this watermark,
- * {@link MediaPlayer} is prepared.</li>
- * <li><strong>rebuffering stage:</strong> has two watermarks, low and high, which are
- * used when {@link MediaPlayer} is playing back content.
+ * <li><strong>initial buffering:</strong> initialMarkMs is used when
+ * {@link MediaPlayer} is being prepared. When cached data amount exceeds this mark
+ * {@link MediaPlayer} is prepared. </li>
+ * <li><strong>rebuffering during playback:</strong> resumePlaybackMarkMs is used when
+ * {@link MediaPlayer} is playing back content.
* <ul>
- * <li> When cached data amount exceeds high watermark, {@link MediaPlayer} will pause
- * buffering. Buffering will resume when cache runs below some limit which could be low
- * watermark or some intermediate value decided by the source component.</li>
- * <li> When cached data amount runs below low watermark, {@link MediaPlayer} will paused
- * playback. Playback will resume when cached data amount exceeds high watermark
- * or reaches end of stream.</li>
- * </ul>
+ * <li> {@link MediaPlayer} has internal mark, namely pausePlaybackMarkMs, to decide when
+ * to pause playback if cached data amount runs low. This internal mark varies based on
+ * type of data source. </li>
+ * <li> When cached data amount exceeds resumePlaybackMarkMs, {@link MediaPlayer} will
+ * resume playback if it has been paused due to low cached data amount. The internal mark
+ * pausePlaybackMarkMs shall be less than resumePlaybackMarkMs. </li>
+ * <li> {@link MediaPlayer} has internal mark, namely pauseRebufferingMarkMs, to decide
+ * when to pause rebuffering. Apparently, this internal mark shall be no less than
+ * resumePlaybackMarkMs. </li>
+ * <li> {@link MediaPlayer} has internal mark, namely resumeRebufferingMarkMs, to decide
+ * when to resume buffering. This internal mark varies based on type of data source. This
+ * mark shall be larger than pausePlaybackMarkMs, and less than pauseRebufferingMarkMs.
+ * </li>
+ * </ul> </li>
* </ul>
* <p>Users should use {@link Builder} to change {@link BufferingParams}.
* @hide
*/
public final class BufferingParams implements Parcelable {
- /**
- * This mode indicates that source buffering is not supported.
- */
- public static final int BUFFERING_MODE_NONE = 0;
- /**
- * This mode indicates that only time based source buffering is supported. This means
- * the watermark(s) are time based.
- */
- public static final int BUFFERING_MODE_TIME_ONLY = 1;
- /**
- * This mode indicates that only size based source buffering is supported. This means
- * the watermark(s) are size based.
- */
- public static final int BUFFERING_MODE_SIZE_ONLY = 2;
- /**
- * This mode indicates that both time and size based source buffering are supported,
- * and time based calculation precedes size based. Size based calculation will be used
- * only when time information is not available from the source.
- */
- public static final int BUFFERING_MODE_TIME_THEN_SIZE = 3;
-
- /** @hide */
- @IntDef(
- value = {
- BUFFERING_MODE_NONE,
- BUFFERING_MODE_TIME_ONLY,
- BUFFERING_MODE_SIZE_ONLY,
- BUFFERING_MODE_TIME_THEN_SIZE,
- }
- )
- @Retention(RetentionPolicy.SOURCE)
- public @interface BufferingMode {}
-
- private static final int BUFFERING_NO_WATERMARK = -1;
+ private static final int BUFFERING_NO_MARK = -1;
// params
- private int mInitialBufferingMode = BUFFERING_MODE_NONE;
- private int mRebufferingMode = BUFFERING_MODE_NONE;
+ private int mInitialMarkMs = BUFFERING_NO_MARK;
- private int mInitialWatermarkMs = BUFFERING_NO_WATERMARK;
- private int mInitialWatermarkKB = BUFFERING_NO_WATERMARK;
-
- private int mRebufferingWatermarkLowMs = BUFFERING_NO_WATERMARK;
- private int mRebufferingWatermarkHighMs = BUFFERING_NO_WATERMARK;
- private int mRebufferingWatermarkLowKB = BUFFERING_NO_WATERMARK;
- private int mRebufferingWatermarkHighKB = BUFFERING_NO_WATERMARK;
+ private int mResumePlaybackMarkMs = BUFFERING_NO_MARK;
private BufferingParams() {
}
/**
- * Return the initial buffering mode used when {@link MediaPlayer} is being prepared.
- * @return one of the values that can be set in {@link Builder#setInitialBufferingMode(int)}
+ * Return initial buffering mark in milliseconds.
+ * @return initial buffering mark in milliseconds
*/
- public int getInitialBufferingMode() {
- return mInitialBufferingMode;
+ public int getInitialMarkMs() {
+ return mInitialMarkMs;
}
/**
- * Return the rebuffering mode used when {@link MediaPlayer} is playing back source.
- * @return one of the values that can be set in {@link Builder#setRebufferingMode(int)}
+ * Return the mark in milliseconds for resuming playback.
+ * @return the mark for resuming playback in milliseconds
*/
- public int getRebufferingMode() {
- return mRebufferingMode;
- }
-
- /**
- * Return the time based initial buffering watermark in milliseconds.
- * It is meaningful only when initial buffering mode obatined from
- * {@link #getInitialBufferingMode()} is time based.
- * @return time based initial buffering watermark in milliseconds
- */
- public int getInitialBufferingWatermarkMs() {
- return mInitialWatermarkMs;
- }
-
- /**
- * Return the size based initial buffering watermark in kilobytes.
- * It is meaningful only when initial buffering mode obatined from
- * {@link #getInitialBufferingMode()} is size based.
- * @return size based initial buffering watermark in kilobytes
- */
- public int getInitialBufferingWatermarkKB() {
- return mInitialWatermarkKB;
- }
-
- /**
- * Return the time based low watermark in milliseconds for rebuffering.
- * It is meaningful only when rebuffering mode obatined from
- * {@link #getRebufferingMode()} is time based.
- * @return time based low watermark for rebuffering in milliseconds
- */
- public int getRebufferingWatermarkLowMs() {
- return mRebufferingWatermarkLowMs;
- }
-
- /**
- * Return the time based high watermark in milliseconds for rebuffering.
- * It is meaningful only when rebuffering mode obatined from
- * {@link #getRebufferingMode()} is time based.
- * @return time based high watermark for rebuffering in milliseconds
- */
- public int getRebufferingWatermarkHighMs() {
- return mRebufferingWatermarkHighMs;
- }
-
- /**
- * Return the size based low watermark in kilobytes for rebuffering.
- * It is meaningful only when rebuffering mode obatined from
- * {@link #getRebufferingMode()} is size based.
- * @return size based low watermark for rebuffering in kilobytes
- */
- public int getRebufferingWatermarkLowKB() {
- return mRebufferingWatermarkLowKB;
- }
-
- /**
- * Return the size based high watermark in kilobytes for rebuffering.
- * It is meaningful only when rebuffering mode obatined from
- * {@link #getRebufferingMode()} is size based.
- * @return size based high watermark for rebuffering in kilobytes
- */
- public int getRebufferingWatermarkHighKB() {
- return mRebufferingWatermarkHighKB;
+ public int getResumePlaybackMarkMs() {
+ return mResumePlaybackMarkMs;
}
/**
@@ -200,27 +98,19 @@
* <pre class="prettyprint">
* BufferingParams myParams = mediaplayer.getDefaultBufferingParams();
* myParams = new BufferingParams.Builder(myParams)
- * .setInitialBufferingWatermarkMs(10000)
- * .build();
+ * .setInitialMarkMs(10000)
+ * .setResumePlaybackMarkMs(15000)
+ * .build();
* mediaplayer.setBufferingParams(myParams);
* </pre>
*/
public static class Builder {
- private int mInitialBufferingMode = BUFFERING_MODE_NONE;
- private int mRebufferingMode = BUFFERING_MODE_NONE;
-
- private int mInitialWatermarkMs = BUFFERING_NO_WATERMARK;
- private int mInitialWatermarkKB = BUFFERING_NO_WATERMARK;
-
- private int mRebufferingWatermarkLowMs = BUFFERING_NO_WATERMARK;
- private int mRebufferingWatermarkHighMs = BUFFERING_NO_WATERMARK;
- private int mRebufferingWatermarkLowKB = BUFFERING_NO_WATERMARK;
- private int mRebufferingWatermarkHighKB = BUFFERING_NO_WATERMARK;
+ private int mInitialMarkMs = BUFFERING_NO_MARK;
+ private int mResumePlaybackMarkMs = BUFFERING_NO_MARK;
/**
* Constructs a new Builder with the defaults.
- * By default, both initial buffering mode and rebuffering mode are
- * {@link BufferingParams#BUFFERING_MODE_NONE}, and all watermarks are -1.
+ * By default, all marks are -1.
*/
public Builder() {
}
@@ -231,16 +121,8 @@
* in the new Builder.
*/
public Builder(BufferingParams bp) {
- mInitialBufferingMode = bp.mInitialBufferingMode;
- mRebufferingMode = bp.mRebufferingMode;
-
- mInitialWatermarkMs = bp.mInitialWatermarkMs;
- mInitialWatermarkKB = bp.mInitialWatermarkKB;
-
- mRebufferingWatermarkLowMs = bp.mRebufferingWatermarkLowMs;
- mRebufferingWatermarkHighMs = bp.mRebufferingWatermarkHighMs;
- mRebufferingWatermarkLowKB = bp.mRebufferingWatermarkLowKB;
- mRebufferingWatermarkHighKB = bp.mRebufferingWatermarkHighKB;
+ mInitialMarkMs = bp.mInitialMarkMs;
+ mResumePlaybackMarkMs = bp.mResumePlaybackMarkMs;
}
/**
@@ -250,179 +132,37 @@
* @return a new {@link BufferingParams} object
*/
public BufferingParams build() {
- if (isTimeBasedMode(mRebufferingMode)
- && mRebufferingWatermarkLowMs > mRebufferingWatermarkHighMs) {
- throw new IllegalStateException("Illegal watermark:"
- + mRebufferingWatermarkLowMs + " : " + mRebufferingWatermarkHighMs);
- }
- if (isSizeBasedMode(mRebufferingMode)
- && mRebufferingWatermarkLowKB > mRebufferingWatermarkHighKB) {
- throw new IllegalStateException("Illegal watermark:"
- + mRebufferingWatermarkLowKB + " : " + mRebufferingWatermarkHighKB);
- }
-
BufferingParams bp = new BufferingParams();
- bp.mInitialBufferingMode = mInitialBufferingMode;
- bp.mRebufferingMode = mRebufferingMode;
+ bp.mInitialMarkMs = mInitialMarkMs;
+ bp.mResumePlaybackMarkMs = mResumePlaybackMarkMs;
- bp.mInitialWatermarkMs = mInitialWatermarkMs;
- bp.mInitialWatermarkKB = mInitialWatermarkKB;
-
- bp.mRebufferingWatermarkLowMs = mRebufferingWatermarkLowMs;
- bp.mRebufferingWatermarkHighMs = mRebufferingWatermarkHighMs;
- bp.mRebufferingWatermarkLowKB = mRebufferingWatermarkLowKB;
- bp.mRebufferingWatermarkHighKB = mRebufferingWatermarkHighKB;
return bp;
}
- private boolean isTimeBasedMode(int mode) {
- return (mode == BUFFERING_MODE_TIME_ONLY || mode == BUFFERING_MODE_TIME_THEN_SIZE);
- }
-
- private boolean isSizeBasedMode(int mode) {
- return (mode == BUFFERING_MODE_SIZE_ONLY || mode == BUFFERING_MODE_TIME_THEN_SIZE);
- }
-
/**
- * Sets the initial buffering mode.
- * @param mode one of {@link BufferingParams#BUFFERING_MODE_NONE},
- * {@link BufferingParams#BUFFERING_MODE_TIME_ONLY},
- * {@link BufferingParams#BUFFERING_MODE_SIZE_ONLY},
- * {@link BufferingParams#BUFFERING_MODE_TIME_THEN_SIZE},
+ * Sets the time based mark in milliseconds for initial buffering.
+ * @param markMs time based mark in milliseconds
* @return the same Builder instance.
*/
- public Builder setInitialBufferingMode(@BufferingMode int mode) {
- switch (mode) {
- case BUFFERING_MODE_NONE:
- case BUFFERING_MODE_TIME_ONLY:
- case BUFFERING_MODE_SIZE_ONLY:
- case BUFFERING_MODE_TIME_THEN_SIZE:
- mInitialBufferingMode = mode;
- break;
- default:
- throw new IllegalArgumentException("Illegal buffering mode " + mode);
- }
+ public Builder setInitialMarkMs(int markMs) {
+ mInitialMarkMs = markMs;
return this;
}
/**
- * Sets the rebuffering mode.
- * @param mode one of {@link BufferingParams#BUFFERING_MODE_NONE},
- * {@link BufferingParams#BUFFERING_MODE_TIME_ONLY},
- * {@link BufferingParams#BUFFERING_MODE_SIZE_ONLY},
- * {@link BufferingParams#BUFFERING_MODE_TIME_THEN_SIZE},
+ * Sets the time based mark in milliseconds for resuming playback.
+ * @param markMs time based mark in milliseconds for resuming playback
* @return the same Builder instance.
*/
- public Builder setRebufferingMode(@BufferingMode int mode) {
- switch (mode) {
- case BUFFERING_MODE_NONE:
- case BUFFERING_MODE_TIME_ONLY:
- case BUFFERING_MODE_SIZE_ONLY:
- case BUFFERING_MODE_TIME_THEN_SIZE:
- mRebufferingMode = mode;
- break;
- default:
- throw new IllegalArgumentException("Illegal buffering mode " + mode);
- }
- return this;
- }
-
- /**
- * Sets the time based watermark in milliseconds for initial buffering.
- * @param watermarkMs time based watermark in milliseconds
- * @return the same Builder instance.
- */
- public Builder setInitialBufferingWatermarkMs(int watermarkMs) {
- mInitialWatermarkMs = watermarkMs;
- return this;
- }
-
- /**
- * Sets the size based watermark in kilobytes for initial buffering.
- * @param watermarkKB size based watermark in kilobytes
- * @return the same Builder instance.
- */
- public Builder setInitialBufferingWatermarkKB(int watermarkKB) {
- mInitialWatermarkKB = watermarkKB;
- return this;
- }
-
- /**
- * Sets the time based low watermark in milliseconds for rebuffering.
- * @param watermarkMs time based low watermark in milliseconds
- * @return the same Builder instance.
- */
- public Builder setRebufferingWatermarkLowMs(int watermarkMs) {
- mRebufferingWatermarkLowMs = watermarkMs;
- return this;
- }
-
- /**
- * Sets the time based high watermark in milliseconds for rebuffering.
- * @param watermarkMs time based high watermark in milliseconds
- * @return the same Builder instance.
- */
- public Builder setRebufferingWatermarkHighMs(int watermarkMs) {
- mRebufferingWatermarkHighMs = watermarkMs;
- return this;
- }
-
- /**
- * Sets the size based low watermark in milliseconds for rebuffering.
- * @param watermarkKB size based low watermark in milliseconds
- * @return the same Builder instance.
- */
- public Builder setRebufferingWatermarkLowKB(int watermarkKB) {
- mRebufferingWatermarkLowKB = watermarkKB;
- return this;
- }
-
- /**
- * Sets the size based high watermark in milliseconds for rebuffering.
- * @param watermarkKB size based high watermark in milliseconds
- * @return the same Builder instance.
- */
- public Builder setRebufferingWatermarkHighKB(int watermarkKB) {
- mRebufferingWatermarkHighKB = watermarkKB;
- return this;
- }
-
- /**
- * Sets the time based low and high watermarks in milliseconds for rebuffering.
- * @param lowWatermarkMs time based low watermark in milliseconds
- * @param highWatermarkMs time based high watermark in milliseconds
- * @return the same Builder instance.
- */
- public Builder setRebufferingWatermarksMs(int lowWatermarkMs, int highWatermarkMs) {
- mRebufferingWatermarkLowMs = lowWatermarkMs;
- mRebufferingWatermarkHighMs = highWatermarkMs;
- return this;
- }
-
- /**
- * Sets the size based low and high watermarks in kilobytes for rebuffering.
- * @param lowWatermarkKB size based low watermark in kilobytes
- * @param highWatermarkKB size based high watermark in kilobytes
- * @return the same Builder instance.
- */
- public Builder setRebufferingWatermarksKB(int lowWatermarkKB, int highWatermarkKB) {
- mRebufferingWatermarkLowKB = lowWatermarkKB;
- mRebufferingWatermarkHighKB = highWatermarkKB;
+ public Builder setResumePlaybackMarkMs(int markMs) {
+ mResumePlaybackMarkMs = markMs;
return this;
}
}
private BufferingParams(Parcel in) {
- mInitialBufferingMode = in.readInt();
- mRebufferingMode = in.readInt();
-
- mInitialWatermarkMs = in.readInt();
- mInitialWatermarkKB = in.readInt();
-
- mRebufferingWatermarkLowMs = in.readInt();
- mRebufferingWatermarkHighMs = in.readInt();
- mRebufferingWatermarkLowKB = in.readInt();
- mRebufferingWatermarkHighKB = in.readInt();
+ mInitialMarkMs = in.readInt();
+ mResumePlaybackMarkMs = in.readInt();
}
public static final Parcelable.Creator<BufferingParams> CREATOR =
@@ -446,15 +186,7 @@
@Override
public void writeToParcel(Parcel dest, int flags) {
- dest.writeInt(mInitialBufferingMode);
- dest.writeInt(mRebufferingMode);
-
- dest.writeInt(mInitialWatermarkMs);
- dest.writeInt(mInitialWatermarkKB);
-
- dest.writeInt(mRebufferingWatermarkLowMs);
- dest.writeInt(mRebufferingWatermarkHighMs);
- dest.writeInt(mRebufferingWatermarkLowKB);
- dest.writeInt(mRebufferingWatermarkHighKB);
+ dest.writeInt(mInitialMarkMs);
+ dest.writeInt(mResumePlaybackMarkMs);
}
}
diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java
index 17af44b..649c091 100644
--- a/media/java/android/media/MediaPlayer.java
+++ b/media/java/android/media/MediaPlayer.java
@@ -1698,21 +1698,9 @@
public native boolean isPlaying();
/**
- * Gets the default buffering management params.
- * Calling it only after {@code setDataSource} has been called.
- * Each type of data source might have different set of default params.
- *
- * @return the default buffering management params supported by the source component.
- * @throws IllegalStateException if the internal player engine has not been
- * initialized, or {@code setDataSource} has not been called.
- * @hide
- */
- @NonNull
- public native BufferingParams getDefaultBufferingParams();
-
- /**
* 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.
*
* @return the current buffering management params used by the source component.
* @throws IllegalStateException if the internal player engine has not been
@@ -1727,8 +1715,7 @@
* The object sets its internal BufferingParams to the input, except that the input is
* invalid or not supported.
* Call it only after {@code setDataSource} has been called.
- * Users should only use supported mode returned by {@link #getDefaultBufferingParams()}
- * or its downsized version as described in {@link BufferingParams}.
+ * The input is a hint to MediaPlayer.
*
* @param params the buffering management params.
*
diff --git a/media/jni/android_media_BufferingParams.h b/media/jni/android_media_BufferingParams.h
index 24c51f5..b004672 100644
--- a/media/jni/android_media_BufferingParams.h
+++ b/media/jni/android_media_BufferingParams.h
@@ -29,14 +29,8 @@
jclass clazz;
jmethodID constructID;
- jfieldID initial_buffering_mode;
- jfieldID rebuffering_mode;
- jfieldID initial_watermark_ms;
- jfieldID initial_watermark_kb;
- jfieldID rebuffering_watermark_low_ms;
- jfieldID rebuffering_watermark_high_ms;
- jfieldID rebuffering_watermark_low_kb;
- jfieldID rebuffering_watermark_high_kb;
+ jfieldID initial_mark_ms;
+ jfieldID resume_playback_mark_ms;
void init(JNIEnv *env) {
jclass lclazz = env->FindClass("android/media/BufferingParams");
@@ -51,14 +45,8 @@
constructID = env->GetMethodID(clazz, "<init>", "()V");
- initial_buffering_mode = env->GetFieldID(clazz, "mInitialBufferingMode", "I");
- rebuffering_mode = env->GetFieldID(clazz, "mRebufferingMode", "I");
- initial_watermark_ms = env->GetFieldID(clazz, "mInitialWatermarkMs", "I");
- initial_watermark_kb = env->GetFieldID(clazz, "mInitialWatermarkKB", "I");
- rebuffering_watermark_low_ms = env->GetFieldID(clazz, "mRebufferingWatermarkLowMs", "I");
- rebuffering_watermark_high_ms = env->GetFieldID(clazz, "mRebufferingWatermarkHighMs", "I");
- rebuffering_watermark_low_kb = env->GetFieldID(clazz, "mRebufferingWatermarkLowKB", "I");
- rebuffering_watermark_high_kb = env->GetFieldID(clazz, "mRebufferingWatermarkHighKB", "I");
+ initial_mark_ms = env->GetFieldID(clazz, "mInitialMarkMs", "I");
+ resume_playback_mark_ms = env->GetFieldID(clazz, "mResumePlaybackMarkMs", "I");
env->DeleteLocalRef(lclazz);
}
@@ -70,22 +58,10 @@
};
void fillFromJobject(JNIEnv *env, const fields_t& fields, jobject params) {
- settings.mInitialBufferingMode =
- (BufferingMode)env->GetIntField(params, fields.initial_buffering_mode);
- settings.mRebufferingMode =
- (BufferingMode)env->GetIntField(params, fields.rebuffering_mode);
- settings.mInitialWatermarkMs =
- env->GetIntField(params, fields.initial_watermark_ms);
- settings.mInitialWatermarkKB =
- env->GetIntField(params, fields.initial_watermark_kb);
- settings.mRebufferingWatermarkLowMs =
- env->GetIntField(params, fields.rebuffering_watermark_low_ms);
- settings.mRebufferingWatermarkHighMs =
- env->GetIntField(params, fields.rebuffering_watermark_high_ms);
- settings.mRebufferingWatermarkLowKB =
- env->GetIntField(params, fields.rebuffering_watermark_low_kb);
- settings.mRebufferingWatermarkHighKB =
- env->GetIntField(params, fields.rebuffering_watermark_high_kb);
+ settings.mInitialMarkMs =
+ env->GetIntField(params, fields.initial_mark_ms);
+ settings.mResumePlaybackMarkMs =
+ env->GetIntField(params, fields.resume_playback_mark_ms);
}
jobject asJobject(JNIEnv *env, const fields_t& fields) {
@@ -93,14 +69,8 @@
if (params == NULL) {
return NULL;
}
- env->SetIntField(params, fields.initial_buffering_mode, (jint)settings.mInitialBufferingMode);
- env->SetIntField(params, fields.rebuffering_mode, (jint)settings.mRebufferingMode);
- env->SetIntField(params, fields.initial_watermark_ms, (jint)settings.mInitialWatermarkMs);
- env->SetIntField(params, fields.initial_watermark_kb, (jint)settings.mInitialWatermarkKB);
- env->SetIntField(params, fields.rebuffering_watermark_low_ms, (jint)settings.mRebufferingWatermarkLowMs);
- env->SetIntField(params, fields.rebuffering_watermark_high_ms, (jint)settings.mRebufferingWatermarkHighMs);
- env->SetIntField(params, fields.rebuffering_watermark_low_kb, (jint)settings.mRebufferingWatermarkLowKB);
- env->SetIntField(params, fields.rebuffering_watermark_high_kb, (jint)settings.mRebufferingWatermarkHighKB);
+ env->SetIntField(params, fields.initial_mark_ms, (jint)settings.mInitialMarkMs);
+ env->SetIntField(params, fields.resume_playback_mark_ms, (jint)settings.mResumePlaybackMarkMs);
return params;
}
diff --git a/media/jni/android_media_MediaPlayer.cpp b/media/jni/android_media_MediaPlayer.cpp
index cfa3cc36..eda22d5 100644
--- a/media/jni/android_media_MediaPlayer.cpp
+++ b/media/jni/android_media_MediaPlayer.cpp
@@ -371,25 +371,6 @@
}
static jobject
-android_media_MediaPlayer_getDefaultBufferingParams(JNIEnv *env, jobject thiz)
-{
- sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
- if (mp == NULL) {
- jniThrowException(env, "java/lang/IllegalStateException", NULL);
- return NULL;
- }
-
- BufferingParams bp;
- BufferingSettings &settings = bp.settings;
- process_media_player_call(
- env, thiz, mp->getDefaultBufferingSettings(&settings),
- "java/lang/IllegalStateException", "unexpected error");
- ALOGV("getDefaultBufferingSettings:{%s}", settings.toString().string());
-
- return bp.asJobject(env, gBufferingParamsFields);
-}
-
-static jobject
android_media_MediaPlayer_getBufferingParams(JNIEnv *env, jobject thiz)
{
sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
@@ -1436,7 +1417,6 @@
{"_setDataSource", "(Ljava/io/FileDescriptor;JJ)V", (void *)android_media_MediaPlayer_setDataSourceFD},
{"_setDataSource", "(Landroid/media/MediaDataSource;)V",(void *)android_media_MediaPlayer_setDataSourceCallback },
{"_setVideoSurface", "(Landroid/view/Surface;)V", (void *)android_media_MediaPlayer_setVideoSurface},
- {"getDefaultBufferingParams", "()Landroid/media/BufferingParams;", (void *)android_media_MediaPlayer_getDefaultBufferingParams},
{"getBufferingParams", "()Landroid/media/BufferingParams;", (void *)android_media_MediaPlayer_getBufferingParams},
{"setBufferingParams", "(Landroid/media/BufferingParams;)V", (void *)android_media_MediaPlayer_setBufferingParams},
{"_prepare", "()V", (void *)android_media_MediaPlayer_prepare},
diff --git a/obex/javax/obex/ObexHelper.java b/obex/javax/obex/ObexHelper.java
index fa50943..478297f 100644
--- a/obex/javax/obex/ObexHelper.java
+++ b/obex/javax/obex/ObexHelper.java
@@ -80,6 +80,9 @@
// The minimum allowed max packet size is 255 according to the OBEX specification
public static final int LOWER_LIMIT_MAX_PACKET_SIZE = 255;
+ // The length of OBEX Byte Sequency Header Id according to the OBEX specification
+ public static final int OBEX_BYTE_SEQ_HEADER_LEN = 0x03;
+
/**
* Temporary workaround to be able to push files to Windows 7.
* TODO: Should be removed as soon as Microsoft updates their driver.
@@ -205,12 +208,15 @@
case 0x40:
boolean trimTail = true;
index++;
- length = 0xFF & headerArray[index];
- length = length << 8;
- index++;
- length += 0xFF & headerArray[index];
- length -= 3;
- index++;
+ length = ((0xFF & headerArray[index]) << 8) +
+ (0xFF & headerArray[index + 1]);
+ index += 2;
+ if (length <= OBEX_BYTE_SEQ_HEADER_LEN) {
+ Log.e(TAG, "Remote sent an OBEX packet with " +
+ "incorrect header length = " + length);
+ break;
+ }
+ length -= OBEX_BYTE_SEQ_HEADER_LEN;
value = new byte[length];
System.arraycopy(headerArray, index, value, 0, length);
if (length == 0 || (length > 0 && (value[length - 1] != 0))) {
diff --git a/packages/SettingsLib/src/com/android/settingslib/Utils.java b/packages/SettingsLib/src/com/android/settingslib/Utils.java
index d90386f..2186169 100644
--- a/packages/SettingsLib/src/com/android/settingslib/Utils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/Utils.java
@@ -94,7 +94,7 @@
public static Drawable getUserIcon(Context context, UserManager um, UserInfo user) {
final int iconSize = UserIconDrawable.getSizeForList(context);
if (user.isManagedProfile()) {
- Drawable drawable = context.getDrawable(com.android.internal.R.drawable.ic_corp_icon);
+ Drawable drawable = context.getDrawable(com.android.internal.R.drawable.ic_corp_badge);
drawable.setBounds(0, 0, iconSize, iconSize);
return drawable;
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/core/AbstractPreferenceController.java b/packages/SettingsLib/src/com/android/settingslib/core/AbstractPreferenceController.java
index 386c4e0..88f0d2b 100644
--- a/packages/SettingsLib/src/com/android/settingslib/core/AbstractPreferenceController.java
+++ b/packages/SettingsLib/src/com/android/settingslib/core/AbstractPreferenceController.java
@@ -20,14 +20,16 @@
* Displays preference in this controller.
*/
public void displayPreference(PreferenceScreen screen) {
+ final String prefKey = getPreferenceKey();
if (isAvailable()) {
+ setVisible(screen, prefKey, true /* visible */);
if (this instanceof Preference.OnPreferenceChangeListener) {
- final Preference preference = screen.findPreference(getPreferenceKey());
+ final Preference preference = screen.findPreference(prefKey);
preference.setOnPreferenceChangeListener(
(Preference.OnPreferenceChangeListener) this);
}
} else {
- removePreference(screen, getPreferenceKey());
+ setVisible(screen, prefKey, false /* visible */);
}
}
@@ -59,13 +61,6 @@
public abstract String getPreferenceKey();
/**
- * Removes preference from screen.
- */
- protected final void removePreference(PreferenceScreen screen, String key) {
- findAndRemovePreference(screen, key);
- }
-
- /**
* Show/hide a preference.
*/
protected final void setVisible(PreferenceGroup group, String key, boolean isVisible) {
@@ -74,25 +69,4 @@
pref.setVisible(isVisible);
}
}
-
- // finds the preference recursively and removes it from its parent
- private boolean findAndRemovePreference(PreferenceGroup prefGroup, String key) {
- final int preferenceCount = prefGroup.getPreferenceCount();
- for (int i = 0; i < preferenceCount; i++) {
- final Preference preference = prefGroup.getPreference(i);
- final String curKey = preference.getKey();
-
- if (curKey != null && curKey.equals(key)) {
- return prefGroup.removePreference(preference);
- }
-
- if (preference instanceof PreferenceGroup) {
- if (findAndRemovePreference((PreferenceGroup) preference, key)) {
- return true;
- }
- }
- }
- return false;
- }
-
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractSerialNumberPreferenceController.java b/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractSerialNumberPreferenceController.java
index ff7536a..90f14ef 100644
--- a/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractSerialNumberPreferenceController.java
+++ b/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractSerialNumberPreferenceController.java
@@ -18,18 +18,20 @@
import android.content.Context;
import android.os.Build;
+import android.support.annotation.VisibleForTesting;
import android.support.v7.preference.Preference;
import android.support.v7.preference.PreferenceScreen;
import android.text.TextUtils;
-import com.android.internal.annotations.VisibleForTesting;
import com.android.settingslib.core.AbstractPreferenceController;
/**
* Preference controller for displaying device serial number. Wraps {@link Build#getSerial()}.
*/
public class AbstractSerialNumberPreferenceController extends AbstractPreferenceController {
- private static final String KEY_SERIAL_NUMBER = "serial_number";
+
+ @VisibleForTesting
+ static final String KEY_SERIAL_NUMBER = "serial_number";
private final String mSerialNumber;
diff --git a/packages/SettingsLib/src/com/android/settingslib/drawer/UserAdapter.java b/packages/SettingsLib/src/com/android/settingslib/drawer/UserAdapter.java
index 750601d..b27d823 100644
--- a/packages/SettingsLib/src/com/android/settingslib/drawer/UserAdapter.java
+++ b/packages/SettingsLib/src/com/android/settingslib/drawer/UserAdapter.java
@@ -57,7 +57,7 @@
if (userInfo.isManagedProfile()) {
mName = context.getString(R.string.managed_user_title);
icon = context.getDrawable(
- com.android.internal.R.drawable.ic_corp_icon);
+ com.android.internal.R.drawable.ic_corp_badge);
} else {
mName = userInfo.name;
final int userId = userInfo.id;
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/AbstractPreferenceControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/AbstractPreferenceControllerTest.java
index c08bc2a..26970e1 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/AbstractPreferenceControllerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/AbstractPreferenceControllerTest.java
@@ -16,17 +16,10 @@
package com.android.settingslib.core;
import static com.google.common.truth.Truth.assertThat;
-import static org.mockito.Matchers.any;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.content.Context;
import android.support.v7.preference.Preference;
-import android.support.v7.preference.PreferenceGroup;
-import android.support.v7.preference.PreferenceManager;
import android.support.v7.preference.PreferenceScreen;
import com.android.settingslib.TestConfig;
@@ -39,134 +32,58 @@
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
-import org.robolectric.shadows.ShadowApplication;
@RunWith(RobolectricTestRunner.class)
@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
public class AbstractPreferenceControllerTest {
@Mock
- private Context mContext;
- @Mock
private PreferenceScreen mScreen;
+ private Context mContext;
private Preference mPreference;
private TestPrefController mTestPrefController;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
- mPreference = new Preference(RuntimeEnvironment.application);
+ mContext = RuntimeEnvironment.application;
+ mPreference = new Preference(mContext);
mPreference.setKey(TestPrefController.KEY_PREF);
+ when(mScreen.findPreference(TestPrefController.KEY_PREF)).thenReturn(mPreference);
mTestPrefController = new TestPrefController(mContext);
}
@Test
- public void removeExistingPref_shouldBeRemoved() {
- when(mScreen.getPreferenceCount()).thenReturn(1);
- when(mScreen.getPreference(0)).thenReturn(mPreference);
-
- mTestPrefController.removePreference(mScreen, TestPrefController.KEY_PREF);
-
- verify(mScreen).removePreference(mPreference);
- }
-
- @Test
- public void removeNonExistingPref_shouldNotRemoveAnything() {
- mTestPrefController.removePreference(mScreen, TestPrefController.KEY_PREF);
-
- verify(mScreen, never()).removePreference(any(Preference.class));
- }
-
- @Test
public void displayPref_ifAvailable() {
mTestPrefController.isAvailable = true;
mTestPrefController.displayPreference(mScreen);
- verify(mScreen, never()).removePreference(any(Preference.class));
+ assertThat(mPreference.isVisible()).isTrue();
}
@Test
public void setVisible_prefIsVisible_shouldSetToVisible() {
- when(mScreen.findPreference(TestPrefController.KEY_PREF)).thenReturn(mPreference);
-
mTestPrefController.setVisible(mScreen, TestPrefController.KEY_PREF, true /* visible */);
+
assertThat(mPreference.isVisible()).isTrue();
}
@Test
public void setVisible_prefNotVisible_shouldSetToInvisible() {
- when(mScreen.findPreference(TestPrefController.KEY_PREF)).thenReturn(mPreference);
-
mTestPrefController.setVisible(mScreen, TestPrefController.KEY_PREF, false /* visible */);
+
assertThat(mPreference.isVisible()).isFalse();
}
@Test
public void doNotDisplayPref_ifNotAvailable() {
- when(mScreen.getPreferenceCount()).thenReturn(1);
- when(mScreen.getPreference(0)).thenReturn(mPreference);
mTestPrefController.isAvailable = false;
mTestPrefController.displayPreference(mScreen);
- verify(mScreen).removePreference(any(Preference.class));
- }
-
- @Test
- public void removePreference_shouldRemoveRecursively() {
- final Context context = ShadowApplication.getInstance().getApplicationContext();
- final PreferenceManager preferenceManager = mock(PreferenceManager.class);
- // Top level
- PreferenceScreen prefRoot = spy(new PreferenceScreen(context, null));
- when(prefRoot.getPreferenceManager()).thenReturn(preferenceManager);
- Preference pref1 = mock(Preference.class);
- when(pref1.getKey()).thenReturn("key1");
- PreferenceGroup prefGroup2 = spy(new PreferenceScreen(context, null));
- when(prefGroup2.getPreferenceManager()).thenReturn(preferenceManager);
- when(prefGroup2.getKey()).thenReturn("group2");
- Preference pref3 = mock(Preference.class);
- when(pref3.getKey()).thenReturn("key3");
- PreferenceGroup prefGroup4 = spy(new PreferenceScreen(context, null));
- when(prefGroup4.getPreferenceManager()).thenReturn(preferenceManager);
- when(prefGroup4.getKey()).thenReturn("group4");
- prefRoot.addPreference(pref1);
- prefRoot.addPreference(prefGroup2);
- prefRoot.addPreference(pref3);
- prefRoot.addPreference(prefGroup4);
-
- // 2nd level
- Preference pref21 = mock(Preference.class);
- when(pref21.getKey()).thenReturn("key21");
- Preference pref22 = mock(Preference.class);
- when(pref22.getKey()).thenReturn("key22");
- prefGroup2.addPreference(pref21);
- prefGroup2.addPreference(pref22);
- PreferenceGroup prefGroup41 = spy(new PreferenceScreen(context, null));
- when(prefGroup41.getKey()).thenReturn("group41");
- when(prefGroup41.getPreferenceManager()).thenReturn(preferenceManager);
- Preference pref42 = mock(Preference.class);
- when(pref42.getKey()).thenReturn("key42");
- prefGroup4.addPreference(prefGroup41);
- prefGroup4.addPreference(pref42);
-
- // 3rd level
- Preference pref411 = mock(Preference.class);
- when(pref411.getKey()).thenReturn("key411");
- Preference pref412 = mock(Preference.class);
- when(pref412.getKey()).thenReturn("key412");
- prefGroup41.addPreference(pref411);
- prefGroup41.addPreference(pref412);
-
- mTestPrefController.removePreference(prefRoot, "key1");
- verify(prefRoot).removePreference(pref1);
-
- mTestPrefController.removePreference(prefRoot, "key411");
- verify(prefGroup41).removePreference(pref411);
-
- mTestPrefController.removePreference(prefRoot, "group41");
- verify(prefGroup4).removePreference(prefGroup41);
+ assertThat(mPreference.isVisible()).isFalse();
}
private class TestPrefController extends AbstractPreferenceController {
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/EnableAdbPreferenceControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/EnableAdbPreferenceControllerTest.java
index 32fa01c..aac736a 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/EnableAdbPreferenceControllerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/EnableAdbPreferenceControllerTest.java
@@ -17,14 +17,9 @@
package com.android.settingslib.development;
import static com.google.common.truth.Truth.assertThat;
-
import static org.mockito.Answers.RETURNS_DEEP_STUBS;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.content.Context;
@@ -67,21 +62,19 @@
shadowContext.setSystemService(Context.USER_SERVICE, mUserManager);
mContext = spy(shadowContext.getApplicationContext());
when(mContext.getPackageManager()).thenReturn(mPackageManager);
- mPreference = new SwitchPreference(mContext);
- when(mScreen.findPreference(anyString())).thenReturn(mPreference);
mController = new ConcreteEnableAdbPreferenceController(mContext);
+ mPreference = new SwitchPreference(mContext);
mPreference.setKey(mController.getPreferenceKey());
+ when(mScreen.findPreference(mPreference.getKey())).thenReturn(mPreference);
}
@Test
public void displayPreference_isNotAdmin_shouldRemovePreference() {
when(mUserManager.isAdminUser()).thenReturn(false);
- when(mScreen.getPreferenceCount()).thenReturn(1);
- when(mScreen.getPreference(0)).thenReturn(mPreference);
mController.displayPreference(mScreen);
- verify(mScreen).removePreference(any(Preference.class));
+ assertThat(mPreference.isVisible()).isFalse();
}
@Test
@@ -90,7 +83,7 @@
mController.displayPreference(mScreen);
- verify(mScreen, never()).removePreference(any(Preference.class));
+ assertThat(mPreference.isVisible()).isTrue();
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/SerialNumberPreferenceControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/SerialNumberPreferenceControllerTest.java
index 4dbb957..34bbf4f 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/SerialNumberPreferenceControllerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/SerialNumberPreferenceControllerTest.java
@@ -17,13 +17,6 @@
package com.android.settingslib.deviceinfo;
import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.Answers.RETURNS_DEEP_STUBS;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.anyString;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.content.Context;
@@ -38,68 +31,67 @@
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
@RunWith(SettingsLibRobolectricTestRunner.class)
@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
public class SerialNumberPreferenceControllerTest {
- @Mock(answer = RETURNS_DEEP_STUBS)
- private Context mContext;
- @Mock(answer = RETURNS_DEEP_STUBS)
+ @Mock
private PreferenceScreen mScreen;
+ private Context mContext;
+ private Preference mPreference;
private AbstractSerialNumberPreferenceController mController;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
+ mContext = RuntimeEnvironment.application;
+ mPreference = new Preference(mContext);
+ mPreference.setKey(AbstractSerialNumberPreferenceController.KEY_SERIAL_NUMBER);
+ when(mScreen.findPreference(mPreference.getKey())).thenReturn(mPreference);
}
@Test
public void testIsAvaiable_noSerial_shouldReturnFalse() {
- mController = new ConcreteSerialNumberPreferenceController(mContext, null);
+ mController = new TestPreferenceController(mContext, null);
assertThat(mController.isAvailable()).isFalse();
}
@Test
- public void testIsAvaiable_hasSerial_shouldReturnTrue() {
- mController = new ConcreteSerialNumberPreferenceController(mContext, "123");
+ public void testIsAvailable_hasSerial_shouldReturnTrue() {
+ mController = new TestPreferenceController(mContext, "123");
assertThat(mController.isAvailable()).isTrue();
}
@Test
public void testDisplay_noSerial_shouldHidePreference() {
- final Preference preference = mock(Preference.class);
- when(mScreen.getPreferenceCount()).thenReturn(1);
- when(mScreen.getPreference(0)).thenReturn(preference);
- mController = new ConcreteSerialNumberPreferenceController(mContext, null);
- when(preference.getKey()).thenReturn(mController.getPreferenceKey());
+ mController = new TestPreferenceController(mContext, null);
mController.displayPreference(mScreen);
- verify(mScreen).removePreference(any(Preference.class));
+ assertThat(mPreference.isVisible()).isFalse();
}
@Test
public void testDisplay_hasSerial_shouldSetSummary() {
final String serial = "123";
- final Preference preference = mock(Preference.class);
- when(mScreen.findPreference(anyString())).thenReturn(preference);
- mController = new ConcreteSerialNumberPreferenceController(mContext, serial);
+ mController = new TestPreferenceController(mContext, serial);
mController.displayPreference(mScreen);
- verify(mScreen, never()).removePreference(any(Preference.class));
- verify(preference).setSummary(serial);
+ assertThat(mPreference.isVisible()).isTrue();
+ assertThat(mPreference.getSummary()).isEqualTo(serial);
}
- private static class ConcreteSerialNumberPreferenceController
+ private static class TestPreferenceController
extends AbstractSerialNumberPreferenceController {
- ConcreteSerialNumberPreferenceController(Context context, String serialNumber) {
+ TestPreferenceController(Context context, String serialNumber) {
super(context, serialNumber);
}
}
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index 67fb4d9..41b205b 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -37,9 +37,11 @@
// Global settings
SettingsState globalSettings = settingsRegistry.getSettingsLocked(
SettingsProvider.SETTINGS_TYPE_GLOBAL, UserHandle.USER_SYSTEM);
- long globalSettingsToken = proto.start(SettingsServiceDumpProto.GLOBAL_SETTINGS);
- dumpProtoGlobalSettingsLocked(globalSettings, proto);
- proto.end(globalSettingsToken);
+ if (globalSettings != null) {
+ long globalSettingsToken = proto.start(SettingsServiceDumpProto.GLOBAL_SETTINGS);
+ dumpProtoGlobalSettingsLocked(globalSettings, proto);
+ proto.end(globalSettingsToken);
+ }
// Per-user settings
SparseBooleanArray users = settingsRegistry.getKnownUsersLocked();
@@ -67,19 +69,26 @@
SettingsState secureSettings = settingsRegistry.getSettingsLocked(
SettingsProvider.SETTINGS_TYPE_SECURE, user.getIdentifier());
- long secureSettingsToken = proto.start(UserSettingsProto.SECURE_SETTINGS);
- dumpProtoSecureSettingsLocked(secureSettings, proto);
- proto.end(secureSettingsToken);
+ if (secureSettings != null) {
+ long secureSettingsToken = proto.start(UserSettingsProto.SECURE_SETTINGS);
+ dumpProtoSecureSettingsLocked(secureSettings, proto);
+ proto.end(secureSettingsToken);
+ }
SettingsState systemSettings = settingsRegistry.getSettingsLocked(
SettingsProvider.SETTINGS_TYPE_SYSTEM, user.getIdentifier());
- long systemSettingsToken = proto.start(UserSettingsProto.SYSTEM_SETTINGS);
- dumpProtoSystemSettingsLocked(systemSettings, proto);
- proto.end(systemSettingsToken);
+ if (systemSettings != null) {
+ long systemSettingsToken = proto.start(UserSettingsProto.SYSTEM_SETTINGS);
+ dumpProtoSystemSettingsLocked(systemSettings, proto);
+ proto.end(systemSettingsToken);
+ }
}
private static void dumpProtoGlobalSettingsLocked(
@NonNull SettingsState s, @NonNull ProtoOutputStream p) {
+ s.dumpHistoricalOperations(p, GlobalSettingsProto.HISTORICAL_OPERATIONS);
+
+ // This uses the same order as in Settings.Global.
dumpSetting(s, p,
Settings.Global.ADD_USERS_WHEN_LOCKED,
GlobalSettingsProto.ADD_USERS_WHEN_LOCKED);
@@ -114,6 +123,9 @@
Settings.Global.AIRPLANE_MODE_TOGGLEABLE_RADIOS,
GlobalSettingsProto.AIRPLANE_MODE_TOGGLEABLE_RADIOS);
dumpSetting(s, p,
+ Settings.Global.BLUETOOTH_CLASS_OF_DEVICE,
+ GlobalSettingsProto.BLUETOOTH_CLASS_OF_DEVICE);
+ dumpSetting(s, p,
Settings.Global.BLUETOOTH_DISABLED_PROFILES,
GlobalSettingsProto.BLUETOOTH_DISABLED_PROFILES);
dumpSetting(s, p,
@@ -194,6 +206,7 @@
dumpSetting(s, p,
Settings.Global.CDMA_SUBSCRIPTION_MODE,
GlobalSettingsProto.CDMA_SUBSCRIPTION_MODE);
+ // Settings.Global.DEFAULT_RESTRICT_BACKGROUND_DATA intentionally excluded.
dumpSetting(s, p,
Settings.Global.DATA_ACTIVITY_TIMEOUT_MOBILE,
GlobalSettingsProto.DATA_ACTIVITY_TIMEOUT_MOBILE);
@@ -209,6 +222,10 @@
dumpSetting(s, p,
Settings.Global.FORCE_ALLOW_ON_EXTERNAL,
GlobalSettingsProto.FORCE_ALLOW_ON_EXTERNAL);
+ // Settings.Global.DEFAULT_SM_DP_PLUS intentionally excluded.
+ dumpSetting(s, p,
+ Settings.Global.EUICC_PROVISIONED,
+ GlobalSettingsProto.EUICC_PROVISIONED);
dumpSetting(s, p,
Settings.Global.DEVELOPMENT_FORCE_RESIZABLE_ACTIVITIES,
GlobalSettingsProto.DEVELOPMENT_FORCE_RESIZABLE_ACTIVITIES);
@@ -236,6 +253,7 @@
dumpSetting(s, p,
Settings.Global.DOWNLOAD_RECOMMENDED_MAX_BYTES_OVER_MOBILE,
GlobalSettingsProto.DOWNLOAD_RECOMMENDED_MAX_BYTES_OVER_MOBILE);
+ // Settings.Global.INSTALL_NON_MARKET_APPS intentionally excluded since it's deprecated.
dumpSetting(s, p,
Settings.Global.HDMI_CONTROL_ENABLED,
GlobalSettingsProto.HDMI_CONTROL_ENABLED);
@@ -249,6 +267,21 @@
Settings.Global.HDMI_CONTROL_AUTO_DEVICE_OFF_ENABLED,
GlobalSettingsProto.HDMI_CONTROL_AUTO_DEVICE_OFF_ENABLED);
dumpSetting(s, p,
+ Settings.Global.LOCATION_BACKGROUND_THROTTLE_INTERVAL_MS,
+ GlobalSettingsProto.LOCATION_BACKGROUND_THROTTLE_INTERVAL_MS);
+ dumpSetting(s, p,
+ Settings.Global.LOCATION_BACKGROUND_THROTTLE_PROXIMITY_ALERT_INTERVAL_MS,
+ GlobalSettingsProto.LOCATION_BACKGROUND_THROTTLE_PROXIMITY_ALERT_INTERVAL_MS);
+ dumpSetting(s, p,
+ Settings.Global.LOCATION_BACKGROUND_THROTTLE_PACKAGE_WHITELIST,
+ GlobalSettingsProto.LOCATION_BACKGROUND_THROTTLE_PACKAGE_WHITELIST);
+ dumpSetting(s, p,
+ Settings.Global.WIFI_SCAN_BACKGROUND_THROTTLE_INTERVAL_MS,
+ GlobalSettingsProto.WIFI_SCAN_BACKGROUND_THROTTLE_INTERVAL_MS);
+ dumpSetting(s, p,
+ Settings.Global.WIFI_SCAN_BACKGROUND_THROTTLE_PACKAGE_WHITELIST,
+ GlobalSettingsProto.WIFI_SCAN_BACKGROUND_THROTTLE_PACKAGE_WHITELIST);
+ dumpSetting(s, p,
Settings.Global.MHL_INPUT_SWITCHING_ENABLED,
GlobalSettingsProto.MHL_INPUT_SWITCHING_ENABLED);
dumpSetting(s, p,
@@ -279,6 +312,9 @@
Settings.Global.NETSTATS_SAMPLE_ENABLED,
GlobalSettingsProto.NETSTATS_SAMPLE_ENABLED);
dumpSetting(s, p,
+ Settings.Global.NETSTATS_AUGMENT_ENABLED,
+ GlobalSettingsProto.NETSTATS_AUGMENT_ENABLED);
+ dumpSetting(s, p,
Settings.Global.NETSTATS_DEV_BUCKET_DURATION,
GlobalSettingsProto.NETSTATS_DEV_BUCKET_DURATION);
dumpSetting(s, p,
@@ -420,6 +456,9 @@
Settings.Global.TETHER_DUN_APN,
GlobalSettingsProto.TETHER_DUN_APN);
dumpSetting(s, p,
+ Settings.Global.TETHER_OFFLOAD_DISABLED,
+ GlobalSettingsProto.TETHER_OFFLOAD_DISABLED);
+ dumpSetting(s, p,
Settings.Global.CARRIER_APP_WHITELIST,
GlobalSettingsProto.CARRIER_APP_WHITELIST);
dumpSetting(s, p,
@@ -450,6 +489,15 @@
Settings.Global.NETWORK_AVOID_BAD_WIFI,
GlobalSettingsProto.NETWORK_AVOID_BAD_WIFI);
dumpSetting(s, p,
+ Settings.Global.NETWORK_METERED_MULTIPATH_PREFERENCE,
+ GlobalSettingsProto.NETWORK_METERED_MULTIPATH_PREFERENCE);
+ dumpSetting(s, p,
+ Settings.Global.NETWORK_WATCHLIST_LAST_REPORT_TIME,
+ GlobalSettingsProto.NETWORK_WATCHLIST_LAST_REPORT_TIME);
+ dumpSetting(s, p,
+ Settings.Global.WIFI_BADGING_THRESHOLDS,
+ GlobalSettingsProto.WIFI_BADGING_THRESHOLDS);
+ dumpSetting(s, p,
Settings.Global.WIFI_DISPLAY_ON,
GlobalSettingsProto.WIFI_DISPLAY_ON);
dumpSetting(s, p,
@@ -489,12 +537,30 @@
Settings.Global.WIFI_WAKEUP_ENABLED,
GlobalSettingsProto.WIFI_WAKEUP_ENABLED);
dumpSetting(s, p,
+ Settings.Global.WIFI_WAKEUP_AVAILABLE,
+ GlobalSettingsProto.WIFI_WAKEUP_AVAILABLE);
+ dumpSetting(s, p,
+ Settings.Global.NETWORK_SCORING_UI_ENABLED,
+ GlobalSettingsProto.NETWORK_SCORING_UI_ENABLED);
+ dumpSetting(s, p,
+ Settings.Global.SPEED_LABEL_CACHE_EVICTION_AGE_MILLIS,
+ GlobalSettingsProto.SPEED_LABEL_CACHE_EVICTION_AGE_MILLIS);
+ dumpSetting(s, p,
Settings.Global.NETWORK_RECOMMENDATIONS_ENABLED,
GlobalSettingsProto.NETWORK_RECOMMENDATIONS_ENABLED);
dumpSetting(s, p,
Settings.Global.NETWORK_RECOMMENDATIONS_PACKAGE,
GlobalSettingsProto.NETWORK_RECOMMENDATIONS_PACKAGE);
dumpSetting(s, p,
+ Settings.Global.USE_OPEN_WIFI_PACKAGE,
+ GlobalSettingsProto.USE_OPEN_WIFI_PACKAGE);
+ dumpSetting(s, p,
+ Settings.Global.NETWORK_RECOMMENDATION_REQUEST_TIMEOUT_MS,
+ GlobalSettingsProto.NETWORK_RECOMMENDATION_REQUEST_TIMEOUT_MS);
+ dumpSetting(s, p,
+ Settings.Global.RECOMMENDED_NETWORK_EVALUATOR_CACHE_EXPIRY_MS,
+ GlobalSettingsProto.RECOMMENDED_NETWORK_EVALUATOR_CACHE_EXPIRY_MS);
+ dumpSetting(s, p,
Settings.Global.BLE_SCAN_ALWAYS_AVAILABLE,
GlobalSettingsProto.BLE_SCAN_ALWAYS_AVAILABLE);
dumpSetting(s, p,
@@ -612,6 +678,12 @@
Settings.Global.SYS_STORAGE_FULL_THRESHOLD_BYTES,
GlobalSettingsProto.SYS_STORAGE_FULL_THRESHOLD_BYTES);
dumpSetting(s, p,
+ Settings.Global.SYS_STORAGE_CACHE_PERCENTAGE,
+ GlobalSettingsProto.SYS_STORAGE_CACHE_PERCENTAGE);
+ dumpSetting(s, p,
+ Settings.Global.SYS_STORAGE_CACHE_MAX_BYTES,
+ GlobalSettingsProto.SYS_STORAGE_CACHE_MAX_BYTES);
+ dumpSetting(s, p,
Settings.Global.SYNC_MAX_RETRY_DELAY_IN_SECONDS,
GlobalSettingsProto.SYNC_MAX_RETRY_DELAY_IN_SECONDS);
dumpSetting(s, p,
@@ -627,6 +699,9 @@
Settings.Global.CAPTIVE_PORTAL_MODE,
GlobalSettingsProto.CAPTIVE_PORTAL_MODE);
dumpSetting(s, p,
+ Settings.Global.CAPTIVE_PORTAL_DETECTION_ENABLED,
+ GlobalSettingsProto.CAPTIVE_PORTAL_DETECTION_ENABLED);
+ dumpSetting(s, p,
Settings.Global.CAPTIVE_PORTAL_SERVER,
GlobalSettingsProto.CAPTIVE_PORTAL_SERVER);
dumpSetting(s, p,
@@ -639,6 +714,9 @@
Settings.Global.CAPTIVE_PORTAL_FALLBACK_URL,
GlobalSettingsProto.CAPTIVE_PORTAL_FALLBACK_URL);
dumpSetting(s, p,
+ Settings.Global.CAPTIVE_PORTAL_OTHER_FALLBACK_URLS,
+ GlobalSettingsProto.CAPTIVE_PORTAL_OTHER_FALLBACK_URLS);
+ dumpSetting(s, p,
Settings.Global.CAPTIVE_PORTAL_USE_HTTPS,
GlobalSettingsProto.CAPTIVE_PORTAL_USE_HTTPS);
dumpSetting(s, p,
@@ -684,6 +762,12 @@
Settings.Global.DEFAULT_DNS_SERVER,
GlobalSettingsProto.DEFAULT_DNS_SERVER);
dumpSetting(s, p,
+ Settings.Global.PRIVATE_DNS_MODE,
+ GlobalSettingsProto.PRIVATE_DNS_MODE);
+ dumpSetting(s, p,
+ Settings.Global.PRIVATE_DNS_SPECIFIER,
+ GlobalSettingsProto.PRIVATE_DNS_SPECIFIER);
+ dumpSetting(s, p,
Settings.Global.BLUETOOTH_HEADSET_PRIORITY_PREFIX,
GlobalSettingsProto.BLUETOOTH_HEADSET_PRIORITY_PREFIX);
dumpSetting(s, p,
@@ -717,12 +801,27 @@
Settings.Global.BLUETOOTH_PAN_PRIORITY_PREFIX,
GlobalSettingsProto.BLUETOOTH_PAN_PRIORITY_PREFIX);
dumpSetting(s, p,
+ Settings.Global.ACTIVITY_MANAGER_CONSTANTS,
+ GlobalSettingsProto.ACTIVITY_MANAGER_CONSTANTS);
+ dumpSetting(s, p,
Settings.Global.DEVICE_IDLE_CONSTANTS,
GlobalSettingsProto.DEVICE_IDLE_CONSTANTS);
dumpSetting(s, p,
+ Settings.Global.BATTERY_SAVER_CONSTANTS,
+ GlobalSettingsProto.BATTERY_SAVER_CONSTANTS);
+ dumpSetting(s, p,
+ Settings.Global.ANOMALY_DETECTION_CONSTANTS,
+ GlobalSettingsProto.ANOMALY_DETECTION_CONSTANTS);
+ dumpSetting(s, p,
+ Settings.Global.ALWAYS_ON_DISPLAY_CONSTANTS,
+ GlobalSettingsProto.ALWAYS_ON_DISPLAY_CONSTANTS);
+ dumpSetting(s, p,
Settings.Global.APP_IDLE_CONSTANTS,
GlobalSettingsProto.APP_IDLE_CONSTANTS);
dumpSetting(s, p,
+ Settings.Global.POWER_MANAGER_CONSTANTS,
+ GlobalSettingsProto.POWER_MANAGER_CONSTANTS);
+ dumpSetting(s, p,
Settings.Global.ALARM_MANAGER_CONSTANTS,
GlobalSettingsProto.ALARM_MANAGER_CONSTANTS);
dumpSetting(s, p,
@@ -732,6 +831,12 @@
Settings.Global.SHORTCUT_MANAGER_CONSTANTS,
GlobalSettingsProto.SHORTCUT_MANAGER_CONSTANTS);
dumpSetting(s, p,
+ Settings.Global.DEVICE_POLICY_CONSTANTS,
+ GlobalSettingsProto.DEVICE_POLICY_CONSTANTS);
+ dumpSetting(s, p,
+ Settings.Global.TEXT_CLASSIFIER_CONSTANTS,
+ GlobalSettingsProto.TEXT_CLASSIFIER_CONSTANTS);
+ dumpSetting(s, p,
Settings.Global.WINDOW_ANIMATION_SCALE,
GlobalSettingsProto.WINDOW_ANIMATION_SCALE);
dumpSetting(s, p,
@@ -764,6 +869,7 @@
dumpSetting(s, p,
Settings.Global.WAIT_FOR_DEBUGGER,
GlobalSettingsProto.WAIT_FOR_DEBUGGER);
+ // Settings.Global.SHOW_PROCESSES intentionally excluded since it's deprecated.
dumpSetting(s, p,
Settings.Global.LOW_POWER_MODE,
GlobalSettingsProto.LOW_POWER_MODE);
@@ -819,6 +925,18 @@
Settings.Global.INTENT_FIREWALL_UPDATE_METADATA_URL,
GlobalSettingsProto.INTENT_FIREWALL_UPDATE_METADATA_URL);
dumpSetting(s, p,
+ Settings.Global.LANG_ID_UPDATE_CONTENT_URL,
+ GlobalSettingsProto.LANG_ID_UPDATE_CONTENT_URL);
+ dumpSetting(s, p,
+ Settings.Global.LANG_ID_UPDATE_METADATA_URL,
+ GlobalSettingsProto.LANG_ID_UPDATE_METADATA_URL);
+ dumpSetting(s, p,
+ Settings.Global.SMART_SELECTION_UPDATE_CONTENT_URL,
+ GlobalSettingsProto.SMART_SELECTION_UPDATE_CONTENT_URL);
+ dumpSetting(s, p,
+ Settings.Global.SMART_SELECTION_UPDATE_METADATA_URL,
+ GlobalSettingsProto.SMART_SELECTION_UPDATE_METADATA_URL);
+ dumpSetting(s, p,
Settings.Global.SELINUX_STATUS,
GlobalSettingsProto.SELINUX_STATUS);
dumpSetting(s, p,
@@ -882,11 +1000,8 @@
Settings.Global.ENABLE_EPHEMERAL_FEATURE,
GlobalSettingsProto.ENABLE_EPHEMERAL_FEATURE);
dumpSetting(s, p,
- Settings.Global.UNINSTALLED_INSTANT_APP_MIN_CACHE_PERIOD,
- GlobalSettingsProto.UNINSTALLED_INSTANT_APP_MIN_CACHE_PERIOD);
- dumpSetting(s, p,
- Settings.Global.UNINSTALLED_INSTANT_APP_MAX_CACHE_PERIOD,
- GlobalSettingsProto.UNINSTALLED_INSTANT_APP_MAX_CACHE_PERIOD);
+ Settings.Global.INSTANT_APP_DEXOPT_ENABLED,
+ GlobalSettingsProto.INSTANT_APP_DEXOPT_ENABLED);
dumpSetting(s, p,
Settings.Global.INSTALLED_INSTANT_APP_MIN_CACHE_PERIOD,
GlobalSettingsProto.INSTALLED_INSTANT_APP_MIN_CACHE_PERIOD);
@@ -894,6 +1009,12 @@
Settings.Global.INSTALLED_INSTANT_APP_MAX_CACHE_PERIOD,
GlobalSettingsProto.INSTALLED_INSTANT_APP_MAX_CACHE_PERIOD);
dumpSetting(s, p,
+ Settings.Global.UNINSTALLED_INSTANT_APP_MIN_CACHE_PERIOD,
+ GlobalSettingsProto.UNINSTALLED_INSTANT_APP_MIN_CACHE_PERIOD);
+ dumpSetting(s, p,
+ Settings.Global.UNINSTALLED_INSTANT_APP_MAX_CACHE_PERIOD,
+ GlobalSettingsProto.UNINSTALLED_INSTANT_APP_MAX_CACHE_PERIOD);
+ dumpSetting(s, p,
Settings.Global.UNUSED_STATIC_SHARED_LIB_MIN_CACHE_PERIOD,
GlobalSettingsProto.UNUSED_STATIC_SHARED_LIB_MIN_CACHE_PERIOD);
dumpSetting(s, p,
@@ -909,12 +1030,30 @@
Settings.Global.DEVICE_DEMO_MODE,
GlobalSettingsProto.DEVICE_DEMO_MODE);
dumpSetting(s, p,
+ Settings.Global.NETWORK_ACCESS_TIMEOUT_MS,
+ GlobalSettingsProto.NETWORK_ACCESS_TIMEOUT_MS);
+ dumpSetting(s, p,
Settings.Global.DATABASE_DOWNGRADE_REASON,
GlobalSettingsProto.DATABASE_DOWNGRADE_REASON);
dumpSetting(s, p,
+ Settings.Global.DATABASE_CREATION_BUILDID,
+ GlobalSettingsProto.DATABASE_CREATION_BUILDID);
+ dumpSetting(s, p,
Settings.Global.CONTACTS_DATABASE_WAL_ENABLED,
GlobalSettingsProto.CONTACTS_DATABASE_WAL_ENABLED);
dumpSetting(s, p,
+ Settings.Global.LOCATION_SETTINGS_LINK_TO_PERMISSIONS_ENABLED,
+ GlobalSettingsProto.LOCATION_SETTINGS_LINK_TO_PERMISSIONS_ENABLED);
+ dumpSetting(s, p,
+ Settings.Global.BACKUP_REFACTORED_SERVICE_DISABLED,
+ GlobalSettingsProto.BACKUP_REFACTORED_SERVICE_DISABLED);
+ dumpSetting(s, p,
+ Settings.Global.EUICC_FACTORY_RESET_TIMEOUT_MILLIS,
+ GlobalSettingsProto.EUICC_FACTORY_RESET_TIMEOUT_MILLIS);
+ dumpSetting(s, p,
+ Settings.Global.STORAGE_SETTINGS_CLOBBER_THRESHOLD,
+ GlobalSettingsProto.STORAGE_SETTINGS_CLOBBER_THRESHOLD);
+ dumpSetting(s, p,
Settings.Global.MULTI_SIM_VOICE_CALL_SUBSCRIPTION,
GlobalSettingsProto.MULTI_SIM_VOICE_CALL_SUBSCRIPTION);
dumpSetting(s, p,
@@ -932,6 +1071,7 @@
dumpSetting(s, p,
Settings.Global.NEW_CONTACT_AGGREGATOR,
GlobalSettingsProto.NEW_CONTACT_AGGREGATOR);
+ // Settings.Global.CONTACT_METADATA_SYNC intentionally excluded since it's deprecated.
dumpSetting(s, p,
Settings.Global.CONTACT_METADATA_SYNC_ENABLED,
GlobalSettingsProto.CONTACT_METADATA_SYNC_ENABLED);
@@ -942,8 +1082,31 @@
Settings.Global.MAX_NOTIFICATION_ENQUEUE_RATE,
GlobalSettingsProto.MAX_NOTIFICATION_ENQUEUE_RATE);
dumpSetting(s, p,
+ Settings.Global.SHOW_NOTIFICATION_CHANNEL_WARNINGS,
+ GlobalSettingsProto.SHOW_NOTIFICATION_CHANNEL_WARNINGS);
+ dumpSetting(s, p,
Settings.Global.CELL_ON,
GlobalSettingsProto.CELL_ON);
+ dumpSetting(s, p,
+ Settings.Global.SHOW_TEMPERATURE_WARNING,
+ GlobalSettingsProto.SHOW_TEMPERATURE_WARNING);
+ dumpSetting(s, p,
+ Settings.Global.WARNING_TEMPERATURE,
+ GlobalSettingsProto.WARNING_TEMPERATURE);
+ dumpSetting(s, p,
+ Settings.Global.ENABLE_DISKSTATS_LOGGING,
+ GlobalSettingsProto.ENABLE_DISKSTATS_LOGGING);
+ dumpSetting(s, p,
+ Settings.Global.ENABLE_CACHE_QUOTA_CALCULATION,
+ GlobalSettingsProto.ENABLE_CACHE_QUOTA_CALCULATION);
+ dumpSetting(s, p,
+ Settings.Global.ENABLE_DELETION_HELPER_NO_THRESHOLD_TOGGLE,
+ GlobalSettingsProto.ENABLE_DELETION_HELPER_NO_THRESHOLD_TOGGLE);
+ // The list of snooze options for notifications. This is encoded as a key=value list,
+ // separated by commas.
+ dumpSetting(s, p,
+ Settings.Global.NOTIFICATION_SNOOZE_OPTIONS,
+ GlobalSettingsProto.NOTIFICATION_SNOOZE_OPTIONS);
}
/** Dump a single {@link SettingsState.Setting} to a proto buf */
@@ -966,9 +1129,19 @@
static void dumpProtoSecureSettingsLocked(
@NonNull SettingsState s, @NonNull ProtoOutputStream p) {
+ s.dumpHistoricalOperations(p, SecureSettingsProto.HISTORICAL_OPERATIONS);
+
+ // This uses the same order as in Settings.Secure.
+
+ // Settings.Secure.DEVELOPMENT_SETTINGS_ENABLED intentionally excluded since it's deprecated.
+ // Settings.Secure.BUGREPORT_IN_POWER_MENU intentionally excluded since it's deprecated.
+ // Settings.Secure.ADB_ENABLED intentionally excluded since it's deprecated.
+ // Settings.Secure.ALLOW_MOCK_LOCATION intentionally excluded since it's deprecated.
dumpSetting(s, p,
Settings.Secure.ANDROID_ID,
SecureSettingsProto.ANDROID_ID);
+ // Settings.Secure.BLUETOOTH_ON intentionally excluded since it's deprecated.
+ // Settings.Secure.DATA_ROAMING intentionally excluded since it's deprecated.
dumpSetting(s, p,
Settings.Secure.DEFAULT_INPUT_METHOD,
SecureSettingsProto.DEFAULT_INPUT_METHOD);
@@ -987,9 +1160,16 @@
dumpSetting(s, p,
Settings.Secure.AUTOFILL_SERVICE,
SecureSettingsProto.AUTOFILL_SERVICE);
+ // Settings.Secure.DEVICE_PROVISIONED intentionally excluded since it's deprecated.
dumpSetting(s, p,
Settings.Secure.USER_SETUP_COMPLETE,
SecureSettingsProto.USER_SETUP_COMPLETE);
+ // Whether the current user has been set up via setup wizard (0 = false, 1 = true). This
+ // value differs from USER_SETUP_COMPLETE in that it can be reset back to 0 in case
+ // SetupWizard has been re-enabled on TV devices.
+ dumpSetting(s, p,
+ Settings.Secure.TV_USER_SETUP_COMPLETE,
+ SecureSettingsProto.TV_USER_SETUP_COMPLETE);
dumpSetting(s, p,
Settings.Secure.COMPLETED_CATEGORY_PREFIX,
SecureSettingsProto.COMPLETED_CATEGORY_PREFIX);
@@ -1002,6 +1182,7 @@
dumpSetting(s, p,
Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD,
SecureSettingsProto.SHOW_IME_WITH_HARD_KEYBOARD);
+ // Settings.Secure.HTTP_PROXY intentionally excluded since it's deprecated.
dumpSetting(s, p,
Settings.Secure.ALWAYS_ON_VPN_APP,
SecureSettingsProto.ALWAYS_ON_VPN_APP);
@@ -1012,17 +1193,33 @@
Settings.Secure.INSTALL_NON_MARKET_APPS,
SecureSettingsProto.INSTALL_NON_MARKET_APPS);
dumpSetting(s, p,
+ Settings.Secure.UNKNOWN_SOURCES_DEFAULT_REVERSED,
+ SecureSettingsProto.UNKNOWN_SOURCES_DEFAULT_REVERSED);
+ // Settings.Secure.LOCATION_PROVIDERS_ALLOWED intentionally excluded since it's deprecated.
+ dumpSetting(s, p,
Settings.Secure.LOCATION_MODE,
SecureSettingsProto.LOCATION_MODE);
dumpSetting(s, p,
Settings.Secure.LOCATION_PREVIOUS_MODE,
SecureSettingsProto.LOCATION_PREVIOUS_MODE);
+ // Settings.Secure.LOCK_BIOMETRIC_WEAK_FLAGS intentionally excluded since it's deprecated.
dumpSetting(s, p,
Settings.Secure.LOCK_TO_APP_EXIT_LOCKED,
SecureSettingsProto.LOCK_TO_APP_EXIT_LOCKED);
+ // Settings.Secure.LOCK_PATTERN_ENABLED intentionally excluded since it's deprecated.
+ // Settings.Secure.LOCK_PATTERN_VISIBLE intentionally excluded since it's deprecated.
+ // Settings.Secure.LOCK_PATTERN_TACTICLE_FEEDBACK_ENABLED intentionally excluded since it's deprecated.
dumpSetting(s, p,
Settings.Secure.LOCK_SCREEN_LOCK_AFTER_TIMEOUT,
SecureSettingsProto.LOCK_SCREEN_LOCK_AFTER_TIMEOUT);
+ // Settings.Secure.LOCK_SCREEN_OWNER_INFO intentionally excluded since it's deprecated.
+ // Settings.Secure.LOCK_SCREEN_APPWIDGET_IDS intentionally excluded since it's deprecated.
+ // Settings.Secure.LOCK_SCREEN_FALLBACK_APPWIDGET_ID intentionally excluded since it's deprecated.
+ // Settings.Secure.LOCK_SCREEN_STICKY_APPWIDGET intentionally excluded since it's deprecated.
+ // Settings.Secure.LOCK_SCREEN_OWNER_INFO_ENABLED intentionally excluded since it's deprecated.
+ dumpSetting(s, p,
+ Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS,
+ SecureSettingsProto.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS);
dumpSetting(s, p,
Settings.Secure.LOCK_SCREEN_ALLOW_REMOTE_INPUT,
SecureSettingsProto.LOCK_SCREEN_ALLOW_REMOTE_INPUT);
@@ -1032,6 +1229,8 @@
dumpSetting(s, p,
Settings.Secure.TRUST_AGENTS_INITIALIZED,
SecureSettingsProto.TRUST_AGENTS_INITIALIZED);
+ // Settings.Secure.LOGGING_ID intentionally excluded since it's deprecated.
+ // Settings.Secure.NETWORK_PREFERENCE intentionally excluded since it's deprecated.
dumpSetting(s, p,
Settings.Secure.PARENTAL_CONTROL_ENABLED,
SecureSettingsProto.PARENTAL_CONTROL_ENABLED);
@@ -1044,10 +1243,27 @@
dumpSetting(s, p,
Settings.Secure.SETTINGS_CLASSNAME,
SecureSettingsProto.SETTINGS_CLASSNAME);
+ // Settings.Secure.USB_MASS_STORAGE_ENABLED intentionally excluded since it's deprecated.
+ // Settings.Secure.USE_GOOGLE_MAIL intentionally excluded since it's deprecated.
dumpSetting(s, p,
Settings.Secure.ACCESSIBILITY_ENABLED,
SecureSettingsProto.ACCESSIBILITY_ENABLED);
dumpSetting(s, p,
+ Settings.Secure.ACCESSIBILITY_SHORTCUT_ENABLED,
+ SecureSettingsProto.ACCESSIBILITY_SHORTCUT_ENABLED);
+ dumpSetting(s, p,
+ Settings.Secure.ACCESSIBILITY_SHORTCUT_ON_LOCK_SCREEN,
+ SecureSettingsProto.ACCESSIBILITY_SHORTCUT_ON_LOCK_SCREEN);
+ dumpSetting(s, p,
+ Settings.Secure.ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN,
+ SecureSettingsProto.ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN);
+ dumpSetting(s, p,
+ Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE,
+ SecureSettingsProto.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE);
+ dumpSetting(s, p,
+ Settings.Secure.ACCESSIBILITY_BUTTON_TARGET_COMPONENT,
+ SecureSettingsProto.ACCESSIBILITY_BUTTON_TARGET_COMPONENT);
+ dumpSetting(s, p,
Settings.Secure.TOUCH_EXPLORATION_ENABLED,
SecureSettingsProto.TOUCH_EXPLORATION_ENABLED);
dumpSetting(s, p,
@@ -1066,9 +1282,15 @@
Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED,
SecureSettingsProto.ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED);
dumpSetting(s, p,
+ Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED,
+ SecureSettingsProto.ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED);
+ dumpSetting(s, p,
Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE,
SecureSettingsProto.ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE);
dumpSetting(s, p,
+ Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_AUTO_UPDATE,
+ SecureSettingsProto.ACCESSIBILITY_DISPLAY_MAGNIFICATION_AUTO_UPDATE);
+ dumpSetting(s, p,
Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE,
SecureSettingsProto.ACCESSIBILITY_SOFT_KEYBOARD_MODE);
dumpSetting(s, p,
@@ -1134,6 +1356,7 @@
dumpSetting(s, p,
Settings.Secure.DISPLAY_DENSITY_FORCED,
SecureSettingsProto.DISPLAY_DENSITY_FORCED);
+ // Settings.Secure.TTS_USE_DEFAULTS intentionally excluded since it's deprecated.
dumpSetting(s, p,
Settings.Secure.TTS_DEFAULT_RATE,
SecureSettingsProto.TTS_DEFAULT_RATE);
@@ -1143,15 +1366,37 @@
dumpSetting(s, p,
Settings.Secure.TTS_DEFAULT_SYNTH,
SecureSettingsProto.TTS_DEFAULT_SYNTH);
+ // Settings.Secure.TTS_DEFAULT_LANG intentionally excluded since it's deprecated.
+ // Settings.Secure.TTS_DEFAULT_COUNTRY intentionally excluded since it's deprecated.
+ // Settings.Secure.TTS_DEFAULT_VARIANT intentionally excluded since it's deprecated.
dumpSetting(s, p,
Settings.Secure.TTS_DEFAULT_LOCALE,
SecureSettingsProto.TTS_DEFAULT_LOCALE);
dumpSetting(s, p,
Settings.Secure.TTS_ENABLED_PLUGINS,
SecureSettingsProto.TTS_ENABLED_PLUGINS);
+ // Settings.Secure.WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON intentionally excluded since it's deprecated.
+ // Settings.Secure.WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY intentionally excluded since it's deprecated.
+ // Settings.Secure.WIFI_NUM_OPEN_NETWORKS_KEPT intentionally excluded since it's deprecated.
+ // Settings.Secure.WIFI_ON intentionally excluded since it's deprecated.
+ // Settings.Secure.WIFI_WATCHDOG_ACCEPTABLE_PACKET_LOSS_PERCENTAGE intentionally excluded since it's deprecated.
+ // Settings.Secure.WIFI_WATCHDOG_AP_COUNT intentionally excluded since it's deprecated.
+ // Settings.Secure.WIFI_WATCHDOG_BACKGROUND_CHECK_DELAY_MS intentionally excluded since it's deprecated.
+ // Settings.Secure.WIFI_WATCHDOG_BACKGROUND_CHECK_ENABLED intentionally excluded since it's deprecated.
+ // Settings.Secure.WIFI_WATCHDOG_BACKGROUND_CHECK_TIMEOUT_MS intentionally excluded since it's deprecated.
+ // Settings.Secure.WIFI_WATCHDOG_INITIAL_IGNORED_PING_COUNT intentionally excluded since it's deprecated.
+ // Settings.Secure.WIFI_WATCHDOG_MAX_AP_CHECKS intentionally excluded since it's deprecated.
+ // Settings.Secure.WIFI_WATCHDOG_ON intentionally excluded since it's deprecated.
+ // Settings.Secure.WIFI_WATCHDOG_WATCH_LIST intentionally excluded since it's deprecated.
+ // Settings.Secure.WIFI_WATCHDOG_PING_COUNT intentionally excluded since it's deprecated.
+ // Settings.Secure.WIFI_WATCHDOG_PING_DELAY_MS intentionally excluded since it's deprecated.
+ // Settings.Secure.WIFI_WATCHDOG_PING_TIMEOUT_MS intentionally excluded since it's deprecated.
+ // Settings.Secure.WIFI_MAX_DHCP_RETRY_COUNT intentionally excluded since it's deprecated.
+ // Settings.Secure.WIFI_MOBILE_DATA_TRANSITION_WAKELOCK_TIMEOUT_MS intentionally excluded since it's deprecated.
dumpSetting(s, p,
Settings.Secure.CONNECTIVITY_RELEASE_PENDING_INTENT_DELAY_MS,
SecureSettingsProto.CONNECTIVITY_RELEASE_PENDING_INTENT_DELAY_MS);
+ // Settings.Secure.BACKGROUND_DATA intentionally excluded since it's deprecated.
dumpSetting(s, p,
Settings.Secure.ALLOWED_GEOLOCATION_ORIGINS,
SecureSettingsProto.ALLOWED_GEOLOCATION_ORIGINS);
@@ -1179,6 +1424,7 @@
dumpSetting(s, p,
Settings.Secure.LAST_SETUP_SHOWN,
SecureSettingsProto.LAST_SETUP_SHOWN);
+ // Settings.Secure.WIFI_IDLE_MS intentionally excluded since it's deprecated.
dumpSetting(s, p,
Settings.Secure.SEARCH_GLOBAL_SEARCH_ACTIVITY,
SecureSettingsProto.SEARCH_GLOBAL_SEARCH_ACTIVITY);
@@ -1285,6 +1531,9 @@
Settings.Secure.DOZE_PULSE_ON_PICK_UP,
SecureSettingsProto.DOZE_PULSE_ON_PICK_UP);
dumpSetting(s, p,
+ Settings.Secure.DOZE_PULSE_ON_LONG_PRESS,
+ SecureSettingsProto.DOZE_PULSE_ON_LONG_PRESS);
+ dumpSetting(s, p,
Settings.Secure.DOZE_PULSE_ON_DOUBLE_TAP,
SecureSettingsProto.DOZE_PULSE_ON_DOUBLE_TAP);
dumpSetting(s, p,
@@ -1351,6 +1600,9 @@
Settings.Secure.PAYMENT_SERVICE_SEARCH_URI,
SecureSettingsProto.PAYMENT_SERVICE_SEARCH_URI);
dumpSetting(s, p,
+ Settings.Secure.AUTOFILL_SERVICE_SEARCH_URI,
+ SecureSettingsProto.AUTOFILL_SERVICE_SEARCH_URI);
+ dumpSetting(s, p,
Settings.Secure.SKIP_FIRST_USE_HINTS,
SecureSettingsProto.SKIP_FIRST_USE_HINTS);
dumpSetting(s, p,
@@ -1387,18 +1639,42 @@
Settings.Secure.CAMERA_DOUBLE_TWIST_TO_FLIP_ENABLED,
SecureSettingsProto.CAMERA_DOUBLE_TWIST_TO_FLIP_ENABLED);
dumpSetting(s, p,
+ Settings.Secure.CAMERA_LIFT_TRIGGER_ENABLED,
+ SecureSettingsProto.CAMERA_LIFT_TRIGGER_ENABLED);
+ dumpSetting(s, p,
+ Settings.Secure.ASSIST_GESTURE_ENABLED,
+ SecureSettingsProto.ASSIST_GESTURE_ENABLED);
+ dumpSetting(s, p,
+ Settings.Secure.ASSIST_GESTURE_SENSITIVITY,
+ SecureSettingsProto.ASSIST_GESTURE_SENSITIVITY);
+ dumpSetting(s, p,
+ Settings.Secure.ASSIST_GESTURE_SILENCE_ALERTS_ENABLED,
+ SecureSettingsProto.ASSIST_GESTURE_SILENCE_ALERTS_ENABLED);
+ dumpSetting(s, p,
+ Settings.Secure.ASSIST_GESTURE_WAKE_ENABLED,
+ SecureSettingsProto.ASSIST_GESTURE_WAKE_ENABLED);
+ dumpSetting(s, p,
+ Settings.Secure.ASSIST_GESTURE_SETUP_COMPLETE,
+ SecureSettingsProto.ASSIST_GESTURE_SETUP_COMPLETE);
+ dumpSetting(s, p,
Settings.Secure.NIGHT_DISPLAY_ACTIVATED,
SecureSettingsProto.NIGHT_DISPLAY_ACTIVATED);
dumpSetting(s, p,
Settings.Secure.NIGHT_DISPLAY_AUTO_MODE,
SecureSettingsProto.NIGHT_DISPLAY_AUTO_MODE);
dumpSetting(s, p,
+ Settings.Secure.NIGHT_DISPLAY_COLOR_TEMPERATURE,
+ SecureSettingsProto.NIGHT_DISPLAY_COLOR_TEMPERATURE);
+ dumpSetting(s, p,
Settings.Secure.NIGHT_DISPLAY_CUSTOM_START_TIME,
SecureSettingsProto.NIGHT_DISPLAY_CUSTOM_START_TIME);
dumpSetting(s, p,
Settings.Secure.NIGHT_DISPLAY_CUSTOM_END_TIME,
SecureSettingsProto.NIGHT_DISPLAY_CUSTOM_END_TIME);
dumpSetting(s, p,
+ Settings.Secure.NIGHT_DISPLAY_LAST_ACTIVATED_TIME,
+ SecureSettingsProto.NIGHT_DISPLAY_LAST_ACTIVATED_TIME);
+ dumpSetting(s, p,
Settings.Secure.ENABLED_VR_LISTENERS,
SecureSettingsProto.ENABLED_VR_LISTENERS);
dumpSetting(s, p,
@@ -1423,6 +1699,9 @@
Settings.Secure.AUTOMATIC_STORAGE_MANAGER_LAST_RUN,
SecureSettingsProto.AUTOMATIC_STORAGE_MANAGER_LAST_RUN);
dumpSetting(s, p,
+ Settings.Secure.AUTOMATIC_STORAGE_MANAGER_TURNED_OFF_BY_POLICY,
+ SecureSettingsProto.AUTOMATIC_STORAGE_MANAGER_TURNED_OFF_BY_POLICY);
+ dumpSetting(s, p,
Settings.Secure.SYSTEM_NAVIGATION_KEYS_ENABLED,
SecureSettingsProto.SYSTEM_NAVIGATION_KEYS_ENABLED);
dumpSetting(s, p,
@@ -1438,33 +1717,76 @@
Settings.Secure.DEVICE_PAIRED,
SecureSettingsProto.DEVICE_PAIRED);
dumpSetting(s, p,
+ Settings.Secure.PACKAGE_VERIFIER_STATE,
+ SecureSettingsProto.PACKAGE_VERIFIER_STATE);
+ dumpSetting(s, p,
+ Settings.Secure.CMAS_ADDITIONAL_BROADCAST_PKG,
+ SecureSettingsProto.CMAS_ADDITIONAL_BROADCAST_PKG);
+ dumpSetting(s, p,
Settings.Secure.NOTIFICATION_BADGING,
SecureSettingsProto.NOTIFICATION_BADGING);
dumpSetting(s, p,
+ Settings.Secure.QS_AUTO_ADDED_TILES,
+ SecureSettingsProto.QS_AUTO_ADDED_TILES);
+ dumpSetting(s, p,
+ Settings.Secure.LOCKDOWN_IN_POWER_MENU,
+ SecureSettingsProto.LOCKDOWN_IN_POWER_MENU);
+ dumpSetting(s, p,
Settings.Secure.BACKUP_MANAGER_CONSTANTS,
SecureSettingsProto.BACKUP_MANAGER_CONSTANTS);
}
private static void dumpProtoSystemSettingsLocked(
@NonNull SettingsState s, @NonNull ProtoOutputStream p) {
+ s.dumpHistoricalOperations(p, SystemSettingsProto.HISTORICAL_OPERATIONS);
+
+ // This uses the same order as in Settings.System.
+
+ // Settings.System.STAY_ON_WHILE_PLUGGED_IN intentionally excluded since it's deprecated.
dumpSetting(s, p,
Settings.System.END_BUTTON_BEHAVIOR,
SystemSettingsProto.END_BUTTON_BEHAVIOR);
dumpSetting(s, p,
Settings.System.ADVANCED_SETTINGS,
SystemSettingsProto.ADVANCED_SETTINGS);
+ // Settings.System.AIRPLANE_MODE_ON intentionally excluded since it's deprecated.
+ // Settings.System.RADIO_BLUETOOTH intentionally excluded since it's deprecated.
+ // Settings.System.RADIO_WIFI intentionally excluded since it's deprecated.
+ // Settings.System.RADIO_WIMAX intentionally excluded since it's deprecated.
+ // Settings.System.RADIO_CELL intentionally excluded since it's deprecated.
+ // Settings.System.RADIO_NFC intentionally excluded since it's deprecated.
+ // Settings.System.AIRPLANE_MODE_RADIOS intentionally excluded since it's deprecated.
+ // Settings.System.AIRPLANE_MODE_TOGGLABLE_RADIOS intentionally excluded since it's deprecated.
+ // Settings.System.WIFI_SLEEP_POLICY intentionally excluded since it's deprecated.
+ // Settings.System.MODE_RINGER intentionally excluded since it's deprecated.
+ // Settings.System.WIFI_USE_STATIC_IP intentionally excluded since it's deprecated.
+ // Settings.System.WIFI_STATIC_IP intentionally excluded since it's deprecated.
+ // Settings.System.WIFI_STATIC_GATEWAY intentionally excluded since it's deprecated.
+ // Settings.System.WIFI_STATIC_NETMASK intentionally excluded since it's deprecated.
+ // Settings.System.WIFI_STATIC_DNS1 intentionally excluded since it's deprecated.
+ // Settings.System.WIFI_STATIC_DNS2 intentionally excluded since it's deprecated.
dumpSetting(s, p,
Settings.System.BLUETOOTH_DISCOVERABILITY,
SystemSettingsProto.BLUETOOTH_DISCOVERABILITY);
dumpSetting(s, p,
Settings.System.BLUETOOTH_DISCOVERABILITY_TIMEOUT,
SystemSettingsProto.BLUETOOTH_DISCOVERABILITY_TIMEOUT);
+ // Settings.System.LOCK_PATTERN_ENABLED intentionally excluded since it's deprecated.
+ // Settings.System.LOCK_PATTERN_VISIBLE intentionally excluded since it's deprecated.
+ // Settings.System.LOCK_PATTERN_TACTILE_FEEDBACK_ENABLED intentionally excluded since it's deprecated.
+ // Settings.System.NEXT_ALARM_FORMATTED intentionally excluded since it's deprecated.
dumpSetting(s, p,
Settings.System.FONT_SCALE,
SystemSettingsProto.FONT_SCALE);
dumpSetting(s, p,
Settings.System.SYSTEM_LOCALES,
SystemSettingsProto.SYSTEM_LOCALES);
+ // Settings.System.DEBUG_APP intentionally excluded since it's deprecated.
+ // Settings.System.WAIT_FOR_DEBUGGER intentionally excluded since it's deprecated.
+ // Settings.System.DIM_SCREEN intentionally excluded since it's deprecated.
+ dumpSetting(s, p,
+ Settings.System.DISPLAY_COLOR_MODE,
+ SystemSettingsProto.DISPLAY_COLOR_MODE);
dumpSetting(s, p,
Settings.System.SCREEN_OFF_TIMEOUT,
SystemSettingsProto.SCREEN_OFF_TIMEOUT);
@@ -1480,6 +1802,8 @@
dumpSetting(s, p,
Settings.System.SCREEN_AUTO_BRIGHTNESS_ADJ,
SystemSettingsProto.SCREEN_AUTO_BRIGHTNESS_ADJ);
+ // Settings.System.SHOW_PROCESSES intentionally excluded since it's deprecated.
+ // Settings.System.ALWAYS_FINISH_ACTIVITIES intentionally excluded since it's deprecated.
dumpSetting(s, p,
Settings.System.MODE_RINGER_STREAMS_AFFECTED,
SystemSettingsProto.MODE_RINGER_STREAMS_AFFECTED);
@@ -1514,11 +1838,15 @@
Settings.System.VOLUME_BLUETOOTH_SCO,
SystemSettingsProto.VOLUME_BLUETOOTH_SCO);
dumpSetting(s, p,
+ Settings.System.VOLUME_ACCESSIBILITY,
+ SystemSettingsProto.VOLUME_ACCESSIBILITY);
+ dumpSetting(s, p,
Settings.System.VOLUME_MASTER,
SystemSettingsProto.VOLUME_MASTER);
dumpSetting(s, p,
Settings.System.MASTER_MONO,
SystemSettingsProto.MASTER_MONO);
+ // Settings.System.NOTIFICATIONS_USE_RING_VOLUME intentionally excluded since it's deprecated.
dumpSetting(s, p,
Settings.System.VIBRATE_IN_SILENT,
SystemSettingsProto.VIBRATE_IN_SILENT);
@@ -1561,6 +1889,9 @@
dumpSetting(s, p,
Settings.System.SHOW_GTALK_SERVICE_STATUS,
SystemSettingsProto.SHOW_GTALK_SERVICE_STATUS);
+ // Settings.System.WALLPAPER_ACTIVITY intentionally excluded since it's deprecated.
+ // Settings.System.AUTO_TIME intentionally excluded since it's deprecated.
+ // Settings.System.AUTO_TIME_ZONE intentionally excluded since it's deprecated.
dumpSetting(s, p,
Settings.System.TIME_12_24,
SystemSettingsProto.TIME_12_24);
@@ -1570,6 +1901,9 @@
dumpSetting(s, p,
Settings.System.SETUP_WIZARD_HAS_RUN,
SystemSettingsProto.SETUP_WIZARD_HAS_RUN);
+ // Settings.System.WINDOW_ANIMATION_SCALE intentionally excluded since it's deprecated.
+ // Settings.System.TRANSITION_ANIMATION_SCALE intentionally excluded since it's deprecated.
+ // Settings.System.ANIMATOR_ANIMATION_SCALE intentionally excluded since it's deprecated.
dumpSetting(s, p,
Settings.System.ACCELEROMETER_ROTATION,
SystemSettingsProto.ACCELEROMETER_ROTATION);
@@ -1600,6 +1934,7 @@
dumpSetting(s, p,
Settings.System.HAPTIC_FEEDBACK_ENABLED,
SystemSettingsProto.HAPTIC_FEEDBACK_ENABLED);
+ // Settings.System.SHOW_WEB_SUGGESTIONS intentionally excluded since it's deprecated.
dumpSetting(s, p,
Settings.System.NOTIFICATION_LIGHT_PULSE,
SystemSettingsProto.NOTIFICATION_LIGHT_PULSE);
@@ -1612,12 +1947,21 @@
dumpSetting(s, p,
Settings.System.WINDOW_ORIENTATION_LISTENER_LOG,
SystemSettingsProto.WINDOW_ORIENTATION_LISTENER_LOG);
+ // Settings.System.POWER_SOUNDS_ENABLED intentionally excluded since it's deprecated.
+ // Settings.System.DOCK_SOUNDS_ENABLED intentionally excluded since it's deprecated.
dumpSetting(s, p,
Settings.System.LOCKSCREEN_SOUNDS_ENABLED,
SystemSettingsProto.LOCKSCREEN_SOUNDS_ENABLED);
dumpSetting(s, p,
Settings.System.LOCKSCREEN_DISABLED,
SystemSettingsProto.LOCKSCREEN_DISABLED);
+ // Settings.System.LOW_BATTERY_SOUND intentionally excluded since it's deprecated.
+ // Settings.System.DESK_DOCK_SOUND intentionally excluded since it's deprecated.
+ // Settings.System.DESK_UNDOCK_SOUND intentionally excluded since it's deprecated.
+ // Settings.System.CAR_DOCK_SOUND intentionally excluded since it's deprecated.
+ // Settings.System.CAR_UNDOCK_SOUND intentionally excluded since it's deprecated.
+ // Settings.System.LOCK_SOUND intentionally excluded since it's deprecated.
+ // Settings.System.UNLOCK_SOUND intentionally excluded since it's deprecated.
dumpSetting(s, p,
Settings.System.SIP_RECEIVE_CALLS,
SystemSettingsProto.SIP_RECEIVE_CALLS);
@@ -1630,6 +1974,7 @@
dumpSetting(s, p,
Settings.System.SIP_ADDRESS_ONLY,
SystemSettingsProto.SIP_ADDRESS_ONLY);
+ // Settings.System.SIP_ASK_ME_EACH_TIME intentionally excluded since it's deprecated.
dumpSetting(s, p,
Settings.System.POINTER_SPEED,
SystemSettingsProto.POINTER_SPEED);
@@ -1640,7 +1985,12 @@
Settings.System.EGG_MODE,
SystemSettingsProto.EGG_MODE);
dumpSetting(s, p,
+ Settings.System.SHOW_BATTERY_PERCENT,
+ SystemSettingsProto.SHOW_BATTERY_PERCENT);
+ dumpSetting(s, p,
Settings.System.WHEN_TO_MAKE_WIFI_CALLS,
SystemSettingsProto.WHEN_TO_MAKE_WIFI_CALLS);
+ // The rest of the settings were moved to Settings.Secure, and are thus excluded here since
+ // they're deprecated from Settings.System.
}
}
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 36f9b84..258c96c 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -18,6 +18,7 @@
import android.Manifest;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.AppGlobals;
import android.app.backup.BackupManager;
@@ -657,7 +658,6 @@
synchronized (mLock) {
SettingsProtoDumpUtil.dumpProtoLocked(mSettingsRegistry, proto);
-
}
proto.flush();
@@ -2284,6 +2284,7 @@
return users;
}
+ @Nullable
public SettingsState getSettingsLocked(int type, int userId) {
final int key = makeKey(type, userId);
return peekSettingsStateLocked(key);
@@ -2578,6 +2579,7 @@
ssaidSettings.deleteSettingLocked(Integer.toString(uid));
}
+ @Nullable
private SettingsState peekSettingsStateLocked(int key) {
SettingsState settingsState = mSettingsStates.get(key);
if (settingsState != null) {
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
index 4151ada..f901bca 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
@@ -434,8 +434,9 @@
* Dump historical operations as a proto buf.
*
* @param proto The proto buf stream to dump to
+ * @param fieldId The repeated field ID to use to save an operation to.
*/
- void dumpProtoHistoricalOperations(@NonNull ProtoOutputStream proto) {
+ void dumpHistoricalOperations(@NonNull ProtoOutputStream proto, long fieldId) {
synchronized (mLock) {
if (mHistoricalOperations == null) {
return;
@@ -448,7 +449,8 @@
index = operationCount + index;
}
HistoricalOperation operation = mHistoricalOperations.get(index);
- long settingsOperationToken = proto.start(GlobalSettingsProto.HISTORICAL_OP);
+
+ final long token = proto.start(fieldId);
proto.write(SettingsOperationProto.TIMESTAMP, operation.mTimestamp);
proto.write(SettingsOperationProto.OPERATION, operation.mOperation);
if (operation.mSetting != null) {
@@ -457,7 +459,7 @@
// add is what the current data is).
proto.write(SettingsOperationProto.SETTING, operation.mSetting.getName());
}
- proto.end(settingsOperationToken);
+ proto.end(token);
}
}
}
diff --git a/packages/SystemUI/res/layout/volume_dialog.xml b/packages/SystemUI/res/layout/volume_dialog.xml
index 3d9a2dc..fac254a 100644
--- a/packages/SystemUI/res/layout/volume_dialog.xml
+++ b/packages/SystemUI/res/layout/volume_dialog.xml
@@ -13,30 +13,36 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<RelativeLayout
+<com.android.systemui.HardwareUiLayout
xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/volume_dialog"
android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginBottom="@dimen/volume_dialog_margin_bottom"
- android:background="@drawable/volume_dialog_background"
- android:paddingTop="@dimen/volume_dialog_padding_top"
+ android:layout_height="match_parent"
android:theme="@style/qs_theme"
- android:translationZ="4dp" >
-
- <LinearLayout
- android:id="@+id/volume_dialog_content"
- android:layout_width="match_parent"
+ android:clipChildren="false" >
+ <RelativeLayout
+ android:id="@+id/volume_dialog"
+ android:layout_width="@dimen/volume_dialog_panel_width"
android:layout_height="wrap_content"
- android:orientation="vertical" >
+ android:layout_gravity="center_vertical|end"
+ android:paddingTop="@dimen/volume_row_padding_bottom"
+ android:layout_margin="12dp"
+ android:background="?android:attr/actionBarItemBackground"
+ android:translationZ="8dp" >
<LinearLayout
- android:id="@+id/volume_dialog_rows"
+ android:id="@+id/volume_dialog_content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical" >
- <!-- volume rows added and removed here! :-) -->
- </LinearLayout>
- </LinearLayout>
-</RelativeLayout>
+ <LinearLayout
+ android:id="@+id/volume_dialog_rows"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical" >
+ <!-- volume rows added and removed here! :-) -->
+ </LinearLayout>
+ </LinearLayout>
+
+ </RelativeLayout>
+</com.android.systemui.HardwareUiLayout>
diff --git a/packages/SystemUI/res/layout/volume_dialog_row.xml b/packages/SystemUI/res/layout/volume_dialog_row.xml
index 7328d05..bf76e78 100644
--- a/packages/SystemUI/res/layout/volume_dialog_row.xml
+++ b/packages/SystemUI/res/layout/volume_dialog_row.xml
@@ -19,8 +19,8 @@
android:layout_height="@dimen/volume_row_height"
android:clipChildren="false"
android:clipToPadding="false"
- android:orientation="vertical"
- android:paddingBottom="@dimen/volume_row_padding_bottom" >
+ android:theme="@style/qs_theme"
+ android:orientation="vertical" >
<TextView
android:id="@+id/volume_row_header"
@@ -28,7 +28,8 @@
android:layout_height="wrap_content"
android:ellipsize="end"
android:maxLines="1"
- android:textAppearance="@style/TextAppearance.Volume.Header"
+ android:textColor="?android:attr/colorControlNormal"
+ android:textAppearance="?android:attr/textAppearanceSmall"
android:paddingStart="@dimen/volume_row_header_padding_start" />
<LinearLayout
@@ -53,4 +54,9 @@
android:paddingStart="@dimen/volume_row_slider_padding_start"/>
</LinearLayout>
+ <Space
+ android:id="@+id/spacer"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/volume_row_padding_bottom"/>
+
</LinearLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index c678111f..f0bad2a 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -240,7 +240,7 @@
<!-- The width of the panel that holds the quick settings. -->
<dimen name="qs_panel_width">@dimen/notification_panel_width</dimen>
- <dimen name="volume_dialog_panel_width">@dimen/standard_notification_panel_width</dimen>
+ <dimen name="volume_dialog_panel_width">315dp</dimen>
<!-- Gravity for the notification panel -->
<integer name="notification_panel_layout_gravity">0x31</integer><!-- center_horizontal|top -->
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 1536b64..b132160 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -124,16 +124,16 @@
<string name="status_bar_use_physical_keyboard">Physical keyboard</string>
<!-- Prompt for the USB device permission dialog [CHAR LIMIT=80] -->
- <string name="usb_device_permission_prompt">Allow the app <xliff:g id="application">%1$s</xliff:g> to access the USB device?</string>
+ <string name="usb_device_permission_prompt">Allow <xliff:g id="application">%1$s</xliff:g> to access <xliff:g id="usb_device">%2$s</xliff:g>?</string>
<!-- Prompt for the USB accessory permission dialog [CHAR LIMIT=80] -->
- <string name="usb_accessory_permission_prompt">Allow the app <xliff:g id="application">%1$s</xliff:g> to access the USB accessory?</string>
+ <string name="usb_accessory_permission_prompt">Allow <xliff:g id="application">%1$s</xliff:g> to access <xliff:g id="usb_accessory">%2$s</xliff:g>?</string>
<!-- Prompt for the USB device confirm dialog [CHAR LIMIT=80] -->
- <string name="usb_device_confirm_prompt">Open <xliff:g id="activity">%1$s</xliff:g> when this USB device is connected?</string>
+ <string name="usb_device_confirm_prompt">Open <xliff:g id="application">%1$s</xliff:g> to handle <xliff:g id="usb_device">%2$s</xliff:g>?</string>
<!-- Prompt for the USB accessory confirm dialog [CHAR LIMIT=80] -->
- <string name="usb_accessory_confirm_prompt">Open <xliff:g id="activity">%1$s</xliff:g> when this USB accessory is connected?</string>
+ <string name="usb_accessory_confirm_prompt">Open <xliff:g id="application">%1$s</xliff:g> to handle <xliff:g id="usb_accessory">%2$s</xliff:g>?</string>
<!-- Prompt for the USB accessory URI dialog [CHAR LIMIT=80] -->
<string name="usb_accessory_uri_prompt">No installed apps work with this USB accessory. Learn more about this accessory at <xliff:g id="url">%1$s</xliff:g></string>
@@ -145,10 +145,10 @@
<string name="label_view">View</string>
<!-- Checkbox label for USB device dialogs. [CHAR LIMIT=50] -->
- <string name="always_use_device">Use by default for this USB device</string>
+ <string name="always_use_device">Always open <xliff:g id="application">%1$s</xliff:g> when <xliff:g id="usb_device">%2$s</xliff:g> is connected</string>
- <!-- Checkbox label for USB accessory dialogs. [CHAR LIMIT=50] -->
- <string name="always_use_accessory">Use by default for this USB accessory</string>
+ <!-- Checkbox label for USB accessory dialogs. [CHAR LIMIT=50]-->
+ <string name="always_use_accessory">Always open <xliff:g id="application">%1$s</xliff:g> when <xliff:g id="usb_accessory">%2$s</xliff:g> is connected</string>
<!-- Title of confirmation dialog for USB debugging -->
<string name="usb_debugging_title">Allow USB debugging?</string>
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/IOverviewProxy.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl
similarity index 82%
rename from packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/IOverviewProxy.aidl
rename to packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl
index 8cf3be8..173a90a 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/IOverviewProxy.aidl
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl
@@ -14,10 +14,12 @@
* limitations under the License.
*/
-package com.android.systemui.shared.recents.model;
+package com.android.systemui.shared.recents;
import android.view.MotionEvent;
+import com.android.systemui.shared.recents.ISystemUiProxy;
oneway interface IOverviewProxy {
+ void onBind(in ISystemUiProxy sysUiProxy);
void onMotionEvent(in MotionEvent event);
}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl
new file mode 100644
index 0000000..d82b010
--- /dev/null
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.shared.recents;
+
+import android.graphics.Bitmap;
+import android.graphics.Rect;
+
+/**
+ * Temporary callbacks into SystemUI.
+ */
+interface ISystemUiProxy {
+
+ /**
+ * Proxies SurfaceControl.screenshot().
+ */
+ Bitmap screenshot(in Rect sourceCrop, int width, int height,
+ int minLayer, int maxLayer, boolean useIdentityTransform,
+ int rotation);
+}
diff --git a/packages/SystemUI/src/com/android/systemui/Dependency.java b/packages/SystemUI/src/com/android/systemui/Dependency.java
index 62a9319..e7e70af 100644
--- a/packages/SystemUI/src/com/android/systemui/Dependency.java
+++ b/packages/SystemUI/src/com/android/systemui/Dependency.java
@@ -26,7 +26,7 @@
import android.view.WindowManagerGlobal;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.app.NightDisplayController;
+import com.android.internal.app.ColorDisplayController;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.util.Preconditions;
import com.android.settingslib.bluetooth.LocalBluetoothManager;
@@ -205,8 +205,8 @@
mProviders.put(BatteryController.class, () ->
new BatteryControllerImpl(mContext));
- mProviders.put(NightDisplayController.class, () ->
- new NightDisplayController(mContext));
+ mProviders.put(ColorDisplayController.class, () ->
+ new ColorDisplayController(mContext));
mProviders.put(ManagedProfileController.class, () ->
new ManagedProfileControllerImpl(mContext));
diff --git a/packages/SystemUI/src/com/android/systemui/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/OverviewProxyService.java
index 528e242..fc1c84a 100644
--- a/packages/SystemUI/src/com/android/systemui/OverviewProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/OverviewProxyService.java
@@ -16,36 +16,75 @@
package com.android.systemui;
+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.graphics.Bitmap;
+import android.graphics.Rect;
+import android.net.Uri;
+import android.os.Binder;
+import android.os.Build;
import android.os.Handler;
import android.os.IBinder;
+import android.os.Looper;
+import android.os.PatternMatcher;
import android.os.RemoteException;
import android.os.UserHandle;
import android.util.Log;
-import com.android.systemui.shared.recents.model.IOverviewProxy;
+import android.view.SurfaceControl;
+
+import com.android.systemui.shared.recents.IOverviewProxy;
+import com.android.systemui.shared.recents.ISystemUiProxy;
+import com.android.systemui.OverviewProxyService.OverviewProxyListener;
+import com.android.systemui.statusbar.policy.CallbackController;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.statusbar.policy.DeviceProvisionedController.DeviceProvisionedListener;
+import java.util.ArrayList;
+import java.util.List;
/**
* Class to send information from overview to launcher with a binder.
*/
-public class OverviewProxyService {
+public class OverviewProxyService implements CallbackController<OverviewProxyListener> {
private static final String TAG = "OverviewProxyService";
private static final long BACKOFF_MILLIS = 5000;
private final Context mContext;
private final Handler mHandler;
- private final Runnable mConnectionRunnable = this::startConnectionToCurrentUser;
+ private final Runnable mConnectionRunnable = this::internalConnectToCurrentUser;
+ private final ComponentName mLauncherComponentName;
private final DeviceProvisionedController mDeviceProvisionedController
= Dependency.get(DeviceProvisionedController.class);
+ private final List<OverviewProxyListener> mConnectionCallbacks = new ArrayList<>();
private IOverviewProxy mOverviewProxy;
private int mConnectionBackoffAttempts;
+ private ISystemUiProxy mSysUiProxy = new ISystemUiProxy.Stub() {
+ public Bitmap screenshot(Rect sourceCrop, int width, int height, int minLayer, int maxLayer,
+ boolean useIdentityTransform, int rotation) {
+ long token = Binder.clearCallingIdentity();
+ try {
+ return SurfaceControl.screenshot(sourceCrop, width, height, minLayer, maxLayer,
+ useIdentityTransform, rotation);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+ };
+
+ private final BroadcastReceiver mLauncherAddedReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ // Reconnect immediately, instead of waiting for resume to arrive.
+ startConnectionToCurrentUser();
+ }
+ };
+
private final ServiceConnection mOverviewServiceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
@@ -58,6 +97,12 @@
} catch (RemoteException e) {
Log.e(TAG, "Lost connection to launcher service", e);
}
+ try {
+ mOverviewProxy.onBind(mSysUiProxy);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to call onBind()", e);
+ }
+ notifyConnectionChanged();
}
}
@@ -72,33 +117,46 @@
@Override
public void onUserSetupChanged() {
if (mDeviceProvisionedController.isCurrentUserSetup()) {
- startConnectionToCurrentUser();
+ internalConnectToCurrentUser();
}
}
@Override
public void onUserSwitched() {
mConnectionBackoffAttempts = 0;
- startConnectionToCurrentUser();
+ internalConnectToCurrentUser();
}
};
// This is the death handler for the binder from the launcher service
- private final IBinder.DeathRecipient mOverviewServiceDeathRcpt = new IBinder.DeathRecipient() {
- @Override
- public void binderDied() {
- mOverviewProxy = null;
- }
- };
+ private final IBinder.DeathRecipient mOverviewServiceDeathRcpt
+ = this::startConnectionToCurrentUser;
public OverviewProxyService(Context context) {
mContext = context;
mHandler = new Handler();
mConnectionBackoffAttempts = 0;
+ mLauncherComponentName = ComponentName
+ .unflattenFromString(context.getString(R.string.config_overviewServiceComponent));
mDeviceProvisionedController.addCallback(mDeviceProvisionedCallback);
+
+ // Listen for the package update changes.
+ IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
+ filter.addDataScheme("package");
+ filter.addDataSchemeSpecificPart(mLauncherComponentName.getPackageName(),
+ PatternMatcher.PATTERN_LITERAL);
+ mContext.registerReceiver(mLauncherAddedReceiver, filter);
}
public void startConnectionToCurrentUser() {
+ if (mHandler.getLooper() != Looper.myLooper()) {
+ mHandler.post(mConnectionRunnable);
+ } else {
+ internalConnectToCurrentUser();
+ }
+ }
+
+ private void internalConnectToCurrentUser() {
disconnectFromLauncherService();
// If user has not setup yet or already connected, do not try to connect
@@ -107,8 +165,7 @@
}
mHandler.removeCallbacks(mConnectionRunnable);
Intent launcherServiceIntent = new Intent();
- launcherServiceIntent.setComponent(ComponentName.unflattenFromString(
- mContext.getString(R.string.config_overviewServiceComponent)));
+ launcherServiceIntent.setComponent(mLauncherComponentName);
boolean bound = mContext.bindServiceAsUser(launcherServiceIntent,
mOverviewServiceConnection, Context.BIND_AUTO_CREATE,
UserHandle.getUserHandleForUid(mDeviceProvisionedController.getCurrentUser()));
@@ -120,14 +177,37 @@
}
}
+ @Override
+ public void addCallback(OverviewProxyListener listener) {
+ mConnectionCallbacks.add(listener);
+ listener.onConnectionChanged(mOverviewProxy != null);
+ }
+
+ @Override
+ public void removeCallback(OverviewProxyListener listener) {
+ mConnectionCallbacks.remove(listener);
+ }
+
public IOverviewProxy getProxy() {
return mOverviewProxy;
}
private void disconnectFromLauncherService() {
if (mOverviewProxy != null) {
+ mOverviewProxy.asBinder().unlinkToDeath(mOverviewServiceDeathRcpt, 0);
mContext.unbindService(mOverviewServiceConnection);
mOverviewProxy = null;
+ notifyConnectionChanged();
}
}
+
+ private void notifyConnectionChanged() {
+ for (int i = mConnectionCallbacks.size() - 1; i >= 0; --i) {
+ mConnectionCallbacks.get(i).onConnectionChanged(mOverviewProxy != null);
+ }
+ }
+
+ public interface OverviewProxyListener {
+ void onConnectionChanged(boolean isConnected);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
index d82f9cd..00e8b1a 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
@@ -80,7 +80,7 @@
import com.android.systemui.colorextraction.SysuiColorExtractor;
import com.android.systemui.plugins.GlobalActions.GlobalActionsManager;
import com.android.systemui.statusbar.phone.ScrimController;
-import com.android.systemui.volume.VolumeDialogMotion.LogAccelerateInterpolator;
+import com.android.systemui.volume.SystemUIInterpolators.LogAccelerateInterpolator;
import java.util.ArrayList;
import java.util.List;
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 28adca9..a35ba9f 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -28,6 +28,7 @@
import android.app.Activity;
import android.app.ActivityManager;
import android.app.AlarmManager;
+import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.StatusBarManager;
import android.app.trust.TrustManager;
@@ -1691,6 +1692,8 @@
mUiOffloadThread.submit(() -> {
// If the stream is muted, don't play the sound
if (mAudioManager.isStreamMute(mUiSoundsStreamType)) return;
+ // If DND blocks the sound, don't play the sound
+ if (areSystemSoundsZenModeBlocked(mContext)) return;
int id = mLockSounds.play(soundId,
mLockSoundVolume, mLockSoundVolume, 1/*priortiy*/, 0/*loop*/, 1.0f/*rate*/);
@@ -1702,6 +1705,25 @@
}
}
+ private boolean areSystemSoundsZenModeBlocked(Context context) {
+ int zenMode = Settings.Global.getInt(context.getContentResolver(),
+ Settings.Global.ZEN_MODE, 0);
+
+ switch (zenMode) {
+ case Settings.Global.ZEN_MODE_NO_INTERRUPTIONS:
+ case Settings.Global.ZEN_MODE_ALARMS:
+ return true;
+ case Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS:
+ final NotificationManager noMan = (NotificationManager) context
+ .getSystemService(Context.NOTIFICATION_SERVICE);
+ return (noMan.getNotificationPolicy().priorityCategories
+ & NotificationManager.Policy.PRIORITY_CATEGORY_MEDIA_SYSTEM_OTHER) == 0;
+ case Settings.Global.ZEN_MODE_OFF:
+ default:
+ return false;
+ }
+ }
+
private void playTrustedSound() {
playSound(mTrustedSoundId);
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java
index 532ead1..5ffd785 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java
@@ -338,7 +338,9 @@
final boolean isDemo = UserManager.isDeviceInDemoMode(mContext);
- mMultiUserSwitch.setVisibility(mExpanded && mMultiUserSwitch.hasMultipleUsers() && !isDemo
+
+ mMultiUserSwitch.setVisibility(mExpanded
+ && UserManager.get(mContext).isUserSwitcherEnabled()
? View.VISIBLE : View.INVISIBLE);
mEdit.setVisibility(isDemo || !mExpanded ? View.INVISIBLE : View.VISIBLE);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/NightDisplayTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/NightDisplayTile.java
index 4c20361..763ffc6 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/NightDisplayTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/NightDisplayTile.java
@@ -22,8 +22,7 @@
import android.service.quicksettings.Tile;
import android.widget.Switch;
-import com.android.internal.app.NightDisplayController;
-import com.android.internal.logging.MetricsLogger;
+import com.android.internal.app.ColorDisplayController;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.systemui.R;
import com.android.systemui.qs.QSHost;
@@ -31,19 +30,19 @@
import com.android.systemui.qs.tileimpl.QSTileImpl;
public class NightDisplayTile extends QSTileImpl<BooleanState>
- implements NightDisplayController.Callback {
+ implements ColorDisplayController.Callback {
- private NightDisplayController mController;
+ private ColorDisplayController mController;
private boolean mIsListening;
public NightDisplayTile(QSHost host) {
super(host);
- mController = new NightDisplayController(mContext, ActivityManager.getCurrentUser());
+ mController = new ColorDisplayController(mContext, ActivityManager.getCurrentUser());
}
@Override
public boolean isAvailable() {
- return NightDisplayController.isAvailable(mContext);
+ return ColorDisplayController.isAvailable(mContext);
}
@Override
@@ -65,7 +64,7 @@
}
// Make a new controller for the new user.
- mController = new NightDisplayController(mContext, newUserId);
+ mController = new ColorDisplayController(mContext, newUserId);
if (mIsListening) {
mController.setListener(this);
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
index a436e17..5eb315e 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
@@ -208,6 +208,8 @@
*
* @param isHomeStackVisible if provided, will return whether the home stack is visible
* regardless of the recents visibility
+ *
+ * TODO(winsonc): Refactor this check to just use the recents activity lifecycle
*/
public boolean isRecentsActivityVisible(MutableBoolean isHomeStackVisible) {
if (mIam == null) return false;
@@ -222,13 +224,13 @@
final WindowConfiguration winConfig = stackInfo.configuration.windowConfiguration;
final int activityType = winConfig.getActivityType();
final int windowingMode = winConfig.getWindowingMode();
- if (activityType == ACTIVITY_TYPE_HOME) {
+ if (homeStackInfo == null && activityType == ACTIVITY_TYPE_HOME) {
homeStackInfo = stackInfo;
- } else if (activityType == ACTIVITY_TYPE_STANDARD
+ } else if (fullscreenStackInfo == null && activityType == ACTIVITY_TYPE_STANDARD
&& (windowingMode == WINDOWING_MODE_FULLSCREEN
|| windowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY)) {
fullscreenStackInfo = stackInfo;
- } else if (activityType == ACTIVITY_TYPE_RECENTS) {
+ } else if (recentsStackInfo == null && activityType == ACTIVITY_TYPE_RECENTS) {
recentsStackInfo = stackInfo;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGuts.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGuts.java
index 54d622b..c4024a5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGuts.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGuts.java
@@ -18,46 +18,21 @@
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
-import android.app.INotificationManager;
-import android.app.NotificationChannel;
-import android.app.NotificationManager;
import android.content.Context;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageInfo;
-import android.content.pm.PackageManager;
-import android.content.res.ColorStateList;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.drawable.Drawable;
import android.os.Handler;
-import android.os.RemoteException;
-import android.os.ServiceManager;
-import android.service.notification.NotificationListenerService;
-import android.service.notification.StatusBarNotification;
import android.util.AttributeSet;
-import android.util.Log;
import android.view.View;
import android.view.ViewAnimationUtils;
-import android.view.ViewGroup;
import android.view.accessibility.AccessibilityEvent;
import android.widget.FrameLayout;
-import android.widget.ImageView;
-import android.widget.LinearLayout;
-import android.widget.SeekBar;
-import android.widget.Switch;
-import android.widget.TextView;
-import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-import com.android.settingslib.Utils;
import com.android.systemui.Interpolators;
import com.android.systemui.R;
-import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
-import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin.MenuItem;
import com.android.systemui.statusbar.stack.StackStateAnimator;
-import java.util.Set;
-
/**
* The guts of a notification revealed when performing a long press.
*/
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGutsManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGutsManager.java
new file mode 100644
index 0000000..b585bdf
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGutsManager.java
@@ -0,0 +1,355 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+package com.android.systemui.statusbar;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.app.INotificationManager;
+import android.app.NotificationChannel;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.res.Resources;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.service.notification.StatusBarNotification;
+import android.util.ArraySet;
+import android.util.Log;
+import android.view.HapticFeedbackConstants;
+import android.view.View;
+import android.view.ViewAnimationUtils;
+import android.view.accessibility.AccessibilityManager;
+
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.nano.MetricsProto;
+import com.android.systemui.Dependency;
+import com.android.systemui.Dumpable;
+import com.android.systemui.Interpolators;
+import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
+import com.android.systemui.statusbar.phone.StatusBar;
+import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
+import com.android.systemui.statusbar.stack.StackStateAnimator;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Handles various NotificationGuts related tasks, such as binding guts to a row, opening and
+ * closing guts, and keeping track of the currently exposed notification guts.
+ */
+public class NotificationGutsManager implements Dumpable {
+ private static final String TAG = "NotificationGutsManager";
+
+ // Must match constant in Settings. Used to highlight preferences when linking to Settings.
+ private static final String EXTRA_FRAGMENT_ARG_KEY = ":settings:fragment_args_key";
+
+ private final MetricsLogger mMetricsLogger = Dependency.get(MetricsLogger.class);
+ private final Set<String> mNonBlockablePkgs;
+ private final NotificationPresenter mPresenter;
+ // TODO: Create NotificationListContainer interface and use it instead of
+ // NotificationStackScrollLayout here
+ private final NotificationStackScrollLayout mStackScroller;
+ private final Context mContext;
+ private final AccessibilityManager mAccessibilityManager;
+ // which notification is currently being longpress-examined by the user
+ private NotificationGuts mNotificationGutsExposed;
+ private NotificationMenuRowPlugin.MenuItem mGutsMenuItem;
+ private final NotificationInfo.CheckSaveListener mCheckSaveListener;
+ private String mKeyToRemoveOnGutsClosed;
+
+ public NotificationGutsManager(
+ NotificationPresenter presenter,
+ NotificationStackScrollLayout stackScroller,
+ NotificationInfo.CheckSaveListener checkSaveListener,
+ Context context) {
+ mPresenter = presenter;
+ mStackScroller = stackScroller;
+ mCheckSaveListener = checkSaveListener;
+ mContext = context;
+ Resources res = context.getResources();
+
+ mNonBlockablePkgs = new HashSet<>();
+ Collections.addAll(mNonBlockablePkgs, res.getStringArray(
+ com.android.internal.R.array.config_nonBlockableNotificationPackages));
+
+ mAccessibilityManager = (AccessibilityManager)
+ mContext.getSystemService(Context.ACCESSIBILITY_SERVICE);
+ }
+
+ public String getKeyToRemoveOnGutsClosed() {
+ return mKeyToRemoveOnGutsClosed;
+ }
+
+ public void setKeyToRemoveOnGutsClosed(String keyToRemoveOnGutsClosed) {
+ mKeyToRemoveOnGutsClosed = keyToRemoveOnGutsClosed;
+ }
+
+ private void saveAndCloseNotificationMenu(
+ ExpandableNotificationRow row, NotificationGuts guts, View done) {
+ guts.resetFalsingCheck();
+ int[] rowLocation = new int[2];
+ int[] doneLocation = new int[2];
+ row.getLocationOnScreen(rowLocation);
+ done.getLocationOnScreen(doneLocation);
+
+ final int centerX = done.getWidth() / 2;
+ final int centerY = done.getHeight() / 2;
+ final int x = doneLocation[0] - rowLocation[0] + centerX;
+ final int y = doneLocation[1] - rowLocation[1] + centerY;
+ closeAndSaveGuts(false /* removeLeavebehind */, false /* force */,
+ true /* removeControls */, x, y, true /* resetMenu */);
+ }
+
+ /**
+ * Sends an intent to open the notification settings for a particular package and optional
+ * channel.
+ */
+ private void startAppNotificationSettingsActivity(String packageName, final int appUid,
+ final NotificationChannel channel) {
+ final Intent intent = new Intent(Settings.ACTION_APP_NOTIFICATION_SETTINGS);
+ intent.putExtra(Settings.EXTRA_APP_PACKAGE, packageName);
+ intent.putExtra(Settings.EXTRA_APP_UID, appUid);
+ if (channel != null) {
+ intent.putExtra(EXTRA_FRAGMENT_ARG_KEY, channel.getId());
+ }
+ mPresenter.startNotificationGutsIntent(intent, appUid);
+ }
+
+ public void bindGuts(final ExpandableNotificationRow row) {
+ bindGuts(row, mGutsMenuItem);
+ }
+
+ private void bindGuts(final ExpandableNotificationRow row,
+ NotificationMenuRowPlugin.MenuItem item) {
+ row.inflateGuts();
+ row.setGutsView(item);
+ final StatusBarNotification sbn = row.getStatusBarNotification();
+ row.setTag(sbn.getPackageName());
+ final NotificationGuts guts = row.getGuts();
+ guts.setClosedListener((NotificationGuts g) -> {
+ if (!g.willBeRemoved() && !row.isRemoved()) {
+ mStackScroller.onHeightChanged(
+ row, !mPresenter.isPresenterFullyCollapsed() /* needsAnimation */);
+ }
+ if (mNotificationGutsExposed == g) {
+ mNotificationGutsExposed = null;
+ mGutsMenuItem = null;
+ }
+ String key = sbn.getKey();
+ if (key.equals(mKeyToRemoveOnGutsClosed)) {
+ mKeyToRemoveOnGutsClosed = null;
+ mPresenter.removeNotification(key, mPresenter.getLatestRankingMap());
+ }
+ });
+
+ View gutsView = item.getGutsView();
+ if (gutsView instanceof NotificationSnooze) {
+ NotificationSnooze snoozeGuts = (NotificationSnooze) gutsView;
+ snoozeGuts.setSnoozeListener(mStackScroller.getSwipeActionHelper());
+ snoozeGuts.setStatusBarNotification(sbn);
+ snoozeGuts.setSnoozeOptions(row.getEntry().snoozeCriteria);
+ guts.setHeightChangedListener((NotificationGuts g) -> {
+ mStackScroller.onHeightChanged(row, row.isShown() /* needsAnimation */);
+ });
+ }
+
+ if (gutsView instanceof NotificationInfo) {
+ final UserHandle userHandle = sbn.getUser();
+ PackageManager pmUser = StatusBar.getPackageManagerForUser(mContext,
+ userHandle.getIdentifier());
+ final INotificationManager iNotificationManager = INotificationManager.Stub.asInterface(
+ ServiceManager.getService(Context.NOTIFICATION_SERVICE));
+ final String pkg = sbn.getPackageName();
+ NotificationInfo info = (NotificationInfo) gutsView;
+ // Settings link is only valid for notifications that specify a user, unless this is the
+ // system user.
+ NotificationInfo.OnSettingsClickListener onSettingsClick = null;
+ if (!userHandle.equals(UserHandle.ALL)
+ || mPresenter.getCurrentUserId() == UserHandle.USER_SYSTEM) {
+ onSettingsClick = (View v, NotificationChannel channel, int appUid) -> {
+ mMetricsLogger.action(MetricsProto.MetricsEvent.ACTION_NOTE_INFO);
+ guts.resetFalsingCheck();
+ startAppNotificationSettingsActivity(pkg, appUid, channel);
+ };
+ }
+ final NotificationInfo.OnAppSettingsClickListener onAppSettingsClick = (View v,
+ Intent intent) -> {
+ mMetricsLogger.action(MetricsProto.MetricsEvent.ACTION_APP_NOTE_SETTINGS);
+ guts.resetFalsingCheck();
+ mPresenter.startNotificationGutsIntent(intent, sbn.getUid());
+ };
+ final View.OnClickListener onDoneClick = (View v) -> {
+ saveAndCloseNotificationMenu(row, guts, v);
+ };
+
+ ArraySet<NotificationChannel> channels = new ArraySet<>();
+ channels.add(row.getEntry().channel);
+ if (row.isSummaryWithChildren()) {
+ // If this is a summary, then add in the children notification channels for the
+ // same user and pkg.
+ final List<ExpandableNotificationRow> childrenRows = row.getNotificationChildren();
+ final int numChildren = childrenRows.size();
+ for (int i = 0; i < numChildren; i++) {
+ final ExpandableNotificationRow childRow = childrenRows.get(i);
+ final NotificationChannel childChannel = childRow.getEntry().channel;
+ final StatusBarNotification childSbn = childRow.getStatusBarNotification();
+ if (childSbn.getUser().equals(userHandle) &&
+ childSbn.getPackageName().equals(pkg)) {
+ channels.add(childChannel);
+ }
+ }
+ }
+ try {
+ info.bindNotification(pmUser, iNotificationManager, pkg, new ArrayList(channels),
+ row.getEntry().channel.getImportance(), sbn, onSettingsClick,
+ onAppSettingsClick, onDoneClick, mCheckSaveListener,
+ mNonBlockablePkgs);
+ } catch (RemoteException e) {
+ Log.e(TAG, e.toString());
+ }
+ }
+ }
+
+ /**
+ * Closes guts or notification menus that might be visible and saves any changes.
+ *
+ * @param removeLeavebehinds true if leavebehinds (e.g. snooze) should be closed.
+ * @param force true if guts should be closed regardless of state (used for snooze only).
+ * @param removeControls true if controls (e.g. info) should be closed.
+ * @param x if closed based on touch location, this is the x touch location.
+ * @param y if closed based on touch location, this is the y touch location.
+ * @param resetMenu if any notification menus that might be revealed should be closed.
+ */
+ public void closeAndSaveGuts(boolean removeLeavebehinds, boolean force, boolean removeControls,
+ int x, int y, boolean resetMenu) {
+ if (mNotificationGutsExposed != null) {
+ mNotificationGutsExposed.closeControls(removeLeavebehinds, removeControls, x, y, force);
+ }
+ if (resetMenu) {
+ mStackScroller.resetExposedMenuView(false /* animate */, true /* force */);
+ }
+ }
+
+ /**
+ * Returns the exposed NotificationGuts or null if none are exposed.
+ */
+ public NotificationGuts getExposedGuts() {
+ return mNotificationGutsExposed;
+ }
+
+ public void setExposedGuts(NotificationGuts guts) {
+ mNotificationGutsExposed = guts;
+ }
+
+ /**
+ * Opens guts on the given ExpandableNotificationRow |v|.
+ *
+ * @param v ExpandableNotificationRow to open guts on
+ * @param x x coordinate of origin of circular reveal
+ * @param y y coordinate of origin of circular reveal
+ * @param item MenuItem the guts should display
+ * @return true if guts was opened
+ */
+ public boolean openGuts(View v, int x, int y,
+ NotificationMenuRowPlugin.MenuItem item) {
+ if (!(v instanceof ExpandableNotificationRow)) {
+ return false;
+ }
+
+ if (v.getWindowToken() == null) {
+ Log.e(TAG, "Trying to show notification guts, but not attached to window");
+ return false;
+ }
+
+ final ExpandableNotificationRow row = (ExpandableNotificationRow) v;
+ if (row.isDark()) {
+ return false;
+ }
+ v.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
+ if (row.areGutsExposed()) {
+ closeAndSaveGuts(false /* removeLeavebehind */, false /* force */,
+ true /* removeControls */, -1 /* x */, -1 /* y */,
+ true /* resetMenu */);
+ return false;
+ }
+ bindGuts(row, item);
+ NotificationGuts guts = row.getGuts();
+
+ // Assume we are a status_bar_notification_row
+ if (guts == null) {
+ // This view has no guts. Examples are the more card or the dismiss all view
+ return false;
+ }
+
+ mMetricsLogger.action(MetricsProto.MetricsEvent.ACTION_NOTE_CONTROLS);
+
+ // ensure that it's laid but not visible until actually laid out
+ guts.setVisibility(View.INVISIBLE);
+ // Post to ensure the the guts are properly laid out.
+ guts.post(new Runnable() {
+ @Override
+ public void run() {
+ if (row.getWindowToken() == null) {
+ Log.e(TAG, "Trying to show notification guts, but not attached to "
+ + "window");
+ return;
+ }
+ closeAndSaveGuts(true /* removeLeavebehind */, true /* force */,
+ true /* removeControls */, -1 /* x */, -1 /* y */,
+ false /* resetMenu */);
+ guts.setVisibility(View.VISIBLE);
+ final double horz = Math.max(guts.getWidth() - x, x);
+ final double vert = Math.max(guts.getHeight() - y, y);
+ final float r = (float) Math.hypot(horz, vert);
+ final Animator a
+ = ViewAnimationUtils.createCircularReveal(guts, x, y, 0, r);
+ a.setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
+ a.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN);
+ a.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ super.onAnimationEnd(animation);
+ // Move the notification view back over the menu
+ row.resetTranslation();
+ }
+ });
+ a.start();
+ final boolean needsFalsingProtection =
+ (mPresenter.isPresenterLocked() &&
+ !mAccessibilityManager.isTouchExplorationEnabled());
+ guts.setExposed(true /* exposed */, needsFalsingProtection);
+ row.closeRemoteInput();
+ mStackScroller.onHeightChanged(row, true /* needsAnimation */);
+ mNotificationGutsExposed = guts;
+ mGutsMenuItem = item;
+ }
+ });
+ return true;
+ }
+
+ @Override
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ pw.print("mKeyToRemoveOnGutsClosed: ");
+ pw.println(mKeyToRemoveOnGutsClosed);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationInfo.java
index 3b23a0c..8d1bb5f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationInfo.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationInfo.java
@@ -84,7 +84,7 @@
public interface CheckSaveListener {
// Invoked when importance has changed and the NotificationInfo wants to try to save it.
// Listener should run saveImportance unless the change should be canceled.
- void checkSave(Runnable saveImportance);
+ void checkSave(Runnable saveImportance, StatusBarNotification sbn);
}
public interface OnSettingsClickListener {
@@ -409,7 +409,7 @@
public boolean handleCloseControls(boolean save, boolean force) {
if (save && hasImportanceChanged()) {
if (mCheckSaveListener != null) {
- mCheckSaveListener.checkSave(() -> { saveImportance(); });
+ mCheckSaveListener.checkSave(this::saveImportance, mSbn);
} else {
saveImportance();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
new file mode 100644
index 0000000..e65bab2
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
@@ -0,0 +1,245 @@
+package com.android.systemui.statusbar;
+
+import android.app.Notification;
+import android.content.Context;
+import android.media.MediaMetadata;
+import android.media.session.MediaController;
+import android.media.session.MediaSession;
+import android.media.session.MediaSessionManager;
+import android.media.session.PlaybackState;
+import android.os.UserHandle;
+import android.util.Log;
+
+import com.android.systemui.Dumpable;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Handles tasks and state related to media notifications. For example, there is a 'current' media
+ * notification, which this class keeps track of.
+ */
+public class NotificationMediaManager implements Dumpable {
+ private static final String TAG = "NotificationMediaManager";
+ public static final boolean DEBUG_MEDIA = false;
+
+ private final NotificationPresenter mPresenter;
+ private final Context mContext;
+ private final MediaSessionManager mMediaSessionManager;
+ private MediaController mMediaController;
+ private String mMediaNotificationKey;
+ private MediaMetadata mMediaMetadata;
+
+ private final MediaController.Callback mMediaListener = new MediaController.Callback() {
+ @Override
+ public void onPlaybackStateChanged(PlaybackState state) {
+ super.onPlaybackStateChanged(state);
+ if (DEBUG_MEDIA) {
+ Log.v(TAG, "DEBUG_MEDIA: onPlaybackStateChanged: " + state);
+ }
+ if (state != null) {
+ if (!isPlaybackActive(state.getState())) {
+ clearCurrentMediaNotification();
+ mPresenter.updateMediaMetaData(true, true);
+ }
+ }
+ }
+
+ @Override
+ public void onMetadataChanged(MediaMetadata metadata) {
+ super.onMetadataChanged(metadata);
+ if (DEBUG_MEDIA) {
+ Log.v(TAG, "DEBUG_MEDIA: onMetadataChanged: " + metadata);
+ }
+ mMediaMetadata = metadata;
+ mPresenter.updateMediaMetaData(true, true);
+ }
+ };
+
+ public NotificationMediaManager(NotificationPresenter presenter, Context context) {
+ mPresenter = presenter;
+ mContext = context;
+ mMediaSessionManager
+ = (MediaSessionManager) mContext.getSystemService(Context.MEDIA_SESSION_SERVICE);
+ // TODO: use MediaSessionManager.SessionListener to hook us up to future updates
+ // in session state
+ }
+
+ public void onNotificationRemoved(String key) {
+ if (key.equals(mMediaNotificationKey)) {
+ clearCurrentMediaNotification();
+ mPresenter.updateMediaMetaData(true, true);
+ }
+ }
+
+ public String getMediaNotificationKey() {
+ return mMediaNotificationKey;
+ }
+
+ public MediaMetadata getMediaMetadata() {
+ return mMediaMetadata;
+ }
+
+ public void findAndUpdateMediaNotifications() {
+ boolean metaDataChanged = false;
+
+ synchronized (mPresenter.getNotificationData()) {
+ ArrayList<NotificationData.Entry> activeNotifications = mPresenter
+ .getNotificationData().getActiveNotifications();
+ final int N = activeNotifications.size();
+
+ // Promote the media notification with a controller in 'playing' state, if any.
+ NotificationData.Entry mediaNotification = null;
+ MediaController controller = null;
+ for (int i = 0; i < N; i++) {
+ final NotificationData.Entry entry = activeNotifications.get(i);
+
+ if (isMediaNotification(entry)) {
+ final MediaSession.Token token =
+ entry.notification.getNotification().extras.getParcelable(
+ Notification.EXTRA_MEDIA_SESSION);
+ if (token != null) {
+ MediaController aController = new MediaController(mContext, token);
+ if (PlaybackState.STATE_PLAYING ==
+ getMediaControllerPlaybackState(aController)) {
+ if (DEBUG_MEDIA) {
+ Log.v(TAG, "DEBUG_MEDIA: found mediastyle controller matching "
+ + entry.notification.getKey());
+ }
+ mediaNotification = entry;
+ controller = aController;
+ break;
+ }
+ }
+ }
+ }
+ if (mediaNotification == null) {
+ // Still nothing? OK, let's just look for live media sessions and see if they match
+ // one of our notifications. This will catch apps that aren't (yet!) using media
+ // notifications.
+
+ if (mMediaSessionManager != null) {
+ // TODO: Should this really be for all users?
+ final List<MediaController> sessions
+ = mMediaSessionManager.getActiveSessionsForUser(
+ null,
+ UserHandle.USER_ALL);
+
+ for (MediaController aController : sessions) {
+ if (PlaybackState.STATE_PLAYING ==
+ getMediaControllerPlaybackState(aController)) {
+ // now to see if we have one like this
+ final String pkg = aController.getPackageName();
+
+ for (int i = 0; i < N; i++) {
+ final NotificationData.Entry entry = activeNotifications.get(i);
+ if (entry.notification.getPackageName().equals(pkg)) {
+ if (DEBUG_MEDIA) {
+ Log.v(TAG, "DEBUG_MEDIA: found controller matching "
+ + entry.notification.getKey());
+ }
+ controller = aController;
+ mediaNotification = entry;
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if (controller != null && !sameSessions(mMediaController, controller)) {
+ // We have a new media session
+ clearCurrentMediaNotification();
+ mMediaController = controller;
+ mMediaController.registerCallback(mMediaListener);
+ mMediaMetadata = mMediaController.getMetadata();
+ if (DEBUG_MEDIA) {
+ Log.v(TAG, "DEBUG_MEDIA: insert listener, receive metadata: "
+ + mMediaMetadata);
+ }
+
+ if (mediaNotification != null) {
+ mMediaNotificationKey = mediaNotification.notification.getKey();
+ if (DEBUG_MEDIA) {
+ Log.v(TAG, "DEBUG_MEDIA: Found new media notification: key="
+ + mMediaNotificationKey + " controller=" + mMediaController);
+ }
+ }
+ metaDataChanged = true;
+ }
+ }
+
+ if (metaDataChanged) {
+ mPresenter.updateNotifications();
+ }
+ mPresenter.updateMediaMetaData(metaDataChanged, true);
+ }
+
+ public void clearCurrentMediaNotification() {
+ mMediaNotificationKey = null;
+ mMediaMetadata = null;
+ if (mMediaController != null) {
+ if (DEBUG_MEDIA) {
+ Log.v(TAG, "DEBUG_MEDIA: Disconnecting from old controller: "
+ + mMediaController.getPackageName());
+ }
+ mMediaController.unregisterCallback(mMediaListener);
+ }
+ mMediaController = null;
+ }
+
+ @Override
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ pw.print(" mMediaSessionManager=");
+ pw.println(mMediaSessionManager);
+ pw.print(" mMediaNotificationKey=");
+ pw.println(mMediaNotificationKey);
+ pw.print(" mMediaController=");
+ pw.print(mMediaController);
+ if (mMediaController != null) {
+ pw.print(" state=" + mMediaController.getPlaybackState());
+ }
+ pw.println();
+ pw.print(" mMediaMetadata=");
+ pw.print(mMediaMetadata);
+ if (mMediaMetadata != null) {
+ pw.print(" title=" + mMediaMetadata.getText(MediaMetadata.METADATA_KEY_TITLE));
+ }
+ pw.println();
+ }
+
+ private boolean isPlaybackActive(int state) {
+ return state != PlaybackState.STATE_STOPPED && state != PlaybackState.STATE_ERROR
+ && state != PlaybackState.STATE_NONE;
+ }
+
+ private boolean sameSessions(MediaController a, MediaController b) {
+ if (a == b) {
+ return true;
+ }
+ if (a == null) {
+ return false;
+ }
+ return a.controlsSameSession(b);
+ }
+
+ private int getMediaControllerPlaybackState(MediaController controller) {
+ if (controller != null) {
+ final PlaybackState playbackState = controller.getPlaybackState();
+ if (playbackState != null) {
+ return playbackState.getState();
+ }
+ }
+ return PlaybackState.STATE_NONE;
+ }
+
+ private boolean isMediaNotification(NotificationData.Entry entry) {
+ // TODO: confirm that there's a valid media key
+ return entry.getExpandedContentView() != null &&
+ entry.getExpandedContentView()
+ .findViewById(com.android.internal.R.id.media_actions) != null;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationPresenter.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationPresenter.java
new file mode 100644
index 0000000..1aca60c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationPresenter.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+package com.android.systemui.statusbar;
+
+import android.content.Intent;
+import android.service.notification.NotificationListenerService;
+
+/**
+ * An abstraction of something that presents notifications, e.g. StatusBar. Contains methods
+ * for both querying the state of the system (some modularised piece of functionality may
+ * want to act differently based on e.g. whether the presenter is visible to the user or not) and
+ * for affecting the state of the system (e.g. starting an intent, given that the presenter may
+ * want to perform some action before doing so).
+ */
+public interface NotificationPresenter {
+
+ /**
+ * Returns true if the presenter is not visible. For example, it may not be necessary to do
+ * animations if this returns true.
+ */
+ boolean isPresenterFullyCollapsed();
+
+ /**
+ * Returns true if the presenter is locked. For example, if the keyguard is active.
+ */
+ boolean isPresenterLocked();
+
+ /**
+ * Returns the current user id. This can change if the user is switched.
+ */
+ int getCurrentUserId();
+
+ /**
+ * Runs the given intent. The presenter may want to run some animations or close itself when
+ * this happens.
+ */
+ void startNotificationGutsIntent(Intent intent, int appUid);
+
+ /**
+ * Returns NotificationData.
+ */
+ NotificationData getNotificationData();
+
+ // TODO: Create NotificationEntryManager and move this method to there.
+ /**
+ * Signals that some notifications have changed, and NotificationPresenter should update itself.
+ */
+ void updateNotifications();
+
+ /**
+ * Refresh or remove lockscreen artwork from media metadata or the lockscreen wallpaper.
+ */
+ void updateMediaMetaData(boolean metaDataChanged, boolean allowEnterAnimation);
+
+ // TODO: Create NotificationUpdateHandler and move this method to there.
+ /**
+ * Removes a notification.
+ */
+ void removeNotification(String key, NotificationListenerService.RankingMap ranking);
+
+ // TODO: Create NotificationEntryManager and move this method to there.
+ /**
+ * Gets the latest ranking map.
+ */
+ NotificationListenerService.RankingMap getLatestRankingMap();
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/MessagingLayoutTransformState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/MessagingLayoutTransformState.java
index 4c2a2f5..fc420eb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/MessagingLayoutTransformState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/MessagingLayoutTransformState.java
@@ -383,6 +383,12 @@
}
}
+ @Override
+ public void prepareFadeIn() {
+ super.prepareFadeIn();
+ setVisible(true /* visible */, false /* force */);
+ }
+
private void resetTransformedView(View child) {
TransformState ownState = TransformState.createFrom(child, mTransformInfo);
ownState.resetTransformedView();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java
index 1bd90fa..149ec0b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java
@@ -20,7 +20,7 @@
import android.provider.Settings.Secure;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.app.NightDisplayController;
+import com.android.internal.app.ColorDisplayController;
import com.android.systemui.Dependency;
import com.android.systemui.Prefs;
import com.android.systemui.Prefs.Key;
@@ -78,8 +78,8 @@
}
if (!mAutoTracker.isAdded(NIGHT)
- && NightDisplayController.isAvailable(mContext)) {
- Dependency.get(NightDisplayController.class).setListener(mNightDisplayCallback);
+ && ColorDisplayController.isAvailable(mContext)) {
+ Dependency.get(ColorDisplayController.class).setListener(mColorDisplayCallback);
}
}
@@ -89,7 +89,7 @@
Dependency.get(HotspotController.class).removeCallback(mHotspotCallback);
Dependency.get(DataSaverController.class).removeCallback(mDataSaverListener);
Dependency.get(ManagedProfileController.class).removeCallback(mProfileCallback);
- Dependency.get(NightDisplayController.class).setListener(null);
+ Dependency.get(ColorDisplayController.class).setListener(null);
}
private final ManagedProfileController.Callback mProfileCallback =
@@ -139,8 +139,8 @@
};
@VisibleForTesting
- final NightDisplayController.Callback mNightDisplayCallback =
- new NightDisplayController.Callback() {
+ final ColorDisplayController.Callback mColorDisplayCallback =
+ new ColorDisplayController.Callback() {
@Override
public void onActivated(boolean activated) {
if (activated) {
@@ -150,8 +150,8 @@
@Override
public void onAutoModeChanged(int autoMode) {
- if (autoMode == NightDisplayController.AUTO_MODE_CUSTOM
- || autoMode == NightDisplayController.AUTO_MODE_TWILIGHT) {
+ if (autoMode == ColorDisplayController.AUTO_MODE_CUSTOM
+ || autoMode == ColorDisplayController.AUTO_MODE_TWILIGHT) {
addNightTile();
}
}
@@ -160,7 +160,7 @@
if (mAutoTracker.isAdded(NIGHT)) return;
mHost.addTile(NIGHT);
mAutoTracker.setTileAdded(NIGHT);
- mHandler.post(() -> Dependency.get(NightDisplayController.class)
+ mHandler.post(() -> Dependency.get(ColorDisplayController.class)
.setListener(null));
}
};
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarGestureHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarGestureHelper.java
index 3ba9ac6..4180884 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarGestureHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarGestureHelper.java
@@ -35,7 +35,7 @@
import com.android.systemui.R;
import com.android.systemui.RecentsComponent;
import com.android.systemui.plugins.statusbar.phone.NavGesture.GestureHelper;
-import com.android.systemui.shared.recents.model.IOverviewProxy;
+import com.android.systemui.shared.recents.IOverviewProxy;
import com.android.systemui.stackdivider.Divider;
import com.android.systemui.tuner.TunerService;
@@ -112,20 +112,26 @@
mIsRTL = isRTL;
}
- public boolean onInterceptTouchEvent(MotionEvent event) {
+ private boolean proxyMotionEvents(MotionEvent event) {
final IOverviewProxy overviewProxy = mOverviewEventSender.getProxy();
if (overviewProxy != null) {
mNavigationBarView.requestUnbufferedDispatch(event);
try {
overviewProxy.onMotionEvent(event);
+ return true;
} catch (RemoteException e) {
Log.e(TAG, "Callback failed", e);
}
}
+ return false;
+ }
- // If we move more than a fixed amount, then start capturing for the
- // task switcher detector
- mTaskSwitcherDetector.onTouchEvent(event);
+ public boolean onInterceptTouchEvent(MotionEvent event) {
+ if (!proxyMotionEvents(event)) {
+ // If we move more than a fixed amount, then start capturing for the
+ // task switcher detector, disabled when proxying motion events to launcher service
+ mTaskSwitcherDetector.onTouchEvent(event);
+ }
int action = event.getAction();
switch (action & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN: {
@@ -289,7 +295,7 @@
}
public boolean onTouchEvent(MotionEvent event) {
- boolean result = mTaskSwitcherDetector.onTouchEvent(event);
+ boolean result = proxyMotionEvents(event) || mTaskSwitcherDetector.onTouchEvent(event);
if (mDockWindowEnabled) {
result |= handleDockWindowEvent(event);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
index 094129c..4e7f205 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -47,6 +47,8 @@
import com.android.settingslib.Utils;
import com.android.systemui.Dependency;
import com.android.systemui.DockedStackExistsListener;
+import com.android.systemui.OverviewProxyService;
+import com.android.systemui.OverviewProxyService.OverviewProxyListener;
import com.android.systemui.R;
import com.android.systemui.RecentsComponent;
import com.android.systemui.plugins.PluginListener;
@@ -196,6 +198,9 @@
}
}
+ private final OverviewProxyListener mOverviewProxyListener =
+ isConnected -> setSlippery(!isConnected);
+
public NavigationBarView(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -531,6 +536,24 @@
}
}
+ private void setSlippery(boolean slippery) {
+ boolean changed = false;
+ final ViewGroup navbarView = ((ViewGroup) getParent());
+ final WindowManager.LayoutParams lp = (WindowManager.LayoutParams) navbarView
+ .getLayoutParams();
+ if (slippery && (lp.flags & WindowManager.LayoutParams.FLAG_SLIPPERY) == 0) {
+ lp.flags |= WindowManager.LayoutParams.FLAG_SLIPPERY;
+ changed = true;
+ } else if (!slippery && (lp.flags & WindowManager.LayoutParams.FLAG_SLIPPERY) != 0) {
+ lp.flags &= ~WindowManager.LayoutParams.FLAG_SLIPPERY;
+ changed = true;
+ }
+ if (changed) {
+ WindowManager wm = (WindowManager)getContext().getSystemService(Context.WINDOW_SERVICE);
+ wm.updateViewLayout(navbarView, lp);
+ }
+ }
+
public void setMenuVisibility(final boolean show) {
setMenuVisibility(show, false);
}
@@ -756,6 +779,7 @@
onPluginDisconnected(null); // Create default gesture helper
Dependency.get(PluginManager.class).addPluginListener(this,
NavGesture.class, false /* Only one */);
+ Dependency.get(OverviewProxyService.class).addCallback(mOverviewProxyListener);
}
@Override
@@ -765,6 +789,7 @@
if (mGestureHelper != null) {
mGestureHelper.destroy();
}
+ Dependency.get(OverviewProxyService.class).removeCallback(mOverviewProxyListener);
}
@Override
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 af03440..86a8f41 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -596,7 +596,7 @@
mLastCameraLaunchSource = KeyguardBottomAreaView.CAMERA_LAUNCH_SOURCE_AFFORDANCE;
}
closeQs();
- mStatusBar.closeAndSaveGuts(true /* leavebehind */, true /* force */,
+ mStatusBar.getGutsManager().closeAndSaveGuts(true /* leavebehind */, true /* force */,
true /* controls */, -1 /* x */, -1 /* y */, true /* resetMenu */);
mNotificationStackScroller.setOverScrollAmount(0f, true /* onTop */, false /* animate */,
true /* cancelAnimators */);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index 46e3aa1..e3ed581 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -20,11 +20,12 @@
import static android.app.StatusBarManager.WINDOW_STATE_HIDDEN;
import static android.app.StatusBarManager.WINDOW_STATE_SHOWING;
import static android.app.StatusBarManager.windowStateToString;
-
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY;
+
import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_ASLEEP;
import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_AWAKE;
import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_WAKING;
+import static com.android.systemui.statusbar.NotificationMediaManager.DEBUG_MEDIA;
import static com.android.systemui.statusbar.notification.NotificationInflater.InflationCallback;
import static com.android.systemui.statusbar.phone.BarTransitions.MODE_LIGHTS_OUT;
import static com.android.systemui.statusbar.phone.BarTransitions.MODE_LIGHTS_OUT_TRANSPARENT;
@@ -40,10 +41,8 @@
import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.ActivityOptions;
-import android.app.INotificationManager;
import android.app.KeyguardManager;
import android.app.Notification;
-import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.RemoteInput;
@@ -80,10 +79,7 @@
import android.graphics.drawable.Drawable;
import android.media.AudioAttributes;
import android.media.MediaMetadata;
-import android.media.session.MediaController;
-import android.media.session.MediaSession;
import android.media.session.MediaSessionManager;
-import android.media.session.PlaybackState;
import android.metrics.LogMaker;
import android.net.Uri;
import android.os.AsyncTask;
@@ -115,14 +111,12 @@
import android.util.SparseArray;
import android.util.SparseBooleanArray;
import android.view.Display;
-import android.view.HapticFeedbackConstants;
import android.view.IWindowManager;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.ThreadedRenderer;
import android.view.View;
-import android.view.ViewAnimationUtils;
import android.view.ViewGroup;
import android.view.ViewParent;
import android.view.ViewTreeObserver;
@@ -181,7 +175,6 @@
import com.android.systemui.keyguard.WakefulnessLifecycle;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.qs.QS;
-import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin.MenuItem;
import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper.SnoozeOption;
import com.android.systemui.qs.QSFragment;
import com.android.systemui.qs.QSPanel;
@@ -207,10 +200,11 @@
import com.android.systemui.statusbar.KeyguardIndicationController;
import com.android.systemui.statusbar.NotificationData;
import com.android.systemui.statusbar.NotificationData.Entry;
-import com.android.systemui.statusbar.NotificationGuts;
+import com.android.systemui.statusbar.NotificationGutsManager;
import com.android.systemui.statusbar.NotificationInfo;
+import com.android.systemui.statusbar.NotificationMediaManager;
+import com.android.systemui.statusbar.NotificationPresenter;
import com.android.systemui.statusbar.NotificationShelf;
-import com.android.systemui.statusbar.NotificationSnooze;
import com.android.systemui.statusbar.RemoteInputController;
import com.android.systemui.statusbar.ScrimView;
import com.android.systemui.statusbar.SignalClusterView;
@@ -243,7 +237,6 @@
import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
import com.android.systemui.statusbar.stack.NotificationStackScrollLayout
.OnChildLocationsChangedListener;
-import com.android.systemui.statusbar.stack.StackStateAnimator;
import com.android.systemui.util.NotificationChannels;
import com.android.systemui.util.leak.LeakDetector;
import com.android.systemui.volume.VolumeComponent;
@@ -255,10 +248,8 @@
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
-import java.util.HashSet;
import java.util.List;
import java.util.Map;
-import java.util.Set;
import java.util.Stack;
public class StatusBar extends SystemUI implements DemoMode,
@@ -267,7 +258,7 @@
ActivatableNotificationView.OnActivatedListener,
ExpandableNotificationRow.ExpansionLogger, NotificationData.Environment,
ExpandableNotificationRow.OnExpandClickListener, InflationCallback,
- ColorExtractor.OnColorsChangedListener, ConfigurationListener {
+ ColorExtractor.OnColorsChangedListener, ConfigurationListener, NotificationPresenter {
public static final boolean MULTIUSER_DEBUG = false;
public static final boolean ENABLE_REMOTE_INPUT =
@@ -287,9 +278,6 @@
protected static final boolean ENABLE_HEADS_UP = true;
protected static final String SETTING_HEADS_UP_TICKER = "ticker_gets_heads_up";
- // Must match constant in Settings. Used to highlight preferences when linking to Settings.
- private static final String EXTRA_FRAGMENT_ARG_KEY = ":settings:fragment_args_key";
-
private static final String PERMISSION_SELF = "com.android.systemui.permission.SELF";
// Should match the values in PhoneWindowManager
@@ -308,7 +296,6 @@
public static final boolean SPEW = false;
public static final boolean DUMPTRUCK = true; // extra dumpsys info
public static final boolean DEBUG_GESTURES = false;
- public static final boolean DEBUG_MEDIA = false;
public static final boolean DEBUG_MEDIA_FAKE_ARTWORK = false;
public static final boolean DEBUG_CAMERA_LIFT = false;
@@ -458,6 +445,8 @@
private final int[] mAbsPos = new int[2];
private final ArrayList<Runnable> mPostCollapseRunnables = new ArrayList<>();
+ private NotificationGutsManager mGutsManager;
+
// for disabling the status bar
private int mDisabled1 = 0;
private int mDisabled2 = 0;
@@ -553,31 +542,7 @@
protected final PorterDuffXfermode mSrcOverXferMode =
new PorterDuffXfermode(PorterDuff.Mode.SRC_OVER);
- private MediaSessionManager mMediaSessionManager;
- private MediaController mMediaController;
- private String mMediaNotificationKey;
- private MediaMetadata mMediaMetadata;
- private final MediaController.Callback mMediaListener = new MediaController.Callback() {
- @Override
- public void onPlaybackStateChanged(PlaybackState state) {
- super.onPlaybackStateChanged(state);
- if (DEBUG_MEDIA) Log.v(TAG, "DEBUG_MEDIA: onPlaybackStateChanged: " + state);
- if (state != null) {
- if (!isPlaybackActive(state.getState())) {
- clearCurrentMediaNotification();
- updateMediaMetaData(true, true);
- }
- }
- }
-
- @Override
- public void onMetadataChanged(MediaMetadata metadata) {
- super.onMetadataChanged(metadata);
- if (DEBUG_MEDIA) Log.v(TAG, "DEBUG_MEDIA: onMetadataChanged: " + metadata);
- mMediaMetadata = metadata;
- updateMediaMetaData(true, true);
- }
- };
+ private NotificationMediaManager mMediaManager;
/** Keys of notifications currently visible to the user. */
private final ArraySet<NotificationVisibility> mCurrentlyVisibleNotifications =
@@ -700,7 +665,6 @@
private final HashMap<String, Entry> mPendingNotifications = new HashMap<>();
private boolean mClearAllEnabled;
@Nullable private View mAmbientIndicationContainer;
- private String mKeyToRemoveOnGutsClosed;
private SysuiColorExtractor mColorExtractor;
private ForegroundServiceController mForegroundServiceController;
private ScreenLifecycle mScreenLifecycle;
@@ -817,6 +781,8 @@
mKeyguardManager = (KeyguardManager) mContext.getSystemService(Context.KEYGUARD_SERVICE);
mLockPatternUtils = new LockPatternUtils(mContext);
+ mMediaManager = new NotificationMediaManager(this, mContext);
+
// Connect in to the status bar manager service
mCommandQueue = getComponent(CommandQueue.class);
mCommandQueue.addCallbacks(this);
@@ -903,16 +869,8 @@
Slog.e(TAG, "Failed to register VR mode state listener: " + e);
}
- mNonBlockablePkgs = new HashSet<>();
- Collections.addAll(mNonBlockablePkgs, res.getStringArray(
- com.android.internal.R.array.config_nonBlockableNotificationPackages));
// end old BaseStatusBar.start().
- mMediaSessionManager
- = (MediaSessionManager) mContext.getSystemService(Context.MEDIA_SESSION_SERVICE);
- // TODO: use MediaSessionManager.SessionListener to hook us up to future updates
- // in session state
-
// Lastly, call to the icon policy to install/update all the icons.
mIconPolicy = new PhoneStatusBarPolicy(mContext, mIconController);
mSettingsObserver.onChange(false); // set up
@@ -960,6 +918,8 @@
// into fragments, but the rest here, it leaves some awkward lifecycle and whatnot.
mNotificationPanel = mStatusBarWindow.findViewById(R.id.notification_panel);
mStackScroller = mStatusBarWindow.findViewById(R.id.notification_stack_scroller);
+ mGutsManager = new NotificationGutsManager(this, mStackScroller,
+ mCheckSaveListener, mContext);
mNotificationPanel.setStatusBar(this);
mNotificationPanel.setGroupManager(mGroupManager);
mAboveShelfObserver = new AboveShelfObserver(mStackScroller);
@@ -1302,12 +1262,12 @@
ArrayList<Entry> activeNotifications = mNotificationData.getActiveNotifications();
for (int i = 0; i < activeNotifications.size(); i++) {
Entry entry = activeNotifications.get(i);
- boolean exposedGuts = mNotificationGutsExposed != null
- && entry.row.getGuts() == mNotificationGutsExposed;
+ boolean exposedGuts = mGutsManager.getExposedGuts() != null
+ && entry.row.getGuts() == mGutsManager.getExposedGuts();
entry.row.onDensityOrFontScaleChanged();
if (exposedGuts) {
- mNotificationGutsExposed = entry.row.getGuts();
- bindGuts(entry.row, mGutsMenuItem);
+ mGutsManager.setExposedGuts(entry.row.getGuts());
+ mGutsManager.bindGuts(entry.row);
}
}
}
@@ -1672,6 +1632,7 @@
updateNotifications();
}
+ @Override
public void removeNotification(String key, RankingMap ranking) {
boolean deferRemoval = false;
abortExistingInflation(key);
@@ -1685,10 +1646,8 @@
|| !mVisualStabilityManager.isReorderingAllowed();
deferRemoval = !mHeadsUpManager.removeNotification(key, ignoreEarliestRemovalTime);
}
- if (key.equals(mMediaNotificationKey)) {
- clearCurrentMediaNotification();
- updateMediaMetaData(true, true);
- }
+ mMediaManager.onNotificationRemoved(key);
+
Entry entry = mNotificationData.get(key);
if (FORCE_REMOTE_INPUT_HISTORY && mRemoteInputController.isSpinning(key)
&& entry.row != null && !entry.row.isDismissed()) {
@@ -1744,12 +1703,12 @@
mRemoteInputEntriesToRemoveOnCollapse.add(entry);
return;
}
- if (entry != null && mNotificationGutsExposed != null
- && mNotificationGutsExposed == entry.row.getGuts() && entry.row.getGuts() != null
- && !entry.row.getGuts().isLeavebehind()) {
+ if (entry != null && mGutsManager.getExposedGuts() != null
+ && mGutsManager.getExposedGuts() == entry.row.getGuts()
+ && entry.row.getGuts() != null && !entry.row.getGuts().isLeavebehind()) {
Log.w(TAG, "Keeping notification because it's showing guts. " + key);
mLatestRankingMap = ranking;
- mKeyToRemoveOnGutsClosed = key;
+ mGutsManager.setKeyToRemoveOnGutsClosed(key);
return;
}
@@ -2142,7 +2101,8 @@
return entry.row.getParent() instanceof NotificationStackScrollLayout;
}
- protected void updateNotifications() {
+ @Override
+ public void updateNotifications() {
mNotificationData.filterAndSort();
updateNotificationShade();
@@ -2184,136 +2144,9 @@
}
}
- findAndUpdateMediaNotifications();
+ mMediaManager.findAndUpdateMediaNotifications();
}
- public void findAndUpdateMediaNotifications() {
- boolean metaDataChanged = false;
-
- synchronized (mNotificationData) {
- ArrayList<Entry> activeNotifications = mNotificationData.getActiveNotifications();
- final int N = activeNotifications.size();
-
- // Promote the media notification with a controller in 'playing' state, if any.
- Entry mediaNotification = null;
- MediaController controller = null;
- for (int i = 0; i < N; i++) {
- final Entry entry = activeNotifications.get(i);
-
- if (isMediaNotification(entry)) {
- final MediaSession.Token token =
- entry.notification.getNotification().extras.getParcelable(
- Notification.EXTRA_MEDIA_SESSION);
- if (token != null) {
- MediaController aController = new MediaController(mContext, token);
- if (PlaybackState.STATE_PLAYING ==
- getMediaControllerPlaybackState(aController)) {
- if (DEBUG_MEDIA) {
- Log.v(TAG, "DEBUG_MEDIA: found mediastyle controller matching "
- + entry.notification.getKey());
- }
- mediaNotification = entry;
- controller = aController;
- break;
- }
- }
- }
- }
- if (mediaNotification == null) {
- // Still nothing? OK, let's just look for live media sessions and see if they match
- // one of our notifications. This will catch apps that aren't (yet!) using media
- // notifications.
-
- if (mMediaSessionManager != null) {
- final List<MediaController> sessions
- = mMediaSessionManager.getActiveSessionsForUser(
- null,
- UserHandle.USER_ALL);
-
- for (MediaController aController : sessions) {
- if (PlaybackState.STATE_PLAYING ==
- getMediaControllerPlaybackState(aController)) {
- // now to see if we have one like this
- final String pkg = aController.getPackageName();
-
- for (int i = 0; i < N; i++) {
- final Entry entry = activeNotifications.get(i);
- if (entry.notification.getPackageName().equals(pkg)) {
- if (DEBUG_MEDIA) {
- Log.v(TAG, "DEBUG_MEDIA: found controller matching "
- + entry.notification.getKey());
- }
- controller = aController;
- mediaNotification = entry;
- break;
- }
- }
- }
- }
- }
- }
-
- if (controller != null && !sameSessions(mMediaController, controller)) {
- // We have a new media session
- clearCurrentMediaNotification();
- mMediaController = controller;
- mMediaController.registerCallback(mMediaListener);
- mMediaMetadata = mMediaController.getMetadata();
- if (DEBUG_MEDIA) {
- Log.v(TAG, "DEBUG_MEDIA: insert listener, receive metadata: "
- + mMediaMetadata);
- }
-
- if (mediaNotification != null) {
- mMediaNotificationKey = mediaNotification.notification.getKey();
- if (DEBUG_MEDIA) {
- Log.v(TAG, "DEBUG_MEDIA: Found new media notification: key="
- + mMediaNotificationKey + " controller=" + mMediaController);
- }
- }
- metaDataChanged = true;
- }
- }
-
- if (metaDataChanged) {
- updateNotifications();
- }
- updateMediaMetaData(metaDataChanged, true);
- }
-
- private int getMediaControllerPlaybackState(MediaController controller) {
- if (controller != null) {
- final PlaybackState playbackState = controller.getPlaybackState();
- if (playbackState != null) {
- return playbackState.getState();
- }
- }
- return PlaybackState.STATE_NONE;
- }
-
- private boolean isPlaybackActive(int state) {
- return state != PlaybackState.STATE_STOPPED && state != PlaybackState.STATE_ERROR
- && state != PlaybackState.STATE_NONE;
- }
-
- private void clearCurrentMediaNotification() {
- mMediaNotificationKey = null;
- mMediaMetadata = null;
- if (mMediaController != null) {
- if (DEBUG_MEDIA) {
- Log.v(TAG, "DEBUG_MEDIA: Disconnecting from old controller: "
- + mMediaController.getPackageName());
- }
- mMediaController.unregisterCallback(mMediaListener);
- }
- mMediaController = null;
- }
-
- private boolean sameSessions(MediaController a, MediaController b) {
- if (a == b) return true;
- if (a == null) return false;
- return a.controlsSameSession(b);
- }
/**
* Hide the album artwork that is fading out and release its bitmap.
@@ -2330,9 +2163,11 @@
}
};
+ // TODO: Move this to NotificationMediaManager.
/**
* Refresh or remove lockscreen artwork from media metadata or the lockscreen wallpaper.
*/
+ @Override
public void updateMediaMetaData(boolean metaDataChanged, boolean allowEnterAnimation) {
Trace.beginSection("StatusBar#updateMediaMetaData");
if (!SHOW_LOCKSCREEN_MEDIA_ARTWORK) {
@@ -2351,19 +2186,22 @@
return;
}
+ MediaMetadata mediaMetadata = mMediaManager.getMediaMetadata();
+
if (DEBUG_MEDIA) {
- Log.v(TAG, "DEBUG_MEDIA: updating album art for notification " + mMediaNotificationKey
- + " metadata=" + mMediaMetadata
+ Log.v(TAG, "DEBUG_MEDIA: updating album art for notification "
+ + mMediaManager.getMediaNotificationKey()
+ + " metadata=" + mediaMetadata
+ " metaDataChanged=" + metaDataChanged
+ " state=" + mState);
}
Drawable artworkDrawable = null;
- if (mMediaMetadata != null) {
+ if (mediaMetadata != null) {
Bitmap artworkBitmap = null;
- artworkBitmap = mMediaMetadata.getBitmap(MediaMetadata.METADATA_KEY_ART);
+ artworkBitmap = mediaMetadata.getBitmap(MediaMetadata.METADATA_KEY_ART);
if (artworkBitmap == null) {
- artworkBitmap = mMediaMetadata.getBitmap(MediaMetadata.METADATA_KEY_ALBUM_ART);
+ artworkBitmap = mediaMetadata.getBitmap(MediaMetadata.METADATA_KEY_ALBUM_ART);
// might still be null
}
if (artworkBitmap != null) {
@@ -2636,7 +2474,7 @@
@Override // NotificationData.Environment
public String getCurrentMediaNotificationKey() {
- return mMediaNotificationKey;
+ return mMediaManager.getMediaNotificationKey();
}
public boolean isScrimSrcModeEnabled() {
@@ -3101,8 +2939,8 @@
mStatusBarWindowManager.setForceStatusBarVisible(false);
// Close any guts that might be visible
- closeAndSaveGuts(true /* removeLeavebehind */, true /* force */, true /* removeControls */,
- -1 /* x */, -1 /* y */, true /* resetMenu */);
+ mGutsManager.closeAndSaveGuts(true /* removeLeavebehind */, true /* force */,
+ true /* removeControls */, -1 /* x */, -1 /* y */, true /* resetMenu */);
runPostCollapseRunnables();
setInteracting(StatusBarManager.WINDOW_STATUS_BAR, false);
@@ -3449,28 +3287,18 @@
pw.println(Settings.Global.zenModeToString(mZenMode));
pw.print(" mUseHeadsUp=");
pw.println(mUseHeadsUp);
- pw.print(" mKeyToRemoveOnGutsClosed=");
- pw.println(mKeyToRemoveOnGutsClosed);
+ pw.print(" mGutsManager: ");
+ if (mGutsManager != null) {
+ mGutsManager.dump(fd, pw, args);
+ }
if (mStatusBarView != null) {
dumpBarTransitions(pw, "mStatusBarView", mStatusBarView.getBarTransitions());
}
- pw.print(" mMediaSessionManager=");
- pw.println(mMediaSessionManager);
- pw.print(" mMediaNotificationKey=");
- pw.println(mMediaNotificationKey);
- pw.print(" mMediaController=");
- pw.print(mMediaController);
- if (mMediaController != null) {
- pw.print(" state=" + mMediaController.getPlaybackState());
+ pw.println(" mMediaManager: ");
+ if (mMediaManager != null) {
+ mMediaManager.dump(fd, pw, args);
}
- pw.println();
- pw.print(" mMediaMetadata=");
- pw.print(mMediaMetadata);
- if (mMediaMetadata != null) {
- pw.print(" title=" + mMediaMetadata.getText(MediaMetadata.METADATA_KEY_TITLE));
- }
- pw.println();
pw.println(" Panels: ");
if (mNotificationPanel != null) {
@@ -3792,7 +3620,7 @@
mReinflateNotificationsOnUserSwitched = false;
}
updateNotificationShade();
- clearCurrentMediaNotification();
+ mMediaManager.clearCurrentMediaNotification();
setLockscreenUser(newUserId);
}
@@ -3872,10 +3700,10 @@
if (visibleToUser) {
boolean pinnedHeadsUp = mHeadsUpManager.hasPinnedHeadsUp();
boolean clearNotificationEffects =
- !isPanelFullyCollapsed() &&
+ !isPresenterFullyCollapsed() &&
(mState == StatusBarState.SHADE || mState == StatusBarState.SHADE_LOCKED);
int notificationLoad = mNotificationData.getActiveNotifications().size();
- if (pinnedHeadsUp && isPanelFullyCollapsed()) {
+ if (pinnedHeadsUp && isPresenterFullyCollapsed()) {
notificationLoad = 1;
}
mBarService.onPanelRevealed(clearNotificationEffects, notificationLoad);
@@ -4152,7 +3980,8 @@
return mState;
}
- public boolean isPanelFullyCollapsed() {
+ @Override
+ public boolean isPresenterFullyCollapsed() {
return mNotificationPanel.isFullyCollapsed();
}
@@ -4735,7 +4564,7 @@
public void onClosingFinished() {
runPostCollapseRunnables();
- if (!isPanelFullyCollapsed()) {
+ if (!isPresenterFullyCollapsed()) {
// if we set it not to be focusable when collapsing, we have to undo it when we aborted
// the closing
mStatusBarWindowManager.setStatusBarFocusable(true);
@@ -5600,10 +5429,6 @@
protected int mZenMode;
- // which notification is currently being longpress-examined by the user
- private NotificationGuts mNotificationGutsExposed;
- private MenuItem mGutsMenuItem;
-
protected NotificationShelf mNotificationShelf;
protected DismissView mDismissView;
protected EmptyShadeView mEmptyShadeView;
@@ -5614,8 +5439,6 @@
protected boolean mVrMode;
- private Set<String> mNonBlockablePkgs;
-
public boolean isDeviceInteractive() {
return mDeviceInteractive;
}
@@ -6147,26 +5970,8 @@
return mGroupManager;
}
- public boolean isMediaNotification(NotificationData.Entry entry) {
- // TODO: confirm that there's a valid media key
- return entry.getExpandedContentView() != null &&
- entry.getExpandedContentView()
- .findViewById(com.android.internal.R.id.media_actions) != null;
- }
-
- // The button in the guts that links to the system notification settings for that app
- private void startAppNotificationSettingsActivity(String packageName, final int appUid,
- final NotificationChannel channel) {
- final Intent intent = new Intent(Settings.ACTION_APP_NOTIFICATION_SETTINGS);
- intent.putExtra(Settings.EXTRA_APP_PACKAGE, packageName);
- intent.putExtra(Settings.EXTRA_APP_UID, appUid);
- if (channel != null) {
- intent.putExtra(EXTRA_FRAGMENT_ARG_KEY, channel.getId());
- }
- startNotificationGutsIntent(intent, appUid);
- }
-
- private void startNotificationGutsIntent(final Intent intent, final int appUid) {
+ @Override
+ public void startNotificationGutsIntent(final Intent intent, final int appUid) {
dismissKeyguardThenExecute(() -> {
AsyncTask.execute(() -> {
TaskStackBuilder.create(mContext)
@@ -6189,231 +5994,8 @@
}
}
- private void bindGuts(final ExpandableNotificationRow row, MenuItem item) {
- row.inflateGuts();
- row.setGutsView(item);
- final StatusBarNotification sbn = row.getStatusBarNotification();
- row.setTag(sbn.getPackageName());
- final NotificationGuts guts = row.getGuts();
- guts.setClosedListener((NotificationGuts g) -> {
- if (!g.willBeRemoved() && !row.isRemoved()) {
- mStackScroller.onHeightChanged(row, !isPanelFullyCollapsed() /* needsAnimation */);
- }
- if (mNotificationGutsExposed == g) {
- mNotificationGutsExposed = null;
- mGutsMenuItem = null;
- }
- String key = sbn.getKey();
- if (key.equals(mKeyToRemoveOnGutsClosed)) {
- mKeyToRemoveOnGutsClosed = null;
- removeNotification(key, mLatestRankingMap);
- }
- });
-
- View gutsView = item.getGutsView();
- if (gutsView instanceof NotificationSnooze) {
- NotificationSnooze snoozeGuts = (NotificationSnooze) gutsView;
- snoozeGuts.setSnoozeListener(mStackScroller.getSwipeActionHelper());
- snoozeGuts.setStatusBarNotification(sbn);
- snoozeGuts.setSnoozeOptions(row.getEntry().snoozeCriteria);
- guts.setHeightChangedListener((NotificationGuts g) -> {
- mStackScroller.onHeightChanged(row, row.isShown() /* needsAnimation */);
- });
- }
-
- if (gutsView instanceof NotificationInfo) {
- final UserHandle userHandle = sbn.getUser();
- PackageManager pmUser = getPackageManagerForUser(mContext,
- userHandle.getIdentifier());
- final INotificationManager iNotificationManager = INotificationManager.Stub.asInterface(
- ServiceManager.getService(Context.NOTIFICATION_SERVICE));
- final String pkg = sbn.getPackageName();
- NotificationInfo info = (NotificationInfo) gutsView;
- // Settings link is only valid for notifications that specify a user, unless this is the
- // system user.
- NotificationInfo.OnSettingsClickListener onSettingsClick = null;
- if (!userHandle.equals(UserHandle.ALL) || mCurrentUserId == UserHandle.USER_SYSTEM) {
- onSettingsClick = (View v, NotificationChannel channel, int appUid) -> {
- mMetricsLogger.action(MetricsEvent.ACTION_NOTE_INFO);
- guts.resetFalsingCheck();
- startAppNotificationSettingsActivity(pkg, appUid, channel);
- };
- }
- final NotificationInfo.OnAppSettingsClickListener onAppSettingsClick = (View v,
- Intent intent) -> {
- mMetricsLogger.action(MetricsEvent.ACTION_APP_NOTE_SETTINGS);
- guts.resetFalsingCheck();
- startNotificationGutsIntent(intent, sbn.getUid());
- };
- final View.OnClickListener onDoneClick = (View v) -> {
- saveAndCloseNotificationMenu(row, guts, v);
- };
- final NotificationInfo.CheckSaveListener checkSaveListener =
- (Runnable saveImportance) -> {
- // If the user has security enabled, show challenge if the setting is changed.
- if (isLockscreenPublicMode(userHandle.getIdentifier())
- && (mState == StatusBarState.KEYGUARD
- || mState == StatusBarState.SHADE_LOCKED)) {
- onLockedNotificationImportanceChange(() -> {
- saveImportance.run();
- return true;
- });
- } else {
- saveImportance.run();
- }
- };
-
- ArraySet<NotificationChannel> channels = new ArraySet<>();
- channels.add(row.getEntry().channel);
- if (row.isSummaryWithChildren()) {
- // If this is a summary, then add in the children notification channels for the
- // same user and pkg.
- final List<ExpandableNotificationRow> childrenRows = row.getNotificationChildren();
- final int numChildren = childrenRows.size();
- for (int i = 0; i < numChildren; i++) {
- final ExpandableNotificationRow childRow = childrenRows.get(i);
- final NotificationChannel childChannel = childRow.getEntry().channel;
- final StatusBarNotification childSbn = childRow.getStatusBarNotification();
- if (childSbn.getUser().equals(userHandle) &&
- childSbn.getPackageName().equals(pkg)) {
- channels.add(childChannel);
- }
- }
- }
- try {
- info.bindNotification(pmUser, iNotificationManager, pkg, new ArrayList(channels),
- row.getEntry().channel.getImportance(), sbn, onSettingsClick,
- onAppSettingsClick, onDoneClick, checkSaveListener,
- mNonBlockablePkgs);
- } catch (RemoteException e) {
- Log.e(TAG, e.toString());
- }
- }
- }
-
- private void saveAndCloseNotificationMenu(
- ExpandableNotificationRow row, NotificationGuts guts, View done) {
- guts.resetFalsingCheck();
- int[] rowLocation = new int[2];
- int[] doneLocation = new int[2];
- row.getLocationOnScreen(rowLocation);
- done.getLocationOnScreen(doneLocation);
-
- final int centerX = done.getWidth() / 2;
- final int centerY = done.getHeight() / 2;
- final int x = doneLocation[0] - rowLocation[0] + centerX;
- final int y = doneLocation[1] - rowLocation[1] + centerY;
- closeAndSaveGuts(false /* removeLeavebehind */, false /* force */,
- true /* removeControls */, x, y, true /* resetMenu */);
- }
-
protected ExpandableNotificationRow.LongPressListener getNotificationLongClicker() {
- return new ExpandableNotificationRow.LongPressListener() {
- @Override
- public boolean onLongPress(View v, final int x, final int y,
- MenuItem item) {
- if (!(v instanceof ExpandableNotificationRow)) {
- return false;
- }
-
- if (v.getWindowToken() == null) {
- Log.e(TAG, "Trying to show notification guts, but not attached to window");
- return false;
- }
-
- final ExpandableNotificationRow row = (ExpandableNotificationRow) v;
- if (row.isDark()) {
- return false;
- }
- v.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
- if (row.areGutsExposed()) {
- closeAndSaveGuts(false /* removeLeavebehind */, false /* force */,
- true /* removeControls */, -1 /* x */, -1 /* y */,
- true /* resetMenu */);
- return true;
- }
- bindGuts(row, item);
- NotificationGuts guts = row.getGuts();
-
- // Assume we are a status_bar_notification_row
- if (guts == null) {
- // This view has no guts. Examples are the more card or the dismiss all view
- return false;
- }
-
- mMetricsLogger.action(MetricsEvent.ACTION_NOTE_CONTROLS);
-
- // ensure that it's laid but not visible until actually laid out
- guts.setVisibility(View.INVISIBLE);
- // Post to ensure the the guts are properly laid out.
- guts.post(new Runnable() {
- @Override
- public void run() {
- if (row.getWindowToken() == null) {
- Log.e(TAG, "Trying to show notification guts, but not attached to "
- + "window");
- return;
- }
- closeAndSaveGuts(true /* removeLeavebehind */, true /* force */,
- true /* removeControls */, -1 /* x */, -1 /* y */,
- false /* resetMenu */);
- guts.setVisibility(View.VISIBLE);
- final double horz = Math.max(guts.getWidth() - x, x);
- final double vert = Math.max(guts.getHeight() - y, y);
- final float r = (float) Math.hypot(horz, vert);
- final Animator a
- = ViewAnimationUtils.createCircularReveal(guts, x, y, 0, r);
- a.setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
- a.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN);
- a.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- super.onAnimationEnd(animation);
- // Move the notification view back over the menu
- row.resetTranslation();
- }
- });
- a.start();
- final boolean needsFalsingProtection =
- (mState == StatusBarState.KEYGUARD &&
- !mAccessibilityManager.isTouchExplorationEnabled());
- guts.setExposed(true /* exposed */, needsFalsingProtection);
- row.closeRemoteInput();
- mStackScroller.onHeightChanged(row, true /* needsAnimation */);
- mNotificationGutsExposed = guts;
- mGutsMenuItem = item;
- }
- });
- return true;
- }
- };
- }
-
- /**
- * Returns the exposed NotificationGuts or null if none are exposed.
- */
- public NotificationGuts getExposedGuts() {
- return mNotificationGutsExposed;
- }
-
- /**
- * Closes guts or notification menus that might be visible and saves any changes.
- *
- * @param removeLeavebehinds true if leavebehinds (e.g. snooze) should be closed.
- * @param force true if guts should be closed regardless of state (used for snooze only).
- * @param removeControls true if controls (e.g. info) should be closed.
- * @param x if closed based on touch location, this is the x touch location.
- * @param y if closed based on touch location, this is the y touch location.
- * @param resetMenu if any notification menus that might be revealed should be closed.
- */
- public void closeAndSaveGuts(boolean removeLeavebehinds, boolean force, boolean removeControls,
- int x, int y, boolean resetMenu) {
- if (mNotificationGutsExposed != null) {
- mNotificationGutsExposed.closeControls(removeLeavebehinds, removeControls, x, y, force);
- }
- if (resetMenu) {
- mStackScroller.resetExposedMenuView(false /* animate */, true /* force */);
- }
+ return (v, x, y, item) -> mGutsManager.openGuts(v, x, y, item);
}
@Override
@@ -6802,7 +6384,7 @@
if (mHeadsUpManager != null && mHeadsUpManager.isHeadsUp(notificationKey)) {
// Release the HUN notification to the shade.
- if (isPanelFullyCollapsed()) {
+ if (isPresenterFullyCollapsed()) {
HeadsUpManager.setIsClickedNotification(row, true);
}
//
@@ -6934,7 +6516,7 @@
if (mVisible != visible) {
mVisible = visible;
if (!visible) {
- closeAndSaveGuts(true /* removeLeavebehind */, true /* force */,
+ mGutsManager.closeAndSaveGuts(true /* removeLeavebehind */, true /* force */,
true /* removeControls */, -1 /* x */, -1 /* y */, true /* resetMenu */);
}
}
@@ -7156,8 +6738,8 @@
}
mHeadsUpEntriesToRemoveOnSwitch.remove(entry);
mRemoteInputEntriesToRemoveOnCollapse.remove(entry);
- if (key.equals(mKeyToRemoveOnGutsClosed)) {
- mKeyToRemoveOnGutsClosed = null;
+ if (key.equals(mGutsManager.getKeyToRemoveOnGutsClosed())) {
+ mGutsManager.setKeyToRemoveOnGutsClosed(null);
Log.w(TAG, "Notification that was kept for guts was updated. " + key);
}
@@ -7355,4 +6937,43 @@
mNavigationBar.getBarTransitions().setAutoDim(true);
}
};
+
+ public NotificationGutsManager getGutsManager() {
+ return mGutsManager;
+ }
+
+ @Override
+ public boolean isPresenterLocked() {
+ return mState == StatusBarState.KEYGUARD;
+ }
+
+ @Override
+ public int getCurrentUserId() {
+ return mCurrentUserId;
+ }
+
+ @Override
+ public NotificationData getNotificationData() {
+ return mNotificationData;
+ }
+
+ @Override
+ public RankingMap getLatestRankingMap() {
+ return mLatestRankingMap;
+ }
+
+ final NotificationInfo.CheckSaveListener mCheckSaveListener =
+ (Runnable saveImportance, StatusBarNotification sbn) -> {
+ // If the user has security enabled, show challenge if the setting is changed.
+ if (isLockscreenPublicMode(sbn.getUser().getIdentifier()) && (
+ mState == StatusBarState.KEYGUARD
+ || mState == StatusBarState.SHADE_LOCKED)) {
+ onLockedNotificationImportanceChange(() -> {
+ saveImportance.run();
+ return true;
+ });
+ } else {
+ saveImportance.run();
+ }
+ };
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
index 1e14626..efe049a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
@@ -1343,7 +1343,7 @@
}
// Check if we need to clear any snooze leavebehinds
- NotificationGuts guts = mStatusBar.getExposedGuts();
+ NotificationGuts guts = mStatusBar.getGutsManager().getExposedGuts();
if (guts != null && !isTouchInView(ev, guts)
&& guts.getGutsContent() instanceof NotificationSnooze) {
NotificationSnooze ns = (NotificationSnooze) guts.getGutsContent();
@@ -2508,12 +2508,13 @@
}
// Check if we need to clear any snooze leavebehinds
boolean isUp = ev.getActionMasked() == MotionEvent.ACTION_UP;
- NotificationGuts guts = mStatusBar.getExposedGuts();
+ NotificationGuts guts = mStatusBar.getGutsManager().getExposedGuts();
if (!isTouchInView(ev, guts) && isUp && !swipeWantsIt && !expandWantsIt
&& !scrollWantsIt) {
mCheckForLeavebehind = false;
- mStatusBar.closeAndSaveGuts(true /* removeLeavebehind */, false /* force */,
- false /* removeControls */, -1 /* x */, -1 /* y */, false /* resetMenu */);
+ mStatusBar.getGutsManager().closeAndSaveGuts(true /* removeLeavebehind */,
+ false /* force */, false /* removeControls */, -1 /* x */, -1 /* y */,
+ false /* resetMenu */);
}
if (ev.getActionMasked() == MotionEvent.ACTION_UP) {
mCheckForLeavebehind = true;
@@ -3065,7 +3066,7 @@
}
if (!childWasSwipedOut) {
Rect clipBounds = child.getClipBounds();
- childWasSwipedOut = clipBounds.height() == 0;
+ childWasSwipedOut = clipBounds != null && clipBounds.height() == 0;
}
int animationType = childWasSwipedOut
? AnimationEvent.ANIMATION_TYPE_REMOVE_SWIPED_OUT
@@ -3355,8 +3356,9 @@
public void checkSnoozeLeavebehind() {
if (mCheckForLeavebehind) {
- mStatusBar.closeAndSaveGuts(true /* removeLeavebehind */, false /* force */,
- false /* removeControls */, -1 /* x */, -1 /* y */, false /* resetMenu */);
+ mStatusBar.getGutsManager().closeAndSaveGuts(true /* removeLeavebehind */,
+ false /* force */, false /* removeControls */, -1 /* x */, -1 /* y */,
+ false /* resetMenu */);
mCheckForLeavebehind = false;
}
}
@@ -4438,8 +4440,9 @@
// of the panel early.
handleChildDismissed(view);
}
- mStatusBar.closeAndSaveGuts(true /* removeLeavebehind */, false /* force */,
- false /* removeControls */, -1 /* x */, -1 /* y */, false /* resetMenu */);
+ mStatusBar.getGutsManager().closeAndSaveGuts(true /* removeLeavebehind */,
+ false /* force */, false /* removeControls */, -1 /* x */, -1 /* y */,
+ false /* resetMenu */);
handleMenuCoveredOrDismissed();
}
@@ -4524,7 +4527,7 @@
}
public void closeControlsIfOutsideTouch(MotionEvent ev) {
- NotificationGuts guts = mStatusBar.getExposedGuts();
+ NotificationGuts guts = mStatusBar.getGutsManager().getExposedGuts();
View view = null;
if (guts != null && !guts.getGutsContent().isLeavebehind()) {
// Only close visible guts if they're not a leavebehind.
@@ -4536,8 +4539,9 @@
}
if (view != null && !isTouchInView(ev, view)) {
// Touch was outside visible guts / menu notification, close what's visible
- mStatusBar.closeAndSaveGuts(false /* removeLeavebehind */, false /* force */,
- true /* removeControls */, -1 /* x */, -1 /* y */, false /* resetMenu */);
+ mStatusBar.getGutsManager().closeAndSaveGuts(false /* removeLeavebehind */,
+ false /* force */, true /* removeControls */, -1 /* x */, -1 /* y */,
+ false /* resetMenu */);
resetExposedMenuView(true /* animate */, true /* force */);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/tuner/PluginFragment.java b/packages/SystemUI/src/com/android/systemui/tuner/PluginFragment.java
index f91e45d..27bf534 100644
--- a/packages/SystemUI/src/com/android/systemui/tuner/PluginFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/tuner/PluginFragment.java
@@ -169,16 +169,23 @@
protected boolean persistBoolean(boolean value) {
final int desiredState = value ? PackageManager.COMPONENT_ENABLED_STATE_ENABLED
: PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
+ boolean shouldSendBroadcast = false;
for (int i = 0; i < mInfo.services.length; i++) {
ComponentName componentName = new ComponentName(mInfo.packageName,
mInfo.services[i].name);
- mPm.setComponentEnabledSetting(componentName, desiredState,
- PackageManager.DONT_KILL_APP);
+
+ if (mPm.getComponentEnabledSetting(componentName) != desiredState) {
+ mPm.setComponentEnabledSetting(componentName, desiredState,
+ PackageManager.DONT_KILL_APP);
+ shouldSendBroadcast = true;
+ }
}
- final String pkg = mInfo.packageName;
- final Intent intent = new Intent(PluginManager.PLUGIN_CHANGED,
- pkg != null ? Uri.fromParts("package", pkg, null) : null);
- getContext().sendBroadcast(intent);
+ if (shouldSendBroadcast) {
+ final String pkg = mInfo.packageName;
+ final Intent intent = new Intent(PluginManager.PLUGIN_CHANGED,
+ pkg != null ? Uri.fromParts("package", pkg, null) : null);
+ getContext().sendBroadcast(intent);
+ }
return true;
}
diff --git a/packages/SystemUI/src/com/android/systemui/usb/UsbConfirmActivity.java b/packages/SystemUI/src/com/android/systemui/usb/UsbConfirmActivity.java
index 3eccccd..e117969 100644
--- a/packages/SystemUI/src/com/android/systemui/usb/UsbConfirmActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/usb/UsbConfirmActivity.java
@@ -71,10 +71,12 @@
ap.mIcon = mResolveInfo.loadIcon(packageManager);
ap.mTitle = appName;
if (mDevice == null) {
- ap.mMessage = getString(R.string.usb_accessory_confirm_prompt, appName);
+ ap.mMessage = getString(R.string.usb_accessory_confirm_prompt, appName,
+ mAccessory.getDescription());
mDisconnectedReceiver = new UsbDisconnectedReceiver(this, mAccessory);
} else {
- ap.mMessage = getString(R.string.usb_device_confirm_prompt, appName);
+ ap.mMessage = getString(R.string.usb_device_confirm_prompt, appName,
+ mDevice.getProductName());
mDisconnectedReceiver = new UsbDisconnectedReceiver(this, mDevice);
}
ap.mPositiveButtonText = getString(android.R.string.ok);
@@ -88,9 +90,11 @@
ap.mView = inflater.inflate(com.android.internal.R.layout.always_use_checkbox, null);
mAlwaysUse = (CheckBox)ap.mView.findViewById(com.android.internal.R.id.alwaysUse);
if (mDevice == null) {
- mAlwaysUse.setText(R.string.always_use_accessory);
+ mAlwaysUse.setText(getString(R.string.always_use_accessory, appName,
+ mAccessory.getDescription()));
} else {
- mAlwaysUse.setText(R.string.always_use_device);
+ mAlwaysUse.setText(getString(R.string.always_use_device, appName,
+ mDevice.getProductName()));
}
mAlwaysUse.setOnCheckedChangeListener(this);
mClearDefaultHint = (TextView)ap.mView.findViewById(
diff --git a/packages/SystemUI/src/com/android/systemui/usb/UsbPermissionActivity.java b/packages/SystemUI/src/com/android/systemui/usb/UsbPermissionActivity.java
index 1e69fc5..87d11b2 100644
--- a/packages/SystemUI/src/com/android/systemui/usb/UsbPermissionActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/usb/UsbPermissionActivity.java
@@ -16,13 +16,17 @@
package com.android.systemui.usb;
+import android.annotation.NonNull;
import android.app.AlertDialog;
import android.app.PendingIntent;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
+import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
+import android.content.res.XmlResourceParser;
import android.hardware.usb.IUsbManager;
import android.hardware.usb.UsbAccessory;
import android.hardware.usb.UsbDevice;
@@ -41,8 +45,13 @@
import com.android.internal.app.AlertActivity;
import com.android.internal.app.AlertController;
+import com.android.internal.util.XmlUtils;
+import android.hardware.usb.AccessoryFilter;
+import android.hardware.usb.DeviceFilter;
import com.android.systemui.R;
+import org.xmlpull.v1.XmlPullParser;
+
public class UsbPermissionActivity extends AlertActivity
implements DialogInterface.OnClickListener, CheckBox.OnCheckedChangeListener {
@@ -84,10 +93,12 @@
ap.mIcon = aInfo.loadIcon(packageManager);
ap.mTitle = appName;
if (mDevice == null) {
- ap.mMessage = getString(R.string.usb_accessory_permission_prompt, appName);
+ ap.mMessage = getString(R.string.usb_accessory_permission_prompt, appName,
+ mAccessory.getDescription());
mDisconnectedReceiver = new UsbDisconnectedReceiver(this, mAccessory);
} else {
- ap.mMessage = getString(R.string.usb_device_permission_prompt, appName);
+ ap.mMessage = getString(R.string.usb_device_permission_prompt, appName,
+ mDevice.getProductName());
mDisconnectedReceiver = new UsbDisconnectedReceiver(this, mDevice);
}
ap.mPositiveButtonText = getString(android.R.string.ok);
@@ -95,25 +106,123 @@
ap.mPositiveButtonListener = this;
ap.mNegativeButtonListener = this;
- // add "always use" checkbox
- LayoutInflater inflater = (LayoutInflater)getSystemService(
- Context.LAYOUT_INFLATER_SERVICE);
- ap.mView = inflater.inflate(com.android.internal.R.layout.always_use_checkbox, null);
- mAlwaysUse = (CheckBox)ap.mView.findViewById(com.android.internal.R.id.alwaysUse);
- if (mDevice == null) {
- mAlwaysUse.setText(R.string.always_use_accessory);
- } else {
- mAlwaysUse.setText(R.string.always_use_device);
+ try {
+ PackageInfo packageInfo = packageManager.getPackageInfo(mPackageName,
+ PackageManager.GET_ACTIVITIES | PackageManager.GET_META_DATA);
+
+ if ((mDevice != null && canBeDefault(mDevice, packageInfo))
+ || (mAccessory != null && canBeDefault(mAccessory, packageInfo))) {
+ // add "open when" checkbox
+ LayoutInflater inflater = (LayoutInflater) getSystemService(
+ Context.LAYOUT_INFLATER_SERVICE);
+ ap.mView = inflater.inflate(com.android.internal.R.layout.always_use_checkbox, null);
+ mAlwaysUse = (CheckBox) ap.mView.findViewById(com.android.internal.R.id.alwaysUse);
+ if (mDevice == null) {
+ mAlwaysUse.setText(getString(R.string.always_use_accessory, appName,
+ mAccessory.getDescription()));
+ } else {
+ mAlwaysUse.setText(getString(R.string.always_use_device, appName,
+ mDevice.getProductName()));
+ }
+ mAlwaysUse.setOnCheckedChangeListener(this);
+
+ mClearDefaultHint = (TextView)ap.mView.findViewById(
+ com.android.internal.R.id.clearDefaultHint);
+ mClearDefaultHint.setVisibility(View.GONE);
+ }
+ } catch (PackageManager.NameNotFoundException e) {
+ // ignore
}
- mAlwaysUse.setOnCheckedChangeListener(this);
- mClearDefaultHint = (TextView)ap.mView.findViewById(
- com.android.internal.R.id.clearDefaultHint);
- mClearDefaultHint.setVisibility(View.GONE);
setupAlert();
}
+ /**
+ * Can the app be the default for the USB device. I.e. can the app be launched by default if
+ * the device is plugged in.
+ *
+ * @param device The device the app would be default for
+ * @param packageInfo The package info of the app
+ *
+ * @return {@code true} iff the app can be default
+ */
+ private boolean canBeDefault(@NonNull UsbDevice device, @NonNull PackageInfo packageInfo) {
+ ActivityInfo[] activities = packageInfo.activities;
+ if (activities != null) {
+ int numActivities = activities.length;
+ for (int i = 0; i < numActivities; i++) {
+ ActivityInfo activityInfo = activities[i];
+
+ try (XmlResourceParser parser = activityInfo.loadXmlMetaData(getPackageManager(),
+ UsbManager.ACTION_USB_DEVICE_ATTACHED)) {
+ if (parser == null) {
+ continue;
+ }
+
+ XmlUtils.nextElement(parser);
+ while (parser.getEventType() != XmlPullParser.END_DOCUMENT) {
+ if ("usb-device".equals(parser.getName())) {
+ DeviceFilter filter = DeviceFilter.read(parser);
+ if (filter.matches(device)) {
+ return true;
+ }
+ }
+
+ XmlUtils.nextElement(parser);
+ }
+ } catch (Exception e) {
+ Log.w(TAG, "Unable to load component info " + activityInfo.toString(), e);
+ }
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Can the app be the default for the USB accessory. I.e. can the app be launched by default if
+ * the accessory is plugged in.
+ *
+ * @param accessory The accessory the app would be default for
+ * @param packageInfo The package info of the app
+ *
+ * @return {@code true} iff the app can be default
+ */
+ private boolean canBeDefault(@NonNull UsbAccessory accessory,
+ @NonNull PackageInfo packageInfo) {
+ ActivityInfo[] activities = packageInfo.activities;
+ if (activities != null) {
+ int numActivities = activities.length;
+ for (int i = 0; i < numActivities; i++) {
+ ActivityInfo activityInfo = activities[i];
+
+ try (XmlResourceParser parser = activityInfo.loadXmlMetaData(getPackageManager(),
+ UsbManager.ACTION_USB_ACCESSORY_ATTACHED)) {
+ if (parser == null) {
+ continue;
+ }
+
+ XmlUtils.nextElement(parser);
+ while (parser.getEventType() != XmlPullParser.END_DOCUMENT) {
+ if ("usb-accessory".equals(parser.getName())) {
+ AccessoryFilter filter = AccessoryFilter.read(parser);
+ if (filter.matches(accessory)) {
+ return true;
+ }
+ }
+
+ XmlUtils.nextElement(parser);
+ }
+ } catch (Exception e) {
+ Log.w(TAG, "Unable to load component info " + activityInfo.toString(), e);
+ }
+ }
+ }
+
+ return false;
+ }
+
@Override
public void onDestroy() {
IBinder b = ServiceManager.getService(USB_SERVICE);
diff --git a/packages/SystemUI/src/com/android/systemui/volume/SystemUIInterpolators.java b/packages/SystemUI/src/com/android/systemui/volume/SystemUIInterpolators.java
new file mode 100644
index 0000000..5ad8840
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/SystemUIInterpolators.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2015 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.volume;
+
+import android.animation.TimeInterpolator;
+
+public class SystemUIInterpolators {
+ public static final class LogDecelerateInterpolator implements TimeInterpolator {
+ private final float mBase;
+ private final float mDrift;
+ private final float mTimeScale;
+ private final float mOutputScale;
+
+ public LogDecelerateInterpolator() {
+ this(400f, 1.4f, 0);
+ }
+
+ private LogDecelerateInterpolator(float base, float timeScale, float drift) {
+ mBase = base;
+ mDrift = drift;
+ mTimeScale = 1f / timeScale;
+
+ mOutputScale = 1f / computeLog(1f);
+ }
+
+ private float computeLog(float t) {
+ return 1f - (float) Math.pow(mBase, -t * mTimeScale) + (mDrift * t);
+ }
+
+ @Override
+ public float getInterpolation(float t) {
+ return computeLog(t) * mOutputScale;
+ }
+ }
+
+ public static final class LogAccelerateInterpolator implements TimeInterpolator {
+ private final int mBase;
+ private final int mDrift;
+ private final float mLogScale;
+
+ public LogAccelerateInterpolator() {
+ this(100, 0);
+ }
+
+ private LogAccelerateInterpolator(int base, int drift) {
+ mBase = base;
+ mDrift = drift;
+ mLogScale = 1f / computeLog(1, mBase, mDrift);
+ }
+
+ private static float computeLog(float t, int base, int drift) {
+ return (float) -Math.pow(base, -t) + 1 + (drift * t);
+ }
+
+ @Override
+ public float getInterpolation(float t) {
+ return 1 - computeLog(1 - t, mBase, mDrift) * mLogScale;
+ }
+ }
+
+ public interface Callback {
+ void onAnimatingChanged(boolean animating);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogComponent.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogComponent.java
index ee8f18e..4dff9bd 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogComponent.java
@@ -62,7 +62,6 @@
private final InterestingConfigChanges mConfigChanges = new InterestingConfigChanges(
ActivityInfo.CONFIG_FONT_SCALE | ActivityInfo.CONFIG_LOCALE
| ActivityInfo.CONFIG_ASSETS_PATHS);
- private final Extension mExtension;
private VolumeDialog mDialog;
private VolumePolicy mVolumePolicy = new VolumePolicy(
DEFAULT_VOLUME_DOWN_TO_ENTER_SILENT, // volumeDownToEnterSilent
@@ -79,7 +78,7 @@
// Allow plugins to reference the VolumeDialogController.
Dependency.get(PluginDependencyProvider.class)
.allowPluginDependency(VolumeDialogController.class);
- mExtension = Dependency.get(ExtensionController.class).newExtension(VolumeDialog.class)
+ Dependency.get(ExtensionController.class).newExtension(VolumeDialog.class)
.withPlugin(VolumeDialog.class)
.withDefault(this::createDefault)
.withCallback(dialog -> {
@@ -151,7 +150,7 @@
@Override
public void onConfigurationChanged(Configuration newConfig) {
if (mConfigChanges.applyNewConfig(mContext.getResources())) {
- mExtension.reload();
+ mController.mCallbacks.onConfigurationChanged();
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
index 1ecaa13..4b8f581 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
@@ -19,6 +19,8 @@
import static android.accessibilityservice.AccessibilityServiceInfo.FEEDBACK_ALL_MASK;
import static android.accessibilityservice.AccessibilityServiceInfo.FEEDBACK_GENERIC;
+import static com.android.systemui.volume.Events.DISMISS_REASON_TOUCH_OUTSIDE;
+
import android.accessibilityservice.AccessibilityServiceInfo;
import android.animation.ObjectAnimator;
import android.annotation.NonNull;
@@ -26,11 +28,11 @@
import android.app.Dialog;
import android.app.KeyguardManager;
import android.content.Context;
+import android.content.DialogInterface;
import android.content.res.ColorStateList;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Color;
-import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.graphics.drawable.ColorDrawable;
import android.media.AudioManager;
@@ -41,12 +43,10 @@
import android.os.Message;
import android.os.SystemClock;
import android.provider.Settings.Global;
-import android.util.DisplayMetrics;
import android.util.Log;
import android.util.Slog;
import android.util.SparseBooleanArray;
import android.view.ContextThemeWrapper;
-import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.AccessibilityDelegate;
@@ -54,7 +54,6 @@
import android.view.View.OnClickListener;
import android.view.View.OnTouchListener;
import android.view.ViewGroup;
-import android.view.ViewGroup.MarginLayoutParams;
import android.view.Window;
import android.view.WindowManager;
import android.view.accessibility.AccessibilityEvent;
@@ -68,12 +67,13 @@
import com.android.settingslib.Utils;
import com.android.systemui.Dependency;
+import com.android.systemui.HardwareUiLayout;
+import com.android.systemui.Interpolators;
import com.android.systemui.R;
import com.android.systemui.plugins.VolumeDialog;
import com.android.systemui.plugins.VolumeDialogController;
import com.android.systemui.plugins.VolumeDialogController.State;
import com.android.systemui.plugins.VolumeDialogController.StreamState;
-import com.android.systemui.statusbar.policy.ZenModeController;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -89,8 +89,6 @@
public class VolumeDialogImpl implements VolumeDialog {
private static final String TAG = Util.logTag(VolumeDialogImpl.class);
- public static final String SHOW_FULL_ZEN = "sysui_show_full_zen";
-
private static final long USER_ATTEMPT_GRACE_PERIOD = 1000;
private static final int UPDATE_ANIMATION_DURATION = 80;
@@ -99,6 +97,7 @@
private final VolumeDialogController mController;
private Window mWindow;
+ private HardwareUiLayout mHardwareLayout;
private CustomDialog mDialog;
private ViewGroup mDialogView;
private ViewGroup mDialogRowsView;
@@ -107,16 +106,11 @@
private ConfigurableTexts mConfigurableTexts;
private final SparseBooleanArray mDynamic = new SparseBooleanArray();
private final KeyguardManager mKeyguard;
- private final AudioManager mAudioManager;
private final AccessibilityManager mAccessibilityMgr;
- private int mExpandButtonAnimationDuration;
private final Object mSafetyWarningLock = new Object();
private final Accessibility mAccessibility = new Accessibility();
private final ColorStateList mActiveSliderTint;
private final ColorStateList mInactiveSliderTint;
- private VolumeDialogMotion mMotion;
- private int mWindowType;
- private final ZenModeController mZenModeController;
private boolean mShowing;
private boolean mShowA11yStream;
@@ -127,19 +121,12 @@
private boolean mSilentMode = VolumePrefs.DEFAULT_ENABLE_SILENT_MODE;
private State mState;
private SafetyWarningDialog mSafetyWarning;
- private Callback mCallback;
- private boolean mPendingStateChanged;
- private boolean mPendingRecheckAll;
- private long mCollapseTime;
private boolean mHovering = false;
- private int mDensity;
public VolumeDialogImpl(Context context) {
mContext = new ContextThemeWrapper(context, com.android.systemui.R.style.qs_theme);
- mZenModeController = Dependency.get(ZenModeController.class);
mController = Dependency.get(VolumeDialogController.class);
mKeyguard = (KeyguardManager) mContext.getSystemService(Context.KEYGUARD_SERVICE);
- mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
mAccessibilityMgr =
(AccessibilityManager) mContext.getSystemService(Context.ACCESSIBILITY_SERVICE);
mActiveSliderTint = ColorStateList.valueOf(Utils.getColorAccent(mContext));
@@ -147,18 +134,12 @@
}
public void init(int windowType, Callback callback) {
- mCallback = callback;
- mWindowType = windowType;
-
initDialog();
mAccessibility.init();
mController.addCallback(mControllerCallbackH, mHandler);
mController.getState();
-
- final Configuration currentConfig = mContext.getResources().getConfiguration();
- mDensity = currentConfig.densityDpi;
}
@Override
@@ -177,25 +158,16 @@
mWindow = mDialog.getWindow();
mWindow.requestFeature(Window.FEATURE_NO_TITLE);
mWindow.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
- mWindow.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
- mWindow.addFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
- | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
- | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
- | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
- | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
- | WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED);
- mDialog.setCanceledOnTouchOutside(true);
- final Resources res = mContext.getResources();
- final WindowManager.LayoutParams lp = mWindow.getAttributes();
- lp.type = mWindowType;
- lp.format = PixelFormat.TRANSLUCENT;
- lp.setTitle(VolumeDialogImpl.class.getSimpleName());
- lp.gravity = Gravity.TOP | Gravity.CENTER_HORIZONTAL;
- lp.y = res.getDimensionPixelSize(R.dimen.volume_offset_top);
- lp.gravity = Gravity.TOP;
- lp.windowAnimations = -1;
- mWindow.setAttributes(lp);
- mWindow.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING);
+ mWindow.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND
+ | WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR);
+ mWindow.addFlags(
+ WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
+ | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
+ | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
+ | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
+ | WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED);
+ mWindow.setType(WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY);
+ mWindow.setWindowAnimations(com.android.internal.R.style.Animation_Toast);
mDialog.setContentView(R.layout.volume_dialog);
mDialogView = (ViewGroup) mDialog.findViewById(R.id.volume_dialog);
@@ -209,26 +181,11 @@
return true;
}
});
+ mHardwareLayout = HardwareUiLayout.get(mDialogView);
+ mHardwareLayout.setOutsideTouchListener(view -> dismiss(DISMISS_REASON_TOUCH_OUTSIDE));
mDialogContentView = mDialog.findViewById(R.id.volume_dialog_content);
mDialogRowsView = mDialogContentView.findViewById(R.id.volume_dialog_rows);
- updateWindowWidthH();
-
- mMotion = new VolumeDialogMotion(mDialog, mDialogView, mDialogContentView,
- new VolumeDialogMotion.Callback() {
- @Override
- public void onAnimatingChanged(boolean animating) {
- if (animating) return;
- if (mPendingStateChanged) {
- mHandler.sendEmptyMessage(H.STATE_CHANGED);
- mPendingStateChanged = false;
- }
- if (mPendingRecheckAll) {
- mHandler.sendEmptyMessage(H.RECHECK_ALL);
- mPendingRecheckAll = false;
- }
- }
- });
if (mRows.isEmpty()) {
addRow(AudioManager.STREAM_MUSIC,
@@ -250,28 +207,12 @@
} else {
addExistingRows();
}
- mExpandButtonAnimationDuration = res.getInteger(R.integer.volume_expand_animation_duration);
}
private ColorStateList loadColorStateList(int colorResId) {
return ColorStateList.valueOf(mContext.getColor(colorResId));
}
- private void updateWindowWidthH() {
- final ViewGroup.MarginLayoutParams lp =
- (ViewGroup.MarginLayoutParams) mDialogView.getLayoutParams();
- final DisplayMetrics dm = mContext.getResources().getDisplayMetrics();
- if (D.BUG) Log.d(TAG, "updateWindowWidth dm.w=" + dm.widthPixels);
- int w = dm.widthPixels;
- final int max = mContext.getResources()
- .getDimensionPixelSize(R.dimen.volume_dialog_panel_width);
- if (w > max) {
- w = max;
- }
- lp.width = w - lp.getMarginEnd() - lp.getMarginStart();
- mDialogView.setLayoutParams(lp);
- }
-
public void setStreamImportant(int stream, boolean important) {
mHandler.obtainMessage(H.SET_STREAM_IMPORTANT, stream, important ? 1 : 0).sendToTarget();
}
@@ -315,6 +256,7 @@
final VolumeRow row = mRows.get(i);
initRow(row, row.stream, row.iconRes, row.iconMuteRes, row.important);
mDialogRowsView.addView(row.view);
+ updateVolumeRowH(row);
}
}
@@ -341,7 +283,6 @@
writer.print(" mDynamic: "); writer.println(mDynamic);
writer.print(" mAutomute: "); writer.println(mAutomute);
writer.print(" mSilentMode: "); writer.println(mSilentMode);
- writer.print(" mCollapseTime: "); writer.println(mCollapseTime);
writer.print(" mAccessibility.mFeedbackEnabled: ");
writer.println(mAccessibility.mFeedbackEnabled);
}
@@ -364,9 +305,9 @@
row.view = mDialog.getLayoutInflater().inflate(R.layout.volume_dialog_row, null);
row.view.setId(row.stream);
row.view.setTag(row);
- row.header = (TextView) row.view.findViewById(R.id.volume_row_header);
+ row.header = row.view.findViewById(R.id.volume_row_header);
row.header.setId(20 * row.stream);
- row.slider = (SeekBar) row.view.findViewById(R.id.volume_row_slider);
+ row.slider = row.view.findViewById(R.id.volume_row_slider);
row.slider.setOnSeekBarChangeListener(new VolumeSeekBarChangeListener(row));
row.anim = null;
@@ -447,11 +388,27 @@
rescheduleTimeoutH();
if (mShowing) return;
mShowing = true;
- mMotion.startShow();
+ mHardwareLayout.setTranslationX(getAnimTranslation());
+ mHardwareLayout.setAlpha(0);
+ mHardwareLayout.animate()
+ .alpha(1)
+ .translationX(0)
+ .setDuration(300)
+ .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
+ .withEndAction(() -> {
+ mDialog.show();
+ mWindow.getDecorView().requestAccessibilityFocus();
+ })
+ .start();
Events.writeEvent(mContext, Events.EVENT_SHOW_DIALOG, reason, mKeyguard.isKeyguardLocked());
mController.notifyVisible(true);
}
+ private float getAnimTranslation() {
+ return mContext.getResources().getDimension(
+ R.dimen.volume_dialog_panel_width) / 2;
+ }
+
protected void rescheduleTimeoutH() {
mHandler.removeMessages(H.DISMISS);
final int timeout = computeTimeoutH();
@@ -470,14 +427,19 @@
}
protected void dismissH(int reason) {
- if (mMotion.isAnimating()) {
- return;
- }
mHandler.removeMessages(H.DISMISS);
mHandler.removeMessages(H.SHOW);
if (!mShowing) return;
mShowing = false;
- mMotion.startDismiss();
+ mHardwareLayout.setTranslationX(0);
+ mHardwareLayout.setAlpha(1);
+ mHardwareLayout.animate()
+ .alpha(0)
+ .translationX(getAnimTranslation())
+ .setDuration(300)
+ .withEndAction(() -> mDialog.dismiss())
+ .setInterpolator(new SystemUIInterpolators.LogAccelerateInterpolator())
+ .start();
if (mAccessibilityMgr.isEnabled()) {
AccessibilityEvent event =
AccessibilityEvent.obtain(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
@@ -497,30 +459,6 @@
}
}
- private void updateDialogBottomMarginH() {
- final long diff = System.currentTimeMillis() - mCollapseTime;
- final boolean collapsing = mCollapseTime != 0 && diff < getConservativeCollapseDuration();
- final ViewGroup.MarginLayoutParams mlp = (MarginLayoutParams) mDialogView.getLayoutParams();
- final int bottomMargin = collapsing ? mDialogContentView.getHeight() :
- mContext.getResources().getDimensionPixelSize(R.dimen.volume_dialog_margin_bottom);
- if (bottomMargin != mlp.bottomMargin) {
- if (D.BUG) Log.d(TAG, "bottomMargin " + mlp.bottomMargin + " -> " + bottomMargin);
- mlp.bottomMargin = bottomMargin;
- mDialogView.setLayoutParams(mlp);
- }
- }
-
- private long getConservativeCollapseDuration() {
- return mExpandButtonAnimationDuration * 3;
- }
-
- private void prepareForCollapse() {
- mHandler.removeMessages(H.UPDATE_BOTTOM_MARGIN);
- mCollapseTime = System.currentTimeMillis();
- updateDialogBottomMarginH();
- mHandler.sendEmptyMessageDelayed(H.UPDATE_BOTTOM_MARGIN, getConservativeCollapseDuration());
- }
-
private boolean shouldBeVisibleH(VolumeRow row, VolumeRow activeRow) {
boolean isActive = row == activeRow;
if (row.stream == AudioSystem.STREAM_ACCESSIBILITY) {
@@ -567,13 +505,7 @@
}
private void onStateChangedH(State state) {
- final boolean animating = mMotion.isAnimating();
- if (D.BUG) Log.d(TAG, "onStateChangedH animating=" + animating);
mState = state;
- if (animating) {
- mPendingStateChanged = true;
- return;
- }
mDynamic.clear();
// add any new dynamic rows
for (int i = 0; i < state.states.size(); i++) {
@@ -642,19 +574,13 @@
row.icon.setAlpha(iconEnabled ? 1 : 0.5f);
final int iconRes =
isRingVibrate ? R.drawable.ic_volume_ringer_vibrate
- : isRingSilent || zenMuted ? row.cachedIconRes
+ : isRingSilent || zenMuted ? row.iconMuteRes
: ss.routedToBluetooth ?
(ss.muted ? R.drawable.ic_volume_media_bt_mute
: R.drawable.ic_volume_media_bt)
: mAutomute && ss.level == 0 ? row.iconMuteRes
: (ss.muted ? row.iconMuteRes : row.iconRes);
- if (iconRes != row.cachedIconRes) {
- if (row.cachedIconRes != 0 && isRingVibrate) {
- mController.vibrate();
- }
- row.cachedIconRes = iconRes;
- row.icon.setImageResource(iconRes);
- }
+ row.icon.setImageResource(iconRes);
row.iconState =
iconRes == R.drawable.ic_volume_ringer_vibrate ? Events.ICON_STATE_VIBRATE
: (iconRes == R.drawable.ic_volume_media_bt_mute || iconRes == row.iconMuteRes)
@@ -864,14 +790,8 @@
@Override
public void onConfigurationChanged() {
- Configuration newConfig = mContext.getResources().getConfiguration();
- final int density = newConfig.densityDpi;
- if (density != mDensity) {
- mDialog.dismiss();
- initDialog();
- mDensity = density;
- }
- updateWindowWidthH();
+ mDialog.dismiss();
+ initDialog();
mConfigurableTexts.update();
}
@@ -908,23 +828,6 @@
}
};
- private final ZenModePanel.Callback mZenPanelCallback = new ZenModePanel.Callback() {
- @Override
- public void onPrioritySettings() {
- mCallback.onZenPrioritySettingsClicked();
- }
-
- @Override
- public void onInteraction() {
- mHandler.sendEmptyMessage(H.RESCHEDULE_TIMEOUT);
- }
-
- @Override
- public void onExpanded(boolean expanded) {
- // noop.
- }
- };
-
private final class H extends Handler {
private static final int SHOW = 1;
private static final int DISMISS = 2;
@@ -933,7 +836,6 @@
private static final int SET_STREAM_IMPORTANT = 5;
private static final int RESCHEDULE_TIMEOUT = 6;
private static final int STATE_CHANGED = 7;
- private static final int UPDATE_BOTTOM_MARGIN = 8;
public H() {
super(Looper.getMainLooper());
@@ -949,14 +851,13 @@
case SET_STREAM_IMPORTANT: setStreamImportantH(msg.arg1, msg.arg2 != 0); break;
case RESCHEDULE_TIMEOUT: rescheduleTimeoutH(); break;
case STATE_CHANGED: onStateChangedH(mState); break;
- case UPDATE_BOTTOM_MARGIN: updateDialogBottomMarginH(); break;
}
}
}
- private final class CustomDialog extends Dialog {
+ private final class CustomDialog extends Dialog implements DialogInterface {
public CustomDialog(Context context) {
- super(context);
+ super(context, com.android.systemui.R.style.qs_theme);
}
@Override
@@ -966,26 +867,15 @@
}
@Override
- protected void onStop() {
- super.onStop();
- final boolean animating = mMotion.isAnimating();
- if (D.BUG) Log.d(TAG, "onStop animating=" + animating);
- if (animating) {
- mPendingRecheckAll = true;
- return;
- }
- mHandler.sendEmptyMessage(H.RECHECK_ALL);
+ protected void onStart() {
+ super.setCanceledOnTouchOutside(true);
+ super.onStart();
}
@Override
- public boolean onTouchEvent(MotionEvent event) {
- if (isShowing()) {
- if (event.getAction() == MotionEvent.ACTION_OUTSIDE) {
- dismissH(Events.DISMISS_REASON_TOUCH_OUTSIDE);
- return true;
- }
- }
- return false;
+ protected void onStop() {
+ super.onStop();
+ mHandler.sendEmptyMessage(H.RECHECK_ALL);
}
@Override
@@ -1128,7 +1018,6 @@
private int iconRes;
private int iconMuteRes;
private boolean important;
- private int cachedIconRes;
private ColorStateList cachedSliderTint;
private int iconState; // from Events
private ObjectAnimator anim; // slider progress animation for non-touch-related updates
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogMotion.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogMotion.java
deleted file mode 100644
index 2b65fbd..0000000
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogMotion.java
+++ /dev/null
@@ -1,265 +0,0 @@
-/*
- * Copyright (C) 2015 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.volume;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.TimeInterpolator;
-import android.animation.ValueAnimator;
-import android.animation.ValueAnimator.AnimatorUpdateListener;
-import android.app.Dialog;
-import android.content.DialogInterface;
-import android.content.DialogInterface.OnDismissListener;
-import android.content.DialogInterface.OnShowListener;
-import android.os.Handler;
-import android.util.Log;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.animation.PathInterpolator;
-
-public class VolumeDialogMotion {
- private static final String TAG = Util.logTag(VolumeDialogMotion.class);
-
- private static final float ANIMATION_SCALE = 1.0f;
- private static final int PRE_DISMISS_DELAY = 50;
-
- private final Dialog mDialog;
- private final View mDialogView;
- private final ViewGroup mContents; // volume rows + zen footer
- private final Handler mHandler = new Handler();
- private final Callback mCallback;
-
- private boolean mAnimating; // show or dismiss animation is running
- private boolean mShowing; // show animation is running
- private boolean mDismissing; // dismiss animation is running
- private ValueAnimator mContentsPositionAnimator;
-
- public VolumeDialogMotion(Dialog dialog, View dialogView, ViewGroup contents,
- Callback callback) {
- mDialog = dialog;
- mDialogView = dialogView;
- mContents = contents;
- mCallback = callback;
- mDialog.setOnDismissListener(new OnDismissListener() {
- @Override
- public void onDismiss(DialogInterface dialog) {
- if (D.BUG) Log.d(TAG, "mDialog.onDismiss");
- }
- });
- mDialog.setOnShowListener(new OnShowListener() {
- @Override
- public void onShow(DialogInterface dialog) {
- if (D.BUG) Log.d(TAG, "mDialog.onShow");
- final int h = mDialogView.getHeight();
- mDialogView.setTranslationY(-h);
- startShowAnimation();
- }
- });
- }
-
- public boolean isAnimating() {
- return mAnimating;
- }
-
- private void setShowing(boolean showing) {
- if (showing == mShowing) return;
- mShowing = showing;
- if (D.BUG) Log.d(TAG, "mShowing = " + mShowing);
- updateAnimating();
- }
-
- private void setDismissing(boolean dismissing) {
- if (dismissing == mDismissing) return;
- mDismissing = dismissing;
- if (D.BUG) Log.d(TAG, "mDismissing = " + mDismissing);
- updateAnimating();
- }
-
- private void updateAnimating() {
- final boolean animating = mShowing || mDismissing;
- if (animating == mAnimating) return;
- mAnimating = animating;
- if (D.BUG) Log.d(TAG, "mAnimating = " + mAnimating);
- if (mCallback != null) {
- mCallback.onAnimatingChanged(mAnimating);
- }
- }
-
- public void startShow() {
- if (D.BUG) Log.d(TAG, "startShow");
- if (mShowing) return;
- setShowing(true);
- if (mDismissing) {
- mDialogView.animate().cancel();
- setDismissing(false);
- startShowAnimation();
- return;
- }
- if (D.BUG) Log.d(TAG, "mDialog.show()");
- mDialog.show();
- }
-
- private void startShowAnimation() {
- if (D.BUG) Log.d(TAG, "startShowAnimation");
- mDialogView.animate()
- .translationY(0)
- .setDuration(scaledDuration(300))
- .setInterpolator(new LogDecelerateInterpolator())
- .setListener(null)
- .start();
-
- mContentsPositionAnimator = ValueAnimator.ofFloat(0).setDuration(scaledDuration(400));
- mContentsPositionAnimator.addListener(new AnimatorListenerAdapter() {
- private boolean mCancelled;
-
- @Override
- public void onAnimationEnd(Animator animation) {
- if (mCancelled) return;
- if (D.BUG) Log.d(TAG, "show.onAnimationEnd");
- setShowing(false);
- }
- @Override
- public void onAnimationCancel(Animator animation) {
- if (D.BUG) Log.d(TAG, "show.onAnimationCancel");
- mCancelled = true;
- }
- });
- mContentsPositionAnimator.addUpdateListener(new AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator animation) {
- float v = (Float) animation.getAnimatedValue();
- mContents.setTranslationY(v + -mDialogView.getTranslationY());
- }
- });
- mContentsPositionAnimator.setInterpolator(new LogDecelerateInterpolator());
- mContentsPositionAnimator.start();
-
- mContents.setAlpha(0);
- mContents.animate()
- .alpha(1)
- .setDuration(scaledDuration(150))
- .setInterpolator(new PathInterpolator(0f, 0f, .2f, 1f))
- .start();
- }
-
- public void startDismiss() {
- if (D.BUG) Log.d(TAG, "startDismiss");
- if (mDismissing) return;
- setDismissing(true);
- if (mShowing) {
- mDialogView.animate().cancel();
- if (mContentsPositionAnimator != null) {
- mContentsPositionAnimator.cancel();
- }
- mContents.animate().cancel();
- setShowing(false);
- }
- mDialogView.animate()
- .translationY(-mDialogView.getHeight())
- .setDuration(scaledDuration(250))
- .setInterpolator(new LogAccelerateInterpolator())
- .setUpdateListener(new AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator animation) {
- mContents.setTranslationY(-mDialogView.getTranslationY());
- }
- })
- .setListener(new AnimatorListenerAdapter() {
- private boolean mCancelled;
- @Override
- public void onAnimationEnd(Animator animation) {
- if (mCancelled) return;
- if (D.BUG) Log.d(TAG, "dismiss.onAnimationEnd");
- mHandler.postDelayed(new Runnable() {
- @Override
- public void run() {
- if (D.BUG) Log.d(TAG, "mDialog.dismiss()");
- mDialog.dismiss();
- setDismissing(false);
- }
- }, PRE_DISMISS_DELAY);
-
- }
- @Override
- public void onAnimationCancel(Animator animation) {
- if (D.BUG) Log.d(TAG, "dismiss.onAnimationCancel");
- mCancelled = true;
- }
- }).start();
- }
-
- private static int scaledDuration(int base) {
- return (int) (base * ANIMATION_SCALE);
- }
-
- public static final class LogDecelerateInterpolator implements TimeInterpolator {
- private final float mBase;
- private final float mDrift;
- private final float mTimeScale;
- private final float mOutputScale;
-
- public LogDecelerateInterpolator() {
- this(400f, 1.4f, 0);
- }
-
- private LogDecelerateInterpolator(float base, float timeScale, float drift) {
- mBase = base;
- mDrift = drift;
- mTimeScale = 1f / timeScale;
-
- mOutputScale = 1f / computeLog(1f);
- }
-
- private float computeLog(float t) {
- return 1f - (float) Math.pow(mBase, -t * mTimeScale) + (mDrift * t);
- }
-
- @Override
- public float getInterpolation(float t) {
- return computeLog(t) * mOutputScale;
- }
- }
-
- public static final class LogAccelerateInterpolator implements TimeInterpolator {
- private final int mBase;
- private final int mDrift;
- private final float mLogScale;
-
- public LogAccelerateInterpolator() {
- this(100, 0);
- }
-
- private LogAccelerateInterpolator(int base, int drift) {
- mBase = base;
- mDrift = drift;
- mLogScale = 1f / computeLog(1, mBase, mDrift);
- }
-
- private static float computeLog(float t, int base, int drift) {
- return (float) -Math.pow(base, -t) + 1 + (drift * t);
- }
-
- @Override
- public float getInterpolation(float t) {
- return 1 - computeLog(1 - t, mBase, mDrift) * mLogScale;
- }
- }
-
- public interface Callback {
- void onAnimatingChanged(boolean animating);
- }
-}
diff --git a/packages/SystemUI/tests/AndroidManifest.xml b/packages/SystemUI/tests/AndroidManifest.xml
index 67fae5b..e74736a 100644
--- a/packages/SystemUI/tests/AndroidManifest.xml
+++ b/packages/SystemUI/tests/AndroidManifest.xml
@@ -43,6 +43,9 @@
<uses-permission android:name="android.permission.USE_FINGERPRINT" />
<uses-permission android:name="android.permission.DEVICE_POWER" />
<uses-permission android:name="android.permission.READ_CONTACTS" />
+ <uses-permission android:name="android.permission.STATUS_BAR" />
+ <uses-permission android:name="android.permission.MANAGE_ACTIVITY_STACKS" />
+ <uses-permission android:name="android.permission.REAL_GET_TASKS" />
<application>
<uses-library android:name="android.test.runner" />
diff --git a/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java b/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java
index 65d9699..fbcbd20 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java
@@ -23,6 +23,7 @@
import android.os.Handler;
import android.os.Looper;
import android.os.MessageQueue;
+import android.os.ParcelFileDescriptor;
import android.support.test.InstrumentationRegistry;
import android.support.test.filters.SmallTest;
import android.testing.LeakCheck;
@@ -34,6 +35,8 @@
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
+import java.io.FileInputStream;
+import java.io.IOException;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
@@ -84,6 +87,16 @@
return mContext;
}
+ protected void runShellCommand(String command) throws IOException {
+ ParcelFileDescriptor pfd = mRealInstrumentation.getUiAutomation()
+ .executeShellCommand(command);
+
+ // Read the input stream fully.
+ FileInputStream fis = new ParcelFileDescriptor.AutoCloseInputStream(pfd);
+ while (fis.read() != -1);
+ fis.close();
+ }
+
protected void waitForIdleSync() {
if (mHandler == null) {
mHandler = new Handler(Looper.getMainLooper());
diff --git a/packages/SystemUI/tests/src/com/android/systemui/recents/RecentsTest.java b/packages/SystemUI/tests/src/com/android/systemui/recents/RecentsTest.java
new file mode 100644
index 0000000..bdbd244
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/recents/RecentsTest.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.recents;
+
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
+
+import static com.android.systemui.recents.RecentsImpl.RECENTS_ACTIVITY;
+import static com.android.systemui.recents.RecentsImpl.RECENTS_PACKAGE;
+
+import static org.junit.Assert.fail;
+
+import android.app.ActivityManager;
+import android.app.ActivityManager.RunningTaskInfo;
+import android.app.IActivityManager;
+import android.os.SystemClock;
+import android.support.test.filters.MediumTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import com.android.systemui.SysuiTestCase;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.List;
+
+@RunWith(AndroidJUnit4.class)
+@MediumTest
+public class RecentsTest extends SysuiTestCase {
+
+ @Test
+ public void testRecentsActivityType() throws Exception {
+ // Clear the state
+ final IActivityManager am = ActivityManager.getService();
+ am.removeStacksWithActivityTypes(new int[] { ACTIVITY_TYPE_RECENTS });
+
+ // Toggle recents, use a shell command because it is not exported
+ runShellCommand("am start -n " + RECENTS_PACKAGE + "/" + RECENTS_ACTIVITY);
+
+ // Verify that an activity was launched with the right activity type
+ int retryCount = 0;
+ while (retryCount < 10) {
+ List<RunningTaskInfo> tasks = am.getTasks(Integer.MAX_VALUE);
+ for (RunningTaskInfo info : tasks) {
+ if (info.configuration.windowConfiguration.getActivityType()
+ == ACTIVITY_TYPE_RECENTS) {
+ // Found a recents activity with the right activity type
+ return;
+ }
+ }
+ SystemClock.sleep(50);
+ retryCount++;
+ }
+ fail("Expected Recents activity with ACTIVITY_TYPE_RECENTS");
+ }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationInfoTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationInfoTest.java
index 88fa659..6721938 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationInfoTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationInfoTest.java
@@ -745,7 +745,7 @@
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel),
mNotificationChannel.getImportance(), mSbn, null, null, null,
- (Runnable saveImportance) -> {
+ (Runnable saveImportance, StatusBarNotification sbn) -> {
},
Collections.singleton(TEST_PACKAGE_NAME));
@@ -762,7 +762,7 @@
mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
TEST_PACKAGE_NAME, Arrays.asList(mNotificationChannel),
mNotificationChannel.getImportance(), mSbn, null, null, null,
- (Runnable saveImportance) -> {
+ (Runnable saveImportance, StatusBarNotification sbn) -> {
saveImportance.run();
},
Collections.singleton(TEST_PACKAGE_NAME));
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java
index 3b401a5..a95e3db 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java
@@ -24,7 +24,7 @@
import android.testing.TestableLooper;
import android.testing.TestableLooper.RunWithLooper;
-import com.android.internal.app.NightDisplayController;
+import com.android.internal.app.ColorDisplayController;
import com.android.systemui.Dependency;
import com.android.systemui.Prefs;
import com.android.systemui.Prefs.Key;
@@ -61,49 +61,49 @@
@Test
public void nightTileAdded_whenActivated() {
- if (!NightDisplayController.isAvailable(mContext)) {
+ if (!ColorDisplayController.isAvailable(mContext)) {
return;
}
- mAutoTileManager.mNightDisplayCallback.onActivated(true);
+ mAutoTileManager.mColorDisplayCallback.onActivated(true);
verify(mQsTileHost).addTile("night");
}
@Test
public void nightTileNotAdded_whenDeactivated() {
- if (!NightDisplayController.isAvailable(mContext)) {
+ if (!ColorDisplayController.isAvailable(mContext)) {
return;
}
- mAutoTileManager.mNightDisplayCallback.onActivated(false);
+ mAutoTileManager.mColorDisplayCallback.onActivated(false);
verify(mQsTileHost, never()).addTile("night");
}
@Test
public void nightTileAdded_whenNightModeTwilight() {
- if (!NightDisplayController.isAvailable(mContext)) {
+ if (!ColorDisplayController.isAvailable(mContext)) {
return;
}
- mAutoTileManager.mNightDisplayCallback.onAutoModeChanged(
- NightDisplayController.AUTO_MODE_TWILIGHT);
+ mAutoTileManager.mColorDisplayCallback.onAutoModeChanged(
+ ColorDisplayController.AUTO_MODE_TWILIGHT);
verify(mQsTileHost).addTile("night");
}
@Test
public void nightTileAdded_whenNightModeCustom() {
- if (!NightDisplayController.isAvailable(mContext)) {
+ if (!ColorDisplayController.isAvailable(mContext)) {
return;
}
- mAutoTileManager.mNightDisplayCallback.onAutoModeChanged(
- NightDisplayController.AUTO_MODE_CUSTOM);
+ mAutoTileManager.mColorDisplayCallback.onAutoModeChanged(
+ ColorDisplayController.AUTO_MODE_CUSTOM);
verify(mQsTileHost).addTile("night");
}
@Test
public void nightTileNotAdded_whenNightModeDisabled() {
- if (!NightDisplayController.isAvailable(mContext)) {
+ if (!ColorDisplayController.isAvailable(mContext)) {
return;
}
- mAutoTileManager.mNightDisplayCallback.onAutoModeChanged(
- NightDisplayController.AUTO_MODE_DISABLED);
+ mAutoTileManager.mColorDisplayCallback.onAutoModeChanged(
+ ColorDisplayController.AUTO_MODE_DISABLED);
verify(mQsTileHost, never()).addTile("night");
}
}
diff --git a/proto/src/ipconnectivity.proto b/proto/src/ipconnectivity.proto
index 327f17d..0e39446 100644
--- a/proto/src/ipconnectivity.proto
+++ b/proto/src/ipconnectivity.proto
@@ -489,7 +489,7 @@
// Represents statistics from NFLOG wakeup events due to ingress packets.
// Since oc-mr1.
-// Next tag: 8.
+// Next tag: 13.
message WakeupStats {
// The time duration in seconds covered by these stats, for deriving
// exact wakeup rates.
@@ -517,6 +517,24 @@
// The total number of wakeup packets with no associated socket or uid.
optional int64 no_uid_wakeups = 7;
+
+ // Counts of all different ethertype values from wakeup packets received.
+ repeated Pair ethertype_counts = 8;
+
+ // Counts of all different IP next header values from wakeup packets received.
+ repeated Pair ip_next_header_counts = 9;
+
+ // The total number of wakeup packets whose destination hardware address was
+ // a unicast address.
+ optional int64 l2_unicast_count = 10;
+
+ // The total number of wakeup packets whose destination hardware address was
+ // a multicast address.
+ optional int64 l2_multicast_count = 11;
+
+ // The total number of wakeup packets whose destination hardware address was
+ // a broadcast address.
+ optional int64 l2_broadcast_count = 12;
}
// Represents one of the IP connectivity event defined in this file.
diff --git a/proto/src/metrics_constants.proto b/proto/src/metrics_constants.proto
index d3dcf5a..73a6d7c 100644
--- a/proto/src/metrics_constants.proto
+++ b/proto/src/metrics_constants.proto
@@ -171,6 +171,12 @@
TEXT_CLASSIFIER_TYPE_URL = 6;
}
+ // Selection invocation methods. Used as sub-type for TEXT_SELECTION_SESSION events.
+ enum TextSelectionInvocationMethod {
+ TEXT_SELECTION_INVOCATION_MANUAL = 1;
+ TEXT_SELECTION_INVOCATION_LINK = 2;
+ }
+
// Known visual elements: views or controls.
enum View {
// Unknown view
@@ -4680,6 +4686,78 @@
// OS: P
DIALOG_FIRMWARE_VERSION = 1247;
+ // OPEN: Settings > Network & internet > Menu > Private DNS
+ // CATEGORY: SETTINGS
+ // OS: P
+ DIALOG_PRIVATE_DNS = 1248;
+
+ // ACTION: A private dns mode been selected by user
+ // CATEGORY: SETTINGS
+ // OS: P
+ ACTION_PRIVATE_DNS_MODE = 1249;
+
+ // FIELD: text select start offset in words (as defined by the ICU BreakIterator).
+ // CATEGORY: TEXT_SELECTION_SESSION
+ // OS: P
+ FIELD_SELECTION_RANGE_START = 1250;
+
+ // FIELD: text select end offset in words (as defined by the ICU BreakIterator).
+ // CATEGORY: TEXT_SELECTION_SESSION
+ // OS: P
+ FIELD_SELECTION_RANGE_END = 1251;
+
+ // FIELD: smart text selection start offset in words (as defined by the ICU BreakIterator),
+ // stored as two packed 16bit integers. (start in MSBs, end in LSBs)
+ // CATEGORY: TEXT_SELECTION_SESSION
+ // OS: P
+ FIELD_SELECTION_SMART_RANGE_START = 1252;
+
+ // FIELD: smart text selection end offset in words (as defined by the ICU BreakIterator),
+ // stored as two packed 16bit integers. (start in MSBs, end in LSBs)
+ // CATEGORY: TEXT_SELECTION_SESSION
+ // OS: P
+ FIELD_SELECTION_SMART_RANGE_END = 1253;
+
+ // FIELD: the entity type of the text currently selected.
+ // CATEGORY: TEXT_SELECTION_SESSION
+ // OS: P
+ FIELD_SELECTION_ENTITY_TYPE = 1254;
+
+ // FIELD: the type of widget the selection was made in.
+ // CATEGORY: TEXT_SELECTION_SESSION
+ // OS: P
+ FIELD_SELECTION_WIDGET_TYPE = 1255;
+
+ // FIELD: the name of the text classifier model used.
+ // CATEGORY: TEXT_SELECTION_SESSION
+ // OS: P
+ FIELD_TEXTCLASSIFIER_MODEL = 1256;
+
+ // OPEN: Settings > Sound & notification > Do Not Disturb > Behavior > Messages
+ // CATEGORY: SETTINGS
+ // OS: P
+ NOTIFICATION_ZEN_MODE_MESSAGES = 1257;
+
+ // OPEN: Settings > Sound & notification > Do Not Disturb > Behavior > Calls
+ // CATEGORY: SETTINGS
+ // OS: P
+ NOTIFICATION_ZEN_MODE_CALLS = 1258;
+
+ // OPEN: Settings > Sound & notification > Do Not Disturb > TURN ON -> Until you turn off
+ // CATEGORY: SETTINGS
+ // OS: P
+ NOTIFICATION_ZEN_MODE_TOGGLE_ON_FOREVER = 1259;
+
+ // OPEN: Settings > Sound & notification > Do Not Disturb > TURN ON -> Time countdown manual rule (ie: for one hour)
+ // CATEGORY: SETTINGS
+ // OS: P
+ NOTIFICATION_ZEN_MODE_TOGGLE_ON_COUNTDOWN = 1260;
+
+ // OPEN: Settings > Sound & notification > Do Not Disturb > TURN ON -> Next Alarm (ie: Until Tue 7:20 AM)
+ // CATEGORY: SETTINGS
+ // OS: P
+ NOTIFICATION_ZEN_MODE_TOGGLE_ON_ALARM = 1261;
+
// Add new aosp constants above this line.
// END OF AOSP CONSTANTS
}
diff --git a/proto/src/wifi.proto b/proto/src/wifi.proto
index 9d25055..8f58a38 100644
--- a/proto/src/wifi.proto
+++ b/proto/src/wifi.proto
@@ -364,6 +364,12 @@
// (one value added per unique ESS - potentially multiple counts per single
// scan!)
repeated NumConnectableNetworksBucket observed_hotspot_r2_aps_per_ess_in_scan_histogram = 88;
+
+ // SoftAP event list tracking sessions and client counts in tethered mode
+ repeated SoftApConnectedClientsEvent soft_ap_connected_clients_events_tethered = 89;
+
+ // SoftAP event list tracking sessions and client counts in local only mode
+ repeated SoftApConnectedClientsEvent soft_ap_connected_clients_events_local_only = 90;
}
// Information that gets logged for every WiFi connection.
@@ -1071,3 +1077,29 @@
// Occurrences of this action.
optional int32 count = 4;
}
+
+// SoftAP event tracking sessions and client counts
+message SoftApConnectedClientsEvent {
+
+ // Soft AP event Types
+ enum SoftApEventType {
+
+ // Soft AP is Up and ready for use
+ SOFT_AP_UP = 0;
+
+ // Soft AP is Down
+ SOFT_AP_DOWN = 1;
+
+ // Number of connected soft AP clients has changed
+ NUM_CLIENTS_CHANGED = 2;
+ }
+
+ // Type of event being recorded
+ optional SoftApEventType event_type = 1;
+
+ // Absolute time when event happened
+ optional int64 time_stamp_millis = 2;
+
+ // Number of connected clients if event_type is NUM_CLIENTS_CHANGED, otherwise zero.
+ optional int32 num_connected_clients = 3;
+}
\ No newline at end of file
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 06c110d..d661754 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -1318,7 +1318,7 @@
private void unbindAllServicesLocked(UserState userState) {
List<AccessibilityServiceConnection> services = userState.mBoundServices;
- for (int count = services.size(); count > 1; count--) {
+ for (int count = services.size(); count > 0; count--) {
// When the service is unbound, it disappears from the list, so there's no need to
// keep track of the index
services.get(0).unbindLocked();
diff --git a/services/art-profile b/services/art-profile
index ac5ecaf..301918f 100644
--- a/services/art-profile
+++ b/services/art-profile
@@ -2374,6 +2374,9 @@
HPLcom/android/server/display/AutomaticBrightnessController;->updateAmbientLux()V
HPLcom/android/server/display/AutomaticBrightnessController;->updateAmbientLux(J)V
HPLcom/android/server/display/AutomaticBrightnessController;->weightIntegral(J)F
+HPLcom/android/server/display/ColorDisplayService$3;->onAnimationUpdate(Landroid/animation/ValueAnimator;)V
+HPLcom/android/server/display/ColorDisplayService$ColorMatrixEvaluator;->evaluate(FLjava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
+HPLcom/android/server/display/ColorDisplayService$ColorMatrixEvaluator;->evaluate(F[F[F)[F
HPLcom/android/server/display/DisplayBlanker;->requestDisplayState(II)V
HPLcom/android/server/display/DisplayManagerService$BinderService;->getDisplayIds()[I
HPLcom/android/server/display/DisplayManagerService$CallbackRecord;->binderDied()V
@@ -2430,9 +2433,6 @@
HPLcom/android/server/display/LogicalDisplay;->getRequestedModeIdLocked()I
HPLcom/android/server/display/LogicalDisplay;->hasContentLocked()Z
HPLcom/android/server/display/LogicalDisplay;->setDisplayInfoOverrideFromWindowManagerLocked(Landroid/view/DisplayInfo;)Z
-HPLcom/android/server/display/NightDisplayService$3;->onAnimationUpdate(Landroid/animation/ValueAnimator;)V
-HPLcom/android/server/display/NightDisplayService$ColorMatrixEvaluator;->evaluate(FLjava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-HPLcom/android/server/display/NightDisplayService$ColorMatrixEvaluator;->evaluate(F[F[F)[F
HPLcom/android/server/display/RampAnimator$1;->run()V
HPLcom/android/server/display/RampAnimator$Listener;->onAnimationEnd()V
HPLcom/android/server/display/RampAnimator;->-get0(Lcom/android/server/display/RampAnimator;)F
@@ -10133,6 +10133,25 @@
PLcom/android/server/display/AutomaticBrightnessController;->setLightSensorEnabled(Z)Z
PLcom/android/server/display/AutomaticBrightnessController;->setScreenAutoBrightnessAdjustment(F)Z
PLcom/android/server/display/AutomaticBrightnessController;->updateAutoBrightness(Z)V
+PLcom/android/server/display/ColorDisplayService$1;-><init>(Lcom/android/server/display/ColorDisplayService;)V
+PLcom/android/server/display/ColorDisplayService$3;-><init>(Lcom/android/server/display/ColorDisplayService;Lcom/android/server/display/DisplayTransformManager;)V
+PLcom/android/server/display/ColorDisplayService$4;-><init>(Lcom/android/server/display/ColorDisplayService;Lcom/android/server/display/DisplayTransformManager;[F)V
+PLcom/android/server/display/ColorDisplayService$4;->onAnimationCancel(Landroid/animation/Animator;)V
+PLcom/android/server/display/ColorDisplayService$4;->onAnimationEnd(Landroid/animation/Animator;)V
+PLcom/android/server/display/ColorDisplayService$ColorMatrixEvaluator;-><init>()V
+PLcom/android/server/display/ColorDisplayService$ColorMatrixEvaluator;-><init>(Lcom/android/server/display/ColorDisplayService$ColorMatrixEvaluator;)V
+PLcom/android/server/display/ColorDisplayService;->-set0(Lcom/android/server/display/ColorDisplayService;Landroid/animation/ValueAnimator;)Landroid/animation/ValueAnimator;
+PLcom/android/server/display/ColorDisplayService;-><init>(Landroid/content/Context;)V
+PLcom/android/server/display/ColorDisplayService;->applyTint(Z)V
+PLcom/android/server/display/ColorDisplayService;->isUserSetupCompleted(Landroid/content/ContentResolver;I)Z
+PLcom/android/server/display/ColorDisplayService;->onActivated(Z)V
+PLcom/android/server/display/ColorDisplayService;->onAutoModeChanged(I)V
+PLcom/android/server/display/ColorDisplayService;->onBootPhase(I)V
+PLcom/android/server/display/ColorDisplayService;->onStart()V
+PLcom/android/server/display/ColorDisplayService;->onStartUser(I)V
+PLcom/android/server/display/ColorDisplayService;->onUserChanged(I)V
+PLcom/android/server/display/ColorDisplayService;->setMatrix(I[F)V
+PLcom/android/server/display/ColorDisplayService;->setUp()V
PLcom/android/server/display/ColorFade;-><init>(I)V
PLcom/android/server/display/ColorFade;->createNativeFloatBuffer(I)Ljava/nio/FloatBuffer;
PLcom/android/server/display/ColorFade;->dismiss()V
@@ -10230,25 +10249,6 @@
PLcom/android/server/display/LogicalDisplay;->getDisplayIdLocked()I
PLcom/android/server/display/LogicalDisplay;->getNonOverrideDisplayInfoLocked(Landroid/view/DisplayInfo;)V
PLcom/android/server/display/LogicalDisplay;->setHasContentLocked(Z)V
-PLcom/android/server/display/NightDisplayService$1;-><init>(Lcom/android/server/display/NightDisplayService;)V
-PLcom/android/server/display/NightDisplayService$3;-><init>(Lcom/android/server/display/NightDisplayService;Lcom/android/server/display/DisplayTransformManager;)V
-PLcom/android/server/display/NightDisplayService$4;-><init>(Lcom/android/server/display/NightDisplayService;Lcom/android/server/display/DisplayTransformManager;[F)V
-PLcom/android/server/display/NightDisplayService$4;->onAnimationCancel(Landroid/animation/Animator;)V
-PLcom/android/server/display/NightDisplayService$4;->onAnimationEnd(Landroid/animation/Animator;)V
-PLcom/android/server/display/NightDisplayService$ColorMatrixEvaluator;-><init>()V
-PLcom/android/server/display/NightDisplayService$ColorMatrixEvaluator;-><init>(Lcom/android/server/display/NightDisplayService$ColorMatrixEvaluator;)V
-PLcom/android/server/display/NightDisplayService;->-set0(Lcom/android/server/display/NightDisplayService;Landroid/animation/ValueAnimator;)Landroid/animation/ValueAnimator;
-PLcom/android/server/display/NightDisplayService;-><init>(Landroid/content/Context;)V
-PLcom/android/server/display/NightDisplayService;->applyTint(Z)V
-PLcom/android/server/display/NightDisplayService;->isUserSetupCompleted(Landroid/content/ContentResolver;I)Z
-PLcom/android/server/display/NightDisplayService;->onActivated(Z)V
-PLcom/android/server/display/NightDisplayService;->onAutoModeChanged(I)V
-PLcom/android/server/display/NightDisplayService;->onBootPhase(I)V
-PLcom/android/server/display/NightDisplayService;->onStart()V
-PLcom/android/server/display/NightDisplayService;->onStartUser(I)V
-PLcom/android/server/display/NightDisplayService;->onUserChanged(I)V
-PLcom/android/server/display/NightDisplayService;->setMatrix(I[F)V
-PLcom/android/server/display/NightDisplayService;->setUp()V
PLcom/android/server/display/OverlayDisplayAdapter$1$1;-><init>(Lcom/android/server/display/OverlayDisplayAdapter$1;Landroid/os/Handler;)V
PLcom/android/server/display/OverlayDisplayAdapter$1;-><init>(Lcom/android/server/display/OverlayDisplayAdapter;)V
PLcom/android/server/display/OverlayDisplayAdapter$1;->run()V
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
index d8eaccc..a3def14 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
@@ -602,7 +602,7 @@
if (isValidEventLocked("setAuthenticationSelected()", sessionId)) {
mEventHistory.addEvent(
new Event(Event.TYPE_AUTHENTICATION_SELECTED, null, clientState, null, null,
- null, null, null, null));
+ null, null, null, null, null, -1));
}
}
}
@@ -616,7 +616,7 @@
if (isValidEventLocked("logDatasetAuthenticationSelected()", sessionId)) {
mEventHistory.addEvent(
new Event(Event.TYPE_DATASET_AUTHENTICATION_SELECTED, selectedDataset,
- clientState, null, null, null, null, null, null));
+ clientState, null, null, null, null, null, null, null, -1));
}
}
}
@@ -628,7 +628,7 @@
synchronized (mLock) {
if (isValidEventLocked("logSaveShown()", sessionId)) {
mEventHistory.addEvent(new Event(Event.TYPE_SAVE_SHOWN, null, clientState, null,
- null, null, null, null, null));
+ null, null, null, null, null, null, -1));
}
}
}
@@ -642,7 +642,7 @@
if (isValidEventLocked("logDatasetSelected()", sessionId)) {
mEventHistory.addEvent(
new Event(Event.TYPE_DATASET_SELECTED, selectedDataset, clientState, null,
- null, null, null, null, null));
+ null, null, null, null, null, null, -1));
}
}
}
@@ -656,13 +656,15 @@
@Nullable ArrayList<AutofillId> changedFieldIds,
@Nullable ArrayList<String> changedDatasetIds,
@Nullable ArrayList<AutofillId> manuallyFilledFieldIds,
- @Nullable ArrayList<ArrayList<String>> manuallyFilledDatasetIds) {
+ @Nullable ArrayList<ArrayList<String>> manuallyFilledDatasetIds,
+ @Nullable String detectedRemoteId, int detectedFieldScore) {
synchronized (mLock) {
if (isValidEventLocked("logDatasetNotSelected()", sessionId)) {
mEventHistory.addEvent(new Event(Event.TYPE_CONTEXT_COMMITTED, null,
clientState, selectedDatasets, ignoredDatasets,
changedFieldIds, changedDatasetIds,
- manuallyFilledFieldIds, manuallyFilledDatasetIds));
+ manuallyFilledFieldIds, manuallyFilledDatasetIds,
+ detectedRemoteId, detectedFieldScore));
}
}
}
@@ -695,6 +697,7 @@
pw.print(prefix); pw.print("Default component: ");
pw.println(mContext.getString(R.string.config_defaultAutofillService));
pw.print(prefix); pw.print("Disabled: "); pw.println(mDisabled);
+ pw.print(prefix); pw.print("Field detection: "); pw.println(isFieldDetectionEnabled());
pw.print(prefix); pw.print("Setup complete: "); pw.println(mSetupComplete);
pw.print(prefix); pw.print("Last prune: "); pw.println(mLastPrune);
@@ -935,6 +938,13 @@
return false;
}
+ // TODO(b/67867469): remove once feature is finished
+ boolean isFieldDetectionEnabled() {
+ return Settings.Secure.getIntForUser(
+ mContext.getContentResolver(), Settings.Secure.AUTOFILL_FEATURE_FIELD_DETECTION, 0,
+ mUserId) == 1;
+ }
+
@Override
public String toString() {
return "AutofillManagerServiceImpl: [userId=" + mUserId
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 3564432..5823ab1 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -54,6 +54,7 @@
import android.os.SystemClock;
import android.service.autofill.AutofillService;
import android.service.autofill.Dataset;
+import android.service.autofill.FieldsDetection;
import android.service.autofill.FillContext;
import android.service.autofill.FillRequest;
import android.service.autofill.FillResponse;
@@ -492,6 +493,13 @@
}
}
+ // TODO(b/67867469): remove once feature is finished
+ if (response.getFieldsDetection() != null && !mService.isFieldDetectionEnabled()) {
+ Slog.w(TAG, "Ignoring " + response + " because field detection is disabled");
+ processNullResponseLocked(requestFlags);
+ return;
+ }
+
mService.setLastResponse(serviceUid, id, response);
int sessionFinishedState = 0;
@@ -913,11 +921,29 @@
}
}
}
- if (!hasAtLeastOneDataset) {
- if (sVerbose) Slog.v(TAG, "logContextCommittedLocked(): skipped (no datasets)");
+ final FieldsDetection fieldsDetection = lastResponse.getFieldsDetection();
+
+ if (!hasAtLeastOneDataset && fieldsDetection == null) {
+ if (sVerbose) {
+ Slog.v(TAG, "logContextCommittedLocked(): skipped (no datasets nor fields "
+ + "detection)");
+ }
return;
}
+ final AutofillId detectableFieldId;
+ final String detectableRemoteId;
+ String detectedRemoteId = null;
+ if (fieldsDetection == null) {
+ detectableFieldId = null;
+ detectableRemoteId = null;
+ } else {
+ detectableFieldId = fieldsDetection.getFieldId();
+ detectableRemoteId = fieldsDetection.getRemoteId();
+ }
+
+ int detectedFieldScore = -1;
+
for (int i = 0; i < mViewStates.size(); i++) {
final ViewState viewState = mViewStates.valueAt(i);
final int state = viewState.getState();
@@ -926,7 +952,6 @@
// - autofilled -> changedDatasetIds
// - not autofilled but matches a dataset value -> manuallyFilledIds
if ((state & ViewState.STATE_CHANGED) != 0) {
-
// Check if autofilled value was changed
if ((state & ViewState.STATE_AUTOFILLED) != 0) {
final String datasetId = viewState.getDatasetId();
@@ -958,7 +983,6 @@
changedFieldIds.add(viewState.id);
changedDatasetIds.add(datasetId);
} else {
- // Check if value match a dataset.
final AutofillValue currentValue = viewState.getCurrentValue();
if (currentValue == null) {
if (sDebug) {
@@ -967,58 +991,78 @@
}
continue;
}
- for (int j = 0; j < responseCount; j++) {
- final FillResponse response = mResponses.valueAt(j);
- final List<Dataset> datasets = response.getDatasets();
- if (datasets == null || datasets.isEmpty()) {
- if (sVerbose) Slog.v(TAG, "logContextCommitted() no datasets at " + j);
- } else {
- for (int k = 0; k < datasets.size(); k++) {
- final Dataset dataset = datasets.get(k);
- final String datasetId = dataset.getId();
- if (datasetId == null) {
- if (sVerbose) {
- Slog.v(TAG, "logContextCommitted() skipping idless dataset "
- + dataset);
- }
- } else {
- final ArrayList<AutofillValue> values = dataset.getFieldValues();
- for (int l = 0; l < values.size(); l++) {
- final AutofillValue candidate = values.get(l);
- if (currentValue.equals(candidate)) {
- if (sDebug) {
- Slog.d(TAG, "field " + viewState.id
- + " was manually filled with value set by "
- + "dataset " + datasetId);
- }
- if (manuallyFilledIds == null) {
- manuallyFilledIds = new ArrayMap<>();
- }
- ArraySet<String> datasetIds =
- manuallyFilledIds.get(viewState.id);
- if (datasetIds == null) {
- datasetIds = new ArraySet<>(1);
- manuallyFilledIds.put(viewState.id, datasetIds);
- }
- datasetIds.add(datasetId);
- }
- }
- if (mSelectedDatasetIds == null
- || !mSelectedDatasetIds.contains(datasetId)) {
- if (sVerbose) {
- Slog.v(TAG, "adding ignored dataset " + datasetId);
- }
- if (ignoredDatasets == null) {
- ignoredDatasets = new ArraySet<>();
- }
- ignoredDatasets.add(datasetId);
- }
+ // Check if value match a dataset.
+ if (hasAtLeastOneDataset) {
+ for (int j = 0; j < responseCount; j++) {
+ final FillResponse response = mResponses.valueAt(j);
+ final List<Dataset> datasets = response.getDatasets();
+ if (datasets == null || datasets.isEmpty()) {
+ if (sVerbose) {
+ Slog.v(TAG, "logContextCommitted() no datasets at " + j);
}
- }
- }
+ } else {
+ for (int k = 0; k < datasets.size(); k++) {
+ final Dataset dataset = datasets.get(k);
+ final String datasetId = dataset.getId();
+ if (datasetId == null) {
+ if (sVerbose) {
+ Slog.v(TAG, "logContextCommitted() skipping idless "
+ + "dataset " + dataset);
+ }
+ } else {
+ final ArrayList<AutofillValue> values =
+ dataset.getFieldValues();
+ for (int l = 0; l < values.size(); l++) {
+ final AutofillValue candidate = values.get(l);
+ if (currentValue.equals(candidate)) {
+ if (sDebug) {
+ Slog.d(TAG, "field " + viewState.id + " was "
+ + "manually filled with value set by "
+ + "dataset " + datasetId);
+ }
+ if (manuallyFilledIds == null) {
+ manuallyFilledIds = new ArrayMap<>();
+ }
+ ArraySet<String> datasetIds =
+ manuallyFilledIds.get(viewState.id);
+ if (datasetIds == null) {
+ datasetIds = new ArraySet<>(1);
+ manuallyFilledIds.put(viewState.id, datasetIds);
+ }
+ datasetIds.add(datasetId);
+ }
+ } // for l
+ if (mSelectedDatasetIds == null
+ || !mSelectedDatasetIds.contains(datasetId)) {
+ if (sVerbose) {
+ Slog.v(TAG, "adding ignored dataset " + datasetId);
+ }
+ if (ignoredDatasets == null) {
+ ignoredDatasets = new ArraySet<>();
+ }
+ ignoredDatasets.add(datasetId);
+ } // if
+ } // if
+ } // for k
+ } // else
+ } // for j
}
- }
- }
+
+ // Check if detectable field changed.
+ if (detectableFieldId != null && detectableFieldId.equals(viewState.id)
+ && currentValue.isText() && currentValue.getTextValue() != null) {
+ final String actualValue = currentValue.getTextValue().toString();
+ final String expectedValue = fieldsDetection.getValue();
+ if (actualValue.equalsIgnoreCase(expectedValue)) {
+ detectedRemoteId = detectableRemoteId;
+ detectedFieldScore = 0;
+ } else if (sVerbose) {
+ Slog.v(TAG, "Detection mismatch for field " + detectableFieldId);
+ }
+ // TODO(b/67867469): set score on partial hits
+ }
+ } // else
+ } // else
}
if (sVerbose) {
@@ -1027,7 +1071,10 @@
+ ", ignoredDatasetIds=" + ignoredDatasets
+ ", changedAutofillIds=" + changedFieldIds
+ ", changedDatasetIds=" + changedDatasetIds
- + ", manuallyFilledIds=" + manuallyFilledIds);
+ + ", manuallyFilledIds=" + manuallyFilledIds
+ + ", detectableFieldId=" + detectableFieldId
+ + ", detectedFieldScore=" + detectedFieldScore
+ );
}
ArrayList<AutofillId> manuallyFilledFieldIds = null;
@@ -1045,9 +1092,11 @@
manuallyFilledDatasetIds.add(new ArrayList<>(datasetIds));
}
}
+
mService.logContextCommitted(id, mClientState, mSelectedDatasetIds, ignoredDatasets,
changedFieldIds, changedDatasetIds,
- manuallyFilledFieldIds, manuallyFilledDatasetIds);
+ manuallyFilledFieldIds, manuallyFilledDatasetIds,
+ detectedRemoteId, detectedFieldScore);
}
/**
@@ -1535,6 +1584,10 @@
viewState = new ViewState(this, id, this,
isIgnored ? ViewState.STATE_IGNORED : ViewState.STATE_INITIAL);
mViewStates.put(id, viewState);
+
+ // TODO(b/67867469): for optimization purposes, should also ignore if change is
+ // detectable, and batch-send them when the session is finished (but that will
+ // require tracking detectable fields on AutofillManager)
if (isIgnored) {
if (sDebug) Slog.d(TAG, "updateLocked(): ignoring view " + id);
return;
diff --git a/services/autofill/java/com/android/server/autofill/ViewState.java b/services/autofill/java/com/android/server/autofill/ViewState.java
index 1d8110f..832a66b 100644
--- a/services/autofill/java/com/android/server/autofill/ViewState.java
+++ b/services/autofill/java/com/android/server/autofill/ViewState.java
@@ -134,7 +134,11 @@
}
String getStateAsString() {
- return DebugUtils.flagsToString(ViewState.class, "STATE_", mState);
+ return getStateAsString(mState);
+ }
+
+ static String getStateAsString(int state) {
+ return DebugUtils.flagsToString(ViewState.class, "STATE_", state);
}
void setState(int state) {
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index e609bd7..831c9cb 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -1356,31 +1356,6 @@
}
}
- public void notifyOemHookRawEventForSubscriber(int subId, byte[] rawData) {
- if (!checkNotifyPermission("notifyOemHookRawEventForSubscriber")) {
- return;
- }
-
- synchronized (mRecords) {
- for (Record r : mRecords) {
- if (VDBG) {
- log("notifyOemHookRawEventForSubscriber: r=" + r + " subId=" + subId);
- }
- if ((r.matchPhoneStateListenerEvent(
- PhoneStateListener.LISTEN_OEM_HOOK_RAW_EVENT)) &&
- ((r.subId == subId) ||
- (r.subId == SubscriptionManager.DEFAULT_SUBSCRIPTION_ID))) {
- try {
- r.callback.onOemHookRawEvent(rawData);
- } catch (RemoteException ex) {
- mRemoveList.add(r.binder);
- }
- }
- }
- handleRemoveListLocked();
- }
- }
-
@Override
public void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
final IndentingPrintWriter pw = new IndentingPrintWriter(writer, " ");
@@ -1673,11 +1648,6 @@
android.Manifest.permission.READ_PRECISE_PHONE_STATE, null);
}
-
- if ((events & PhoneStateListener.LISTEN_OEM_HOOK_RAW_EVENT) != 0) {
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, null);
- }
}
private void handleRemoveListLocked() {
diff --git a/services/core/java/com/android/server/am/ActivityDisplay.java b/services/core/java/com/android/server/am/ActivityDisplay.java
index c04ddf8..ea7b443 100644
--- a/services/core/java/com/android/server/am/ActivityDisplay.java
+++ b/services/core/java/com/android/server/am/ActivityDisplay.java
@@ -404,8 +404,8 @@
if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY
|| windowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY) {
- return supportsSplitScreen && WindowConfiguration.supportSplitScreenWindowingMode(
- windowingMode, activityType);
+ return supportsSplitScreen
+ && WindowConfiguration.supportSplitScreenWindowingMode(activityType);
}
if (!supportsFreeform && windowingMode == WINDOWING_MODE_FREEFORM) {
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 842a5b8..02ba6a0 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -3889,7 +3889,7 @@
}
if (app.info.isPrivilegedApp() &&
- !SystemProperties.getBoolean("pm.dexopt.priv-apps", true)) {
+ SystemProperties.getBoolean("pm.dexopt.priv-apps-oob", false)) {
runtimeFlags |= Zygote.DISABLE_VERIFIER;
runtimeFlags |= Zygote.ONLY_USE_SYSTEM_OAT_FILES;
}
@@ -7075,10 +7075,12 @@
}
// We deprecated Build.SERIAL and it is not accessible to
- // apps that target the v2 security sandbox. Since access to
- // the serial is now behind a permission we push down the value.
- String buildSerial = appInfo.targetSandboxVersion < 2
- ? sTheRealBuildSerial : Build.UNKNOWN;
+ // apps that target the v2 security sandbox and to apps that
+ // target APIs higher than O MR1. Since access to the serial
+ // is now behind a permission we push down the value.
+ final String buildSerial = (appInfo.targetSandboxVersion < 2
+ && appInfo.targetSdkVersion <= Build.VERSION_CODES.O_MR1)
+ ? sTheRealBuildSerial : Build.UNKNOWN;
// Check if this is a secondary process that should be incorporated into some
// currently active instrumentation. (Note we do this AFTER all of the profiling
diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java
index 1db2629..b2308d5 100644
--- a/services/core/java/com/android/server/am/ActivityRecord.java
+++ b/services/core/java/com/android/server/am/ActivityRecord.java
@@ -201,6 +201,8 @@
private static final String TAG_STATES = TAG + POSTFIX_STATES;
private static final String TAG_SWITCH = TAG + POSTFIX_SWITCH;
private static final String TAG_VISIBILITY = TAG + POSTFIX_VISIBILITY;
+ // TODO(b/67864419): Remove once recents component is overridden
+ private static final String LEGACY_RECENTS_PACKAGE_NAME = "com.android.systemui.recents";
private static final boolean SHOW_ACTIVITY_START_TIME = true;
@@ -1057,7 +1059,8 @@
// We only allow home activities to be resizeable if they explicitly requested it.
info.resizeMode = RESIZE_MODE_UNRESIZEABLE;
}
- } else if (service.getRecentTasks().isRecentsComponent(realActivity, appInfo.uid)) {
+ } else if (realActivity.getClassName().contains(LEGACY_RECENTS_PACKAGE_NAME) ||
+ service.getRecentTasks().isRecentsComponent(realActivity, appInfo.uid)) {
activityType = ACTIVITY_TYPE_RECENTS;
} else if (options != null && options.getLaunchActivityType() == ACTIVITY_TYPE_ASSISTANT
&& canLaunchAssistActivity(launchedFromPackage)) {
@@ -2179,26 +2182,6 @@
mTmpConfig.unset();
computeBounds(mTmpBounds);
if (mTmpBounds.equals(mBounds)) {
- final ActivityStack stack = getStack();
- if (!mBounds.isEmpty() || task == null || stack == null || !task.mFullscreen) {
- // We don't want to influence the override configuration here if our task is in
- // multi-window mode or there is a bounds specified to calculate the override
- // config. In both of this cases the app should be compatible with whatever the
- // current configuration is or will be.
- return;
- }
-
- // Currently limited to the top activity for now to avoid situations where non-top
- // visible activity and top might have conflicting requests putting the non-top activity
- // windows in an odd state.
- final ActivityRecord top = mStackSupervisor.topRunningActivityLocked();
- final Configuration parentConfig = getParent().getConfiguration();
- if (top != this || isConfigurationCompatible(parentConfig)) {
- onOverrideConfigurationChanged(mTmpConfig);
- } else if (isConfigurationCompatible(
- mLastReportedConfiguration.getMergedConfiguration())) {
- onOverrideConfigurationChanged(mLastReportedConfiguration.getMergedConfiguration());
- }
return;
}
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index 88403fc..8cc584e 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -20,6 +20,7 @@
import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
@@ -870,6 +871,29 @@
}
}
+ /**
+ * @param reason The reason for moving the stack to the back.
+ * @param task If non-null, the task will be moved to the bottom of the stack.
+ **/
+ void moveToBack(String reason, TaskRecord task) {
+ if (!isAttached()) {
+ return;
+ }
+
+ getDisplay().positionChildAtBottom(this);
+ mStackSupervisor.setFocusStackUnchecked(reason, getDisplay().getTopStack());
+ if (task != null) {
+ insertTaskAtBottom(task);
+ return;
+ } else {
+ task = bottomTask();
+ if (task != null) {
+ mWindowContainerController.positionChildAtBottom(
+ task.getWindowContainerController(), true /* includingParents */);
+ }
+ }
+ }
+
boolean isFocusable() {
if (getWindowConfiguration().canReceiveKeys()) {
return true;
@@ -1778,6 +1802,15 @@
return inPinnedWindowingMode();
}
+ /** @return True if the resizing of the primary-split-screen stack affects this stack size. */
+ boolean affectedBySplitScreenResize() {
+ if (!supportsSplitScreenWindowingMode()) {
+ return false;
+ }
+ final int windowingMode = getWindowingMode();
+ return windowingMode != WINDOWING_MODE_FREEFORM && windowingMode != WINDOWING_MODE_PINNED;
+ }
+
/**
* @return the top most visible activity that wants to dismiss Keyguard
*/
@@ -2581,6 +2614,9 @@
if (position >= mTaskHistory.size()) {
insertTaskAtTop(task, null);
return;
+ } else if (position <= 0) {
+ insertTaskAtBottom(task);
+ return;
}
position = getAdjustedPositionForTask(task, position, null /* starting */);
mTaskHistory.remove(task);
@@ -2601,6 +2637,16 @@
true /* includingParents */);
}
+ private void insertTaskAtBottom(TaskRecord task) {
+ // Unlike insertTaskAtPosition, this will also position parents of the windowcontroller.
+ mTaskHistory.remove(task);
+ final int position = getAdjustedPositionForTask(task, 0, null);
+ mTaskHistory.add(position, task);
+ updateTaskMovement(task, true);
+ mWindowContainerController.positionChildAtBottom(task.getWindowContainerController(),
+ true /* includingParents */);
+ }
+
final void startActivityLocked(ActivityRecord r, ActivityRecord focusedTopActivity,
boolean newTask, boolean keepCurTransition, ActivityOptions options) {
TaskRecord rTask = r.getTask();
@@ -4370,8 +4416,7 @@
updateTaskMovement(tr, false);
mWindowManager.prepareAppTransition(TRANSIT_TASK_TO_BACK, false);
- mWindowContainerController.positionChildAtBottom(tr.getWindowContainerController(),
- true /* includingParents */);
+ moveToBack("moveTaskToBackLocked", tr);
if (inPinnedWindowingMode()) {
mStackSupervisor.removeStack(this);
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index a0aeb63..062083c 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -2441,7 +2441,7 @@
Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "am.resizeStack_" + stack.mStackId);
mWindowManager.deferSurfaceLayout();
try {
- if (stack.supportsSplitScreenWindowingMode()) {
+ if (stack.affectedBySplitScreenResize()) {
if (bounds == null && stack.inSplitScreenWindowingMode()) {
// null bounds = fullscreen windowing mode...at least for now.
stack.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
@@ -2541,8 +2541,6 @@
null, mTmpOptions, task, task.getActivityType(), onTop);
if (onTop) {
- final int returnToType =
- toDisplay.getTopVisibleStackActivityType(WINDOWING_MODE_PINNED);
final boolean isTopTask = i == (size - 1);
// Defer resume until all the tasks have been moved to the fullscreen stack
task.reparent(toStack, ON_TOP, REPARENT_MOVE_STACK_TO_FRONT,
@@ -2631,7 +2629,7 @@
if (current.getWindowingMode() == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
continue;
}
- if (!current.supportsSplitScreenWindowingMode()) {
+ if (!current.affectedBySplitScreenResize()) {
continue;
}
// Need to set windowing mode here before we try to get the dock bounds.
@@ -4177,8 +4175,8 @@
}
// Handle incorrect launch/move to secondary display if needed.
- final boolean launchOnSecondaryDisplayFailed;
if (isSecondaryDisplayPreferred) {
+ final boolean launchOnSecondaryDisplayFailed;
final int actualDisplayId = task.getStack().mDisplayId;
if (!task.canBeLaunchedOnDisplay(actualDisplayId)) {
// The task landed on an inappropriate display somehow, move it to the default
@@ -4193,34 +4191,34 @@
|| (preferredDisplayId != INVALID_DISPLAY
&& preferredDisplayId != actualDisplayId);
}
- } else {
- // The task wasn't requested to be on a secondary display.
- launchOnSecondaryDisplayFailed = false;
- }
-
- final ActivityRecord topActivity = task.getTopActivity();
- if (launchOnSecondaryDisplayFailed
- || !task.supportsSplitScreenWindowingMode() || forceNonResizable) {
if (launchOnSecondaryDisplayFailed) {
// Display a warning toast that we tried to put a non-resizeable task on a secondary
// display with config different from global config.
mService.mTaskChangeNotificationController
.notifyActivityLaunchOnSecondaryDisplayFailed();
- } else {
- // Display a warning toast that we tried to put a non-dockable task in the docked
- // stack.
- mService.mTaskChangeNotificationController.notifyActivityDismissingDockedStack();
+ return;
}
+ }
+
+ final ActivityRecord topActivity = task.getTopActivity();
+ if (!task.supportsSplitScreenWindowingMode() || forceNonResizable) {
+ // Display a warning toast that we tried to put a non-dockable task in the docked
+ // stack.
+ mService.mTaskChangeNotificationController.notifyActivityDismissingDockedStack();
// Dismiss docked stack. If task appeared to be in docked stack but is not resizable -
// we need to move it to top of fullscreen stack, otherwise it will be covered.
- final ActivityStack dockedStack = task.getStack().getDisplay().getSplitScreenPrimaryStack();
+ final ActivityStack dockedStack =
+ task.getStack().getDisplay().getSplitScreenPrimaryStack();
if (dockedStack != null) {
moveTasksToFullscreenStackLocked(dockedStack, actualStack == dockedStack);
}
- } else if (topActivity != null && topActivity.isNonResizableOrForcedResizable()
- && !topActivity.noDisplay) {
+ return;
+ }
+
+ if (topActivity != null && topActivity.isNonResizableOrForcedResizable()
+ && !topActivity.noDisplay) {
final String packageName = topActivity.appInfo.packageName;
final int reason = isSecondaryDisplayPreferred
? FORCED_RESIZEABLE_REASON_SECONDARY_DISPLAY
@@ -4489,9 +4487,12 @@
"startActivityFromRecents: Task " + taskId + " not found.");
}
- // We always want to return to the home activity instead of the recents activity from
- // whatever is started from the recents activity, so move the home stack forward.
- moveHomeStackToFront("startActivityFromRecents");
+ if (windowingMode != WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
+ // We always want to return to the home activity instead of the recents activity
+ // from whatever is started from the recents activity, so move the home stack
+ // forward.
+ moveHomeStackToFront("startActivityFromRecents");
+ }
// If the user must confirm credentials (e.g. when first launching a work app and the
// Work Challenge is present) let startActivityInPackage handle the intercepting.
diff --git a/services/core/java/com/android/server/am/KeyguardController.java b/services/core/java/com/android/server/am/KeyguardController.java
index c3fed17..76b4679 100644
--- a/services/core/java/com/android/server/am/KeyguardController.java
+++ b/services/core/java/com/android/server/am/KeyguardController.java
@@ -41,10 +41,8 @@
import android.os.Trace;
import android.util.Slog;
import android.util.proto.ProtoOutputStream;
-
import com.android.internal.policy.IKeyguardDismissCallback;
import com.android.server.wm.WindowManagerService;
-
import java.io.PrintWriter;
/**
@@ -236,24 +234,33 @@
final ActivityRecord lastDismissingKeyguardActivity = mDismissingKeyguardActivity;
mOccluded = false;
mDismissingKeyguardActivity = null;
- final ActivityDisplay display = mStackSupervisor.getDefaultDisplay();
- for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
- final ActivityStack stack = display.getChildAt(stackNdx);
- // Only the focused stack top activity may control occluded state
- if (mStackSupervisor.isFocusedStack(stack)) {
+ for (int displayNdx = mStackSupervisor.getChildCount() - 1; displayNdx >= 0; displayNdx--) {
+ final ActivityDisplay display = mStackSupervisor.getChildAt(displayNdx);
+ for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
+ final ActivityStack stack = display.getChildAt(stackNdx);
- // A dismissing activity occludes Keyguard in the insecure case for legacy reasons.
- final ActivityRecord topDismissing = stack.getTopDismissingKeyguardActivity();
- mOccluded = stack.topActivityOccludesKeyguard()
- || (topDismissing != null
- && stack.topRunningActivityLocked() == topDismissing
- && canShowWhileOccluded(true /* dismissKeyguard */,
- false /* showWhenLocked */));
- }
- if (mDismissingKeyguardActivity == null
- && stack.getTopDismissingKeyguardActivity() != null) {
- mDismissingKeyguardActivity = stack.getTopDismissingKeyguardActivity();
+ // Only the top activity of the focused stack on the default display may control
+ // occluded state.
+ if (display.mDisplayId == DEFAULT_DISPLAY
+ && mStackSupervisor.isFocusedStack(stack)) {
+
+ // A dismissing activity occludes Keyguard in the insecure case for legacy
+ // reasons.
+ final ActivityRecord topDismissing = stack.getTopDismissingKeyguardActivity();
+ mOccluded =
+ stack.topActivityOccludesKeyguard()
+ || (topDismissing != null
+ && stack.topRunningActivityLocked() == topDismissing
+ && canShowWhileOccluded(
+ true /* dismissKeyguard */,
+ false /* showWhenLocked */));
+ }
+
+ if (mDismissingKeyguardActivity == null
+ && stack.getTopDismissingKeyguardActivity() != null) {
+ mDismissingKeyguardActivity = stack.getTopDismissingKeyguardActivity();
+ }
}
}
mOccluded |= mWindowManager.isShowingDream();
diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java
index c5514fb..40b1cac 100644
--- a/services/core/java/com/android/server/am/TaskRecord.java
+++ b/services/core/java/com/android/server/am/TaskRecord.java
@@ -1139,6 +1139,9 @@
mActivities.remove(newTop);
mActivities.add(newTop);
+
+ // Make sure window manager is aware of the position change.
+ mWindowContainerController.positionChildAtTop(newTop.mWindowContainerController);
updateEffectiveIntent();
setFrontOfTask();
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 3f23737..15a418dc 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -1345,8 +1345,9 @@
} else {
final int maybeActiveStreamType = getActiveStreamType(suggestedStreamType);
final boolean activeForReal;
- if (maybeActiveStreamType == AudioSystem.STREAM_MUSIC) {
- activeForReal = isAfMusicActiveRecently(0);
+ if (maybeActiveStreamType == AudioSystem.STREAM_RING
+ || maybeActiveStreamType == AudioSystem.STREAM_NOTIFICATION) {
+ activeForReal = wasStreamActiveRecently(maybeActiveStreamType, 0);
} else {
activeForReal = AudioSystem.isStreamActive(maybeActiveStreamType, 0);
}
@@ -3883,13 +3884,13 @@
/**
* For code clarity for getActiveStreamType(int)
- * @param delay_ms max time since last STREAM_MUSIC activity to consider
- * @return true if STREAM_MUSIC is active in streams handled by AudioFlinger now or
+ * @param delay_ms max time since last stream activity to consider
+ * @return true if stream is active in streams handled by AudioFlinger now or
* in the last "delay_ms" ms.
*/
- private boolean isAfMusicActiveRecently(int delay_ms) {
- return AudioSystem.isStreamActive(AudioSystem.STREAM_MUSIC, delay_ms)
- || AudioSystem.isStreamActiveRemotely(AudioSystem.STREAM_MUSIC, delay_ms);
+ private boolean wasStreamActiveRecently(int stream, int delay_ms) {
+ return AudioSystem.isStreamActive(stream, delay_ms)
+ || AudioSystem.isStreamActiveRemotely(stream, delay_ms);
}
private int getActiveStreamType(int suggestedStreamType) {
@@ -3910,21 +3911,30 @@
return AudioSystem.STREAM_VOICE_CALL;
}
} else if (suggestedStreamType == AudioManager.USE_DEFAULT_STREAM_TYPE) {
- if (isAfMusicActiveRecently(sStreamOverrideDelayMs)) {
+ if (wasStreamActiveRecently(AudioSystem.STREAM_RING, sStreamOverrideDelayMs)) {
if (DEBUG_VOL)
- Log.v(TAG, "getActiveStreamType: Forcing STREAM_MUSIC stream active");
+ Log.v(TAG, "getActiveStreamType: Forcing STREAM_RING stream active");
+ return AudioSystem.STREAM_RING;
+ } else if (wasStreamActiveRecently(
+ AudioSystem.STREAM_NOTIFICATION, sStreamOverrideDelayMs)) {
+ if (DEBUG_VOL)
+ Log.v(TAG, "getActiveStreamType: Forcing STREAM_NOTIFICATION stream active");
+ return AudioSystem.STREAM_NOTIFICATION;
+ } else {
+ if (DEBUG_VOL)
+ Log.v(TAG, "getActiveStreamType: Forcing STREAM_MUSIC b/c default");
return AudioSystem.STREAM_MUSIC;
- } else {
- if (DEBUG_VOL)
- Log.v(TAG, "getActiveStreamType: Forcing STREAM_RING b/c default");
- return AudioSystem.STREAM_RING;
}
- } else if (isAfMusicActiveRecently(0)) {
+ } else if (
+ wasStreamActiveRecently(AudioSystem.STREAM_NOTIFICATION, sStreamOverrideDelayMs)) {
if (DEBUG_VOL)
- Log.v(TAG, "getActiveStreamType: Forcing STREAM_MUSIC stream active");
- return AudioSystem.STREAM_MUSIC;
+ Log.v(TAG, "getActiveStreamType: Forcing STREAM_NOTIFICATION stream active");
+ return AudioSystem.STREAM_NOTIFICATION;
+ } else if (wasStreamActiveRecently(AudioSystem.STREAM_RING, sStreamOverrideDelayMs)) {
+ if (DEBUG_VOL)
+ Log.v(TAG, "getActiveStreamType: Forcing STREAM_RING stream active");
+ return AudioSystem.STREAM_RING;
}
- break;
default:
if (isInCommunication()) {
if (AudioSystem.getForceUse(AudioSystem.FOR_COMMUNICATION)
@@ -3935,20 +3945,26 @@
if (DEBUG_VOL) Log.v(TAG, "getActiveStreamType: Forcing STREAM_VOICE_CALL");
return AudioSystem.STREAM_VOICE_CALL;
}
- } else if (AudioSystem.isStreamActive(AudioSystem.STREAM_NOTIFICATION,
- sStreamOverrideDelayMs) ||
- AudioSystem.isStreamActive(AudioSystem.STREAM_RING,
- sStreamOverrideDelayMs)) {
+ } else if (AudioSystem.isStreamActive(
+ AudioSystem.STREAM_NOTIFICATION, sStreamOverrideDelayMs)) {
if (DEBUG_VOL) Log.v(TAG, "getActiveStreamType: Forcing STREAM_NOTIFICATION");
return AudioSystem.STREAM_NOTIFICATION;
+ } else if (AudioSystem.isStreamActive(
+ AudioSystem.STREAM_RING, sStreamOverrideDelayMs)) {
+ if (DEBUG_VOL) Log.v(TAG, "getActiveStreamType: Forcing STREAM_RING");
+ return AudioSystem.STREAM_RING;
} else if (suggestedStreamType == AudioManager.USE_DEFAULT_STREAM_TYPE) {
- if (isAfMusicActiveRecently(sStreamOverrideDelayMs)) {
- if (DEBUG_VOL) Log.v(TAG, "getActiveStreamType: forcing STREAM_MUSIC");
- return AudioSystem.STREAM_MUSIC;
- } else {
- if (DEBUG_VOL) Log.v(TAG,
- "getActiveStreamType: using STREAM_NOTIFICATION as default");
+ if (AudioSystem.isStreamActive(
+ AudioSystem.STREAM_NOTIFICATION, sStreamOverrideDelayMs)) {
+ if (DEBUG_VOL) Log.v(TAG, "getActiveStreamType: Forcing STREAM_NOTIFICATION");
return AudioSystem.STREAM_NOTIFICATION;
+ } else if (AudioSystem.isStreamActive(
+ AudioSystem.STREAM_RING, sStreamOverrideDelayMs)) {
+ if (DEBUG_VOL) Log.v(TAG, "getActiveStreamType: Forcing STREAM_RING");
+ return AudioSystem.STREAM_RING;
+ } else {
+ if (DEBUG_VOL) Log.v(TAG, "getActiveStreamType: using STREAM_MUSIC as default");
+ return AudioSystem.STREAM_MUSIC;
}
}
break;
@@ -6349,10 +6365,10 @@
// stream override timeout when adjusting volume
//---------------------------------------------------------------------------------
- // AudioService.getActiveStreamType() will return:
// - STREAM_NOTIFICATION on tablets during this period after a notification stopped
- // - STREAM_MUSIC on phones during this period after music or talkback/voice search prompt
- // stopped
+ // - STREAM_RING on phones during this period after a notification stopped
+ // - STREAM_MUSIC otherwise
+
private static final int DEFAULT_STREAM_TYPE_OVERRIDE_DELAY_MS = 0;
private static final int TOUCH_EXPLORE_STREAM_TYPE_OVERRIDE_DELAY_MS = 1000;
diff --git a/services/core/java/com/android/server/connectivity/IpConnectivityEventBuilder.java b/services/core/java/com/android/server/connectivity/IpConnectivityEventBuilder.java
index a011692b..397af7b 100644
--- a/services/core/java/com/android/server/connectivity/IpConnectivityEventBuilder.java
+++ b/services/core/java/com/android/server/connectivity/IpConnectivityEventBuilder.java
@@ -46,7 +46,6 @@
import com.android.server.connectivity.metrics.nano.IpConnectivityLogClass;
import com.android.server.connectivity.metrics.nano.IpConnectivityLogClass.IpConnectivityEvent;
import com.android.server.connectivity.metrics.nano.IpConnectivityLogClass.IpConnectivityLog;
-import com.android.server.connectivity.metrics.nano.IpConnectivityLogClass.NetworkId;
import com.android.server.connectivity.metrics.nano.IpConnectivityLogClass.Pair;
import java.io.IOException;
import java.util.ArrayList;
@@ -128,6 +127,11 @@
wakeupStats.nonApplicationWakeups = in.nonApplicationWakeups;
wakeupStats.applicationWakeups = in.applicationWakeups;
wakeupStats.noUidWakeups = in.noUidWakeups;
+ wakeupStats.l2UnicastCount = in.l2UnicastCount;
+ wakeupStats.l2MulticastCount = in.l2MulticastCount;
+ wakeupStats.l2BroadcastCount = in.l2BroadcastCount;
+ wakeupStats.ethertypeCounts = toPairArray(in.ethertypes);
+ wakeupStats.ipNextHeaderCounts = toPairArray(in.ipNextHeaders);
final IpConnectivityEvent out = buildEvent(0, 0, in.iface);
out.setWakeupStats(wakeupStats);
return out;
@@ -242,7 +246,6 @@
private static void setNetworkEvent(IpConnectivityEvent out, NetworkEvent in) {
IpConnectivityLogClass.NetworkEvent networkEvent =
new IpConnectivityLogClass.NetworkEvent();
- networkEvent.networkId = netIdOf(in.netId);
networkEvent.eventType = in.eventType;
networkEvent.latencyMs = (int) in.durationMs;
out.setNetworkEvent(networkEvent);
@@ -321,12 +324,6 @@
return pairs;
}
- private static NetworkId netIdOf(int netid) {
- final NetworkId ni = new NetworkId();
- ni.networkId = netid;
- return ni;
- }
-
private static int ipSupportOf(DefaultNetworkEvent in) {
if (in.ipv4 && in.ipv6) {
return IpConnectivityLogClass.DefaultNetworkEvent.DUAL;
diff --git a/services/core/java/com/android/server/connectivity/IpConnectivityMetrics.java b/services/core/java/com/android/server/connectivity/IpConnectivityMetrics.java
index 5cc390a..f427819 100644
--- a/services/core/java/com/android/server/connectivity/IpConnectivityMetrics.java
+++ b/services/core/java/com/android/server/connectivity/IpConnectivityMetrics.java
@@ -23,8 +23,6 @@
import android.net.metrics.ApfProgramEvent;
import android.net.metrics.IpConnectivityLog;
import android.os.Binder;
-import android.os.IBinder;
-import android.os.Parcelable;
import android.os.Process;
import android.provider.Settings;
import android.text.TextUtils;
@@ -322,22 +320,22 @@
}
@Override
- public boolean registerNetdEventCallback(INetdEventCallback callback) {
+ public boolean addNetdEventCallback(int callerType, INetdEventCallback callback) {
enforceNetdEventListeningPermission();
if (mNetdListener == null) {
return false;
}
- return mNetdListener.registerNetdEventCallback(callback);
+ return mNetdListener.addNetdEventCallback(callerType, callback);
}
@Override
- public boolean unregisterNetdEventCallback() {
+ public boolean removeNetdEventCallback(int callerType) {
enforceNetdEventListeningPermission();
if (mNetdListener == null) {
// if the service is null, we aren't registered anyway
return true;
}
- return mNetdListener.unregisterNetdEventCallback();
+ return mNetdListener.removeNetdEventCallback(callerType);
}
};
diff --git a/services/core/java/com/android/server/connectivity/NetdEventListenerService.java b/services/core/java/com/android/server/connectivity/NetdEventListenerService.java
index 61b11e1..4bdbbe3 100644
--- a/services/core/java/com/android/server/connectivity/NetdEventListenerService.java
+++ b/services/core/java/com/android/server/connectivity/NetdEventListenerService.java
@@ -58,7 +58,6 @@
private static final String TAG = NetdEventListenerService.class.getSimpleName();
private static final boolean DBG = false;
- private static final boolean VDBG = false;
// Rate limit connect latency logging to 1 measurement per 15 seconds (5760 / day) with maximum
// bursts of 5000 measurements.
@@ -98,21 +97,55 @@
@GuardedBy("this")
private final TokenBucket mConnectTb =
new TokenBucket(CONNECT_LATENCY_FILL_RATE, CONNECT_LATENCY_BURST_LIMIT);
- // Callback should only be registered/unregistered when logging is being enabled/disabled in DPM
- // by the device owner. It's DevicePolicyManager's responsibility to ensure that.
- @GuardedBy("this")
- private INetdEventCallback mNetdEventCallback;
- public synchronized boolean registerNetdEventCallback(INetdEventCallback callback) {
- mNetdEventCallback = callback;
+
+ /**
+ * There are only 2 possible callbacks.
+ *
+ * mNetdEventCallbackList[CALLBACK_CALLER_DEVICE_POLICY].
+ * Callback registered/unregistered when logging is being enabled/disabled in DPM
+ * by the device owner. It's DevicePolicyManager's responsibility to ensure that.
+ *
+ * mNetdEventCallbackList[CALLBACK_CALLER_NETWORK_WATCHLIST]
+ * Callback registered/unregistered by NetworkWatchlistService.
+ */
+ @GuardedBy("this")
+ private static final int[] ALLOWED_CALLBACK_TYPES = {
+ INetdEventCallback.CALLBACK_CALLER_DEVICE_POLICY,
+ INetdEventCallback.CALLBACK_CALLER_NETWORK_WATCHLIST
+ };
+
+ @GuardedBy("this")
+ private INetdEventCallback[] mNetdEventCallbackList =
+ new INetdEventCallback[ALLOWED_CALLBACK_TYPES.length];
+
+ public synchronized boolean addNetdEventCallback(int callerType, INetdEventCallback callback) {
+ if (!isValidCallerType(callerType)) {
+ Log.e(TAG, "Invalid caller type: " + callerType);
+ return false;
+ }
+ mNetdEventCallbackList[callerType] = callback;
return true;
}
- public synchronized boolean unregisterNetdEventCallback() {
- mNetdEventCallback = null;
+ public synchronized boolean removeNetdEventCallback(int callerType) {
+ if (!isValidCallerType(callerType)) {
+ Log.e(TAG, "Invalid caller type: " + callerType);
+ return false;
+ }
+ mNetdEventCallbackList[callerType] = null;
return true;
}
+ private static boolean isValidCallerType(int callerType) {
+ for (int i = 0; i < ALLOWED_CALLBACK_TYPES.length; i++) {
+ if (callerType == ALLOWED_CALLBACK_TYPES[i]) {
+ return true;
+ }
+ }
+ return false;
+ }
+
public NetdEventListenerService(Context context) {
this(context.getSystemService(ConnectivityManager.class));
}
@@ -164,13 +197,13 @@
public synchronized void onDnsEvent(int netId, int eventType, int returnCode, int latencyMs,
String hostname, String[] ipAddresses, int ipAddressesCount, int uid)
throws RemoteException {
- maybeVerboseLog("onDnsEvent(%d, %d, %d, %dms)", netId, eventType, returnCode, latencyMs);
-
long timestamp = System.currentTimeMillis();
getMetricsForNetwork(timestamp, netId).addDnsResult(eventType, returnCode, latencyMs);
- if (mNetdEventCallback != null) {
- mNetdEventCallback.onDnsEvent(hostname, ipAddresses, ipAddressesCount, timestamp, uid);
+ for (INetdEventCallback callback : mNetdEventCallbackList) {
+ if (callback != null) {
+ callback.onDnsEvent(hostname, ipAddresses, ipAddressesCount, timestamp, uid);
+ }
}
}
@@ -179,22 +212,23 @@
// This method must not block or perform long-running operations.
public synchronized void onConnectEvent(int netId, int error, int latencyMs, String ipAddr,
int port, int uid) throws RemoteException {
- maybeVerboseLog("onConnectEvent(%d, %d, %dms)", netId, error, latencyMs);
-
long timestamp = System.currentTimeMillis();
getMetricsForNetwork(timestamp, netId).addConnectResult(error, latencyMs, ipAddr);
- if (mNetdEventCallback != null) {
- mNetdEventCallback.onConnectEvent(ipAddr, port, timestamp, uid);
+ for (INetdEventCallback callback : mNetdEventCallbackList) {
+ if (callback != null) {
+ // TODO(rickywai): Remove this checking to collect ip in watchlist.
+ if (callback ==
+ mNetdEventCallbackList[INetdEventCallback.CALLBACK_CALLER_DEVICE_POLICY]) {
+ callback.onConnectEvent(ipAddr, port, timestamp, uid);
+ }
+ }
}
}
@Override
- public synchronized void onWakeupEvent(String prefix, int uid, int gid, long timestampNs) {
- maybeVerboseLog("onWakeupEvent(%s, %d, %d, %sns)", prefix, uid, gid, timestampNs);
-
- // TODO: add ip protocol and port
-
+ public synchronized void onWakeupEvent(String prefix, int uid, int ethertype, int ipNextHeader,
+ byte[] dstHw, String srcIp, String dstIp, int srcPort, int dstPort, long timestampNs) {
String iface = prefix.replaceFirst(WAKEUP_EVENT_IFACE_PREFIX, "");
final long timestampMs;
if (timestampNs > 0) {
@@ -203,15 +237,22 @@
timestampMs = System.currentTimeMillis();
}
- addWakeupEvent(iface, timestampMs, uid);
- }
-
- @GuardedBy("this")
- private void addWakeupEvent(String iface, long timestampMs, int uid) {
WakeupEvent event = new WakeupEvent();
event.iface = iface;
event.timestampMs = timestampMs;
event.uid = uid;
+ event.ethertype = ethertype;
+ event.dstHwAddr = dstHw;
+ event.srcIp = srcIp;
+ event.dstIp = dstIp;
+ event.ipNextHeader = ipNextHeader;
+ event.srcPort = srcPort;
+ event.dstPort = dstPort;
+ addWakeupEvent(event);
+ }
+
+ private void addWakeupEvent(WakeupEvent event) {
+ String iface = event.iface;
mWakeupEvents.append(event);
WakeupStats stats = mWakeupStats.get(iface);
if (stats == null) {
@@ -291,10 +332,6 @@
if (DBG) Log.d(TAG, String.format(s, args));
}
- private static void maybeVerboseLog(String s, Object... args) {
- if (VDBG) Log.d(TAG, String.format(s, args));
- }
-
/** Helper class for buffering summaries of NetworkMetrics at regular time intervals */
static class NetworkMetricsSnapshot {
diff --git a/services/core/java/com/android/server/connectivity/NetworkMonitor.java b/services/core/java/com/android/server/connectivity/NetworkMonitor.java
index 8b886d6..7684030 100644
--- a/services/core/java/com/android/server/connectivity/NetworkMonitor.java
+++ b/services/core/java/com/android/server/connectivity/NetworkMonitor.java
@@ -1129,7 +1129,8 @@
}
private void logNetworkEvent(int evtype) {
- mMetricsLog.log(new NetworkEvent(mNetId, evtype));
+ int[] transports = mNetworkAgentInfo.networkCapabilities.getTransportTypes();
+ mMetricsLog.log(mNetId, transports, new NetworkEvent(evtype));
}
private int networkEventType(ValidationStage s, EvaluationResult r) {
@@ -1150,7 +1151,8 @@
private void maybeLogEvaluationResult(int evtype) {
if (mEvaluationTimer.isRunning()) {
- mMetricsLog.log(new NetworkEvent(mNetId, evtype, mEvaluationTimer.stop()));
+ int[] transports = mNetworkAgentInfo.networkCapabilities.getTransportTypes();
+ mMetricsLog.log(mNetId, transports, new NetworkEvent(evtype, mEvaluationTimer.stop()));
mEvaluationTimer.reset();
}
}
diff --git a/services/core/java/com/android/server/display/NightDisplayService.java b/services/core/java/com/android/server/display/ColorDisplayService.java
similarity index 97%
rename from services/core/java/com/android/server/display/NightDisplayService.java
rename to services/core/java/com/android/server/display/ColorDisplayService.java
index a7c3ff9..af8ecad 100644
--- a/services/core/java/com/android/server/display/NightDisplayService.java
+++ b/services/core/java/com/android/server/display/ColorDisplayService.java
@@ -42,7 +42,7 @@
import android.util.Slog;
import android.view.animation.AnimationUtils;
-import com.android.internal.app.NightDisplayController;
+import com.android.internal.app.ColorDisplayController;
import com.android.server.SystemService;
import com.android.server.twilight.TwilightListener;
import com.android.server.twilight.TwilightManager;
@@ -60,10 +60,10 @@
/**
* Tints the display at night.
*/
-public final class NightDisplayService extends SystemService
- implements NightDisplayController.Callback {
+public final class ColorDisplayService extends SystemService
+ implements ColorDisplayController.Callback {
- private static final String TAG = "NightDisplayService";
+ private static final String TAG = "ColorDisplayService";
/**
* The transition time, in milliseconds, for Night Display to turn on/off.
@@ -119,12 +119,12 @@
private ContentObserver mUserSetupObserver;
private boolean mBootCompleted;
- private NightDisplayController mController;
+ private ColorDisplayController mController;
private ValueAnimator mColorMatrixAnimator;
private Boolean mIsActivated;
private AutoMode mAutoMode;
- public NightDisplayService(Context context) {
+ public ColorDisplayService(Context context) {
super(context);
mHandler = new Handler(Looper.getMainLooper());
}
@@ -228,7 +228,7 @@
Slog.d(TAG, "setUp: currentUser=" + mCurrentUser);
// Create a new controller for the current user and start listening for changes.
- mController = new NightDisplayController(getContext(), mCurrentUser);
+ mController = new ColorDisplayController(getContext(), mCurrentUser);
mController.setListener(this);
setCoefficientMatrix(getContext());
@@ -293,9 +293,9 @@
mAutoMode = null;
}
- if (autoMode == NightDisplayController.AUTO_MODE_CUSTOM) {
+ if (autoMode == ColorDisplayController.AUTO_MODE_CUSTOM) {
mAutoMode = new CustomAutoMode();
- } else if (autoMode == NightDisplayController.AUTO_MODE_TWILIGHT) {
+ } else if (autoMode == ColorDisplayController.AUTO_MODE_TWILIGHT) {
mAutoMode = new TwilightAutoMode();
}
@@ -463,7 +463,7 @@
return ldt.isBefore(compareTime) ? ldt.plusDays(1) : ldt;
}
- private abstract class AutoMode implements NightDisplayController.Callback {
+ private abstract class AutoMode implements ColorDisplayController.Callback {
public abstract void onStart();
public abstract void onStop();
diff --git a/services/core/java/com/android/server/display/DisplayTransformManager.java b/services/core/java/com/android/server/display/DisplayTransformManager.java
index bef6898..338e331 100644
--- a/services/core/java/com/android/server/display/DisplayTransformManager.java
+++ b/services/core/java/com/android/server/display/DisplayTransformManager.java
@@ -29,7 +29,7 @@
import android.util.SparseArray;
import com.android.internal.annotations.GuardedBy;
-import com.android.internal.app.NightDisplayController;
+import com.android.internal.app.ColorDisplayController;
import java.util.Arrays;
/**
@@ -223,13 +223,13 @@
}
public boolean setColorMode(int colorMode) {
- if (colorMode == NightDisplayController.COLOR_MODE_NATURAL) {
+ if (colorMode == ColorDisplayController.COLOR_MODE_NATURAL) {
applySaturation(COLOR_SATURATION_NATURAL);
setNativeMode(false);
- } else if (colorMode == NightDisplayController.COLOR_MODE_BOOSTED) {
+ } else if (colorMode == ColorDisplayController.COLOR_MODE_BOOSTED) {
applySaturation(COLOR_SATURATION_BOOSTED);
setNativeMode(false);
- } else if (colorMode == NightDisplayController.COLOR_MODE_SATURATED) {
+ } else if (colorMode == ColorDisplayController.COLOR_MODE_SATURATED) {
applySaturation(COLOR_SATURATION_NATURAL);
setNativeMode(true);
}
diff --git a/services/core/java/com/android/server/job/JobPackageTracker.java b/services/core/java/com/android/server/job/JobPackageTracker.java
index 025ff0b..296743b 100644
--- a/services/core/java/com/android/server/job/JobPackageTracker.java
+++ b/services/core/java/com/android/server/job/JobPackageTracker.java
@@ -16,15 +16,19 @@
package com.android.server.job;
+import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock;
+import static com.android.server.job.JobSchedulerService.sSystemClock;
+import static com.android.server.job.JobSchedulerService.sUptimeMillisClock;
+
import android.app.job.JobInfo;
import android.app.job.JobParameters;
-import android.os.SystemClock;
import android.os.UserHandle;
import android.text.format.DateFormat;
import android.util.ArrayMap;
import android.util.SparseArray;
import android.util.SparseIntArray;
import android.util.TimeUtils;
+
import com.android.internal.util.RingBufferIndices;
import com.android.server.job.controllers.JobStatus;
@@ -57,7 +61,7 @@
public void addEvent(int cmd, int uid, String tag, int jobId, int stopReason) {
int index = mEventIndices.add();
mEventCmds[index] = cmd | ((stopReason<<EVENT_STOP_REASON_SHIFT) & EVENT_STOP_REASON_MASK);
- mEventTimes[index] = SystemClock.elapsedRealtime();
+ mEventTimes[index] = sElapsedRealtimeClock.millis();
mEventUids[index] = uid;
mEventTags[index] = tag;
mEventJobIds[index] = jobId;
@@ -125,9 +129,9 @@
}
public DataSet() {
- mStartUptimeTime = SystemClock.uptimeMillis();
- mStartElapsedTime = SystemClock.elapsedRealtime();
- mStartClockTime = System.currentTimeMillis();
+ mStartUptimeTime = sUptimeMillisClock.millis();
+ mStartElapsedTime = sElapsedRealtimeClock.millis();
+ mStartClockTime = sSystemClock.millis();
}
private PackageEntry getOrCreateEntry(int uid, String pkg) {
@@ -376,20 +380,20 @@
}
public void notePending(JobStatus job) {
- final long now = SystemClock.uptimeMillis();
+ final long now = sUptimeMillisClock.millis();
job.madePending = now;
rebatchIfNeeded(now);
mCurDataSet.incPending(job.getSourceUid(), job.getSourcePackageName(), now);
}
public void noteNonpending(JobStatus job) {
- final long now = SystemClock.uptimeMillis();
+ final long now = sUptimeMillisClock.millis();
mCurDataSet.decPending(job.getSourceUid(), job.getSourcePackageName(), now);
rebatchIfNeeded(now);
}
public void noteActive(JobStatus job) {
- final long now = SystemClock.uptimeMillis();
+ final long now = sUptimeMillisClock.millis();
job.madeActive = now;
rebatchIfNeeded(now);
if (job.lastEvaluatedPriority >= JobInfo.PRIORITY_TOP_APP) {
@@ -402,7 +406,7 @@
}
public void noteInactive(JobStatus job, int stopReason) {
- final long now = SystemClock.uptimeMillis();
+ final long now = sUptimeMillisClock.millis();
if (job.lastEvaluatedPriority >= JobInfo.PRIORITY_TOP_APP) {
mCurDataSet.decActiveTop(job.getSourceUid(), job.getSourcePackageName(), now,
stopReason);
@@ -431,7 +435,7 @@
if (cur == null && last == null) {
return 0;
}
- final long now = SystemClock.uptimeMillis();
+ final long now = sUptimeMillisClock.millis();
long time = 0;
if (cur != null) {
time += cur.getActiveTime(now) + cur.getPendingTime(now);
@@ -445,8 +449,8 @@
}
public void dump(PrintWriter pw, String prefix, int filterUid) {
- final long now = SystemClock.uptimeMillis();
- final long nowEllapsed = SystemClock.elapsedRealtime();
+ final long now = sUptimeMillisClock.millis();
+ final long nowEllapsed = sElapsedRealtimeClock.millis();
final DataSet total;
if (mLastDataSets[0] != null) {
total = new DataSet(mLastDataSets[0]);
@@ -470,7 +474,7 @@
return false;
}
pw.println(" Job history:");
- final long now = SystemClock.elapsedRealtime();
+ final long now = sElapsedRealtimeClock.millis();
for (int i=0; i<size; i++) {
final int index = mEventIndices.indexOf(i);
final int uid = mEventUids[index];
diff --git a/services/core/java/com/android/server/job/JobSchedulerService.java b/services/core/java/com/android/server/job/JobSchedulerService.java
index 78aa2f9..b549768 100644
--- a/services/core/java/com/android/server/job/JobSchedulerService.java
+++ b/services/core/java/com/android/server/job/JobSchedulerService.java
@@ -19,24 +19,15 @@
import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER;
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.Iterator;
-import java.util.List;
-
import android.app.Activity;
import android.app.ActivityManager;
import android.app.AppGlobals;
import android.app.IUidObserver;
+import android.app.job.IJobScheduler;
import android.app.job.JobInfo;
import android.app.job.JobParameters;
import android.app.job.JobScheduler;
import android.app.job.JobService;
-import android.app.job.IJobScheduler;
import android.app.job.JobWorkItem;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
@@ -46,8 +37,8 @@
import android.content.IntentFilter;
import android.content.pm.IPackageManager;
import android.content.pm.PackageManager;
-import android.content.pm.ServiceInfo;
import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.ServiceInfo;
import android.database.ContentObserver;
import android.net.Uri;
import android.os.BatteryStats;
@@ -55,8 +46,8 @@
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
-import android.os.Process;
import android.os.PowerManager;
+import android.os.Process;
import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.ServiceManager;
@@ -71,6 +62,7 @@
import android.util.SparseIntArray;
import android.util.TimeUtils;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.IBatteryStats;
import com.android.internal.app.procstats.ProcessStats;
import com.android.internal.util.ArrayUtils;
@@ -93,6 +85,16 @@
import libcore.util.EmptyArray;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.time.Clock;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Iterator;
+import java.util.List;
+
/**
* Responsible for taking jobs representing work to be performed by a client app, and determining
* based on the criteria specified when that job should be run against the client application's
@@ -117,6 +119,12 @@
/** The maximum number of jobs that we allow an unprivileged app to schedule */
private static final int MAX_JOBS_PER_APP = 100;
+ @VisibleForTesting
+ public static Clock sSystemClock = Clock.systemUTC();
+ @VisibleForTesting
+ public static Clock sUptimeMillisClock = SystemClock.uptimeMillisClock();
+ @VisibleForTesting
+ public static Clock sElapsedRealtimeClock = SystemClock.elapsedRealtimeClock();
/** Global local for all job scheduler state. */
final Object mLock = new Object();
@@ -960,7 +968,7 @@
if (Intent.ACTION_TIME_CHANGED.equals(intent.getAction())) {
// When we reach clock sanity, recalculate the temporal windows
// of all affected jobs.
- if (mJobs.clockNowValidToInflate(System.currentTimeMillis())) {
+ if (mJobs.clockNowValidToInflate(sSystemClock.millis())) {
Slog.i(TAG, "RTC now valid; recalculating persisted job windows");
// We've done our job now, so stop watching the time.
@@ -1068,7 +1076,7 @@
if (!jobStatus.isPreparedLocked()) {
Slog.wtf(TAG, "Not yet prepared when started tracking: " + jobStatus);
}
- jobStatus.enqueueTime = SystemClock.elapsedRealtime();
+ jobStatus.enqueueTime = sElapsedRealtimeClock.millis();
final boolean update = mJobs.add(jobStatus);
if (mReadyToRock) {
for (int i = 0; i < mControllers.size(); i++) {
@@ -1156,7 +1164,7 @@
* @see #maybeQueueReadyJobsForExecutionLocked
*/
private JobStatus getRescheduleJobForFailureLocked(JobStatus failureToReschedule) {
- final long elapsedNowMillis = SystemClock.elapsedRealtime();
+ final long elapsedNowMillis = sElapsedRealtimeClock.millis();
final JobInfo job = failureToReschedule.getJob();
final long initialBackoffMillis = job.getInitialBackoffMillis();
@@ -1200,7 +1208,7 @@
Math.min(delayMillis, JobInfo.MAX_BACKOFF_DELAY_MILLIS);
JobStatus newJob = new JobStatus(failureToReschedule, elapsedNowMillis + delayMillis,
JobStatus.NO_LATEST_RUNTIME, backoffAttempts,
- failureToReschedule.getLastSuccessfulRunTime(), System.currentTimeMillis());
+ failureToReschedule.getLastSuccessfulRunTime(), sSystemClock.millis());
for (int ic=0; ic<mControllers.size(); ic++) {
StateController controller = mControllers.get(ic);
controller.rescheduleForFailureLocked(newJob, failureToReschedule);
@@ -1220,7 +1228,7 @@
* recurring job.
*/
private JobStatus getRescheduleJobForPeriodic(JobStatus periodicToReschedule) {
- final long elapsedNow = SystemClock.elapsedRealtime();
+ final long elapsedNow = sElapsedRealtimeClock.millis();
// Compute how much of the period is remaining.
long runEarly = 0L;
@@ -1239,7 +1247,7 @@
}
return new JobStatus(periodicToReschedule, newEarliestRunTimeElapsed,
newLatestRuntimeElapsed, 0 /* backoffAttempt */,
- System.currentTimeMillis() /* lastSuccessfulRunTime */,
+ sSystemClock.millis() /* lastSuccessfulRunTime */,
periodicToReschedule.getLastFailedRunTime());
}
@@ -2367,8 +2375,8 @@
}
final int filterUidFinal = UserHandle.getAppId(filterUid);
- final long nowElapsed = SystemClock.elapsedRealtime();
- final long nowUptime = SystemClock.uptimeMillis();
+ final long nowElapsed = sElapsedRealtimeClock.millis();
+ final long nowUptime = sUptimeMillisClock.millis();
synchronized (mLock) {
mConstants.dump(pw);
pw.println();
diff --git a/services/core/java/com/android/server/job/JobServiceContext.java b/services/core/java/com/android/server/job/JobServiceContext.java
index a7e674b..ac7297e 100644
--- a/services/core/java/com/android/server/job/JobServiceContext.java
+++ b/services/core/java/com/android/server/job/JobServiceContext.java
@@ -16,11 +16,13 @@
package com.android.server.job;
+import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock;
+
import android.app.ActivityManager;
-import android.app.job.JobInfo;
-import android.app.job.JobParameters;
import android.app.job.IJobCallback;
import android.app.job.IJobService;
+import android.app.job.JobInfo;
+import android.app.job.JobParameters;
import android.app.job.JobWorkItem;
import android.content.ComponentName;
import android.content.Context;
@@ -34,7 +36,6 @@
import android.os.Message;
import android.os.PowerManager;
import android.os.RemoteException;
-import android.os.SystemClock;
import android.os.UserHandle;
import android.os.WorkSource;
import android.util.Slog;
@@ -202,7 +203,7 @@
mRunningCallback = new JobCallback();
final boolean isDeadlineExpired =
job.hasDeadlineConstraint() &&
- (job.getLatestRunTimeElapsed() < SystemClock.elapsedRealtime());
+ (job.getLatestRunTimeElapsed() < sElapsedRealtimeClock.millis());
Uri[] triggeredUris = null;
if (job.changedUris != null) {
triggeredUris = new Uri[job.changedUris.size()];
@@ -217,7 +218,7 @@
mParams = new JobParameters(mRunningCallback, job.getJobId(), ji.getExtras(),
ji.getTransientExtras(), ji.getClipData(), ji.getClipGrantFlags(),
isDeadlineExpired, triggeredUris, triggeredAuthorities, job.network);
- mExecutionStartTimeElapsed = SystemClock.elapsedRealtime();
+ mExecutionStartTimeElapsed = sElapsedRealtimeClock.millis();
// Once we'e begun executing a job, we by definition no longer care whether
// it was inflated from disk with not-yet-coherent delay/deadline bounds.
@@ -428,7 +429,7 @@
sb.append("Caller no longer running");
if (cb.mStoppedReason != null) {
sb.append(", last stopped ");
- TimeUtils.formatDuration(SystemClock.elapsedRealtime() - cb.mStoppedTime, sb);
+ TimeUtils.formatDuration(sElapsedRealtimeClock.millis() - cb.mStoppedTime, sb);
sb.append(" because: ");
sb.append(cb.mStoppedReason);
}
@@ -457,7 +458,7 @@
sb.append("Ignoring timeout of no longer active job");
if (jc.mStoppedReason != null) {
sb.append(", stopped ");
- TimeUtils.formatDuration(SystemClock.elapsedRealtime()
+ TimeUtils.formatDuration(sElapsedRealtimeClock.millis()
- jc.mStoppedTime, sb);
sb.append(" because: ");
sb.append(jc.mStoppedReason);
@@ -740,7 +741,7 @@
private void applyStoppedReasonLocked(String reason) {
if (reason != null && mStoppedReason == null) {
mStoppedReason = reason;
- mStoppedTime = SystemClock.elapsedRealtime();
+ mStoppedTime = sElapsedRealtimeClock.millis();
if (mRunningCallback != null) {
mRunningCallback.mStoppedReason = mStoppedReason;
mRunningCallback.mStoppedTime = mStoppedTime;
@@ -777,7 +778,7 @@
}
Message m = mCallbackHandler.obtainMessage(MSG_TIMEOUT, mRunningCallback);
mCallbackHandler.sendMessageDelayed(m, timeoutMillis);
- mTimeoutElapsed = SystemClock.elapsedRealtime() + timeoutMillis;
+ mTimeoutElapsed = sElapsedRealtimeClock.millis() + timeoutMillis;
}
diff --git a/services/core/java/com/android/server/job/JobStore.java b/services/core/java/com/android/server/job/JobStore.java
index aa9f77c..1af3b39 100644
--- a/services/core/java/com/android/server/job/JobStore.java
+++ b/services/core/java/com/android/server/job/JobStore.java
@@ -16,20 +16,23 @@
package com.android.server.job;
+import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock;
+import static com.android.server.job.JobSchedulerService.sSystemClock;
+
import android.app.ActivityManager;
import android.app.IActivityManager;
-import android.content.ComponentName;
import android.app.job.JobInfo;
+import android.content.ComponentName;
import android.content.Context;
+import android.net.NetworkRequest;
import android.os.Environment;
import android.os.Handler;
import android.os.PersistableBundle;
import android.os.Process;
-import android.os.SystemClock;
import android.os.UserHandle;
import android.text.format.DateUtils;
-import android.util.AtomicFile;
import android.util.ArraySet;
+import android.util.AtomicFile;
import android.util.Pair;
import android.util.Slog;
import android.util.SparseArray;
@@ -37,11 +40,16 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.ArrayUtils;
+import com.android.internal.util.BitUtils;
import com.android.internal.util.FastXmlSerializer;
import com.android.server.IoThread;
import com.android.server.job.JobSchedulerInternal.JobStorePersistStats;
import com.android.server.job.controllers.JobStatus;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
@@ -53,10 +61,6 @@
import java.util.List;
import java.util.Set;
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-import org.xmlpull.v1.XmlSerializer;
-
/**
* Maintains the master list of jobs that the job scheduler is tracking. These jobs are compared by
* reference, so none of the functions in this class should make a copy.
@@ -141,7 +145,7 @@
// a *correct* timestamp, see a bunch of overdue jobs, and run them; then
// settle into normal operation.
mXmlTimestamp = mJobsFile.getLastModifiedTime();
- mRtcGood = (System.currentTimeMillis() > mXmlTimestamp);
+ mRtcGood = (sSystemClock.millis() > mXmlTimestamp);
readJobMapFromDisk(mJobSet, mRtcGood);
}
@@ -161,7 +165,7 @@
*/
public void getRtcCorrectedJobsLocked(final ArrayList<JobStatus> toAdd,
final ArrayList<JobStatus> toRemove) {
- final long elapsedNow = SystemClock.elapsedRealtime();
+ final long elapsedNow = sElapsedRealtimeClock.millis();
// Find the jobs that need to be fixed up, collecting them for post-iteration
// replacement with their new versions
@@ -323,7 +327,7 @@
private final Runnable mWriteRunnable = new Runnable() {
@Override
public void run() {
- final long startElapsed = SystemClock.elapsedRealtime();
+ final long startElapsed = sElapsedRealtimeClock.millis();
final List<JobStatus> storeCopy = new ArrayList<JobStatus>();
synchronized (mLock) {
// Clone the jobs so we can release the lock before writing.
@@ -338,7 +342,7 @@
}
writeJobsMapImpl(storeCopy);
if (DEBUG) {
- Slog.v(TAG, "Finished writing, took " + (SystemClock.elapsedRealtime()
+ Slog.v(TAG, "Finished writing, took " + (sElapsedRealtimeClock.millis()
- startElapsed) + "ms");
}
}
@@ -454,17 +458,12 @@
*/
private void writeConstraintsToXml(XmlSerializer out, JobStatus jobStatus) throws IOException {
out.startTag(null, XML_TAG_PARAMS_CONSTRAINTS);
- if (jobStatus.needsAnyConnectivity()) {
- out.attribute(null, "connectivity", Boolean.toString(true));
- }
- if (jobStatus.needsMeteredConnectivity()) {
- out.attribute(null, "metered", Boolean.toString(true));
- }
- if (jobStatus.needsUnmeteredConnectivity()) {
- out.attribute(null, "unmetered", Boolean.toString(true));
- }
- if (jobStatus.needsNonRoamingConnectivity()) {
- out.attribute(null, "not-roaming", Boolean.toString(true));
+ if (jobStatus.hasConnectivityConstraint()) {
+ final NetworkRequest network = jobStatus.getJob().getRequiredNetwork();
+ out.attribute(null, "net-capabilities", Long.toString(
+ BitUtils.packBits(network.networkCapabilities.getCapabilities())));
+ out.attribute(null, "net-transport-types", Long.toString(
+ BitUtils.packBits(network.networkCapabilities.getTransportTypes())));
}
if (jobStatus.hasIdleConstraint()) {
out.attribute(null, "idle", Boolean.toString(true));
@@ -497,8 +496,8 @@
Slog.i(TAG, "storing original UTC timestamps for " + jobStatus);
}
- final long nowRTC = System.currentTimeMillis();
- final long nowElapsed = SystemClock.elapsedRealtime();
+ final long nowRTC = sSystemClock.millis();
+ final long nowElapsed = sElapsedRealtimeClock.millis();
if (jobStatus.hasDeadlineConstraint()) {
// Wall clock deadline.
final long deadlineWallclock = (utcJobTimes == null)
@@ -538,7 +537,7 @@
*/
private static Pair<Long, Long> convertRtcBoundsToElapsed(Pair<Long, Long> rtcTimes,
long nowElapsed) {
- final long nowWallclock = System.currentTimeMillis();
+ final long nowWallclock = sSystemClock.millis();
final long earliest = (rtcTimes.first > JobStatus.NO_EARLIEST_RUNTIME)
? nowElapsed + Math.max(rtcTimes.first - nowWallclock, 0)
: JobStatus.NO_EARLIEST_RUNTIME;
@@ -581,7 +580,7 @@
synchronized (mLock) {
jobs = readJobMapImpl(fis, rtcGood);
if (jobs != null) {
- long now = SystemClock.elapsedRealtime();
+ long now = sElapsedRealtimeClock.millis();
IActivityManager am = ActivityManager.getService();
for (int i=0; i<jobs.size(); i++) {
JobStatus js = jobs.get(i);
@@ -754,7 +753,7 @@
return null;
}
- final long elapsedNow = SystemClock.elapsedRealtime();
+ final long elapsedNow = sElapsedRealtimeClock.millis();
Pair<Long, Long> elapsedRuntimes = convertRtcBoundsToElapsed(rtcRuntimes, elapsedNow);
if (XML_TAG_PERIODIC.equals(parser.getName())) {
@@ -862,22 +861,38 @@
}
private void buildConstraintsFromXml(JobInfo.Builder jobBuilder, XmlPullParser parser) {
- String val = parser.getAttributeValue(null, "connectivity");
- if (val != null) {
- jobBuilder.setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY);
+ String val;
+
+ final String netCapabilities = parser.getAttributeValue(null, "net-capabilities");
+ final String netTransportTypes = parser.getAttributeValue(null, "net-transport-types");
+ if (netCapabilities != null && netTransportTypes != null) {
+ final NetworkRequest request = new NetworkRequest.Builder().build();
+ // We're okay throwing NFE here; caught by caller
+ request.networkCapabilities.setCapabilities(
+ BitUtils.unpackBits(Long.parseLong(netCapabilities)));
+ request.networkCapabilities.setTransportTypes(
+ BitUtils.unpackBits(Long.parseLong(netTransportTypes)));
+ jobBuilder.setRequiredNetwork(request);
+ } else {
+ // Read legacy values
+ val = parser.getAttributeValue(null, "connectivity");
+ if (val != null) {
+ jobBuilder.setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY);
+ }
+ val = parser.getAttributeValue(null, "metered");
+ if (val != null) {
+ jobBuilder.setRequiredNetworkType(JobInfo.NETWORK_TYPE_METERED);
+ }
+ val = parser.getAttributeValue(null, "unmetered");
+ if (val != null) {
+ jobBuilder.setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED);
+ }
+ val = parser.getAttributeValue(null, "not-roaming");
+ if (val != null) {
+ jobBuilder.setRequiredNetworkType(JobInfo.NETWORK_TYPE_NOT_ROAMING);
+ }
}
- val = parser.getAttributeValue(null, "metered");
- if (val != null) {
- jobBuilder.setRequiredNetworkType(JobInfo.NETWORK_TYPE_METERED);
- }
- val = parser.getAttributeValue(null, "unmetered");
- if (val != null) {
- jobBuilder.setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED);
- }
- val = parser.getAttributeValue(null, "not-roaming");
- if (val != null) {
- jobBuilder.setRequiredNetworkType(JobInfo.NETWORK_TYPE_NOT_ROAMING);
- }
+
val = parser.getAttributeValue(null, "idle");
if (val != null) {
jobBuilder.setRequiresDeviceIdle(true);
@@ -937,8 +952,8 @@
private Pair<Long, Long> buildExecutionTimesFromXml(XmlPullParser parser)
throws NumberFormatException {
// Pull out execution time data.
- final long nowWallclock = System.currentTimeMillis();
- final long nowElapsed = SystemClock.elapsedRealtime();
+ final long nowWallclock = sSystemClock.millis();
+ final long nowElapsed = sElapsedRealtimeClock.millis();
long earliestRunTimeElapsed = JobStatus.NO_EARLIEST_RUNTIME;
long latestRunTimeElapsed = JobStatus.NO_LATEST_RUNTIME;
diff --git a/services/core/java/com/android/server/job/controllers/AppIdleController.java b/services/core/java/com/android/server/job/controllers/AppIdleController.java
index 39f2a96..caa8522 100644
--- a/services/core/java/com/android/server/job/controllers/AppIdleController.java
+++ b/services/core/java/com/android/server/job/controllers/AppIdleController.java
@@ -174,7 +174,7 @@
private final class AppIdleStateChangeListener
extends UsageStatsManagerInternal.AppIdleStateChangeListener {
@Override
- public void onAppIdleStateChanged(String packageName, int userId, boolean idle) {
+ public void onAppIdleStateChanged(String packageName, int userId, boolean idle, int bucket) {
boolean changed = false;
synchronized (mLock) {
if (mAppIdleParoleOn) {
diff --git a/services/core/java/com/android/server/job/controllers/BatteryController.java b/services/core/java/com/android/server/job/controllers/BatteryController.java
index 9111969..76ff834 100644
--- a/services/core/java/com/android/server/job/controllers/BatteryController.java
+++ b/services/core/java/com/android/server/job/controllers/BatteryController.java
@@ -16,13 +16,14 @@
package com.android.server.job.controllers;
+import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock;
+
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.BatteryManager;
import android.os.BatteryManagerInternal;
-import android.os.SystemClock;
import android.os.UserHandle;
import android.util.ArraySet;
import android.util.Slog;
@@ -203,7 +204,7 @@
if (Intent.ACTION_BATTERY_LOW.equals(action)) {
if (DEBUG) {
Slog.d(TAG, "Battery life too low to do work. @ "
- + SystemClock.elapsedRealtime());
+ + sElapsedRealtimeClock.millis());
}
// If we get this action, the battery is discharging => it isn't plugged in so
// there's no work to cancel. We track this variable for the case where it is
@@ -213,14 +214,14 @@
} else if (Intent.ACTION_BATTERY_OKAY.equals(action)) {
if (DEBUG) {
Slog.d(TAG, "Battery life healthy enough to do work. @ "
- + SystemClock.elapsedRealtime());
+ + sElapsedRealtimeClock.millis());
}
mBatteryHealthy = true;
maybeReportNewChargingStateLocked();
} else if (BatteryManager.ACTION_CHARGING.equals(action)) {
if (DEBUG) {
Slog.d(TAG, "Received charging intent, fired @ "
- + SystemClock.elapsedRealtime());
+ + sElapsedRealtimeClock.millis());
}
mCharging = true;
maybeReportNewChargingStateLocked();
diff --git a/services/core/java/com/android/server/job/controllers/ConnectivityController.java b/services/core/java/com/android/server/job/controllers/ConnectivityController.java
index ddee345..da28769 100644
--- a/services/core/java/com/android/server/job/controllers/ConnectivityController.java
+++ b/services/core/java/com/android/server/job/controllers/ConnectivityController.java
@@ -54,7 +54,6 @@
private final ConnectivityManager mConnManager;
private final NetworkPolicyManager mNetPolicyManager;
private boolean mConnected;
- private boolean mValidated;
@GuardedBy("mLock")
private final ArraySet<JobStatus> mTrackedJobs = new ArraySet<>();
@@ -79,7 +78,7 @@
mConnManager = mContext.getSystemService(ConnectivityManager.class);
mNetPolicyManager = mContext.getSystemService(NetworkPolicyManager.class);
- mConnected = mValidated = false;
+ mConnected = false;
mConnManager.registerDefaultNetworkCallback(mNetworkCallback);
mNetPolicyManager.registerListener(mNetPolicyListener);
@@ -149,32 +148,21 @@
}
private boolean updateConstraintsSatisfied(JobStatus jobStatus) {
+ // TODO: consider matching against non-active networks
+
final int jobUid = jobStatus.getSourceUid();
final boolean ignoreBlocked = (jobStatus.getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) != 0;
final Network network = mConnManager.getActiveNetworkForUid(jobUid, ignoreBlocked);
final NetworkInfo info = mConnManager.getNetworkInfoForUid(network, jobUid, ignoreBlocked);
-
- final NetworkCapabilities capabilities = (network != null)
- ? mConnManager.getNetworkCapabilities(network) : null;
+ final NetworkCapabilities capabilities = mConnManager.getNetworkCapabilities(network);
final boolean connected = (info != null) && info.isConnected();
- final boolean validated = (capabilities != null)
- && capabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED);
+ final boolean satisfied = jobStatus.getJob().getRequiredNetwork().networkCapabilities
+ .satisfiedByNetworkCapabilities(capabilities);
final boolean sane = isSane(jobStatus, capabilities);
- final boolean connectionUsable = connected && validated && sane;
- final boolean metered = connected && (capabilities != null)
- && !capabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED);
- final boolean unmetered = connected && (capabilities != null)
- && capabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED);
- final boolean notRoaming = connected && (info != null)
- && !info.isRoaming();
-
- boolean changed = false;
- changed |= jobStatus.setConnectivityConstraintSatisfied(connectionUsable);
- changed |= jobStatus.setMeteredConstraintSatisfied(metered);
- changed |= jobStatus.setUnmeteredConstraintSatisfied(unmetered);
- changed |= jobStatus.setNotRoamingConstraintSatisfied(notRoaming);
+ final boolean changed = jobStatus
+ .setConnectivityConstraintSatisfied(connected && satisfied && sane);
// Pass along the evaluated network for job to use; prevents race
// conditions as default routes change over time, and opens the door to
@@ -185,17 +173,13 @@
// overall state of connectivity constraint satisfiability.
if (jobUid == Process.SYSTEM_UID) {
mConnected = connected;
- mValidated = validated;
}
if (DEBUG) {
Slog.i(TAG, "Connectivity " + (changed ? "CHANGED" : "unchanged")
- + " for " + jobStatus + ": usable=" + connectionUsable
- + " connected=" + connected
- + " validated=" + validated
- + " metered=" + metered
- + " unmetered=" + unmetered
- + " notRoaming=" + notRoaming);
+ + " for " + jobStatus + ": connected=" + connected
+ + " satisfied=" + satisfied
+ + " sane=" + sane);
}
return changed;
}
@@ -292,8 +276,6 @@
public void dumpControllerStateLocked(PrintWriter pw, int filterUid) {
pw.print("Connectivity: connected=");
pw.print(mConnected);
- pw.print(" validated=");
- pw.println(mValidated);
pw.print("Tracking ");
pw.print(mTrackedJobs.size());
pw.println(":");
@@ -304,10 +286,7 @@
js.printUniqueId(pw);
pw.print(" from ");
UserHandle.formatUid(pw, js.getSourceUid());
- pw.print(": C="); pw.print(js.needsAnyConnectivity());
- pw.print(": M="); pw.print(js.needsMeteredConnectivity());
- pw.print(": UM="); pw.print(js.needsUnmeteredConnectivity());
- pw.print(": NR="); pw.println(js.needsNonRoamingConnectivity());
+ pw.print(": "); pw.print(js.getJob().getRequiredNetwork());
}
}
}
diff --git a/services/core/java/com/android/server/job/controllers/IdleController.java b/services/core/java/com/android/server/job/controllers/IdleController.java
index 9eda046..7bde174 100644
--- a/services/core/java/com/android/server/job/controllers/IdleController.java
+++ b/services/core/java/com/android/server/job/controllers/IdleController.java
@@ -16,7 +16,7 @@
package com.android.server.job.controllers;
-import java.io.PrintWriter;
+import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock;
import android.app.AlarmManager;
import android.app.PendingIntent;
@@ -24,7 +24,6 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
-import android.os.SystemClock;
import android.os.UserHandle;
import android.util.ArraySet;
import android.util.Slog;
@@ -33,6 +32,8 @@
import com.android.server.job.JobSchedulerService;
import com.android.server.job.StateChangedListener;
+import java.io.PrintWriter;
+
public final class IdleController extends StateController {
private static final String TAG = "IdleController";
@@ -169,7 +170,7 @@
// when the screen goes off or dreaming starts, we schedule the
// alarm that will tell us when we have decided the device is
// truly idle.
- final long nowElapsed = SystemClock.elapsedRealtime();
+ final long nowElapsed = sElapsedRealtimeClock.millis();
final long when = nowElapsed + mInactivityIdleThreshold;
if (DEBUG) {
Slog.v(TAG, "Scheduling idle : " + action + " now:" + nowElapsed + " when="
@@ -182,7 +183,7 @@
// idle time starts now. Do not set mIdle if screen is on.
if (!mIdle && !mScreenOn) {
if (DEBUG) {
- Slog.v(TAG, "Idle trigger fired @ " + SystemClock.elapsedRealtime());
+ Slog.v(TAG, "Idle trigger fired @ " + sElapsedRealtimeClock.millis());
}
mIdle = true;
reportNewIdleState(mIdle);
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 46ed84e..a5906cb 100644
--- a/services/core/java/com/android/server/job/controllers/JobStatus.java
+++ b/services/core/java/com/android/server/job/controllers/JobStatus.java
@@ -16,6 +16,8 @@
package com.android.server.job.controllers;
+import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock;
+
import android.app.AppGlobals;
import android.app.IActivityManager;
import android.app.job.JobInfo;
@@ -25,7 +27,6 @@
import android.net.Network;
import android.net.Uri;
import android.os.RemoteException;
-import android.os.SystemClock;
import android.os.UserHandle;
import android.text.format.Time;
import android.util.ArraySet;
@@ -64,19 +65,12 @@
static final int CONSTRAINT_STORAGE_NOT_LOW = JobInfo.CONSTRAINT_FLAG_STORAGE_NOT_LOW;
static final int CONSTRAINT_TIMING_DELAY = 1<<31;
static final int CONSTRAINT_DEADLINE = 1<<30;
- static final int CONSTRAINT_UNMETERED = 1<<29;
static final int CONSTRAINT_CONNECTIVITY = 1<<28;
static final int CONSTRAINT_APP_NOT_IDLE = 1<<27;
static final int CONSTRAINT_CONTENT_TRIGGER = 1<<26;
static final int CONSTRAINT_DEVICE_NOT_DOZING = 1<<25;
- static final int CONSTRAINT_NOT_ROAMING = 1<<24;
- static final int CONSTRAINT_METERED = 1<<23;
static final int CONSTRAINT_BACKGROUND_NOT_RESTRICTED = 1<<22;
- static final int CONNECTIVITY_MASK =
- CONSTRAINT_UNMETERED | CONSTRAINT_CONNECTIVITY |
- CONSTRAINT_NOT_ROAMING | CONSTRAINT_METERED;
-
// Soft override: ignore constraints like time that don't affect API availability
public static final int OVERRIDE_SOFT = 1;
// Full override: ignore all constraints including API-affecting like connectivity
@@ -264,27 +258,9 @@
int requiredConstraints = job.getConstraintFlags();
- switch (job.getNetworkType()) {
- case JobInfo.NETWORK_TYPE_NONE:
- // No constraint.
- break;
- case JobInfo.NETWORK_TYPE_ANY:
- requiredConstraints |= CONSTRAINT_CONNECTIVITY;
- break;
- case JobInfo.NETWORK_TYPE_UNMETERED:
- requiredConstraints |= CONSTRAINT_UNMETERED;
- break;
- case JobInfo.NETWORK_TYPE_NOT_ROAMING:
- requiredConstraints |= CONSTRAINT_NOT_ROAMING;
- break;
- case JobInfo.NETWORK_TYPE_METERED:
- requiredConstraints |= CONSTRAINT_METERED;
- break;
- default:
- Slog.w(TAG, "Unrecognized networking constraint " + job.getNetworkType());
- break;
+ if (job.getRequiredNetwork() != null) {
+ requiredConstraints |= CONSTRAINT_CONNECTIVITY;
}
-
if (earliestRunTimeElapsedMillis != NO_EARLIEST_RUNTIME) {
requiredConstraints |= CONSTRAINT_TIMING_DELAY;
}
@@ -365,7 +341,7 @@
*/
public static JobStatus createFromJobInfo(JobInfo job, int callingUid, String sourcePackageName,
int sourceUserId, String tag) {
- final long elapsedNow = SystemClock.elapsedRealtime();
+ final long elapsedNow = sElapsedRealtimeClock.millis();
final long earliestRunTimeElapsedMillis, latestRunTimeElapsedMillis;
if (job.isPeriodic()) {
latestRunTimeElapsedMillis = elapsedNow + job.getIntervalMillis();
@@ -610,25 +586,9 @@
/** Does this job have any sort of networking constraint? */
public boolean hasConnectivityConstraint() {
- return (requiredConstraints&CONNECTIVITY_MASK) != 0;
- }
-
- public boolean needsAnyConnectivity() {
return (requiredConstraints&CONSTRAINT_CONNECTIVITY) != 0;
}
- public boolean needsUnmeteredConnectivity() {
- return (requiredConstraints&CONSTRAINT_UNMETERED) != 0;
- }
-
- public boolean needsMeteredConnectivity() {
- return (requiredConstraints&CONSTRAINT_METERED) != 0;
- }
-
- public boolean needsNonRoamingConnectivity() {
- return (requiredConstraints&CONSTRAINT_NOT_ROAMING) != 0;
- }
-
public boolean hasChargingConstraint() {
return (requiredConstraints&CONSTRAINT_CHARGING) != 0;
}
@@ -725,18 +685,6 @@
return setConstraintSatisfied(CONSTRAINT_CONNECTIVITY, state);
}
- boolean setUnmeteredConstraintSatisfied(boolean state) {
- return setConstraintSatisfied(CONSTRAINT_UNMETERED, state);
- }
-
- boolean setMeteredConstraintSatisfied(boolean state) {
- return setConstraintSatisfied(CONSTRAINT_METERED, state);
- }
-
- boolean setNotRoamingConstraintSatisfied(boolean state) {
- return setConstraintSatisfied(CONSTRAINT_NOT_ROAMING, state);
- }
-
boolean setAppNotIdleConstraintSatisfied(boolean state) {
return setConstraintSatisfied(CONSTRAINT_APP_NOT_IDLE, state);
}
@@ -817,12 +765,9 @@
&& notRestrictedInBg;
}
- static final int CONSTRAINTS_OF_INTEREST =
- CONSTRAINT_CHARGING | CONSTRAINT_BATTERY_NOT_LOW | CONSTRAINT_STORAGE_NOT_LOW |
- CONSTRAINT_TIMING_DELAY |
- CONSTRAINT_CONNECTIVITY | CONSTRAINT_UNMETERED |
- CONSTRAINT_NOT_ROAMING | CONSTRAINT_METERED |
- CONSTRAINT_IDLE | CONSTRAINT_CONTENT_TRIGGER;
+ static final int CONSTRAINTS_OF_INTEREST = CONSTRAINT_CHARGING | CONSTRAINT_BATTERY_NOT_LOW
+ | CONSTRAINT_STORAGE_NOT_LOW | CONSTRAINT_TIMING_DELAY | CONSTRAINT_CONNECTIVITY
+ | CONSTRAINT_IDLE | CONSTRAINT_CONTENT_TRIGGER;
// Soft override covers all non-"functional" constraints
static final int SOFT_OVERRIDE_CONSTRAINTS =
@@ -870,15 +815,14 @@
sb.append(getSourceUid());
if (earliestRunTimeElapsedMillis != NO_EARLIEST_RUNTIME
|| latestRunTimeElapsedMillis != NO_LATEST_RUNTIME) {
- long now = SystemClock.elapsedRealtime();
+ long now = sElapsedRealtimeClock.millis();
sb.append(" TIME=");
formatRunTime(sb, earliestRunTimeElapsedMillis, NO_EARLIEST_RUNTIME, now);
sb.append(":");
formatRunTime(sb, latestRunTimeElapsedMillis, NO_LATEST_RUNTIME, now);
}
- if (job.getNetworkType() != JobInfo.NETWORK_TYPE_NONE) {
- sb.append(" NET=");
- sb.append(job.getNetworkType());
+ if (job.getRequiredNetwork() != null) {
+ sb.append(" NET");
}
if (job.isRequireCharging()) {
sb.append(" CHARGING");
@@ -985,15 +929,6 @@
if ((constraints&CONSTRAINT_CONNECTIVITY) != 0) {
pw.print(" CONNECTIVITY");
}
- if ((constraints&CONSTRAINT_UNMETERED) != 0) {
- pw.print(" UNMETERED");
- }
- if ((constraints&CONSTRAINT_NOT_ROAMING) != 0) {
- pw.print(" NOT_ROAMING");
- }
- if ((constraints&CONSTRAINT_METERED) != 0) {
- pw.print(" METERED");
- }
if ((constraints&CONSTRAINT_APP_NOT_IDLE) != 0) {
pw.print(" APP_NOT_IDLE");
}
@@ -1084,11 +1019,13 @@
pw.print(prefix); pw.println(" Granted URI permissions:");
uriPerms.dump(pw, prefix + " ");
}
- if (job.getNetworkType() != JobInfo.NETWORK_TYPE_NONE) {
- pw.print(prefix); pw.print(" Network type: "); pw.println(job.getNetworkType());
+ if (job.getRequiredNetwork() != null) {
+ pw.print(prefix); pw.print(" Network type: ");
+ pw.println(job.getRequiredNetwork());
}
if (totalNetworkBytes != JobInfo.NETWORK_BYTES_UNKNOWN) {
- pw.print(prefix); pw.print(" Network bytes: "); pw.println(totalNetworkBytes);
+ pw.print(prefix); pw.print(" Network bytes: ");
+ pw.println(totalNetworkBytes);
}
if (job.getMinLatencyMillis() != 0) {
pw.print(prefix); pw.print(" Minimum latency: ");
diff --git a/services/core/java/com/android/server/job/controllers/StorageController.java b/services/core/java/com/android/server/job/controllers/StorageController.java
index c24e563..84782f5 100644
--- a/services/core/java/com/android/server/job/controllers/StorageController.java
+++ b/services/core/java/com/android/server/job/controllers/StorageController.java
@@ -16,11 +16,12 @@
package com.android.server.job.controllers;
+import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock;
+
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
-import android.os.SystemClock;
import android.os.UserHandle;
import android.util.ArraySet;
import android.util.Slog;
@@ -154,13 +155,13 @@
if (Intent.ACTION_DEVICE_STORAGE_LOW.equals(action)) {
if (DEBUG) {
Slog.d(TAG, "Available storage too low to do work. @ "
- + SystemClock.elapsedRealtime());
+ + sElapsedRealtimeClock.millis());
}
mStorageLow = true;
} else if (Intent.ACTION_DEVICE_STORAGE_OK.equals(action)) {
if (DEBUG) {
Slog.d(TAG, "Available stoage high enough to do work. @ "
- + SystemClock.elapsedRealtime());
+ + sElapsedRealtimeClock.millis());
}
mStorageLow = false;
maybeReportNewStorageState();
diff --git a/services/core/java/com/android/server/job/controllers/TimeController.java b/services/core/java/com/android/server/job/controllers/TimeController.java
index ee4c606..cb9e43a 100644
--- a/services/core/java/com/android/server/job/controllers/TimeController.java
+++ b/services/core/java/com/android/server/job/controllers/TimeController.java
@@ -16,10 +16,11 @@
package com.android.server.job.controllers;
+import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock;
+
import android.app.AlarmManager;
import android.app.AlarmManager.OnAlarmListener;
import android.content.Context;
-import android.os.SystemClock;
import android.os.UserHandle;
import android.os.WorkSource;
import android.util.Slog;
@@ -84,7 +85,7 @@
// pattern of having a job with a 0 deadline constraint ("run immediately").
// Unlike most controllers, once one of our constraints has been satisfied, it
// will never be unsatisfied (our time base can not go backwards).
- final long nowElapsedMillis = SystemClock.elapsedRealtime();
+ final long nowElapsedMillis = sElapsedRealtimeClock.millis();
if (job.hasDeadlineConstraint() && evaluateDeadlineConstraint(job, nowElapsedMillis)) {
return;
} else if (job.hasTimingDelayConstraint() && evaluateTimingDelayConstraint(job,
@@ -157,7 +158,7 @@
long nextExpiryTime = Long.MAX_VALUE;
int nextExpiryUid = 0;
String nextExpiryPackageName = null;
- final long nowElapsedMillis = SystemClock.elapsedRealtime();
+ final long nowElapsedMillis = sElapsedRealtimeClock.millis();
Iterator<JobStatus> it = mTrackedJobs.iterator();
while (it.hasNext()) {
@@ -201,7 +202,7 @@
*/
private void checkExpiredDelaysAndResetAlarm() {
synchronized (mLock) {
- final long nowElapsedMillis = SystemClock.elapsedRealtime();
+ final long nowElapsedMillis = sElapsedRealtimeClock.millis();
long nextDelayTime = Long.MAX_VALUE;
int nextDelayUid = 0;
String nextDelayPackageName = null;
@@ -283,7 +284,7 @@
}
private long maybeAdjustAlarmTime(long proposedAlarmTimeElapsedMillis) {
- final long earliestWakeupTimeElapsed = SystemClock.elapsedRealtime();
+ final long earliestWakeupTimeElapsed = sElapsedRealtimeClock.millis();
if (proposedAlarmTimeElapsedMillis < earliestWakeupTimeElapsed) {
return earliestWakeupTimeElapsed;
}
@@ -328,9 +329,9 @@
@Override
public void dumpControllerStateLocked(PrintWriter pw, int filterUid) {
- final long nowElapsed = SystemClock.elapsedRealtime();
+ final long nowElapsed = sElapsedRealtimeClock.millis();
pw.print("Alarms: now=");
- pw.print(SystemClock.elapsedRealtime());
+ pw.print(sElapsedRealtimeClock.millis());
pw.println();
pw.print("Next delay alarm in ");
TimeUtils.formatDuration(mNextDelayExpiredElapsedMillis, nowElapsed, pw);
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index 551cb10..3fa3cd4 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -3746,7 +3746,7 @@
extends UsageStatsManagerInternal.AppIdleStateChangeListener {
@Override
- public void onAppIdleStateChanged(String packageName, int userId, boolean idle) {
+ public void onAppIdleStateChanged(String packageName, int userId, boolean idle, int bucket) {
try {
final int uid = mContext.getPackageManager().getPackageUidAsUser(packageName,
PackageManager.MATCH_UNINSTALLED_PACKAGES, userId);
diff --git a/services/core/java/com/android/server/net/watchlist/DigestUtils.java b/services/core/java/com/android/server/net/watchlist/DigestUtils.java
new file mode 100644
index 0000000..57becb0
--- /dev/null
+++ b/services/core/java/com/android/server/net/watchlist/DigestUtils.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.net.watchlist;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+
+/**
+ * Utils for calculating digests.
+ */
+public class DigestUtils {
+
+ private static final int FILE_READ_BUFFER_SIZE = 16 * 1024;
+
+ private DigestUtils() {}
+
+ /** @return SHA256 hash of the provided file */
+ public static byte[] getSha256Hash(File apkFile) throws IOException, NoSuchAlgorithmException {
+ try (InputStream stream = new FileInputStream(apkFile)) {
+ return getSha256Hash(stream);
+ }
+ }
+
+ /** @return SHA256 hash of data read from the provided input stream */
+ public static byte[] getSha256Hash(InputStream stream)
+ throws IOException, NoSuchAlgorithmException {
+ MessageDigest digester = MessageDigest.getInstance("SHA256");
+
+ int bytesRead;
+ byte[] buf = new byte[FILE_READ_BUFFER_SIZE];
+ while ((bytesRead = stream.read(buf)) >= 0) {
+ digester.update(buf, 0, bytesRead);
+ }
+ return digester.digest();
+ }
+}
\ No newline at end of file
diff --git a/services/core/java/com/android/server/net/watchlist/HarmfulDigests.java b/services/core/java/com/android/server/net/watchlist/HarmfulDigests.java
new file mode 100644
index 0000000..27c22ce
--- /dev/null
+++ b/services/core/java/com/android/server/net/watchlist/HarmfulDigests.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.net.watchlist;
+
+import com.android.internal.util.HexDump;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Helper class to store all harmful digests in memory.
+ * TODO: Optimize memory usage using byte array with binary search.
+ */
+class HarmfulDigests {
+
+ private final Set<String> mDigestSet;
+
+ HarmfulDigests(List<byte[]> digests) {
+ final HashSet<String> tmpDigestSet = new HashSet<>();
+ final int size = digests.size();
+ for (int i = 0; i < size; i++) {
+ tmpDigestSet.add(HexDump.toHexString(digests.get(i)));
+ }
+ mDigestSet = Collections.unmodifiableSet(tmpDigestSet);
+ }
+
+ public boolean contains(byte[] digest) {
+ return mDigestSet.contains(HexDump.toHexString(digest));
+ }
+
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ for (String digest : mDigestSet) {
+ pw.println(digest);
+ }
+ pw.println("");
+ }
+}
diff --git a/services/core/java/com/android/server/net/watchlist/NetworkWatchlistService.java b/services/core/java/com/android/server/net/watchlist/NetworkWatchlistService.java
new file mode 100644
index 0000000..171703a
--- /dev/null
+++ b/services/core/java/com/android/server/net/watchlist/NetworkWatchlistService.java
@@ -0,0 +1,267 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.net.watchlist;
+
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.net.IIpConnectivityMetrics;
+import android.net.INetdEventCallback;
+import android.net.NetworkWatchlistManager;
+import android.net.metrics.IpConnectivityLog;
+import android.os.Binder;
+import android.os.Process;
+import android.os.SharedMemory;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.SystemProperties;
+import android.text.TextUtils;
+import android.util.Slog;
+
+import com.android.internal.R;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.DumpUtils;
+import com.android.internal.net.INetworkWatchlistManager;
+import com.android.server.ServiceThread;
+import com.android.server.SystemService;
+
+import java.io.FileDescriptor;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.List;
+
+/**
+ * Implementation of network watchlist service.
+ */
+public class NetworkWatchlistService extends INetworkWatchlistManager.Stub {
+
+ private static final String TAG = NetworkWatchlistService.class.getSimpleName();
+ static final boolean DEBUG = false;
+
+ private static final String PROPERTY_NETWORK_WATCHLIST_ENABLED =
+ "ro.network_watchlist_enabled";
+
+ private static final int MAX_NUM_OF_WATCHLIST_DIGESTS = 10000;
+
+ public static class Lifecycle extends SystemService {
+ private NetworkWatchlistService mService;
+
+ public Lifecycle(Context context) {
+ super(context);
+ }
+
+ @Override
+ public void onStart() {
+ if (!SystemProperties.getBoolean(PROPERTY_NETWORK_WATCHLIST_ENABLED, false)) {
+ // Watchlist service is disabled
+ return;
+ }
+ mService = new NetworkWatchlistService(getContext());
+ publishBinderService(Context.NETWORK_WATCHLIST_SERVICE, mService);
+ }
+
+ @Override
+ public void onBootPhase(int phase) {
+ if (!SystemProperties.getBoolean(PROPERTY_NETWORK_WATCHLIST_ENABLED, false)) {
+ // Watchlist service is disabled
+ return;
+ }
+ if (phase == SystemService.PHASE_ACTIVITY_MANAGER_READY) {
+ try {
+ mService.initIpConnectivityMetrics();
+ mService.startWatchlistLogging();
+ } catch (RemoteException e) {
+ // Should not happen
+ }
+ ReportWatchlistJobService.schedule(getContext());
+ }
+ }
+ }
+
+ private volatile boolean mIsLoggingEnabled = false;
+ private final Object mLoggingSwitchLock = new Object();
+
+ private final WatchlistSettings mSettings;
+ private final Context mContext;
+
+ // Separate thread to handle expensive watchlist logging work.
+ private final ServiceThread mHandlerThread;
+
+ @VisibleForTesting
+ IIpConnectivityMetrics mIpConnectivityMetrics;
+ @VisibleForTesting
+ WatchlistLoggingHandler mNetworkWatchlistHandler;
+
+ public NetworkWatchlistService(Context context) {
+ mContext = context;
+ mSettings = WatchlistSettings.getInstance();
+ mHandlerThread = new ServiceThread(TAG, Process.THREAD_PRIORITY_BACKGROUND,
+ /* allowIo */ false);
+ mHandlerThread.start();
+ mNetworkWatchlistHandler = new WatchlistLoggingHandler(mContext,
+ mHandlerThread.getLooper());
+ mNetworkWatchlistHandler.reportWatchlistIfNecessary();
+ }
+
+ // For testing only
+ @VisibleForTesting
+ NetworkWatchlistService(Context context, ServiceThread handlerThread,
+ WatchlistLoggingHandler handler, IIpConnectivityMetrics ipConnectivityMetrics) {
+ mContext = context;
+ mSettings = WatchlistSettings.getInstance();
+ mHandlerThread = handlerThread;
+ mNetworkWatchlistHandler = handler;
+ mIpConnectivityMetrics = ipConnectivityMetrics;
+ }
+
+ private void initIpConnectivityMetrics() {
+ mIpConnectivityMetrics = (IIpConnectivityMetrics) IIpConnectivityMetrics.Stub.asInterface(
+ ServiceManager.getService(IpConnectivityLog.SERVICE_NAME));
+ }
+
+ private final INetdEventCallback mNetdEventCallback = new INetdEventCallback.Stub() {
+ @Override
+ public void onDnsEvent(String hostname, String[] ipAddresses, int ipAddressesCount,
+ long timestamp, int uid) {
+ if (!mIsLoggingEnabled) {
+ return;
+ }
+ mNetworkWatchlistHandler.asyncNetworkEvent(hostname, ipAddresses, uid);
+ }
+
+ @Override
+ public void onConnectEvent(String ipAddr, int port, long timestamp, int uid) {
+ if (!mIsLoggingEnabled) {
+ return;
+ }
+ mNetworkWatchlistHandler.asyncNetworkEvent(null, new String[]{ipAddr}, uid);
+ }
+ };
+
+ @VisibleForTesting
+ protected boolean startWatchlistLoggingImpl() throws RemoteException {
+ if (DEBUG) {
+ Slog.i(TAG, "Starting watchlist logging.");
+ }
+ synchronized (mLoggingSwitchLock) {
+ if (mIsLoggingEnabled) {
+ Slog.w(TAG, "Watchlist logging is already running");
+ return true;
+ }
+ try {
+ if (mIpConnectivityMetrics.addNetdEventCallback(
+ INetdEventCallback.CALLBACK_CALLER_NETWORK_WATCHLIST, mNetdEventCallback)) {
+ mIsLoggingEnabled = true;
+ return true;
+ } else {
+ return false;
+ }
+ } catch (RemoteException re) {
+ // Should not happen
+ return false;
+ }
+ }
+ }
+
+ @Override
+ public boolean startWatchlistLogging() throws RemoteException {
+ enforceWatchlistLoggingPermission();
+ return startWatchlistLoggingImpl();
+ }
+
+ @VisibleForTesting
+ protected boolean stopWatchlistLoggingImpl() {
+ if (DEBUG) {
+ Slog.i(TAG, "Stopping watchlist logging");
+ }
+ synchronized (mLoggingSwitchLock) {
+ if (!mIsLoggingEnabled) {
+ Slog.w(TAG, "Watchlist logging is not running");
+ return true;
+ }
+ // stop the logging regardless of whether we fail to unregister listener
+ mIsLoggingEnabled = false;
+
+ try {
+ return mIpConnectivityMetrics.removeNetdEventCallback(
+ INetdEventCallback.CALLBACK_CALLER_NETWORK_WATCHLIST);
+ } catch (RemoteException re) {
+ // Should not happen
+ return false;
+ }
+ }
+ }
+
+ @Override
+ public boolean stopWatchlistLogging() throws RemoteException {
+ enforceWatchlistLoggingPermission();
+ return stopWatchlistLoggingImpl();
+ }
+
+ private void enforceWatchlistLoggingPermission() {
+ final int uid = Binder.getCallingUid();
+ if (uid != Process.SYSTEM_UID) {
+ throw new SecurityException(String.format("Uid %d has no permission to change watchlist"
+ + " setting.", uid));
+ }
+ }
+
+ /**
+ * Set a new network watchlist.
+ * This method should be called by ConfigUpdater only.
+ *
+ * @return True if network watchlist is updated.
+ */
+ public boolean setNetworkSecurityWatchlist(List<byte[]> domainsCrc32Digests,
+ List<byte[]> domainsSha256Digests,
+ List<byte[]> ipAddressesCrc32Digests,
+ List<byte[]> ipAddressesSha256Digests) {
+ Slog.i(TAG, "Setting network watchlist");
+ if (domainsCrc32Digests == null || domainsSha256Digests == null
+ || ipAddressesCrc32Digests == null || ipAddressesSha256Digests == null) {
+ Slog.e(TAG, "Parameters cannot be null");
+ return false;
+ }
+ if (domainsCrc32Digests.size() != domainsSha256Digests.size()
+ || ipAddressesCrc32Digests.size() != ipAddressesSha256Digests.size()) {
+ Slog.e(TAG, "Must need to have the same number of CRC32 and SHA256 digests");
+ return false;
+ }
+ if (domainsSha256Digests.size() + ipAddressesSha256Digests.size()
+ > MAX_NUM_OF_WATCHLIST_DIGESTS) {
+ Slog.e(TAG, "Total watchlist size cannot exceed " + MAX_NUM_OF_WATCHLIST_DIGESTS);
+ return false;
+ }
+ mSettings.writeSettingsToDisk(domainsCrc32Digests, domainsSha256Digests,
+ ipAddressesCrc32Digests, ipAddressesSha256Digests);
+ Slog.i(TAG, "Set network watchlist: Success");
+ return true;
+ }
+
+ @Override
+ public void reportWatchlistIfNecessary() {
+ // Allow any apps to trigger report event, as we won't run it if it's too early.
+ mNetworkWatchlistHandler.reportWatchlistIfNecessary();
+ }
+
+ @Override
+ protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
+ mSettings.dump(fd, pw, args);
+ }
+
+}
diff --git a/services/core/java/com/android/server/net/watchlist/ReportWatchlistJobService.java b/services/core/java/com/android/server/net/watchlist/ReportWatchlistJobService.java
new file mode 100644
index 0000000..dfeb1b2
--- /dev/null
+++ b/services/core/java/com/android/server/net/watchlist/ReportWatchlistJobService.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.net.watchlist;
+
+import android.app.job.JobInfo;
+import android.app.job.JobParameters;
+import android.app.job.JobScheduler;
+import android.app.job.JobService;
+import android.content.ComponentName;
+import android.content.Context;
+import android.net.NetworkWatchlistManager;
+import android.util.Slog;
+
+import java.util.concurrent.TimeUnit;
+
+/**
+ * A job that periodically report watchlist records.
+ */
+public class ReportWatchlistJobService extends JobService {
+
+ private static final boolean DEBUG = NetworkWatchlistService.DEBUG;
+ private static final String TAG = "WatchlistJobService";
+
+ // Unique job id used in system service, other jobs should not use the same value.
+ public static final int REPORT_WATCHLIST_RECORDS_JOB_ID = 0xd7689;
+ public static final long REPORT_WATCHLIST_RECORDS_PERIOD_MILLIS =
+ TimeUnit.HOURS.toMillis(12);
+
+ @Override
+ public boolean onStartJob(final JobParameters jobParameters) {
+ if (jobParameters.getJobId() != REPORT_WATCHLIST_RECORDS_JOB_ID) {
+ return false;
+ }
+ if (DEBUG) Slog.d(TAG, "Start scheduled job.");
+ new NetworkWatchlistManager(this).reportWatchlistIfNecessary();
+ jobFinished(jobParameters, false);
+ return true;
+ }
+
+ @Override
+ public boolean onStopJob(JobParameters jobParameters) {
+ return true; // Reschedule when possible.
+ }
+
+ /**
+ * Schedule the {@link ReportWatchlistJobService} to run periodically.
+ */
+ public static void schedule(Context context) {
+ if (DEBUG) Slog.d(TAG, "Scheduling records aggregator task");
+ final JobScheduler scheduler =
+ (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE);
+ scheduler.schedule(new JobInfo.Builder(REPORT_WATCHLIST_RECORDS_JOB_ID,
+ new ComponentName(context, ReportWatchlistJobService.class))
+ //.setOverrideDeadline(45 * 1000) // Schedule job soon, for testing.
+ .setPeriodic(REPORT_WATCHLIST_RECORDS_PERIOD_MILLIS)
+ .setRequiresDeviceIdle(true)
+ .setRequiresBatteryNotLow(true)
+ .setPersisted(false)
+ .build());
+ }
+
+}
diff --git a/services/core/java/com/android/server/net/watchlist/WatchlistLoggingHandler.java b/services/core/java/com/android/server/net/watchlist/WatchlistLoggingHandler.java
new file mode 100644
index 0000000..2247558
--- /dev/null
+++ b/services/core/java/com/android/server/net/watchlist/WatchlistLoggingHandler.java
@@ -0,0 +1,298 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.net.watchlist;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.os.Bundle;
+import android.os.DropBoxManager;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.text.TextUtils;
+import android.util.Slog;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.ArrayUtils;
+
+import java.io.File;
+import java.io.IOException;
+import java.security.NoSuchAlgorithmException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * A Handler class for network watchlist logging on a background thread.
+ */
+class WatchlistLoggingHandler extends Handler {
+
+ private static final String TAG = WatchlistLoggingHandler.class.getSimpleName();
+ private static final boolean DEBUG = NetworkWatchlistService.DEBUG;
+
+ @VisibleForTesting
+ static final int LOG_WATCHLIST_EVENT_MSG = 1;
+ @VisibleForTesting
+ static final int REPORT_RECORDS_IF_NECESSARY_MSG = 2;
+
+ private static final long ONE_DAY_MS = TimeUnit.DAYS.toMillis(1);
+ private static final String DROPBOX_TAG = "network_watchlist_report";
+
+ private final Context mContext;
+ private final ContentResolver mResolver;
+ private final PackageManager mPm;
+ private final WatchlistReportDbHelper mDbHelper;
+ private final WatchlistSettings mSettings;
+ // A cache for uid and apk digest mapping.
+ // As uid won't be reused until reboot, it's safe to assume uid is unique per signature and app.
+ // TODO: Use more efficient data structure.
+ private final HashMap<Integer, byte[]> mCachedUidDigestMap = new HashMap<>();
+
+ private interface WatchlistEventKeys {
+ String HOST = "host";
+ String IP_ADDRESSES = "ipAddresses";
+ String UID = "uid";
+ String TIMESTAMP = "timestamp";
+ }
+
+ WatchlistLoggingHandler(Context context, Looper looper) {
+ super(looper);
+ mContext = context;
+ mPm = mContext.getPackageManager();
+ mResolver = mContext.getContentResolver();
+ mDbHelper = WatchlistReportDbHelper.getInstance(context);
+ mSettings = WatchlistSettings.getInstance();
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case LOG_WATCHLIST_EVENT_MSG: {
+ final Bundle data = msg.getData();
+ handleNetworkEvent(
+ data.getString(WatchlistEventKeys.HOST),
+ data.getStringArray(WatchlistEventKeys.IP_ADDRESSES),
+ data.getInt(WatchlistEventKeys.UID),
+ data.getLong(WatchlistEventKeys.TIMESTAMP)
+ );
+ break;
+ }
+ case REPORT_RECORDS_IF_NECESSARY_MSG:
+ tryAggregateRecords();
+ break;
+ default: {
+ Slog.d(TAG, "WatchlistLoggingHandler received an unknown of message.");
+ break;
+ }
+ }
+ }
+
+ /**
+ * Report network watchlist records if we collected enough data.
+ */
+ public void reportWatchlistIfNecessary() {
+ final Message msg = obtainMessage(REPORT_RECORDS_IF_NECESSARY_MSG);
+ sendMessage(msg);
+ }
+
+ /**
+ * Insert network traffic event to watchlist async queue processor.
+ */
+ public void asyncNetworkEvent(String host, String[] ipAddresses, int uid) {
+ final Message msg = obtainMessage(LOG_WATCHLIST_EVENT_MSG);
+ final Bundle bundle = new Bundle();
+ bundle.putString(WatchlistEventKeys.HOST, host);
+ bundle.putStringArray(WatchlistEventKeys.IP_ADDRESSES, ipAddresses);
+ bundle.putInt(WatchlistEventKeys.UID, uid);
+ bundle.putLong(WatchlistEventKeys.TIMESTAMP, System.currentTimeMillis());
+ msg.setData(bundle);
+ sendMessage(msg);
+ }
+
+ private void handleNetworkEvent(String hostname, String[] ipAddresses,
+ int uid, long timestamp) {
+ if (DEBUG) {
+ Slog.i(TAG, "handleNetworkEvent with host: " + hostname + ", uid: " + uid);
+ }
+ final String cncDomain = searchAllSubDomainsInWatchlist(hostname);
+ if (cncDomain != null) {
+ insertRecord(getDigestFromUid(uid), cncDomain, timestamp);
+ } else {
+ final String cncIp = searchIpInWatchlist(ipAddresses);
+ if (cncIp != null) {
+ insertRecord(getDigestFromUid(uid), cncIp, timestamp);
+ }
+ }
+ }
+
+ private boolean insertRecord(byte[] digest, String cncHost, long timestamp) {
+ final boolean result = mDbHelper.insertNewRecord(digest, cncHost, timestamp);
+ tryAggregateRecords();
+ return result;
+ }
+
+ private boolean shouldReportNetworkWatchlist() {
+ final long lastReportTime = Settings.Global.getLong(mResolver,
+ Settings.Global.NETWORK_WATCHLIST_LAST_REPORT_TIME, 0L);
+ final long currentTimestamp = System.currentTimeMillis();
+ if (currentTimestamp < lastReportTime) {
+ Slog.i(TAG, "Last report time is larger than current time, reset report");
+ mDbHelper.cleanup();
+ return false;
+ }
+ return currentTimestamp >= lastReportTime + ONE_DAY_MS;
+ }
+
+ private void tryAggregateRecords() {
+ if (shouldReportNetworkWatchlist()) {
+ Slog.i(TAG, "Start aggregating watchlist records.");
+ final DropBoxManager dbox = mContext.getSystemService(DropBoxManager.class);
+ if (dbox != null && !dbox.isTagEnabled(DROPBOX_TAG)) {
+ final WatchlistReportDbHelper.AggregatedResult aggregatedResult =
+ mDbHelper.getAggregatedRecords();
+ final byte[] encodedResult = encodeAggregatedResult(aggregatedResult);
+ if (encodedResult != null) {
+ addEncodedReportToDropBox(encodedResult);
+ }
+ }
+ mDbHelper.cleanup();
+ Settings.Global.putLong(mResolver, Settings.Global.NETWORK_WATCHLIST_LAST_REPORT_TIME,
+ System.currentTimeMillis());
+ } else {
+ Slog.i(TAG, "No need to aggregate record yet.");
+ }
+ }
+
+ private byte[] encodeAggregatedResult(
+ WatchlistReportDbHelper.AggregatedResult aggregatedResult) {
+ // TODO: Encode results using differential privacy.
+ return null;
+ }
+
+ private void addEncodedReportToDropBox(byte[] encodedReport) {
+ final DropBoxManager dbox = mContext.getSystemService(DropBoxManager.class);
+ dbox.addData(DROPBOX_TAG, encodedReport, 0);
+ }
+
+ /**
+ * Get app digest from app uid.
+ */
+ private byte[] getDigestFromUid(int uid) {
+ final byte[] cachedDigest = mCachedUidDigestMap.get(uid);
+ if (cachedDigest != null) {
+ return cachedDigest;
+ }
+ final String[] packageNames = mPm.getPackagesForUid(uid);
+ final int userId = UserHandle.getUserId(uid);
+ if (!ArrayUtils.isEmpty(packageNames)) {
+ for (String packageName : packageNames) {
+ try {
+ final String apkPath = mPm.getPackageInfoAsUser(packageName,
+ PackageManager.MATCH_DIRECT_BOOT_AWARE
+ | PackageManager.MATCH_DIRECT_BOOT_UNAWARE, userId)
+ .applicationInfo.publicSourceDir;
+ if (TextUtils.isEmpty(apkPath)) {
+ Slog.w(TAG, "Cannot find apkPath for " + packageName);
+ continue;
+ }
+ final byte[] digest = DigestUtils.getSha256Hash(new File(apkPath));
+ mCachedUidDigestMap.put(uid, digest);
+ return digest;
+ } catch (NameNotFoundException | NoSuchAlgorithmException | IOException e) {
+ Slog.e(TAG, "Should not happen", e);
+ return null;
+ }
+ }
+ } else {
+ Slog.e(TAG, "Should not happen");
+ }
+ return null;
+ }
+
+ /**
+ * Search if any ip addresses are in watchlist.
+ *
+ * @param ipAddresses Ip address that you want to search in watchlist.
+ * @return Ip address that exists in watchlist, null if it does not match anything.
+ */
+ private String searchIpInWatchlist(String[] ipAddresses) {
+ for (String ipAddress : ipAddresses) {
+ if (isIpInWatchlist(ipAddress)) {
+ return ipAddress;
+ }
+ }
+ return null;
+ }
+
+ /** Search if the ip is in watchlist */
+ private boolean isIpInWatchlist(String ipAddr) {
+ if (ipAddr == null) {
+ return false;
+ }
+ return mSettings.containsIp(ipAddr);
+ }
+
+ /** Search if the host is in watchlist */
+ private boolean isHostInWatchlist(String host) {
+ if (host == null) {
+ return false;
+ }
+ return mSettings.containsDomain(host);
+ }
+
+ /**
+ * Search if any sub-domain in host is in watchlist.
+ *
+ * @param host Host that we want to search.
+ * @return Domain that exists in watchlist, null if it does not match anything.
+ */
+ private String searchAllSubDomainsInWatchlist(String host) {
+ if (host == null) {
+ return null;
+ }
+ final String[] subDomains = getAllSubDomains(host);
+ for (String subDomain : subDomains) {
+ if (isHostInWatchlist(subDomain)) {
+ return subDomain;
+ }
+ }
+ return null;
+ }
+
+ /** Get all sub-domains in a host */
+ @VisibleForTesting
+ static String[] getAllSubDomains(String host) {
+ if (host == null) {
+ return null;
+ }
+ final ArrayList<String> subDomainList = new ArrayList<>();
+ subDomainList.add(host);
+ int index = host.indexOf(".");
+ while (index != -1) {
+ host = host.substring(index + 1);
+ if (!TextUtils.isEmpty(host)) {
+ subDomainList.add(host);
+ }
+ index = host.indexOf(".");
+ }
+ return subDomainList.toArray(new String[0]);
+ }
+}
diff --git a/services/core/java/com/android/server/net/watchlist/WatchlistReportDbHelper.java b/services/core/java/com/android/server/net/watchlist/WatchlistReportDbHelper.java
new file mode 100644
index 0000000..f48463f
--- /dev/null
+++ b/services/core/java/com/android/server/net/watchlist/WatchlistReportDbHelper.java
@@ -0,0 +1,203 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.net.watchlist;
+
+import android.content.ContentValues;
+import android.content.Context;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteOpenHelper;
+import android.util.Pair;
+
+import com.android.internal.util.HexDump;
+
+import java.util.ArrayList;
+import java.util.GregorianCalendar;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Helper class to process watchlist read / save watchlist reports.
+ */
+class WatchlistReportDbHelper extends SQLiteOpenHelper {
+
+ private static final String TAG = "WatchlistReportDbHelper";
+
+ private static final String NAME = "watchlist_report.db";
+ private static final int VERSION = 2;
+
+ private static final int IDLE_CONNECTION_TIMEOUT_MS = 30000;
+
+ private static class WhiteListReportContract {
+ private static final String TABLE = "records";
+ private static final String APP_DIGEST = "app_digest";
+ private static final String CNC_DOMAIN = "cnc_domain";
+ private static final String TIMESTAMP = "timestamp";
+ }
+
+ private static final String CREATE_TABLE_MODEL = "CREATE TABLE "
+ + WhiteListReportContract.TABLE + "("
+ + WhiteListReportContract.APP_DIGEST + " BLOB,"
+ + WhiteListReportContract.CNC_DOMAIN + " TEXT,"
+ + WhiteListReportContract.TIMESTAMP + " INTEGER DEFAULT 0" + " )";
+
+ private static final int INDEX_DIGEST = 0;
+ private static final int INDEX_CNC_DOMAIN = 1;
+ private static final int INDEX_TIMESTAMP = 2;
+
+ private static final String[] DIGEST_DOMAIN_PROJECTION =
+ new String[] {
+ WhiteListReportContract.APP_DIGEST,
+ WhiteListReportContract.CNC_DOMAIN
+ };
+
+ private static WatchlistReportDbHelper sInstance;
+
+ /**
+ * Aggregated watchlist records.
+ */
+ public static class AggregatedResult {
+ // A list of digests that visited c&c domain or ip before.
+ Set<String> appDigestList;
+
+ // The c&c domain or ip visited before.
+ String cncDomainVisited;
+
+ // A list of app digests and c&c domain visited.
+ HashMap<String, String> appDigestCNCList;
+ }
+
+ private WatchlistReportDbHelper(Context context) {
+ super(context, WatchlistSettings.getSystemWatchlistFile(NAME).getAbsolutePath(),
+ null, VERSION);
+ // Memory optimization - close idle connections after 30s of inactivity
+ setIdleConnectionTimeout(IDLE_CONNECTION_TIMEOUT_MS);
+ }
+
+ public static synchronized WatchlistReportDbHelper getInstance(Context context) {
+ if (sInstance != null) {
+ return sInstance;
+ }
+ sInstance = new WatchlistReportDbHelper(context);
+ return sInstance;
+ }
+
+ @Override
+ public void onCreate(SQLiteDatabase db) {
+ db.execSQL(CREATE_TABLE_MODEL);
+ }
+
+ @Override
+ public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
+ // TODO: For now, drop older tables and recreate new ones.
+ db.execSQL("DROP TABLE IF EXISTS " + WhiteListReportContract.TABLE);
+ onCreate(db);
+ }
+
+ /**
+ * Insert new watchlist record.
+ *
+ * @param appDigest The digest of an app.
+ * @param cncDomain C&C domain that app visited.
+ * @return True if success.
+ */
+ public boolean insertNewRecord(byte[] appDigest, String cncDomain,
+ long timestamp) {
+ final SQLiteDatabase db = getWritableDatabase();
+ final ContentValues values = new ContentValues();
+ values.put(WhiteListReportContract.APP_DIGEST, appDigest);
+ values.put(WhiteListReportContract.CNC_DOMAIN, cncDomain);
+ values.put(WhiteListReportContract.TIMESTAMP, timestamp);
+ return db.insert(WhiteListReportContract.TABLE, null, values) != -1;
+ }
+
+ /**
+ * Aggregate the records in database, and return a rappor encoded result.
+ */
+ public AggregatedResult getAggregatedRecords() {
+ final long twoDaysBefore = getTwoDaysBeforeTimestamp();
+ final long yesterday = getYesterdayTimestamp();
+ final String selectStatement = WhiteListReportContract.TIMESTAMP + " >= ? AND " +
+ WhiteListReportContract.TIMESTAMP + " <= ?";
+
+ final SQLiteDatabase db = getReadableDatabase();
+ Cursor c = null;
+ try {
+ c = db.query(true /* distinct */,
+ WhiteListReportContract.TABLE, DIGEST_DOMAIN_PROJECTION, selectStatement,
+ new String[]{"" + twoDaysBefore, "" + yesterday}, null, null,
+ null, null);
+ if (c == null || c.getCount() == 0) {
+ return null;
+ }
+ final AggregatedResult result = new AggregatedResult();
+ result.cncDomainVisited = null;
+ // After aggregation, each digest maximum will have only 1 record.
+ result.appDigestList = new HashSet<>();
+ result.appDigestCNCList = new HashMap<>();
+ while (c.moveToNext()) {
+ // We use hex string here as byte[] cannot be a key in HashMap.
+ String digestHexStr = HexDump.toHexString(c.getBlob(INDEX_DIGEST));
+ String cncDomain = c.getString(INDEX_CNC_DOMAIN);
+
+ result.appDigestList.add(digestHexStr);
+ if (result.cncDomainVisited != null) {
+ result.cncDomainVisited = cncDomain;
+ }
+ result.appDigestCNCList.put(digestHexStr, cncDomain);
+ }
+ return result;
+ } finally {
+ if (c != null) {
+ c.close();
+ }
+ }
+ }
+
+ /**
+ * Remove all the records before yesterday.
+ *
+ * @return True if success.
+ */
+ public boolean cleanup() {
+ final SQLiteDatabase db = getWritableDatabase();
+ final long twoDaysBefore = getTwoDaysBeforeTimestamp();
+ final String clause = WhiteListReportContract.TIMESTAMP + "< " + twoDaysBefore;
+ return db.delete(WhiteListReportContract.TABLE, clause, null) != 0;
+ }
+
+ static long getTwoDaysBeforeTimestamp() {
+ return getMidnightTimestamp(2);
+ }
+
+ static long getYesterdayTimestamp() {
+ return getMidnightTimestamp(1);
+ }
+
+ static long getMidnightTimestamp(int daysBefore) {
+ java.util.Calendar date = new GregorianCalendar();
+ // reset hour, minutes, seconds and millis
+ date.set(java.util.Calendar.HOUR_OF_DAY, 0);
+ date.set(java.util.Calendar.MINUTE, 0);
+ date.set(java.util.Calendar.SECOND, 0);
+ date.set(java.util.Calendar.MILLISECOND, 0);
+ date.add(java.util.Calendar.DAY_OF_MONTH, -daysBefore);
+ return date.getTimeInMillis();
+ }
+}
\ No newline at end of file
diff --git a/services/core/java/com/android/server/net/watchlist/WatchlistSettings.java b/services/core/java/com/android/server/net/watchlist/WatchlistSettings.java
new file mode 100644
index 0000000..c50f0d5
--- /dev/null
+++ b/services/core/java/com/android/server/net/watchlist/WatchlistSettings.java
@@ -0,0 +1,284 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.net.watchlist;
+
+import android.os.Environment;
+import android.util.AtomicFile;
+import android.util.Log;
+import android.util.Xml;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.FastXmlSerializer;
+import com.android.internal.util.HexDump;
+import com.android.internal.util.XmlUtils;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
+import java.io.File;
+import java.io.FileDescriptor;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.nio.charset.StandardCharsets;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.zip.CRC32;
+
+/**
+ * A util class to do watchlist settings operations, like setting watchlist, query if a domain
+ * exists in watchlist.
+ */
+class WatchlistSettings {
+ private static final String TAG = "WatchlistSettings";
+
+ // Settings xml will be stored in /data/system/network_watchlist/watchlist_settings.xml
+ static final String SYSTEM_WATCHLIST_DIR = "network_watchlist";
+
+ private static final String WATCHLIST_XML_FILE = "watchlist_settings.xml";
+
+ private static class XmlTags {
+ private static final String WATCHLIST_SETTINGS = "watchlist-settings";
+ private static final String SHA256_DOMAIN = "sha256-domain";
+ private static final String CRC32_DOMAIN = "crc32-domain";
+ private static final String SHA256_IP = "sha256-ip";
+ private static final String CRC32_IP = "crc32-ip";
+ private static final String HASH = "hash";
+ }
+
+ private static WatchlistSettings sInstance = new WatchlistSettings();
+ private final AtomicFile mXmlFile;
+ private final Object mLock = new Object();
+ private HarmfulDigests mCrc32DomainDigests = new HarmfulDigests(new ArrayList<>());
+ private HarmfulDigests mSha256DomainDigests = new HarmfulDigests(new ArrayList<>());
+ private HarmfulDigests mCrc32IpDigests = new HarmfulDigests(new ArrayList<>());
+ private HarmfulDigests mSha256IpDigests = new HarmfulDigests(new ArrayList<>());
+
+ public static synchronized WatchlistSettings getInstance() {
+ return sInstance;
+ }
+
+ private WatchlistSettings() {
+ this(getSystemWatchlistFile(WATCHLIST_XML_FILE));
+ }
+
+ @VisibleForTesting
+ protected WatchlistSettings(File xmlFile) {
+ mXmlFile = new AtomicFile(xmlFile);
+ readSettingsLocked();
+ }
+
+ static File getSystemWatchlistFile(String filename) {
+ final File dataSystemDir = Environment.getDataSystemDirectory();
+ final File systemWatchlistDir = new File(dataSystemDir, SYSTEM_WATCHLIST_DIR);
+ systemWatchlistDir.mkdirs();
+ return new File(systemWatchlistDir, filename);
+ }
+
+ private void readSettingsLocked() {
+ synchronized (mLock) {
+ FileInputStream stream;
+ try {
+ stream = mXmlFile.openRead();
+ } catch (FileNotFoundException e) {
+ Log.i(TAG, "No watchlist settings: " + mXmlFile.getBaseFile().getAbsolutePath());
+ return;
+ }
+
+ final List<byte[]> crc32DomainList = new ArrayList<>();
+ final List<byte[]> sha256DomainList = new ArrayList<>();
+ final List<byte[]> crc32IpList = new ArrayList<>();
+ final List<byte[]> sha256IpList = new ArrayList<>();
+
+ try {
+ XmlPullParser parser = Xml.newPullParser();
+ parser.setInput(stream, StandardCharsets.UTF_8.name());
+ parser.nextTag();
+ parser.require(XmlPullParser.START_TAG, null, XmlTags.WATCHLIST_SETTINGS);
+ while (parser.nextTag() == XmlPullParser.START_TAG) {
+ String tagName = parser.getName();
+ switch (tagName) {
+ case XmlTags.CRC32_DOMAIN:
+ parseHash(parser, tagName, crc32DomainList);
+ break;
+ case XmlTags.CRC32_IP:
+ parseHash(parser, tagName, crc32IpList);
+ break;
+ case XmlTags.SHA256_DOMAIN:
+ parseHash(parser, tagName, sha256DomainList);
+ break;
+ case XmlTags.SHA256_IP:
+ parseHash(parser, tagName, sha256IpList);
+ break;
+ default:
+ Log.w(TAG, "Unknown element: " + parser.getName());
+ XmlUtils.skipCurrentTag(parser);
+ }
+ }
+ parser.require(XmlPullParser.END_TAG, null, XmlTags.WATCHLIST_SETTINGS);
+ writeSettingsToMemory(crc32DomainList, sha256DomainList, crc32IpList, sha256IpList);
+ } catch (IllegalStateException | NullPointerException | NumberFormatException |
+ XmlPullParserException | IOException | IndexOutOfBoundsException e) {
+ Log.w(TAG, "Failed parsing " + e);
+ } finally {
+ try {
+ stream.close();
+ } catch (IOException e) {
+ }
+ }
+ }
+ }
+
+ private void parseHash(XmlPullParser parser, String tagName, List<byte[]> hashSet)
+ throws IOException, XmlPullParserException {
+ parser.require(XmlPullParser.START_TAG, null, tagName);
+ while (parser.nextTag() == XmlPullParser.START_TAG) {
+ parser.require(XmlPullParser.START_TAG, null, XmlTags.HASH);
+ byte[] hash = HexDump.hexStringToByteArray(parser.nextText());
+ parser.require(XmlPullParser.END_TAG, null, XmlTags.HASH);
+ hashSet.add(hash);
+ }
+ parser.require(XmlPullParser.END_TAG, null, tagName);
+ }
+
+ /**
+ * Write network watchlist settings to disk.
+ * Adb should not use it, should use writeSettingsToMemory directly instead.
+ */
+ public void writeSettingsToDisk(List<byte[]> newCrc32DomainList,
+ List<byte[]> newSha256DomainList,
+ List<byte[]> newCrc32IpList,
+ List<byte[]> newSha256IpList) {
+ synchronized (mLock) {
+ FileOutputStream stream;
+ try {
+ stream = mXmlFile.startWrite();
+ } catch (IOException e) {
+ Log.w(TAG, "Failed to write display settings: " + e);
+ return;
+ }
+
+ try {
+ XmlSerializer out = new FastXmlSerializer();
+ out.setOutput(stream, StandardCharsets.UTF_8.name());
+ out.startDocument(null, true);
+ out.startTag(null, XmlTags.WATCHLIST_SETTINGS);
+
+ writeHashSetToXml(out, XmlTags.SHA256_DOMAIN, newSha256DomainList);
+ writeHashSetToXml(out, XmlTags.SHA256_IP, newSha256IpList);
+ writeHashSetToXml(out, XmlTags.CRC32_DOMAIN, newCrc32DomainList);
+ writeHashSetToXml(out, XmlTags.CRC32_IP, newCrc32IpList);
+
+ out.endTag(null, XmlTags.WATCHLIST_SETTINGS);
+ out.endDocument();
+ mXmlFile.finishWrite(stream);
+ writeSettingsToMemory(newCrc32DomainList, newSha256DomainList, newCrc32IpList,
+ newSha256IpList);
+ } catch (IOException e) {
+ Log.w(TAG, "Failed to write display settings, restoring backup.", e);
+ mXmlFile.failWrite(stream);
+ }
+ }
+ }
+
+ /**
+ * Write network watchlist settings to memory.
+ */
+ public void writeSettingsToMemory(List<byte[]> newCrc32DomainList,
+ List<byte[]> newSha256DomainList,
+ List<byte[]> newCrc32IpList,
+ List<byte[]> newSha256IpList) {
+ synchronized (mLock) {
+ mCrc32DomainDigests = new HarmfulDigests(newCrc32DomainList);
+ mCrc32IpDigests = new HarmfulDigests(newCrc32IpList);
+ mSha256DomainDigests = new HarmfulDigests(newSha256DomainList);
+ mSha256IpDigests = new HarmfulDigests(newSha256IpList);
+ }
+ }
+
+ private static void writeHashSetToXml(XmlSerializer out, String tagName, List<byte[]> hashSet)
+ throws IOException {
+ out.startTag(null, tagName);
+ for (byte[] hash : hashSet) {
+ out.startTag(null, XmlTags.HASH);
+ out.text(HexDump.toHexString(hash));
+ out.endTag(null, XmlTags.HASH);
+ }
+ out.endTag(null, tagName);
+ }
+
+ public boolean containsDomain(String domain) {
+ // First it does a quick CRC32 check.
+ final byte[] crc32 = getCrc32(domain);
+ if (!mCrc32DomainDigests.contains(crc32)) {
+ return false;
+ }
+ // Now we do a slow SHA256 check.
+ final byte[] sha256 = getSha256(domain);
+ return mSha256DomainDigests.contains(sha256);
+ }
+
+ public boolean containsIp(String ip) {
+ // First it does a quick CRC32 check.
+ final byte[] crc32 = getCrc32(ip);
+ if (!mCrc32IpDigests.contains(crc32)) {
+ return false;
+ }
+ // Now we do a slow SHA256 check.
+ final byte[] sha256 = getSha256(ip);
+ return mSha256IpDigests.contains(sha256);
+ }
+
+
+ /** Get CRC32 of a string */
+ private byte[] getCrc32(String str) {
+ final CRC32 crc = new CRC32();
+ crc.update(str.getBytes());
+ final long tmp = crc.getValue();
+ return new byte[]{(byte)(tmp >> 24 & 255), (byte)(tmp >> 16 & 255),
+ (byte)(tmp >> 8 & 255), (byte)(tmp & 255)};
+ }
+
+ /** Get SHA256 of a string */
+ private byte[] getSha256(String str) {
+ MessageDigest messageDigest;
+ try {
+ messageDigest = MessageDigest.getInstance("SHA256");
+ } catch (NoSuchAlgorithmException e) {
+ /* can't happen */
+ return null;
+ }
+ messageDigest.update(str.getBytes());
+ return messageDigest.digest();
+ }
+
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ pw.println("Domain CRC32 digest list:");
+ mCrc32DomainDigests.dump(fd, pw, args);
+ pw.println("Domain SHA256 digest list:");
+ mSha256DomainDigests.dump(fd, pw, args);
+ pw.println("Ip CRC32 digest list:");
+ mCrc32IpDigests.dump(fd, pw, args);
+ pw.println("Ip SHA256 digest list:");
+ mSha256IpDigests.dump(fd, pw, args);
+ }
+}
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index f61cec9..1e9fab5 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -835,7 +835,8 @@
// alarm restrictions
final boolean muteMediaAndSystemSounds = zen && !mConfig.allowMediaSystemOther;
// total silence restrictions
- final boolean muteEverything = mZenMode == Global.ZEN_MODE_NO_INTERRUPTIONS;
+ final boolean muteEverything = mZenMode == Global.ZEN_MODE_NO_INTERRUPTIONS
+ || areAllBehaviorSoundsMuted();
for (int usage : AudioAttributes.SDK_USAGES) {
final int suppressionBehavior = AudioAttributes.SUPPRESSIBLE_USAGES.get(usage);
@@ -855,9 +856,11 @@
}
}
+
@VisibleForTesting
protected void applyRestrictions(boolean mute, int usage) {
final String[] exceptionPackages = null; // none (for now)
+
mAppOps.setRestriction(AppOpsManager.OP_VIBRATE, usage,
mute ? AppOpsManager.MODE_IGNORED : AppOpsManager.MODE_ALLOWED,
exceptionPackages);
@@ -866,6 +869,12 @@
exceptionPackages);
}
+ private boolean areAllBehaviorSoundsMuted() {
+ return !mConfig.allowAlarms && !mConfig.allowMediaSystemOther && !mConfig.allowReminders
+ && !mConfig.allowCalls && !mConfig.allowMessages && !mConfig.allowEvents
+ && !mConfig.allowRepeatCallers;
+ }
+
private void applyZenToRingerMode() {
if (mAudioManager == null) return;
// force the ringer mode into compliance
diff --git a/services/core/java/com/android/server/pm/PackageDexOptimizer.java b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
index 50ac409..29f48ee 100644
--- a/services/core/java/com/android/server/pm/PackageDexOptimizer.java
+++ b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
@@ -110,9 +110,9 @@
return false;
}
- // We do not dexopt a priv-app package when pm.dexopt.priv-apps is false.
+ // We do not dexopt a priv-app package when pm.dexopt.priv-apps-oob is true.
if (pkg.isPrivileged()) {
- return SystemProperties.getBoolean("pm.dexopt.priv-apps", true);
+ return !SystemProperties.getBoolean("pm.dexopt.priv-apps-oob", false);
}
return true;
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 6a0ea4ee..83cffe5 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -551,6 +551,8 @@
private static final String VENDOR_OVERLAY_DIR = "/vendor/overlay";
+ private static final String PROPERTY_NAME_PM_DEXOPT_PRIV_APPS_OOB = "pm.dexopt.priv-apps-oob";
+
/** Canonical intent used to identify what counts as a "web browser" app */
private static final Intent sBrowserIntent;
static {
@@ -9598,7 +9600,7 @@
if (Build.IS_DEBUGGABLE &&
pkg.isPrivileged() &&
- !SystemProperties.getBoolean("pm.dexopt.priv-apps", true)) {
+ SystemProperties.getBoolean(PROPERTY_NAME_PM_DEXOPT_PRIV_APPS_OOB, false)) {
PackageManagerServiceUtils.logPackageHasUncompressedCode(pkg);
}
@@ -19900,6 +19902,23 @@
.getUriFor(Secure.INSTANT_APPS_ENABLED), false, co, UserHandle.USER_SYSTEM);
co.onChange(true);
+ // This observer provides an one directional mapping from Global.PRIV_APP_OOB_ENABLED to
+ // pm.dexopt.priv-apps-oob property. This is only for experiment and should be removed once
+ // it is done.
+ ContentObserver privAppOobObserver = new ContentObserver(mHandler) {
+ @Override
+ public void onChange(boolean selfChange) {
+ int oobEnabled = Global.getInt(resolver, Global.PRIV_APP_OOB_ENABLED, 0);
+ SystemProperties.set(PROPERTY_NAME_PM_DEXOPT_PRIV_APPS_OOB,
+ oobEnabled == 1 ? "true" : "false");
+ }
+ };
+ mContext.getContentResolver().registerContentObserver(
+ Global.getUriFor(Global.PRIV_APP_OOB_ENABLED), false, privAppOobObserver,
+ UserHandle.USER_SYSTEM);
+ // At boot, restore the value from the setting, which persists across reboot.
+ privAppOobObserver.onChange(true);
+
// Disable any carrier apps. We do this very early in boot to prevent the apps from being
// disabled after already being started.
CarrierAppUtils.disableCarrierAppsUntilPrivileged(mContext.getOpPackageName(), this,
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceCompilerMapping.java b/services/core/java/com/android/server/pm/PackageManagerServiceCompilerMapping.java
index 781216c..19b0d9b 100644
--- a/services/core/java/com/android/server/pm/PackageManagerServiceCompilerMapping.java
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceCompilerMapping.java
@@ -52,18 +52,7 @@
// Load the property for the given reason and check for validity. This will throw an
// exception in case the reason or value are invalid.
private static String getAndCheckValidity(int reason) {
- String sysPropName = getSystemPropertyName(reason);
- String sysPropValue;
- // TODO: This is a temporary hack to keep marlin booting on aosp/master while we
- // figure out how to deal with these system properties that currently appear on
- // vendor.
- if ("pm.dexopt.inactive".equals(sysPropName)) {
- sysPropValue = "verify";
- } else if ("pm.dexopt.shared".equals(sysPropName)) {
- sysPropValue = "speed";
- } else {
- sysPropValue = SystemProperties.get(sysPropName);
- }
+ String sysPropValue = SystemProperties.get(getSystemPropertyName(reason));
if (sysPropValue == null || sysPropValue.isEmpty() ||
!DexFile.isValidCompilerFilter(sysPropValue)) {
throw new IllegalStateException("Value \"" + sysPropValue +"\" not valid "
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 1e5245c..1152310 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -1010,6 +1010,23 @@
}
}
+ @Override
+ public boolean hasRestrictedProfiles() {
+ checkManageUsersPermission("hasRestrictedProfiles");
+ final int callingUserId = UserHandle.getCallingUserId();
+ synchronized (mUsersLock) {
+ final int userSize = mUsers.size();
+ for (int i = 0; i < userSize; i++) {
+ UserInfo profile = mUsers.valueAt(i).info;
+ if (callingUserId != profile.id
+ && profile.restrictedProfileParentId == callingUserId) {
+ return true;
+ }
+ }
+ return false;
+ }
+ }
+
/*
* Should be locked on mUsers before calling this.
*/
diff --git a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
index c86122f..4b13404 100644
--- a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
+++ b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
@@ -66,6 +66,7 @@
public static final Set<String> USER_RESTRICTIONS = newSetWithUniqueCheck(new String[] {
UserManager.DISALLOW_CONFIG_WIFI,
+ UserManager.DISALLOW_CONFIG_LOCALE,
UserManager.DISALLOW_MODIFY_ACCOUNTS,
UserManager.DISALLOW_INSTALL_APPS,
UserManager.DISALLOW_UNINSTALL_APPS,
@@ -112,6 +113,7 @@
UserManager.DISALLOW_OEM_UNLOCK,
UserManager.DISALLOW_UNMUTE_DEVICE,
UserManager.DISALLOW_AUTOFILL,
+ UserManager.DISALLOW_USER_SWITCH
});
/**
@@ -143,6 +145,13 @@
);
/**
+ * User restrictions that cannot be set by profile owners. Applied to all users.
+ */
+ private static final Set<String> DEVICE_OWNER_ONLY_RESTRICTIONS = Sets.newArraySet(
+ UserManager.DISALLOW_USER_SWITCH
+ );
+
+ /**
* User restrictions that can't be changed by device owner or profile owner.
*/
private static final Set<String> IMMUTABLE_BY_OWNERS = Sets.newArraySet(
@@ -311,6 +320,7 @@
*/
public static boolean canProfileOwnerChange(String restriction, int userId) {
return !IMMUTABLE_BY_OWNERS.contains(restriction)
+ && !DEVICE_OWNER_ONLY_RESTRICTIONS.contains(restriction)
&& !(userId != UserHandle.USER_SYSTEM
&& PRIMARY_USER_ONLY_RESTRICTIONS.contains(restriction));
}
@@ -363,8 +373,9 @@
*/
private static boolean isGlobal(boolean isDeviceOwner, String key) {
return (isDeviceOwner &&
- (PRIMARY_USER_ONLY_RESTRICTIONS.contains(key)|| GLOBAL_RESTRICTIONS.contains(key)))
- || PROFILE_GLOBAL_RESTRICTIONS.contains(key);
+ (PRIMARY_USER_ONLY_RESTRICTIONS.contains(key) || GLOBAL_RESTRICTIONS.contains(key)))
+ || PROFILE_GLOBAL_RESTRICTIONS.contains(key)
+ || DEVICE_OWNER_ONLY_RESTRICTIONS.contains(key);
}
/**
diff --git a/services/core/java/com/android/server/pm/crossprofile/CrossProfileAppsService.java b/services/core/java/com/android/server/pm/crossprofile/CrossProfileAppsService.java
new file mode 100644
index 0000000..0913269
--- /dev/null
+++ b/services/core/java/com/android/server/pm/crossprofile/CrossProfileAppsService.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.pm.crossprofile;
+
+import android.content.Context;
+
+import com.android.server.SystemService;
+
+public class CrossProfileAppsService extends SystemService {
+ private CrossProfileAppsServiceImpl mServiceImpl;
+
+ public CrossProfileAppsService(Context context) {
+ super(context);
+ mServiceImpl = new CrossProfileAppsServiceImpl(context);
+ }
+
+ @Override
+ public void onStart() {
+ publishBinderService(Context.CROSS_PROFILE_APPS_SERVICE, mServiceImpl);
+ }
+}
diff --git a/services/core/java/com/android/server/pm/crossprofile/CrossProfileAppsServiceImpl.java b/services/core/java/com/android/server/pm/crossprofile/CrossProfileAppsServiceImpl.java
new file mode 100644
index 0000000..854b704
--- /dev/null
+++ b/services/core/java/com/android/server/pm/crossprofile/CrossProfileAppsServiceImpl.java
@@ -0,0 +1,264 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.pm.crossprofile;
+
+import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AWARE;
+import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
+
+import android.annotation.UserIdInt;
+import android.app.AppOpsManager;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManagerInternal;
+import android.content.pm.ResolveInfo;
+import android.content.pm.crossprofile.ICrossProfileApps;
+import android.graphics.Rect;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.text.TextUtils;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.Preconditions;
+import com.android.server.LocalServices;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class CrossProfileAppsServiceImpl extends ICrossProfileApps.Stub {
+ private static final String TAG = "CrossProfileAppsService";
+
+ private Context mContext;
+ private Injector mInjector;
+
+ public CrossProfileAppsServiceImpl(Context context) {
+ this(context, new InjectorImpl(context));
+ }
+
+ @VisibleForTesting
+ CrossProfileAppsServiceImpl(Context context, Injector injector) {
+ mContext = context;
+ mInjector = injector;
+ }
+
+ @Override
+ public List<UserHandle> getTargetUserProfiles(String callingPackage) {
+ Preconditions.checkNotNull(callingPackage);
+
+ verifyCallingPackage(callingPackage);
+
+ return getTargetUserProfilesUnchecked(
+ callingPackage, mInjector.getCallingUserId());
+ }
+
+ @Override
+ public void startActivityAsUser(
+ String callingPackage,
+ ComponentName component,
+ Rect sourceBounds,
+ Bundle startActivityOptions,
+ UserHandle user) throws RemoteException {
+ Preconditions.checkNotNull(callingPackage);
+ Preconditions.checkNotNull(component);
+ Preconditions.checkNotNull(user);
+
+ verifyCallingPackage(callingPackage);
+
+ List<UserHandle> allowedTargetUsers = getTargetUserProfilesUnchecked(
+ callingPackage, mInjector.getCallingUserId());
+ if (!allowedTargetUsers.contains(user)) {
+ throw new SecurityException(
+ callingPackage + " cannot access unrelated user " + user.getIdentifier());
+ }
+
+ // Verify that caller package is starting activity in its own package.
+ if (!callingPackage.equals(component.getPackageName())) {
+ throw new SecurityException(
+ callingPackage + " attempts to start an activity in other package - "
+ + component.getPackageName());
+ }
+
+ final int callingUid = mInjector.getCallingUid();
+
+ // Verify that target activity does handle the intent with ACTION_MAIN and
+ // CATEGORY_LAUNCHER as calling startActivityAsUser ignore them if component is present.
+ final Intent launchIntent = new Intent(Intent.ACTION_MAIN);
+ launchIntent.addCategory(Intent.CATEGORY_LAUNCHER);
+ launchIntent.setSourceBounds(sourceBounds);
+ launchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK
+ | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
+ // Only package name is set here, as opposed to component name, because intent action and
+ // category are ignored if component name is present while we are resolving intent.
+ launchIntent.setPackage(component.getPackageName());
+ verifyActivityCanHandleIntentAndExported(launchIntent, component, callingUid, user);
+
+ final long ident = mInjector.clearCallingIdentity();
+ try {
+ launchIntent.setComponent(component);
+ mContext.startActivityAsUser(launchIntent, startActivityOptions, user);
+ } finally {
+ mInjector.restoreCallingIdentity(ident);
+ }
+ }
+
+ private List<UserHandle> getTargetUserProfilesUnchecked(
+ String callingPackage, @UserIdInt int callingUserId) {
+ final long ident = mInjector.clearCallingIdentity();
+ try {
+ final int[] enabledProfileIds =
+ mInjector.getUserManager().getEnabledProfileIds(callingUserId);
+
+ List<UserHandle> targetProfiles = new ArrayList<>();
+ for (final int userId : enabledProfileIds) {
+ if (userId == callingUserId) {
+ continue;
+ }
+ if (!isPackageEnabled(callingPackage, userId)) {
+ continue;
+ }
+ targetProfiles.add(UserHandle.of(userId));
+ }
+ return targetProfiles;
+ } finally {
+ mInjector.restoreCallingIdentity(ident);
+ }
+ }
+
+ private boolean isPackageEnabled(String packageName, @UserIdInt int userId) {
+ final int callingUid = mInjector.getCallingUid();
+ final long ident = mInjector.clearCallingIdentity();
+ try {
+ final PackageInfo info = mInjector.getPackageManagerInternal()
+ .getPackageInfo(
+ packageName,
+ MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE,
+ callingUid,
+ userId);
+ return info != null && info.applicationInfo.enabled;
+ } finally {
+ mInjector.restoreCallingIdentity(ident);
+ }
+ }
+
+ /**
+ * Verify that the specified intent does resolved to the specified component and the resolved
+ * activity is exported.
+ */
+ private void verifyActivityCanHandleIntentAndExported(
+ Intent launchIntent, ComponentName component, int callingUid, UserHandle user) {
+ final long ident = mInjector.clearCallingIdentity();
+ try {
+ final List<ResolveInfo> apps =
+ mInjector.getPackageManagerInternal().queryIntentActivities(
+ launchIntent,
+ MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE,
+ callingUid,
+ user.getIdentifier());
+ final int size = apps.size();
+ for (int i = 0; i < size; ++i) {
+ final ActivityInfo activityInfo = apps.get(i).activityInfo;
+ if (TextUtils.equals(activityInfo.packageName, component.getPackageName())
+ && TextUtils.equals(activityInfo.name, component.getClassName())
+ && activityInfo.exported) {
+ return;
+ }
+ }
+ throw new SecurityException("Attempt to launch activity without "
+ + " category Intent.CATEGORY_LAUNCHER or activity is not exported" + component);
+ } finally {
+ mInjector.restoreCallingIdentity(ident);
+ }
+ }
+
+ /**
+ * Verify that the given calling package is belong to the calling UID.
+ */
+ private void verifyCallingPackage(String callingPackage) {
+ mInjector.getAppOpsManager().checkPackage(mInjector.getCallingUid(), callingPackage);
+ }
+
+ private static class InjectorImpl implements Injector {
+ private Context mContext;
+
+ public InjectorImpl(Context context) {
+ mContext = context;
+ }
+
+ public int getCallingUid() {
+ return Binder.getCallingUid();
+ }
+
+ public int getCallingUserId() {
+ return UserHandle.getCallingUserId();
+ }
+
+ public UserHandle getCallingUserHandle() {
+ return Binder.getCallingUserHandle();
+ }
+
+ public long clearCallingIdentity() {
+ return Binder.clearCallingIdentity();
+ }
+
+ public void restoreCallingIdentity(long token) {
+ Binder.restoreCallingIdentity(token);
+ }
+
+ public UserManager getUserManager() {
+ return mContext.getSystemService(UserManager.class);
+ }
+
+ public PackageManagerInternal getPackageManagerInternal() {
+ return LocalServices.getService(PackageManagerInternal.class);
+ }
+
+ public PackageManager getPackageManager() {
+ return mContext.getPackageManager();
+ }
+
+ public AppOpsManager getAppOpsManager() {
+ return mContext.getSystemService(AppOpsManager.class);
+ }
+ }
+
+ @VisibleForTesting
+ public interface Injector {
+ int getCallingUid();
+
+ int getCallingUserId();
+
+ UserHandle getCallingUserHandle();
+
+ long clearCallingIdentity();
+
+ void restoreCallingIdentity(long token);
+
+ UserManager getUserManager();
+
+ PackageManagerInternal getPackageManagerInternal();
+
+ PackageManager getPackageManager();
+
+ AppOpsManager getAppOpsManager();
+
+ }
+}
diff --git a/services/core/java/com/android/server/policy/GlobalActions.java b/services/core/java/com/android/server/policy/GlobalActions.java
index 7a2e630..3707a5e 100644
--- a/services/core/java/com/android/server/policy/GlobalActions.java
+++ b/services/core/java/com/android/server/policy/GlobalActions.java
@@ -58,7 +58,7 @@
public void showDialog(boolean keyguardShowing, boolean deviceProvisioned) {
if (DEBUG) Slog.d(TAG, "showDialog " + keyguardShowing + " " + deviceProvisioned);
- if (mStatusBarInternal.isGlobalActionsDisabled()) {
+ if (mStatusBarInternal != null && mStatusBarInternal.isGlobalActionsDisabled()) {
return;
}
mKeyguardShowing = keyguardShowing;
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 936ffdf..9162a97 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -59,6 +59,8 @@
import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS;
import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED;
+import static android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION;
+import static android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS;
import static android.view.WindowManager.LayoutParams.LAST_APPLICATION_WINDOW;
import static android.view.WindowManager.LayoutParams.LAST_SUB_WINDOW;
import static android.view.WindowManager.LayoutParams.LAST_SYSTEM_WINDOW;
@@ -66,6 +68,7 @@
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_ACQUIRES_SLEEP_TOKEN;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DRAW_STATUS_BAR_BACKGROUND;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_STATUS_BAR_VISIBLE_TRANSPARENT;
+import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_INHERIT_TRANSLUCENT_DECOR;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_IS_SCREEN_DECOR;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_KEYGUARD;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
@@ -123,11 +126,8 @@
import static android.view.WindowManagerPolicy.WindowManagerFuncs.LID_CLOSED;
import static android.view.WindowManagerPolicy.WindowManagerFuncs.LID_OPEN;
-import static com.android.server.wm.proto.WindowManagerPolicyProto.STABLE_BOUNDS;
-
import android.annotation.Nullable;
import android.app.ActivityManager;
-import android.app.ActivityManager.StackId;
import android.app.ActivityManagerInternal;
import android.app.ActivityManagerInternal.SleepToken;
import android.app.ActivityThread;
@@ -210,6 +210,7 @@
import android.util.SparseArray;
import android.util.proto.ProtoOutputStream;
import android.view.Display;
+import android.view.DisplayFrames;
import android.view.Gravity;
import android.view.HapticFeedbackConstants;
import android.view.IApplicationToken;
@@ -596,47 +597,6 @@
PointerLocationView mPointerLocationView;
- // The current size of the screen; really; extends into the overscan area of
- // the screen and doesn't account for any system elements like the status bar.
- int mOverscanScreenLeft, mOverscanScreenTop;
- int mOverscanScreenWidth, mOverscanScreenHeight;
- // The current visible size of the screen; really; (ir)regardless of whether the status
- // bar can be hidden but not extending into the overscan area.
- int mUnrestrictedScreenLeft, mUnrestrictedScreenTop;
- int mUnrestrictedScreenWidth, mUnrestrictedScreenHeight;
- // Like mOverscanScreen*, but allowed to move into the overscan region where appropriate.
- int mRestrictedOverscanScreenLeft, mRestrictedOverscanScreenTop;
- int mRestrictedOverscanScreenWidth, mRestrictedOverscanScreenHeight;
- // The current size of the screen; these may be different than (0,0)-(dw,dh)
- // if the status bar can't be hidden; in that case it effectively carves out
- // that area of the display from all other windows.
- int mRestrictedScreenLeft, mRestrictedScreenTop;
- int mRestrictedScreenWidth, mRestrictedScreenHeight;
- // During layout, the current screen borders accounting for any currently
- // visible system UI elements.
- int mSystemLeft, mSystemTop, mSystemRight, mSystemBottom;
- // For applications requesting stable content insets, these are them.
- int mStableLeft, mStableTop, mStableRight, mStableBottom;
- // For applications requesting stable content insets but have also set the
- // fullscreen window flag, these are the stable dimensions without the status bar.
- int mStableFullscreenLeft, mStableFullscreenTop;
- int mStableFullscreenRight, mStableFullscreenBottom;
- // During layout, the current screen borders with all outer decoration
- // (status bar, input method dock) accounted for.
- int mCurLeft, mCurTop, mCurRight, mCurBottom;
- // During layout, the frame in which content should be displayed
- // to the user, accounting for all screen decoration except for any
- // space they deem as available for other content. This is usually
- // the same as mCur*, but may be larger if the screen decor has supplied
- // content insets.
- int mContentLeft, mContentTop, mContentRight, mContentBottom;
- // During layout, the frame in which voice content should be displayed
- // to the user, accounting for all screen decoration except for any
- // space they deem as available for other content.
- int mVoiceContentLeft, mVoiceContentTop, mVoiceContentRight, mVoiceContentBottom;
- // During layout, the current screen borders along which input method
- // windows are placed.
- int mDockLeft, mDockTop, mDockRight, mDockBottom;
// During layout, the layer at which the doc window is placed.
int mDockLayer;
// During layout, this is the layer of the status bar.
@@ -734,18 +694,11 @@
Display mDisplay;
- private int mDisplayRotation;
-
int mLandscapeRotation = 0; // default landscape rotation
int mSeascapeRotation = 0; // "other" landscape rotation, 180 degrees from mLandscapeRotation
int mPortraitRotation = 0; // default portrait rotation
int mUpsideDownRotation = 0; // "other" portrait rotation
- int mOverscanLeft = 0;
- int mOverscanTop = 0;
- int mOverscanRight = 0;
- int mOverscanBottom = 0;
-
// What we do when the user long presses on home
private int mLongPressOnHomeBehavior;
@@ -1061,7 +1014,7 @@
View.NAVIGATION_BAR_UNHIDE,
View.NAVIGATION_BAR_TRANSLUCENT,
StatusBarManager.WINDOW_NAVIGATION_BAR,
- WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION,
+ FLAG_TRANSLUCENT_NAVIGATION,
View.NAVIGATION_BAR_TRANSPARENT);
private final BarController.OnBarVisibilityChangedListener mNavBarVisibilityListener =
@@ -1172,14 +1125,12 @@
+ ", mOrientationSensorEnabled=" + mOrientationSensorEnabled
+ ", mKeyguardDrawComplete=" + mKeyguardDrawComplete
+ ", mWindowManagerDrawComplete=" + mWindowManagerDrawComplete);
- final boolean keyguardGoingAway = mWindowManagerInternal.isKeyguardGoingAway();
boolean disable = true;
// Note: We postpone the rotating of the screen until the keyguard as well as the
// window manager have reported a draw complete or the keyguard is going away in dismiss
// mode.
- if (mScreenOnEarly && mAwake && ((mKeyguardDrawComplete && mWindowManagerDrawComplete)
- || keyguardGoingAway)) {
+ if (mScreenOnEarly && mAwake && ((mKeyguardDrawComplete && mWindowManagerDrawComplete))) {
if (needSensorRunningLp()) {
disable = false;
//enable listener if not already enabled
@@ -1190,7 +1141,7 @@
// the sensor reading was cleared which can cause it to relaunch the app that
// will show in the wrong orientation first before correcting leading to app
// launch delays.
- mOrientationListener.enable(!keyguardGoingAway /* clearCurrentRotation */);
+ mOrientationListener.enable(true /* clearCurrentRotation */);
if(localLOGV) Slog.v(TAG, "Enabling listeners");
mOrientationSensorEnabled = true;
}
@@ -2070,6 +2021,7 @@
context.registerReceiver(mMultiuserReceiver, filter);
// monitor for system gestures
+ // TODO(multi-display): Needs to be display specific.
mSystemGestures = new SystemGesturesPointerEventListener(context,
new SystemGesturesPointerEventListener.Callbacks() {
@Override
@@ -2316,17 +2268,6 @@
return mForceDefaultOrientation;
}
- @Override
- public void setDisplayOverscan(Display display, int left, int top, int right, int bottom) {
- // TODO(multi-display): Define policy for secondary displays.
- if (display.getDisplayId() == DEFAULT_DISPLAY) {
- mOverscanLeft = left;
- mOverscanTop = top;
- mOverscanRight = right;
- mOverscanBottom = bottom;
- }
- }
-
public void updateSettings() {
ContentResolver resolver = mContext.getContentResolver();
boolean updateRotation = false;
@@ -4323,12 +4264,16 @@
}
@Override
+ // TODO: Should probably be moved into DisplayFrames.
public boolean getInsetHintLw(WindowManager.LayoutParams attrs, Rect taskBounds,
- int displayRotation, int displayWidth, int displayHeight, Rect outContentInsets,
- Rect outStableInsets, Rect outOutsets) {
+ DisplayFrames displayFrames, Rect outContentInsets, Rect outStableInsets,
+ Rect outOutsets) {
final int fl = PolicyControl.getWindowFlags(null, attrs);
final int sysuiVis = PolicyControl.getSystemUiVisibility(null, attrs);
final int systemUiVisibility = (sysuiVis | attrs.subtreeSystemUiVisibility);
+ final int displayRotation = displayFrames.mRotation;
+ final int displayWidth = displayFrames.mDisplayWidth;
+ final int displayHeight = displayFrames.mDisplayHeight;
final boolean useOutsets = outOutsets != null && shouldUseOutsets(attrs, fl);
if (useOutsets) {
@@ -4351,34 +4296,33 @@
int availRight, availBottom;
if (canHideNavigationBar() &&
(systemUiVisibility & View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION) != 0) {
- availRight = mUnrestrictedScreenLeft + mUnrestrictedScreenWidth;
- availBottom = mUnrestrictedScreenTop + mUnrestrictedScreenHeight;
+ availRight = displayFrames.mUnrestricted.right;
+ availBottom = displayFrames.mUnrestricted.bottom;
} else {
- availRight = mRestrictedScreenLeft + mRestrictedScreenWidth;
- availBottom = mRestrictedScreenTop + mRestrictedScreenHeight;
+ availRight = displayFrames.mRestricted.right;
+ availBottom = displayFrames.mRestricted.bottom;
}
+ outStableInsets.set(displayFrames.mStable.left, displayFrames.mStable.top,
+ availRight - displayFrames.mStable.right,
+ availBottom - displayFrames.mStable.bottom);
+
if ((systemUiVisibility & View.SYSTEM_UI_FLAG_LAYOUT_STABLE) != 0) {
if ((fl & FLAG_FULLSCREEN) != 0) {
- outContentInsets.set(mStableFullscreenLeft, mStableFullscreenTop,
- availRight - mStableFullscreenRight,
- availBottom - mStableFullscreenBottom);
+ outContentInsets.set(displayFrames.mStableFullscreen.left,
+ displayFrames.mStableFullscreen.top,
+ availRight - displayFrames.mStableFullscreen.right,
+ availBottom - displayFrames.mStableFullscreen.bottom);
} else {
- outContentInsets.set(mStableLeft, mStableTop,
- availRight - mStableRight, availBottom - mStableBottom);
+ outContentInsets.set(outStableInsets);
}
} else if ((fl & FLAG_FULLSCREEN) != 0 || (fl & FLAG_LAYOUT_IN_OVERSCAN) != 0) {
outContentInsets.setEmpty();
- } else if ((systemUiVisibility & (View.SYSTEM_UI_FLAG_FULLSCREEN
- | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN)) == 0) {
- outContentInsets.set(mCurLeft, mCurTop,
- availRight - mCurRight, availBottom - mCurBottom);
} else {
- outContentInsets.set(mCurLeft, mCurTop,
- availRight - mCurRight, availBottom - mCurBottom);
+ outContentInsets.set(displayFrames.mCurrent.left, displayFrames.mCurrent.top,
+ availRight - displayFrames.mCurrent.right,
+ availBottom - displayFrames.mCurrent.bottom);
}
- outStableInsets.set(mStableLeft, mStableTop,
- availRight - mStableRight, availBottom - mStableBottom);
if (taskBounds != null) {
calculateRelevantTaskInsets(taskBounds, outContentInsets,
displayWidth, displayHeight);
@@ -4415,68 +4359,11 @@
/** {@inheritDoc} */
@Override
- public void beginLayoutLw(int displayId, int displayWidth, int displayHeight,
- int displayRotation, int uiMode) {
- final boolean isDefaultDisplay = displayId == DEFAULT_DISPLAY;
- mDisplayRotation = displayRotation;
- final int overscanLeft, overscanTop, overscanRight, overscanBottom;
- if (isDefaultDisplay) {
- switch (displayRotation) {
- case Surface.ROTATION_90:
- overscanLeft = mOverscanTop;
- overscanTop = mOverscanRight;
- overscanRight = mOverscanBottom;
- overscanBottom = mOverscanLeft;
- break;
- case Surface.ROTATION_180:
- overscanLeft = mOverscanRight;
- overscanTop = mOverscanBottom;
- overscanRight = mOverscanLeft;
- overscanBottom = mOverscanTop;
- break;
- case Surface.ROTATION_270:
- overscanLeft = mOverscanBottom;
- overscanTop = mOverscanLeft;
- overscanRight = mOverscanTop;
- overscanBottom = mOverscanRight;
- break;
- default:
- overscanLeft = mOverscanLeft;
- overscanTop = mOverscanTop;
- overscanRight = mOverscanRight;
- overscanBottom = mOverscanBottom;
- break;
- }
- } else {
- overscanLeft = 0;
- overscanTop = 0;
- overscanRight = 0;
- overscanBottom = 0;
- }
- mOverscanScreenLeft = mRestrictedOverscanScreenLeft = 0;
- mOverscanScreenTop = mRestrictedOverscanScreenTop = 0;
- mOverscanScreenWidth = mRestrictedOverscanScreenWidth = displayWidth;
- mOverscanScreenHeight = mRestrictedOverscanScreenHeight = displayHeight;
- mSystemLeft = 0;
- mSystemTop = 0;
- mSystemRight = displayWidth;
- mSystemBottom = displayHeight;
- mUnrestrictedScreenLeft = overscanLeft;
- mUnrestrictedScreenTop = overscanTop;
- mUnrestrictedScreenWidth = displayWidth - overscanLeft - overscanRight;
- mUnrestrictedScreenHeight = displayHeight - overscanTop - overscanBottom;
- mRestrictedScreenLeft = mUnrestrictedScreenLeft;
- mRestrictedScreenTop = mUnrestrictedScreenTop;
- mRestrictedScreenWidth = mSystemGestures.screenWidth = mUnrestrictedScreenWidth;
- mRestrictedScreenHeight = mSystemGestures.screenHeight = mUnrestrictedScreenHeight;
- mDockLeft = mContentLeft = mVoiceContentLeft = mStableLeft = mStableFullscreenLeft
- = mCurLeft = mUnrestrictedScreenLeft;
- mDockTop = mContentTop = mVoiceContentTop = mStableTop = mStableFullscreenTop
- = mCurTop = mUnrestrictedScreenTop;
- mDockRight = mContentRight = mVoiceContentRight = mStableRight = mStableFullscreenRight
- = mCurRight = displayWidth - overscanRight;
- mDockBottom = mContentBottom = mVoiceContentBottom = mStableBottom = mStableFullscreenBottom
- = mCurBottom = displayHeight - overscanBottom;
+ public void beginLayoutLw(DisplayFrames displayFrames, int uiMode) {
+ displayFrames.onBeginLayout();
+ // TODO(multi-display): This doesn't seem right...Maybe only apply to default display?
+ mSystemGestures.screenWidth = displayFrames.mUnrestricted.width();
+ mSystemGestures.screenHeight = displayFrames.mUnrestricted.height();
mDockLayer = 0x10000000;
mStatusBarLayer = -1;
@@ -4486,13 +4373,13 @@
final Rect of = mTmpOverscanFrame;
final Rect vf = mTmpVisibleFrame;
final Rect dcf = mTmpDecorFrame;
- pf.left = df.left = of.left = vf.left = mDockLeft;
- pf.top = df.top = of.top = vf.top = mDockTop;
- pf.right = df.right = of.right = vf.right = mDockRight;
- pf.bottom = df.bottom = of.bottom = vf.bottom = mDockBottom;
+ vf.set(displayFrames.mDock);
+ of.set(displayFrames.mDock);
+ df.set(displayFrames.mDock);
+ pf.set(displayFrames.mDock);
dcf.setEmpty(); // Decor frame N/A for system bars.
- if (isDefaultDisplay) {
+ if (displayFrames.mDisplayId == DEFAULT_DISPLAY) {
// For purposes of putting out fake window up to steal focus, we will
// drive nav being hidden only by whether it is requested.
final int sysui = mLastSystemUiFlags;
@@ -4511,10 +4398,9 @@
&& mStatusBar.getAttrs().height == MATCH_PARENT
&& mStatusBar.getAttrs().width == MATCH_PARENT;
- // When the navigation bar isn't visible, we put up a fake
- // input window to catch all touch events. This way we can
- // detect when the user presses anywhere to bring back the nav
- // bar and ensure the application doesn't see the event.
+ // When the navigation bar isn't visible, we put up a fake input window to catch all
+ // touch events. This way we can detect when the user presses anywhere to bring back the
+ // nav bar and ensure the application doesn't see the event.
if (navVisible || navAllowedHidden) {
if (mInputConsumer != null) {
mHandler.sendMessage(
@@ -4530,30 +4416,32 @@
InputManager.getInstance().setPointerIconType(PointerIcon.TYPE_NULL);
}
- // For purposes of positioning and showing the nav bar, if we have
- // decided that it can't be hidden (because of the screen aspect ratio),
- // then take that into account.
+ // For purposes of positioning and showing the nav bar, if we have decided that it can't
+ // be hidden (because of the screen aspect ratio), then take that into account.
navVisible |= !canHideNavigationBar();
- boolean updateSysUiVisibility = layoutNavigationBar(displayWidth, displayHeight,
- displayRotation, uiMode, overscanLeft, overscanRight, overscanBottom, dcf, navVisible, navTranslucent,
- navAllowedHidden, statusBarExpandedNotKeyguard);
- if (DEBUG_LAYOUT) Slog.i(TAG, String.format("mDock rect: (%d,%d - %d,%d)",
- mDockLeft, mDockTop, mDockRight, mDockBottom));
- updateSysUiVisibility |= layoutStatusBar(pf, df, of, vf, dcf, sysui, isKeyguardShowing);
+ boolean updateSysUiVisibility = layoutNavigationBar(displayFrames, uiMode, dcf,
+ navVisible, navTranslucent, navAllowedHidden, statusBarExpandedNotKeyguard);
+ if (DEBUG_LAYOUT) Slog.i(TAG, "mDock rect:" + displayFrames.mDock);
+ updateSysUiVisibility |= layoutStatusBar(
+ displayFrames, pf, df, of, vf, dcf, sysui, isKeyguardShowing);
if (updateSysUiVisibility) {
updateSystemUiVisibilityLw();
}
}
- layoutScreenDecorWindows(displayId, displayWidth, displayHeight, pf, df, dcf);
+ layoutScreenDecorWindows(displayFrames, pf, df, dcf);
}
- private void layoutScreenDecorWindows(int displayId, int displayWidth, int displayHeight,
- Rect pf, Rect df, Rect dcf) {
+ private void layoutScreenDecorWindows(DisplayFrames displayFrames, Rect pf, Rect df, Rect dcf) {
if (mScreenDecorWindows.isEmpty()) {
return;
}
+ final int displayId = displayFrames.mDisplayId;
+ final Rect dockFrame = displayFrames.mDock;
+ final int displayHeight = displayFrames.mDisplayHeight;
+ final int displayWidth = displayFrames.mDisplayWidth;
+
for (int i = mScreenDecorWindows.size() - 1; i >= 0; --i) {
final WindowState w = mScreenDecorWindows.valueAt(i);
if (w.getDisplayId() != displayId || !w.isVisibleLw()) {
@@ -4570,10 +4458,10 @@
// Docked at left or top.
if (frame.bottom >= displayHeight) {
// Docked left.
- mDockLeft = Math.max(frame.right, mDockLeft);
+ dockFrame.left = Math.max(frame.right, dockFrame.left);
} else if (frame.right >= displayWidth ) {
// Docked top.
- mDockTop = Math.max(frame.bottom, mDockTop);
+ dockFrame.top = Math.max(frame.bottom, dockFrame.top);
} else {
Slog.w(TAG, "layoutScreenDecorWindows: Ignoring decor win=" + w
+ " not docked on left or top of display. frame=" + frame
@@ -4583,10 +4471,10 @@
// Docked at right or bottom.
if (frame.top <= 0) {
// Docked right.
- mDockRight = Math.min(frame.left, mDockRight);
+ dockFrame.right = Math.min(frame.left, dockFrame.right);
} else if (frame.left <= 0) {
// Docked bottom.
- mDockBottom = Math.min(frame.top, mDockBottom);
+ dockFrame.bottom = Math.min(frame.top, dockFrame.bottom);
} else {
Slog.w(TAG, "layoutScreenDecorWindows: Ignoring decor win=" + w
+ " not docked on right or bottom" + " of display. frame=" + frame
@@ -4600,194 +4488,165 @@
}
}
- mContentTop = mSystemTop = mVoiceContentTop = mCurTop = mRestrictedScreenTop = mDockTop;
- mContentLeft = mSystemLeft = mVoiceContentLeft = mCurLeft = mRestrictedScreenLeft
- = mRestrictedOverscanScreenLeft = mDockLeft;
- mContentBottom = mSystemBottom = mVoiceContentBottom = mCurBottom = mDockBottom;
- mContentRight = mSystemRight = mVoiceContentRight = mCurRight = mDockRight;
-
- mRestrictedScreenWidth = mDockRight - mRestrictedScreenLeft;
- mRestrictedScreenHeight = mDockBottom - mRestrictedScreenTop;
- mRestrictedOverscanScreenWidth = mDockRight - mRestrictedOverscanScreenLeft;
- mRestrictedOverscanScreenHeight = mDockBottom - mRestrictedOverscanScreenTop;
+ displayFrames.mRestricted.set(dockFrame);
+ displayFrames.mCurrent.set(dockFrame);
+ displayFrames.mVoiceContent.set(dockFrame);
+ displayFrames.mSystem.set(dockFrame);
+ displayFrames.mContent.set(dockFrame);
+ displayFrames.mRestrictedOverscan.set(dockFrame);
}
- private boolean layoutStatusBar(Rect pf, Rect df, Rect of, Rect vf, Rect dcf, int sysui,
- boolean isKeyguardShowing) {
+ private boolean layoutStatusBar(DisplayFrames displayFrames, Rect pf, Rect df, Rect of, Rect vf,
+ Rect dcf, int sysui, boolean isKeyguardShowing) {
// decide where the status bar goes ahead of time
- if (mStatusBar != null) {
- // apply any navigation bar insets
- pf.left = df.left = of.left = mUnrestrictedScreenLeft;
- pf.top = df.top = of.top = mUnrestrictedScreenTop;
- pf.right = df.right = of.right = mUnrestrictedScreenWidth + mUnrestrictedScreenLeft;
- pf.bottom = df.bottom = of.bottom = mUnrestrictedScreenHeight
- + mUnrestrictedScreenTop;
- vf.left = mStableLeft;
- vf.top = mStableTop;
- vf.right = mStableRight;
- vf.bottom = mStableBottom;
+ if (mStatusBar == null) {
+ return false;
+ }
+ // apply any navigation bar insets
+ of.set(displayFrames.mUnrestricted);
+ df.set(displayFrames.mUnrestricted);
+ pf.set(displayFrames.mUnrestricted);
+ vf.set(displayFrames.mStable);
- mStatusBarLayer = mStatusBar.getSurfaceLayer();
+ mStatusBarLayer = mStatusBar.getSurfaceLayer();
- // Let the status bar determine its size.
- mStatusBar.computeFrameLw(pf /* parentFrame */, df /* displayFrame */,
- vf /* overlayFrame */, vf /* contentFrame */, vf /* visibleFrame */,
- dcf /* decorFrame */, vf /* stableFrame */, vf /* outsetFrame */);
+ // Let the status bar determine its size.
+ mStatusBar.computeFrameLw(pf /* parentFrame */, df /* displayFrame */,
+ vf /* overlayFrame */, vf /* contentFrame */, vf /* visibleFrame */,
+ dcf /* decorFrame */, vf /* stableFrame */, vf /* outsetFrame */);
- // For layout, the status bar is always at the top with our fixed height.
- mStableTop = mUnrestrictedScreenTop + mStatusBarHeight;
+ // For layout, the status bar is always at the top with our fixed height.
+ displayFrames.mStable.top = displayFrames.mUnrestricted.top + mStatusBarHeight;
- boolean statusBarTransient = (sysui & View.STATUS_BAR_TRANSIENT) != 0;
- boolean statusBarTranslucent = (sysui
- & (View.STATUS_BAR_TRANSLUCENT | View.STATUS_BAR_TRANSPARENT)) != 0;
- if (!isKeyguardShowing) {
- statusBarTranslucent &= areTranslucentBarsAllowed();
- }
+ boolean statusBarTransient = (sysui & View.STATUS_BAR_TRANSIENT) != 0;
+ boolean statusBarTranslucent = (sysui
+ & (View.STATUS_BAR_TRANSLUCENT | View.STATUS_BAR_TRANSPARENT)) != 0;
+ if (!isKeyguardShowing) {
+ statusBarTranslucent &= areTranslucentBarsAllowed();
+ }
- // If the status bar is hidden, we don't want to cause
- // windows behind it to scroll.
- if (mStatusBar.isVisibleLw() && !statusBarTransient) {
- // Status bar may go away, so the screen area it occupies
- // is available to apps but just covering them when the
- // status bar is visible.
- mDockTop = mUnrestrictedScreenTop + mStatusBarHeight;
+ // If the status bar is hidden, we don't want to cause windows behind it to scroll.
+ if (mStatusBar.isVisibleLw() && !statusBarTransient) {
+ // Status bar may go away, so the screen area it occupies is available to apps but just
+ // covering them when the status bar is visible.
+ final Rect dockFrame = displayFrames.mDock;
+ dockFrame.top = displayFrames.mStable.top;
+ displayFrames.mContent.set(dockFrame);
+ displayFrames.mVoiceContent.set(dockFrame);
- mContentTop = mVoiceContentTop = mCurTop = mDockTop;
- mContentBottom = mVoiceContentBottom = mCurBottom = mDockBottom;
- mContentLeft = mVoiceContentLeft = mCurLeft = mDockLeft;
- mContentRight = mVoiceContentRight = mCurRight = mDockRight;
+ if (DEBUG_LAYOUT) Slog.v(TAG, "Status bar: " + String.format(
+ "dock=%s content=%s cur=%s", dockFrame.toString(),
+ displayFrames.mContent.toString(), displayFrames.mCurrent.toString()));
- if (DEBUG_LAYOUT) Slog.v(TAG, "Status bar: " +
- String.format(
- "dock=[%d,%d][%d,%d] content=[%d,%d][%d,%d] cur=[%d,%d][%d,%d]",
- mDockLeft, mDockTop, mDockRight, mDockBottom,
- mContentLeft, mContentTop, mContentRight, mContentBottom,
- mCurLeft, mCurTop, mCurRight, mCurBottom));
- }
- if (mStatusBar.isVisibleLw() && !mStatusBar.isAnimatingLw()
- && !statusBarTransient && !statusBarTranslucent
+ if (!mStatusBar.isAnimatingLw() && !statusBarTranslucent
&& !mStatusBarController.wasRecentlyTranslucent()) {
- // If the opaque status bar is currently requested to be visible,
- // and not in the process of animating on or off, then
- // we can tell the app that it is covered by it.
- mSystemTop = mUnrestrictedScreenTop + mStatusBarHeight;
- }
- if (mStatusBarController.checkHiddenLw()) {
- return true;
+ // If the opaque status bar is currently requested to be visible, and not in the
+ // process of animating on or off, then we can tell the app that it is covered by it.
+ displayFrames.mSystem.top = displayFrames.mStable.top;
}
}
- return false;
+ return mStatusBarController.checkHiddenLw();
}
- private boolean layoutNavigationBar(int displayWidth, int displayHeight, int displayRotation,
- int uiMode, int overscanLeft, int overscanRight, int overscanBottom, Rect dcf,
+ private boolean layoutNavigationBar(DisplayFrames displayFrames, int uiMode, Rect dcf,
boolean navVisible, boolean navTranslucent, boolean navAllowedHidden,
boolean statusBarExpandedNotKeyguard) {
- if (mNavigationBar != null) {
- boolean transientNavBarShowing = mNavigationBarController.isTransientShowing();
- // Force the navigation bar to its appropriate place and
- // size. We need to do this directly, instead of relying on
- // it to bubble up from the nav bar, because this needs to
- // change atomically with screen rotations.
- mNavigationBarPosition = navigationBarPosition(displayWidth, displayHeight,
- displayRotation);
- if (mNavigationBarPosition == NAV_BAR_BOTTOM) {
- // It's a system nav bar or a portrait screen; nav bar goes on bottom.
- int top = displayHeight - overscanBottom
- - getNavigationBarHeight(displayRotation, uiMode);
- mTmpNavigationFrame.set(0, top, displayWidth, displayHeight - overscanBottom);
- mStableBottom = mStableFullscreenBottom = mTmpNavigationFrame.top;
- if (transientNavBarShowing) {
- mNavigationBarController.setBarShowingLw(true);
- } else if (navVisible) {
- mNavigationBarController.setBarShowingLw(true);
- mDockBottom = mTmpNavigationFrame.top;
- mRestrictedScreenHeight = mDockBottom - mRestrictedScreenTop;
- mRestrictedOverscanScreenHeight = mDockBottom - mRestrictedOverscanScreenTop;
- } else {
- // We currently want to hide the navigation UI - unless we expanded the status
- // bar.
- mNavigationBarController.setBarShowingLw(statusBarExpandedNotKeyguard);
- }
- if (navVisible && !navTranslucent && !navAllowedHidden
- && !mNavigationBar.isAnimatingLw()
- && !mNavigationBarController.wasRecentlyTranslucent()) {
- // If the opaque nav bar is currently requested to be visible,
- // and not in the process of animating on or off, then
- // we can tell the app that it is covered by it.
- mSystemBottom = mTmpNavigationFrame.top;
- }
- } else if (mNavigationBarPosition == NAV_BAR_RIGHT) {
- // Landscape screen; nav bar goes to the right.
- int left = displayWidth - overscanRight
- - getNavigationBarWidth(displayRotation, uiMode);
- mTmpNavigationFrame.set(left, 0, displayWidth - overscanRight, displayHeight);
- mStableRight = mStableFullscreenRight = mTmpNavigationFrame.left;
- if (transientNavBarShowing) {
- mNavigationBarController.setBarShowingLw(true);
- } else if (navVisible) {
- mNavigationBarController.setBarShowingLw(true);
- mDockRight = mTmpNavigationFrame.left;
- mRestrictedScreenWidth = mDockRight - mRestrictedScreenLeft;
- mRestrictedOverscanScreenWidth = mDockRight - mRestrictedOverscanScreenLeft;
- } else {
- // We currently want to hide the navigation UI - unless we expanded the status
- // bar.
- mNavigationBarController.setBarShowingLw(statusBarExpandedNotKeyguard);
- }
- if (navVisible && !navTranslucent && !navAllowedHidden
- && !mNavigationBar.isAnimatingLw()
- && !mNavigationBarController.wasRecentlyTranslucent()) {
- // If the nav bar is currently requested to be visible,
- // and not in the process of animating on or off, then
- // we can tell the app that it is covered by it.
- mSystemRight = mTmpNavigationFrame.left;
- }
- } else if (mNavigationBarPosition == NAV_BAR_LEFT) {
- // Seascape screen; nav bar goes to the left.
- int right = overscanLeft + getNavigationBarWidth(displayRotation, uiMode);
- mTmpNavigationFrame.set(overscanLeft, 0, right, displayHeight);
- mStableLeft = mStableFullscreenLeft = mTmpNavigationFrame.right;
- if (transientNavBarShowing) {
- mNavigationBarController.setBarShowingLw(true);
- } else if (navVisible) {
- mNavigationBarController.setBarShowingLw(true);
- mDockLeft = mTmpNavigationFrame.right;
- // TODO: not so sure about those:
- mRestrictedScreenLeft = mRestrictedOverscanScreenLeft = mDockLeft;
- mRestrictedScreenWidth = mDockRight - mRestrictedScreenLeft;
- mRestrictedOverscanScreenWidth = mDockRight - mRestrictedOverscanScreenLeft;
- } else {
- // We currently want to hide the navigation UI - unless we expanded the status
- // bar.
- mNavigationBarController.setBarShowingLw(statusBarExpandedNotKeyguard);
- }
- if (navVisible && !navTranslucent && !navAllowedHidden
- && !mNavigationBar.isAnimatingLw()
- && !mNavigationBarController.wasRecentlyTranslucent()) {
- // If the nav bar is currently requested to be visible,
- // and not in the process of animating on or off, then
- // we can tell the app that it is covered by it.
- mSystemLeft = mTmpNavigationFrame.right;
- }
+ if (mNavigationBar == null) {
+ return false;
+ }
+ boolean transientNavBarShowing = mNavigationBarController.isTransientShowing();
+ // Force the navigation bar to its appropriate place and size. We need to do this directly,
+ // instead of relying on it to bubble up from the nav bar, because this needs to change
+ // atomically with screen rotations.
+ final int rotation = displayFrames.mRotation;
+ final int displayHeight = displayFrames.mDisplayHeight;
+ final int displayWidth = displayFrames.mDisplayWidth;
+ final Rect dockFrame = displayFrames.mDock;
+ mNavigationBarPosition = navigationBarPosition(displayWidth, displayHeight, rotation);
+
+ if (mNavigationBarPosition == NAV_BAR_BOTTOM) {
+ // It's a system nav bar or a portrait screen; nav bar goes on bottom.
+ final int top = displayFrames.mUnrestricted.bottom
+ - getNavigationBarHeight(rotation, uiMode);
+ mTmpNavigationFrame.set(0, top, displayWidth, displayFrames.mUnrestricted.bottom);
+ displayFrames.mStable.bottom = displayFrames.mStableFullscreen.bottom = top;
+ if (transientNavBarShowing) {
+ mNavigationBarController.setBarShowingLw(true);
+ } else if (navVisible) {
+ mNavigationBarController.setBarShowingLw(true);
+ dockFrame.bottom = displayFrames.mRestricted.bottom
+ = displayFrames.mRestrictedOverscan.bottom = top;
+ } else {
+ // We currently want to hide the navigation UI - unless we expanded the status bar.
+ mNavigationBarController.setBarShowingLw(statusBarExpandedNotKeyguard);
}
- // Make sure the content and current rectangles are updated to
- // account for the restrictions from the navigation bar.
- mContentTop = mVoiceContentTop = mCurTop = mDockTop;
- mContentBottom = mVoiceContentBottom = mCurBottom = mDockBottom;
- mContentLeft = mVoiceContentLeft = mCurLeft = mDockLeft;
- mContentRight = mVoiceContentRight = mCurRight = mDockRight;
- mStatusBarLayer = mNavigationBar.getSurfaceLayer();
- // And compute the final frame.
- mNavigationBar.computeFrameLw(mTmpNavigationFrame, mTmpNavigationFrame,
- mTmpNavigationFrame, mTmpNavigationFrame, mTmpNavigationFrame, dcf,
- mTmpNavigationFrame, mTmpNavigationFrame);
- if (DEBUG_LAYOUT) Slog.i(TAG, "mNavigationBar frame: " + mTmpNavigationFrame);
- if (mNavigationBarController.checkHiddenLw()) {
- return true;
+ if (navVisible && !navTranslucent && !navAllowedHidden
+ && !mNavigationBar.isAnimatingLw()
+ && !mNavigationBarController.wasRecentlyTranslucent()) {
+ // If the opaque nav bar is currently requested to be visible and not in the process
+ // of animating on or off, then we can tell the app that it is covered by it.
+ displayFrames.mSystem.bottom = top;
+ }
+ } else if (mNavigationBarPosition == NAV_BAR_RIGHT) {
+ // Landscape screen; nav bar goes to the right.
+ final int left = displayFrames.mUnrestricted.right
+ - getNavigationBarWidth(rotation, uiMode);
+ mTmpNavigationFrame.set(left, 0, displayFrames.mUnrestricted.right, displayHeight);
+ displayFrames.mStable.right = displayFrames.mStableFullscreen.right = left;
+ if (transientNavBarShowing) {
+ mNavigationBarController.setBarShowingLw(true);
+ } else if (navVisible) {
+ mNavigationBarController.setBarShowingLw(true);
+ dockFrame.right = displayFrames.mRestricted.right
+ = displayFrames.mRestrictedOverscan.right = left;
+ } else {
+ // We currently want to hide the navigation UI - unless we expanded the status bar.
+ mNavigationBarController.setBarShowingLw(statusBarExpandedNotKeyguard);
+ }
+ if (navVisible && !navTranslucent && !navAllowedHidden
+ && !mNavigationBar.isAnimatingLw()
+ && !mNavigationBarController.wasRecentlyTranslucent()) {
+ // If the nav bar is currently requested to be visible, and not in the process of
+ // animating on or off, then we can tell the app that it is covered by it.
+ displayFrames.mSystem.right = left;
+ }
+ } else if (mNavigationBarPosition == NAV_BAR_LEFT) {
+ // Seascape screen; nav bar goes to the left.
+ final int right = displayFrames.mUnrestricted.left
+ + getNavigationBarWidth(rotation, uiMode);
+ mTmpNavigationFrame.set(displayFrames.mUnrestricted.left, 0, right, displayHeight);
+ displayFrames.mStable.left = displayFrames.mStableFullscreen.left = right;
+ if (transientNavBarShowing) {
+ mNavigationBarController.setBarShowingLw(true);
+ } else if (navVisible) {
+ mNavigationBarController.setBarShowingLw(true);
+ dockFrame.left = displayFrames.mRestricted.left =
+ displayFrames.mRestrictedOverscan.left = right;
+ } else {
+ // We currently want to hide the navigation UI - unless we expanded the status bar.
+ mNavigationBarController.setBarShowingLw(statusBarExpandedNotKeyguard);
+ }
+ if (navVisible && !navTranslucent && !navAllowedHidden
+ && !mNavigationBar.isAnimatingLw()
+ && !mNavigationBarController.wasRecentlyTranslucent()) {
+ // If the nav bar is currently requested to be visible, and not in the process of
+ // animating on or off, then we can tell the app that it is covered by it.
+ displayFrames.mSystem.left = right;
}
}
- return false;
+
+ // Make sure the content and current rectangles are updated to account for the restrictions
+ // from the navigation bar.
+ displayFrames.mCurrent.set(dockFrame);
+ displayFrames.mVoiceContent.set(dockFrame);
+ displayFrames.mContent.set(dockFrame);
+ mStatusBarLayer = mNavigationBar.getSurfaceLayer();
+ // And compute the final frame.
+ mNavigationBar.computeFrameLw(mTmpNavigationFrame, mTmpNavigationFrame,
+ mTmpNavigationFrame, mTmpNavigationFrame, mTmpNavigationFrame, dcf,
+ mTmpNavigationFrame, mTmpNavigationFrame);
+ if (DEBUG_LAYOUT) Slog.i(TAG, "mNavigationBar frame: " + mTmpNavigationFrame);
+ return mNavigationBarController.checkHiddenLw();
}
private int navigationBarPosition(int displayWidth, int displayHeight, int displayRotation) {
@@ -4815,32 +4674,26 @@
return 0;
}
- @Override
- public void getContentRectLw(Rect r) {
- r.set(mContentLeft, mContentTop, mContentRight, mContentBottom);
- }
-
- void setAttachedWindowFrames(WindowState win, int fl, int adjust, WindowState attached,
- boolean insetDecors, Rect pf, Rect df, Rect of, Rect cf, Rect vf) {
+ private void setAttachedWindowFrames(WindowState win, int fl, int adjust, WindowState attached,
+ boolean insetDecors, Rect pf, Rect df, Rect of, Rect cf, Rect vf,
+ DisplayFrames displayFrames) {
if (win.getSurfaceLayer() > mDockLayer && attached.getSurfaceLayer() < mDockLayer) {
- // Here's a special case: if this attached window is a panel that is
- // above the dock window, and the window it is attached to is below
- // the dock window, then the frames we computed for the window it is
- // attached to can not be used because the dock is effectively part
- // of the underlying window and the attached window is floating on top
- // of the whole thing. So, we ignore the attached window and explicitly
- // compute the frames that would be appropriate without the dock.
- df.left = of.left = cf.left = vf.left = mDockLeft;
- df.top = of.top = cf.top = vf.top = mDockTop;
- df.right = of.right = cf.right = vf.right = mDockRight;
- df.bottom = of.bottom = cf.bottom = vf.bottom = mDockBottom;
+ // Here's a special case: if this attached window is a panel that is above the dock
+ // window, and the window it is attached to is below the dock window, then the frames we
+ // computed for the window it is attached to can not be used because the dock is
+ // effectively part of the underlying window and the attached window is floating on top
+ // of the whole thing. So, we ignore the attached window and explicitly compute the
+ // frames that would be appropriate without the dock.
+ vf.set(displayFrames.mDock);
+ cf.set(displayFrames.mDock);
+ of.set(displayFrames.mDock);
+ df.set(displayFrames.mDock);
} else {
- // The effective display frame of the attached window depends on
- // whether it is taking care of insetting its content. If not,
- // we need to use the parent's content frame so that the entire
- // window is positioned within that content. Otherwise we can use
- // the overscan frame and let the attached window take care of
- // positioning its content appropriately.
+ // The effective display frame of the attached window depends on whether it is taking
+ // care of insetting its content. If not, we need to use the parent's content frame so
+ // that the entire window is positioned within that content. Otherwise we can use the
+ // overscan frame and let the attached window take care of positioning its content
+ // appropriately.
if (adjust != SOFT_INPUT_ADJUST_RESIZE) {
// Set the content frame of the attached window to the parent's decor frame
// (same as content frame when IME isn't present) if specifically requested by
@@ -4849,51 +4702,37 @@
cf.set((fl & FLAG_LAYOUT_ATTACHED_IN_DECOR) != 0
? attached.getContentFrameLw() : attached.getOverscanFrameLw());
} else {
- // If the window is resizing, then we want to base the content
- // frame on our attached content frame to resize... however,
- // things can be tricky if the attached window is NOT in resize
- // mode, in which case its content frame will be larger.
- // Ungh. So to deal with that, make sure the content frame
- // we end up using is not covering the IM dock.
+ // If the window is resizing, then we want to base the content frame on our attached
+ // content frame to resize...however, things can be tricky if the attached window is
+ // NOT in resize mode, in which case its content frame will be larger.
+ // Ungh. So to deal with that, make sure the content frame we end up using is not
+ // covering the IM dock.
cf.set(attached.getContentFrameLw());
if (attached.isVoiceInteraction()) {
- if (cf.left < mVoiceContentLeft) cf.left = mVoiceContentLeft;
- if (cf.top < mVoiceContentTop) cf.top = mVoiceContentTop;
- if (cf.right > mVoiceContentRight) cf.right = mVoiceContentRight;
- if (cf.bottom > mVoiceContentBottom) cf.bottom = mVoiceContentBottom;
+ cf.intersectUnchecked(displayFrames.mVoiceContent);
} else if (attached.getSurfaceLayer() < mDockLayer) {
- if (cf.left < mContentLeft) cf.left = mContentLeft;
- if (cf.top < mContentTop) cf.top = mContentTop;
- if (cf.right > mContentRight) cf.right = mContentRight;
- if (cf.bottom > mContentBottom) cf.bottom = mContentBottom;
+ cf.intersectUnchecked(displayFrames.mContent);
}
}
df.set(insetDecors ? attached.getDisplayFrameLw() : cf);
of.set(insetDecors ? attached.getOverscanFrameLw() : cf);
vf.set(attached.getVisibleFrameLw());
}
- // The LAYOUT_IN_SCREEN flag is used to determine whether the attached
- // window should be positioned relative to its parent or the entire
- // screen.
- pf.set((fl & FLAG_LAYOUT_IN_SCREEN) == 0
- ? attached.getFrameLw() : df);
+ // The LAYOUT_IN_SCREEN flag is used to determine whether the attached window should be
+ // positioned relative to its parent or the entire screen.
+ pf.set((fl & FLAG_LAYOUT_IN_SCREEN) == 0 ? attached.getFrameLw() : df);
}
- private void applyStableConstraints(int sysui, int fl, Rect r) {
- if ((sysui & View.SYSTEM_UI_FLAG_LAYOUT_STABLE) != 0) {
- // If app is requesting a stable layout, don't let the
- // content insets go below the stable values.
- if ((fl & FLAG_FULLSCREEN) != 0) {
- if (r.left < mStableFullscreenLeft) r.left = mStableFullscreenLeft;
- if (r.top < mStableFullscreenTop) r.top = mStableFullscreenTop;
- if (r.right > mStableFullscreenRight) r.right = mStableFullscreenRight;
- if (r.bottom > mStableFullscreenBottom) r.bottom = mStableFullscreenBottom;
- } else {
- if (r.left < mStableLeft) r.left = mStableLeft;
- if (r.top < mStableTop) r.top = mStableTop;
- if (r.right > mStableRight) r.right = mStableRight;
- if (r.bottom > mStableBottom) r.bottom = mStableBottom;
- }
+ private void applyStableConstraints(int sysui, int fl, Rect r, DisplayFrames displayFrames) {
+ if ((sysui & View.SYSTEM_UI_FLAG_LAYOUT_STABLE) == 0) {
+ return;
+ }
+ // If app is requesting a stable layout, don't let the content insets go below the stable
+ // values.
+ if ((fl & FLAG_FULLSCREEN) != 0) {
+ r.intersectUnchecked(displayFrames.mStableFullscreen);
+ } else {
+ r.intersectUnchecked(displayFrames.mStable);
}
}
@@ -4908,7 +4747,7 @@
/** {@inheritDoc} */
@Override
- public void layoutWindowLw(WindowState win, WindowState attached) {
+ public void layoutWindowLw(WindowState win, WindowState attached, DisplayFrames displayFrames) {
// We've already done the navigation bar, status bar, and all screen decor windows. If the
// status bar can receive input, we need to layout it again to accommodate for the IME
// window.
@@ -4922,9 +4761,10 @@
(win == mLastInputMethodTargetWindow && mLastInputMethodWindow != null);
if (needsToOffsetInputMethodTarget) {
if (DEBUG_LAYOUT) Slog.i(TAG, "Offset ime target window by the last ime window state");
- offsetInputMethodWindowLw(mLastInputMethodWindow);
+ offsetInputMethodWindowLw(mLastInputMethodWindow, displayFrames);
}
+ final int type = attrs.type;
final int fl = PolicyControl.getWindowFlags(win, attrs);
final int pfl = attrs.privateFlags;
final int sim = attrs.softInputMode;
@@ -4945,120 +4785,83 @@
final int adjust = sim & SOFT_INPUT_MASK_ADJUST;
- if (isDefaultDisplay) {
- sf.set(mStableLeft, mStableTop, mStableRight, mStableBottom);
- } else {
- sf.set(mOverscanLeft, mOverscanTop, mOverscanRight, mOverscanBottom);
- }
+ sf.set(displayFrames.mStable);
- if (!isDefaultDisplay) {
- // TODO: Need to fix this and above to take into account decor windows.
- if (attached != null) {
- // If this window is attached to another, our display
- // frame is the same as the one we are attached to.
- setAttachedWindowFrames(win, fl, adjust, attached, true, pf, df, of, cf, vf);
- } else {
- // Give the window full screen.
- pf.left = df.left = of.left = cf.left = mOverscanScreenLeft;
- pf.top = df.top = of.top = cf.top = mOverscanScreenTop;
- pf.right = df.right = of.right = cf.right
- = mOverscanScreenLeft + mOverscanScreenWidth;
- pf.bottom = df.bottom = of.bottom = cf.bottom
- = mOverscanScreenTop + mOverscanScreenHeight;
- }
- } else if (attrs.type == TYPE_INPUT_METHOD) {
- pf.left = df.left = of.left = cf.left = vf.left = mDockLeft;
- pf.top = df.top = of.top = cf.top = vf.top = mDockTop;
- pf.right = df.right = of.right = cf.right = vf.right = mDockRight;
+ if (type == TYPE_INPUT_METHOD) {
+ vf.set(displayFrames.mDock);
+ cf.set(displayFrames.mDock);
+ of.set(displayFrames.mDock);
+ df.set(displayFrames.mDock);
+ pf.set(displayFrames.mDock);
// IM dock windows layout below the nav bar...
- pf.bottom = df.bottom = of.bottom = mUnrestrictedScreenTop + mUnrestrictedScreenHeight;
+ pf.bottom = df.bottom = of.bottom = displayFrames.mUnrestricted.bottom;
// ...with content insets above the nav bar
- cf.bottom = vf.bottom = mStableBottom;
+ cf.bottom = vf.bottom = displayFrames.mStable.bottom;
if (mStatusBar != null && mFocusedWindow == mStatusBar && canReceiveInput(mStatusBar)) {
// The status bar forces the navigation bar while it's visible. Make sure the IME
// avoids the navigation bar in that case.
if (mNavigationBarPosition == NAV_BAR_RIGHT) {
- pf.right = df.right = of.right = cf.right = vf.right = mStableRight;
+ pf.right = df.right = of.right = cf.right = vf.right =
+ displayFrames.mStable.right;
} else if (mNavigationBarPosition == NAV_BAR_LEFT) {
- pf.left = df.left = of.left = cf.left = vf.left = mStableLeft;
+ pf.left = df.left = of.left = cf.left = vf.left = displayFrames.mStable.left;
}
}
// IM dock windows always go to the bottom of the screen.
attrs.gravity = Gravity.BOTTOM;
mDockLayer = win.getSurfaceLayer();
- } else if (attrs.type == TYPE_VOICE_INTERACTION) {
- pf.left = df.left = of.left = mUnrestrictedScreenLeft;
- pf.top = df.top = of.top = mUnrestrictedScreenTop;
- pf.right = df.right = of.right = mUnrestrictedScreenLeft + mUnrestrictedScreenWidth;
- pf.bottom = df.bottom = of.bottom = mUnrestrictedScreenTop + mUnrestrictedScreenHeight;
+ } else if (type == TYPE_VOICE_INTERACTION) {
+ of.set(displayFrames.mUnrestricted);
+ df.set(displayFrames.mUnrestricted);
+ pf.set(displayFrames.mUnrestricted);
if (adjust != SOFT_INPUT_ADJUST_RESIZE) {
- cf.left = mDockLeft;
- cf.top = mDockTop;
- cf.right = mDockRight;
- cf.bottom = mDockBottom;
+ cf.set(displayFrames.mDock);
} else {
- cf.left = mContentLeft;
- cf.top = mContentTop;
- cf.right = mContentRight;
- cf.bottom = mContentBottom;
+ cf.set(displayFrames.mContent);
}
if (adjust != SOFT_INPUT_ADJUST_NOTHING) {
- vf.left = mCurLeft;
- vf.top = mCurTop;
- vf.right = mCurRight;
- vf.bottom = mCurBottom;
+ vf.set(displayFrames.mCurrent);
} else {
vf.set(cf);
}
- } else if (attrs.type == TYPE_WALLPAPER) {
- layoutWallpaper(win, pf, df, of, cf);
+ } else if (type == TYPE_WALLPAPER) {
+ layoutWallpaper(displayFrames, pf, df, of, cf);
} else if (win == mStatusBar) {
- pf.left = df.left = of.left = mUnrestrictedScreenLeft;
- pf.top = df.top = of.top = mUnrestrictedScreenTop;
- pf.right = df.right = of.right = mUnrestrictedScreenWidth + mUnrestrictedScreenLeft;
- pf.bottom = df.bottom = of.bottom = mUnrestrictedScreenHeight + mUnrestrictedScreenTop;
- cf.left = vf.left = mStableLeft;
- cf.top = vf.top = mStableTop;
- cf.right = vf.right = mStableRight;
- vf.bottom = mStableBottom;
+ of.set(displayFrames.mUnrestricted);
+ df.set(displayFrames.mUnrestricted);
+ pf.set(displayFrames.mUnrestricted);
+ cf.set(displayFrames.mStable);
+ vf.set(displayFrames.mStable);
if (adjust == SOFT_INPUT_ADJUST_RESIZE) {
- cf.bottom = mContentBottom;
+ cf.bottom = displayFrames.mContent.bottom;
} else {
- cf.bottom = mDockBottom;
- vf.bottom = mContentBottom;
+ cf.bottom = displayFrames.mDock.bottom;
+ vf.bottom = displayFrames.mContent.bottom;
}
} else {
-
- // Default policy decor for the default display
- dcf.left = mSystemLeft;
- dcf.top = mSystemTop;
- dcf.right = mSystemRight;
- dcf.bottom = mSystemBottom;
- final boolean inheritTranslucentDecor = (attrs.privateFlags
- & WindowManager.LayoutParams.PRIVATE_FLAG_INHERIT_TRANSLUCENT_DECOR) != 0;
+ dcf.set(displayFrames.mSystem);
+ final boolean inheritTranslucentDecor =
+ (attrs.privateFlags & PRIVATE_FLAG_INHERIT_TRANSLUCENT_DECOR) != 0;
final boolean isAppWindow =
- attrs.type >= WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW &&
- attrs.type <= WindowManager.LayoutParams.LAST_APPLICATION_WINDOW;
+ type >= FIRST_APPLICATION_WINDOW && type <= LAST_APPLICATION_WINDOW;
final boolean topAtRest =
win == mTopFullscreenOpaqueWindowState && !win.isAnimatingLw();
if (isAppWindow && !inheritTranslucentDecor && !topAtRest) {
if ((sysUiFl & View.SYSTEM_UI_FLAG_FULLSCREEN) == 0
- && (fl & WindowManager.LayoutParams.FLAG_FULLSCREEN) == 0
- && (fl & WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS) == 0
- && (fl & WindowManager.LayoutParams.
- FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) == 0
+ && (fl & FLAG_FULLSCREEN) == 0
+ && (fl & FLAG_TRANSLUCENT_STATUS) == 0
+ && (fl & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) == 0
&& (pfl & PRIVATE_FLAG_FORCE_DRAW_STATUS_BAR_BACKGROUND) == 0) {
// Ensure policy decor includes status bar
- dcf.top = mStableTop;
+ dcf.top = displayFrames.mStable.top;
}
- if ((fl & WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION) == 0
+ if ((fl & FLAG_TRANSLUCENT_NAVIGATION) == 0
&& (sysUiFl & View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) == 0
- && (fl & WindowManager.LayoutParams.
- FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) == 0) {
+ && (fl & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) == 0) {
// Ensure policy decor includes navigation bar
- dcf.bottom = mStableBottom;
- dcf.right = mStableRight;
+ dcf.bottom = displayFrames.mStable.bottom;
+ dcf.right = displayFrames.mStable.right;
}
}
@@ -5066,117 +4869,83 @@
== (FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR)) {
if (DEBUG_LAYOUT) Slog.v(TAG, "layoutWindowLw(" + attrs.getTitle()
+ "): IN_SCREEN, INSET_DECOR");
- // This is the case for a normal activity window: we want it
- // to cover all of the screen space, and it can take care of
- // moving its contents to account for screen decorations that
- // intrude into that space.
+ // This is the case for a normal activity window: we want it to cover all of the
+ // screen space, and it can take care of moving its contents to account for screen
+ // decorations that intrude into that space.
if (attached != null) {
// If this window is attached to another, our display
// frame is the same as the one we are attached to.
- setAttachedWindowFrames(win, fl, adjust, attached, true, pf, df, of, cf, vf);
+ setAttachedWindowFrames(win, fl, adjust, attached, true, pf, df, of, cf, vf,
+ displayFrames);
} else {
- if (attrs.type == TYPE_STATUS_BAR_PANEL
- || attrs.type == TYPE_STATUS_BAR_SUB_PANEL) {
- // Status bar panels are the only windows who can go on top of
- // the status bar. They are protected by the STATUS_BAR_SERVICE
- // permission, so they have the same privileges as the status
- // bar itself.
+ if (type == TYPE_STATUS_BAR_PANEL || type == TYPE_STATUS_BAR_SUB_PANEL) {
+ // Status bar panels are the only windows who can go on top of the status
+ // bar. They are protected by the STATUS_BAR_SERVICE permission, so they
+ // have the same privileges as the status bar itself.
//
// However, they should still dodge the navigation bar if it exists.
pf.left = df.left = of.left = hasNavBar
- ? mDockLeft : mUnrestrictedScreenLeft;
- pf.top = df.top = of.top = mUnrestrictedScreenTop;
+ ? displayFrames.mDock.left : displayFrames.mUnrestricted.left;
+ pf.top = df.top = of.top = displayFrames.mUnrestricted.top;
pf.right = df.right = of.right = hasNavBar
- ? mRestrictedScreenLeft+mRestrictedScreenWidth
- : mUnrestrictedScreenLeft + mUnrestrictedScreenWidth;
+ ? displayFrames.mRestricted.right
+ : displayFrames.mUnrestricted.right;
pf.bottom = df.bottom = of.bottom = hasNavBar
- ? mRestrictedScreenTop+mRestrictedScreenHeight
- : mUnrestrictedScreenTop + mUnrestrictedScreenHeight;
+ ? displayFrames.mRestricted.bottom
+ : displayFrames.mUnrestricted.bottom;
if (DEBUG_LAYOUT) Slog.v(TAG, String.format(
"Laying out status bar window: (%d,%d - %d,%d)",
pf.left, pf.top, pf.right, pf.bottom));
} else if ((fl & FLAG_LAYOUT_IN_OVERSCAN) != 0
- && attrs.type >= WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW
- && attrs.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW) {
+ && type >= FIRST_APPLICATION_WINDOW && type <= LAST_SUB_WINDOW) {
// Asking to layout into the overscan region, so give it that pure
// unrestricted area.
- pf.left = df.left = of.left = mOverscanScreenLeft;
- pf.top = df.top = of.top = mOverscanScreenTop;
- pf.right = df.right = of.right = mOverscanScreenLeft + mOverscanScreenWidth;
- pf.bottom = df.bottom = of.bottom = mOverscanScreenTop
- + mOverscanScreenHeight;
+ of.set(displayFrames.mOverscan);
+ df.set(displayFrames.mOverscan);
+ pf.set(displayFrames.mOverscan);
} else if (canHideNavigationBar()
&& (sysUiFl & View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION) != 0
- && attrs.type >= WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW
- && attrs.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW) {
- // Asking for layout as if the nav bar is hidden, lets the
- // application extend into the unrestricted overscan screen area. We
- // only do this for application windows to ensure no window that
- // can be above the nav bar can do this.
- pf.left = df.left = mOverscanScreenLeft;
- pf.top = df.top = mOverscanScreenTop;
- pf.right = df.right = mOverscanScreenLeft + mOverscanScreenWidth;
- pf.bottom = df.bottom = mOverscanScreenTop + mOverscanScreenHeight;
- // We need to tell the app about where the frame inside the overscan
- // is, so it can inset its content by that amount -- it didn't ask
- // to actually extend itself into the overscan region.
- of.left = mUnrestrictedScreenLeft;
- of.top = mUnrestrictedScreenTop;
- of.right = mUnrestrictedScreenLeft + mUnrestrictedScreenWidth;
- of.bottom = mUnrestrictedScreenTop + mUnrestrictedScreenHeight;
+ && type >= FIRST_APPLICATION_WINDOW && type <= LAST_SUB_WINDOW) {
+ // Asking for layout as if the nav bar is hidden, lets the application
+ // extend into the unrestricted overscan screen area. We only do this for
+ // application windows to ensure no window that can be above the nav bar can
+ // do this.
+ df.set(displayFrames.mOverscan);
+ pf.set(displayFrames.mOverscan);
+ // We need to tell the app about where the frame inside the overscan is, so
+ // it can inset its content by that amount -- it didn't ask to actually
+ // extend itself into the overscan region.
+ of.set(displayFrames.mUnrestricted);
} else {
- pf.left = df.left = mRestrictedOverscanScreenLeft;
- pf.top = df.top = mRestrictedOverscanScreenTop;
- pf.right = df.right = mRestrictedOverscanScreenLeft
- + mRestrictedOverscanScreenWidth;
- pf.bottom = df.bottom = mRestrictedOverscanScreenTop
- + mRestrictedOverscanScreenHeight;
+ df.set(displayFrames.mRestrictedOverscan);
+ pf.set(displayFrames.mRestrictedOverscan);
// We need to tell the app about where the frame inside the overscan
// is, so it can inset its content by that amount -- it didn't ask
// to actually extend itself into the overscan region.
- of.left = mUnrestrictedScreenLeft;
- of.top = mUnrestrictedScreenTop;
- of.right = mUnrestrictedScreenLeft + mUnrestrictedScreenWidth;
- of.bottom = mUnrestrictedScreenTop + mUnrestrictedScreenHeight;
+ of.set(displayFrames.mUnrestricted);
}
if ((fl & FLAG_FULLSCREEN) == 0) {
if (win.isVoiceInteraction()) {
- cf.left = mVoiceContentLeft;
- cf.top = mVoiceContentTop;
- cf.right = mVoiceContentRight;
- cf.bottom = mVoiceContentBottom;
+ cf.set(displayFrames.mVoiceContent);
} else {
if (adjust != SOFT_INPUT_ADJUST_RESIZE) {
- cf.left = mDockLeft;
- cf.top = mDockTop;
- cf.right = mDockRight;
- cf.bottom = mDockBottom;
+ cf.set(displayFrames.mDock);
} else {
- cf.left = mContentLeft;
- cf.top = mContentTop;
- cf.right = mContentRight;
- cf.bottom = mContentBottom;
+ cf.set(displayFrames.mContent);
}
}
} else {
- // Full screen windows are always given a layout that is as if the
- // status bar and other transient decors are gone. This is to avoid
- // bad states when moving from a window that is not hding the
- // status bar to one that is.
- cf.left = mRestrictedScreenLeft;
- cf.top = mRestrictedScreenTop;
- cf.right = mRestrictedScreenLeft + mRestrictedScreenWidth;
- cf.bottom = mRestrictedScreenTop + mRestrictedScreenHeight;
+ // Full screen windows are always given a layout that is as if the status
+ // bar and other transient decors are gone. This is to avoid bad states when
+ // moving from a window that is not hiding the status bar to one that is.
+ cf.set(displayFrames.mRestricted);
}
- applyStableConstraints(sysUiFl, fl, cf);
+ applyStableConstraints(sysUiFl, fl, cf, displayFrames);
if (adjust != SOFT_INPUT_ADJUST_NOTHING) {
- vf.left = mCurLeft;
- vf.top = mCurTop;
- vf.right = mCurRight;
- vf.bottom = mCurBottom;
+ vf.set(displayFrames.mCurrent);
} else {
vf.set(cf);
}
@@ -5184,86 +4953,70 @@
} else if ((fl & FLAG_LAYOUT_IN_SCREEN) != 0 || (sysUiFl
& (View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
| View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION)) != 0) {
- if (DEBUG_LAYOUT) Slog.v(TAG, "layoutWindowLw(" + attrs.getTitle() +
- "): IN_SCREEN");
+ if (DEBUG_LAYOUT) Slog.v(TAG, "layoutWindowLw(" + attrs.getTitle()
+ + "): IN_SCREEN");
// A window that has requested to fill the entire screen just
// gets everything, period.
- if (attrs.type == TYPE_STATUS_BAR_PANEL
- || attrs.type == TYPE_STATUS_BAR_SUB_PANEL) {
- pf.left = df.left = of.left = cf.left = hasNavBar
- ? mDockLeft : mUnrestrictedScreenLeft;
- pf.top = df.top = of.top = cf.top = mUnrestrictedScreenTop;
- pf.right = df.right = of.right = cf.right = hasNavBar
- ? mRestrictedScreenLeft + mRestrictedScreenWidth
- : mUnrestrictedScreenLeft + mUnrestrictedScreenWidth;
- pf.bottom = df.bottom = of.bottom = cf.bottom = hasNavBar
- ? mRestrictedScreenTop + mRestrictedScreenHeight
- : mUnrestrictedScreenTop + mUnrestrictedScreenHeight;
+ if (type == TYPE_STATUS_BAR_PANEL || type == TYPE_STATUS_BAR_SUB_PANEL) {
+ cf.set(displayFrames.mUnrestricted);
+ of.set(displayFrames.mUnrestricted);
+ df.set(displayFrames.mUnrestricted);
+ pf.set(displayFrames.mUnrestricted);
+ if (hasNavBar) {
+ pf.left = df.left = of.left = cf.left = displayFrames.mDock.left;
+ pf.right = df.right = of.right = cf.right = displayFrames.mRestricted.right;
+ pf.bottom = df.bottom = of.bottom = cf.bottom =
+ displayFrames.mRestricted.bottom;
+ }
if (DEBUG_LAYOUT) Slog.v(TAG, String.format(
"Laying out IN_SCREEN status bar window: (%d,%d - %d,%d)",
pf.left, pf.top, pf.right, pf.bottom));
- } else if (attrs.type == TYPE_VOLUME_OVERLAY) {
+ } else if (type == TYPE_VOLUME_OVERLAY) {
// Volume overlay covers everything, including the status and navbar
- pf.left = df.left = of.left = cf.left = mUnrestrictedScreenLeft;
- pf.top = df.top = of.top = cf.top = mUnrestrictedScreenTop;
- pf.right = df.right = of.right = cf.right =
- mUnrestrictedScreenLeft + mUnrestrictedScreenWidth;
- pf.bottom = df.bottom = of.bottom = cf.bottom =
- mUnrestrictedScreenTop + mUnrestrictedScreenHeight;
+ cf.set(displayFrames.mUnrestricted);
+ of.set(displayFrames.mUnrestricted);
+ df.set(displayFrames.mUnrestricted);
+ pf.set(displayFrames.mUnrestricted);
if (DEBUG_LAYOUT) Slog.v(TAG, String.format(
"Laying out IN_SCREEN status bar window: (%d,%d - %d,%d)",
pf.left, pf.top, pf.right, pf.bottom));
- } else if (attrs.type == TYPE_NAVIGATION_BAR
- || attrs.type == TYPE_NAVIGATION_BAR_PANEL) {
+ } else if (type == TYPE_NAVIGATION_BAR || type == TYPE_NAVIGATION_BAR_PANEL) {
// The navigation bar has Real Ultimate Power.
- pf.left = df.left = of.left = mUnrestrictedScreenLeft;
- pf.top = df.top = of.top = mUnrestrictedScreenTop;
- pf.right = df.right = of.right = mUnrestrictedScreenLeft
- + mUnrestrictedScreenWidth;
- pf.bottom = df.bottom = of.bottom = mUnrestrictedScreenTop
- + mUnrestrictedScreenHeight;
+ of.set(displayFrames.mUnrestricted);
+ df.set(displayFrames.mUnrestricted);
+ pf.set(displayFrames.mUnrestricted);
if (DEBUG_LAYOUT) Slog.v(TAG, String.format(
"Laying out navigation bar window: (%d,%d - %d,%d)",
pf.left, pf.top, pf.right, pf.bottom));
- } else if ((attrs.type == TYPE_SECURE_SYSTEM_OVERLAY
- || attrs.type == TYPE_BOOT_PROGRESS
- || attrs.type == TYPE_SCREENSHOT)
+ } else if ((type == TYPE_SECURE_SYSTEM_OVERLAY || type == TYPE_SCREENSHOT)
&& ((fl & FLAG_FULLSCREEN) != 0)) {
// Fullscreen secure system overlays get what they ask for. Screenshot region
// selection overlay should also expand to full screen.
- pf.left = df.left = of.left = cf.left = mOverscanScreenLeft;
- pf.top = df.top = of.top = cf.top = mOverscanScreenTop;
- pf.right = df.right = of.right = cf.right = mOverscanScreenLeft
- + mOverscanScreenWidth;
- pf.bottom = df.bottom = of.bottom = cf.bottom = mOverscanScreenTop
- + mOverscanScreenHeight;
- } else if (attrs.type == TYPE_BOOT_PROGRESS) {
+ cf.set(displayFrames.mOverscan);
+ of.set(displayFrames.mOverscan);
+ df.set(displayFrames.mOverscan);
+ pf.set(displayFrames.mOverscan);
+ } else if (type == TYPE_BOOT_PROGRESS) {
// Boot progress screen always covers entire display.
- pf.left = df.left = of.left = cf.left = mOverscanScreenLeft;
- pf.top = df.top = of.top = cf.top = mOverscanScreenTop;
- pf.right = df.right = of.right = cf.right = mOverscanScreenLeft
- + mOverscanScreenWidth;
- pf.bottom = df.bottom = of.bottom = cf.bottom = mOverscanScreenTop
- + mOverscanScreenHeight;
+ cf.set(displayFrames.mOverscan);
+ of.set(displayFrames.mOverscan);
+ df.set(displayFrames.mOverscan);
+ pf.set(displayFrames.mOverscan);
} else if ((fl & FLAG_LAYOUT_IN_OVERSCAN) != 0
- && attrs.type >= WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW
- && attrs.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW) {
- // Asking to layout into the overscan region, so give it that pure
- // unrestricted area.
- pf.left = df.left = of.left = cf.left = mOverscanScreenLeft;
- pf.top = df.top = of.top = cf.top = mOverscanScreenTop;
- pf.right = df.right = of.right = cf.right
- = mOverscanScreenLeft + mOverscanScreenWidth;
- pf.bottom = df.bottom = of.bottom = cf.bottom
- = mOverscanScreenTop + mOverscanScreenHeight;
+ && type >= FIRST_APPLICATION_WINDOW && type <= LAST_SUB_WINDOW) {
+ // Asking to layout into the overscan region, so give it that pure unrestricted
+ // area.
+ cf.set(displayFrames.mOverscan);
+ of.set(displayFrames.mOverscan);
+ df.set(displayFrames.mOverscan);
+ pf.set(displayFrames.mOverscan);
} else if (canHideNavigationBar()
&& (sysUiFl & View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION) != 0
- && (attrs.type == TYPE_STATUS_BAR
- || attrs.type == TYPE_TOAST
- || attrs.type == TYPE_DOCK_DIVIDER
- || attrs.type == TYPE_VOICE_INTERACTION_STARTING
- || (attrs.type >= WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW
- && attrs.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW))) {
+ && (type == TYPE_STATUS_BAR
+ || type == TYPE_TOAST
+ || type == TYPE_DOCK_DIVIDER
+ || type == TYPE_VOICE_INTERACTION_STARTING
+ || (type >= FIRST_APPLICATION_WINDOW && type <= LAST_SUB_WINDOW))) {
// Asking for layout as if the nav bar is hidden, lets the
// application extend into the unrestricted screen area. We
// only do this for application windows (or toasts) to ensure no window that
@@ -5271,102 +5024,76 @@
// XXX This assumes that an app asking for this will also
// ask for layout in only content. We can't currently figure out
// what the screen would be if only laying out to hide the nav bar.
- pf.left = df.left = of.left = cf.left = mUnrestrictedScreenLeft;
- pf.top = df.top = of.top = cf.top = mUnrestrictedScreenTop;
- pf.right = df.right = of.right = cf.right = mUnrestrictedScreenLeft
- + mUnrestrictedScreenWidth;
- pf.bottom = df.bottom = of.bottom = cf.bottom = mUnrestrictedScreenTop
- + mUnrestrictedScreenHeight;
+ cf.set(displayFrames.mUnrestricted);
+ of.set(displayFrames.mUnrestricted);
+ df.set(displayFrames.mUnrestricted);
+ pf.set(displayFrames.mUnrestricted);
} else if ((sysUiFl & View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN) != 0) {
- pf.left = df.left = of.left = mRestrictedScreenLeft;
- pf.top = df.top = of.top = mRestrictedScreenTop;
- pf.right = df.right = of.right = mRestrictedScreenLeft + mRestrictedScreenWidth;
- pf.bottom = df.bottom = of.bottom = mRestrictedScreenTop
- + mRestrictedScreenHeight;
+ of.set(displayFrames.mRestricted);
+ df.set(displayFrames.mRestricted);
+ pf.set(displayFrames.mRestricted);
if (adjust != SOFT_INPUT_ADJUST_RESIZE) {
- cf.left = mDockLeft;
- cf.top = mDockTop;
- cf.right = mDockRight;
- cf.bottom = mDockBottom;
+ cf.set(displayFrames.mDock);
} else {
- cf.left = mContentLeft;
- cf.top = mContentTop;
- cf.right = mContentRight;
- cf.bottom = mContentBottom;
+ cf.set(displayFrames.mContent);
}
} else {
- pf.left = df.left = of.left = cf.left = mRestrictedScreenLeft;
- pf.top = df.top = of.top = cf.top = mRestrictedScreenTop;
- pf.right = df.right = of.right = cf.right = mRestrictedScreenLeft
- + mRestrictedScreenWidth;
- pf.bottom = df.bottom = of.bottom = cf.bottom = mRestrictedScreenTop
- + mRestrictedScreenHeight;
+ cf.set(displayFrames.mRestricted);
+ of.set(displayFrames.mRestricted);
+ df.set(displayFrames.mRestricted);
+ pf.set(displayFrames.mRestricted);
}
- applyStableConstraints(sysUiFl, fl, cf);
+ applyStableConstraints(sysUiFl, fl, cf,displayFrames);
if (adjust != SOFT_INPUT_ADJUST_NOTHING) {
- vf.left = mCurLeft;
- vf.top = mCurTop;
- vf.right = mCurRight;
- vf.bottom = mCurBottom;
+ vf.set(displayFrames.mCurrent);
} else {
vf.set(cf);
}
} else if (attached != null) {
- if (DEBUG_LAYOUT) Slog.v(TAG, "layoutWindowLw(" + attrs.getTitle() +
- "): attached to " + attached);
+ if (DEBUG_LAYOUT) Slog.v(TAG, "layoutWindowLw(" + attrs.getTitle()
+ + "): attached to " + attached);
// A child window should be placed inside of the same visible
// frame that its parent had.
- setAttachedWindowFrames(win, fl, adjust, attached, false, pf, df, of, cf, vf);
+ setAttachedWindowFrames(win, fl, adjust, attached, false, pf, df, of, cf, vf,
+ displayFrames);
} else {
if (DEBUG_LAYOUT) Slog.v(TAG, "layoutWindowLw(" + attrs.getTitle() +
"): normal window");
// Otherwise, a normal window must be placed inside the content
// of all screen decorations.
- if (attrs.type == TYPE_STATUS_BAR_PANEL || attrs.type == TYPE_VOLUME_OVERLAY) {
+ if (type == TYPE_STATUS_BAR_PANEL || type == TYPE_VOLUME_OVERLAY) {
// Status bar panels and the volume dialog are the only windows who can go on
- // top of the status bar. They are protected by the STATUS_BAR_SERVICE
- // permission, so they have the same privileges as the status
- // bar itself.
- pf.left = df.left = of.left = cf.left = mRestrictedScreenLeft;
- pf.top = df.top = of.top = cf.top = mRestrictedScreenTop;
- pf.right = df.right = of.right = cf.right = mRestrictedScreenLeft
- + mRestrictedScreenWidth;
- pf.bottom = df.bottom = of.bottom = cf.bottom = mRestrictedScreenTop
- + mRestrictedScreenHeight;
- } else if (attrs.type == TYPE_TOAST || attrs.type == TYPE_SYSTEM_ALERT) {
+ // top of the status bar. They are protected by the STATUS_BAR_SERVICE
+ // permission, so they have the same privileges as the status bar itself.
+ cf.set(displayFrames.mRestricted);
+ of.set(displayFrames.mRestricted);
+ df.set(displayFrames.mRestricted);
+ pf.set(displayFrames.mRestricted);
+ } else if (type == TYPE_TOAST || type == TYPE_SYSTEM_ALERT) {
// These dialogs are stable to interim decor changes.
- pf.left = df.left = of.left = cf.left = mStableLeft;
- pf.top = df.top = of.top = cf.top = mStableTop;
- pf.right = df.right = of.right = cf.right = mStableRight;
- pf.bottom = df.bottom = of.bottom = cf.bottom = mStableBottom;
+ cf.set(displayFrames.mStable);
+ of.set(displayFrames.mStable);
+ df.set(displayFrames.mStable);
+ pf.set(displayFrames.mStable);
} else {
- pf.left = mContentLeft;
- pf.top = mContentTop;
- pf.right = mContentRight;
- pf.bottom = mContentBottom;
+ pf.set(displayFrames.mContent);
if (win.isVoiceInteraction()) {
- df.left = of.left = cf.left = mVoiceContentLeft;
- df.top = of.top = cf.top = mVoiceContentTop;
- df.right = of.right = cf.right = mVoiceContentRight;
- df.bottom = of.bottom = cf.bottom = mVoiceContentBottom;
+ cf.set(displayFrames.mVoiceContent);
+ of.set(displayFrames.mVoiceContent);
+ df.set(displayFrames.mVoiceContent);
} else if (adjust != SOFT_INPUT_ADJUST_RESIZE) {
- df.left = of.left = cf.left = mDockLeft;
- df.top = of.top = cf.top = mDockTop;
- df.right = of.right = cf.right = mDockRight;
- df.bottom = of.bottom = cf.bottom = mDockBottom;
+ cf.set(displayFrames.mDock);
+ of.set(displayFrames.mDock);
+ df.set(displayFrames.mDock);
} else {
- df.left = of.left = cf.left = mContentLeft;
- df.top = of.top = cf.top = mContentTop;
- df.right = of.right = cf.right = mContentRight;
- df.bottom = of.bottom = cf.bottom = mContentBottom;
+ cf.set(displayFrames.mContent);
+ of.set(displayFrames.mContent);
+ df.set(displayFrames.mContent);
}
if (adjust != SOFT_INPUT_ADJUST_NOTHING) {
- vf.left = mCurLeft;
- vf.top = mCurTop;
- vf.right = mCurRight;
- vf.bottom = mCurBottom;
+ vf.set(displayFrames.mCurrent);
} else {
vf.set(cf);
}
@@ -5376,11 +5103,11 @@
// TYPE_SYSTEM_ERROR is above the NavigationBar so it can't be allowed to extend over it.
// Also, we don't allow windows in multi-window mode to extend out of the screen.
- if ((fl & FLAG_LAYOUT_NO_LIMITS) != 0 && attrs.type != TYPE_SYSTEM_ERROR
+ if ((fl & FLAG_LAYOUT_NO_LIMITS) != 0 && type != TYPE_SYSTEM_ERROR
&& !win.isInMultiWindowMode()) {
df.left = df.top = -10000;
df.right = df.bottom = 10000;
- if (attrs.type != TYPE_WALLPAPER) {
+ if (type != TYPE_WALLPAPER) {
of.left = of.top = cf.left = cf.top = vf.left = vf.top = -10000;
of.right = of.bottom = cf.right = cf.bottom = vf.right = vf.bottom = 10000;
}
@@ -5396,7 +5123,7 @@
osf.set(cf.left, cf.top, cf.right, cf.bottom);
int outset = ScreenShapeHelper.getWindowOutsetBottomPx(mContext.getResources());
if (outset > 0) {
- int rotation = mDisplayRotation;
+ int rotation = displayFrames.mRotation;
if (rotation == Surface.ROTATION_0) {
osf.bottom += outset;
} else if (rotation == Surface.ROTATION_90) {
@@ -5413,7 +5140,7 @@
if (DEBUG_LAYOUT) Slog.v(TAG, "Compute frame " + attrs.getTitle()
+ ": sim=#" + Integer.toHexString(sim)
- + " attach=" + attached + " type=" + attrs.type
+ + " attach=" + attached + " type=" + type
+ String.format(" flags=0x%08x", fl)
+ " pf=" + pf.toShortString() + " df=" + df.toShortString()
+ " of=" + of.toShortString()
@@ -5426,62 +5153,42 @@
// Dock windows carve out the bottom of the screen, so normal windows
// can't appear underneath them.
- if (attrs.type == TYPE_INPUT_METHOD && win.isVisibleLw()
+ if (type == TYPE_INPUT_METHOD && win.isVisibleLw()
&& !win.getGivenInsetsPendingLw()) {
setLastInputMethodWindowLw(null, null);
- offsetInputMethodWindowLw(win);
+ offsetInputMethodWindowLw(win, displayFrames);
}
- if (attrs.type == TYPE_VOICE_INTERACTION && win.isVisibleLw()
+ if (type == TYPE_VOICE_INTERACTION && win.isVisibleLw()
&& !win.getGivenInsetsPendingLw()) {
- offsetVoiceInputWindowLw(win);
+ offsetVoiceInputWindowLw(win, displayFrames);
}
}
- private void layoutWallpaper(WindowState win, Rect pf, Rect df, Rect of, Rect cf) {
-
- // The wallpaper also has Real Ultimate Power, but we want to tell
- // it about the overscan area.
- pf.left = df.left = mOverscanScreenLeft;
- pf.top = df.top = mOverscanScreenTop;
- pf.right = df.right = mOverscanScreenLeft + mOverscanScreenWidth;
- pf.bottom = df.bottom = mOverscanScreenTop + mOverscanScreenHeight;
- of.left = cf.left = mUnrestrictedScreenLeft;
- of.top = cf.top = mUnrestrictedScreenTop;
- of.right = cf.right = mUnrestrictedScreenLeft + mUnrestrictedScreenWidth;
- of.bottom = cf.bottom = mUnrestrictedScreenTop + mUnrestrictedScreenHeight;
+ private void layoutWallpaper(DisplayFrames displayFrames, Rect pf, Rect df, Rect of, Rect cf) {
+ // The wallpaper has Real Ultimate Power, but we want to tell it about the overscan area.
+ df.set(displayFrames.mOverscan);
+ pf.set(displayFrames.mOverscan);
+ cf.set(displayFrames.mUnrestricted);
+ of.set(displayFrames.mUnrestricted);
}
- private void offsetInputMethodWindowLw(WindowState win) {
+ private void offsetInputMethodWindowLw(WindowState win, DisplayFrames displayFrames) {
int top = Math.max(win.getDisplayFrameLw().top, win.getContentFrameLw().top);
top += win.getGivenContentInsetsLw().top;
- if (mContentBottom > top) {
- mContentBottom = top;
- }
- if (mVoiceContentBottom > top) {
- mVoiceContentBottom = top;
- }
+ displayFrames.mContent.bottom = Math.min(displayFrames.mContent.bottom, top);
+ displayFrames.mVoiceContent.bottom = Math.min(displayFrames.mVoiceContent.bottom, top);
top = win.getVisibleFrameLw().top;
top += win.getGivenVisibleInsetsLw().top;
- if (mCurBottom > top) {
- mCurBottom = top;
- }
+ displayFrames.mCurrent.bottom = Math.min(displayFrames.mCurrent.bottom, top);
if (DEBUG_LAYOUT) Slog.v(TAG, "Input method: mDockBottom="
- + mDockBottom + " mContentBottom="
- + mContentBottom + " mCurBottom=" + mCurBottom);
+ + displayFrames.mDock.bottom + " mContentBottom="
+ + displayFrames.mContent.bottom + " mCurBottom=" + displayFrames.mCurrent.bottom);
}
- private void offsetVoiceInputWindowLw(WindowState win) {
+ private void offsetVoiceInputWindowLw(WindowState win, DisplayFrames displayFrames) {
int top = Math.max(win.getDisplayFrameLw().top, win.getContentFrameLw().top);
top += win.getGivenContentInsetsLw().top;
- if (mVoiceContentBottom > top) {
- mVoiceContentBottom = top;
- }
- }
-
- /** {@inheritDoc} */
- @Override
- public void finishLayoutLw() {
- return;
+ displayFrames.mVoiceContent.bottom = Math.min(displayFrames.mVoiceContent.bottom, top);
}
/** {@inheritDoc} */
@@ -8334,11 +8041,6 @@
}
@Override
- public int getInputMethodWindowVisibleHeightLw() {
- return mDockBottom - mCurBottom;
- }
-
- @Override
public void setCurrentUserLw(int newUserId) {
mCurrentUserId = newUserId;
if (mKeyguardDelegate != null) {
@@ -8427,8 +8129,6 @@
@Override
public void writeToProto(ProtoOutputStream proto, long fieldId) {
final long token = proto.start(fieldId);
- new Rect(mStableLeft, mStableTop, mStableRight, mStableBottom).writeToProto(proto,
- STABLE_BOUNDS);
proto.end(token);
}
@@ -8534,58 +8234,6 @@
pw.print(" mScreenOnFully="); pw.println(mScreenOnFully);
pw.print(prefix); pw.print("mKeyguardDrawComplete="); pw.print(mKeyguardDrawComplete);
pw.print(" mWindowManagerDrawComplete="); pw.println(mWindowManagerDrawComplete);
- pw.print(prefix); pw.print("mOverscanScreen=("); pw.print(mOverscanScreenLeft);
- pw.print(","); pw.print(mOverscanScreenTop);
- pw.print(") "); pw.print(mOverscanScreenWidth);
- pw.print("x"); pw.println(mOverscanScreenHeight);
- if (mOverscanLeft != 0 || mOverscanTop != 0
- || mOverscanRight != 0 || mOverscanBottom != 0) {
- pw.print(prefix); pw.print("mOverscan left="); pw.print(mOverscanLeft);
- pw.print(" top="); pw.print(mOverscanTop);
- pw.print(" right="); pw.print(mOverscanRight);
- pw.print(" bottom="); pw.println(mOverscanBottom);
- }
- pw.print(prefix); pw.print("mRestrictedOverscanScreen=(");
- pw.print(mRestrictedOverscanScreenLeft);
- pw.print(","); pw.print(mRestrictedOverscanScreenTop);
- pw.print(") "); pw.print(mRestrictedOverscanScreenWidth);
- pw.print("x"); pw.println(mRestrictedOverscanScreenHeight);
- pw.print(prefix); pw.print("mUnrestrictedScreen=("); pw.print(mUnrestrictedScreenLeft);
- pw.print(","); pw.print(mUnrestrictedScreenTop);
- pw.print(") "); pw.print(mUnrestrictedScreenWidth);
- pw.print("x"); pw.println(mUnrestrictedScreenHeight);
- pw.print(prefix); pw.print("mRestrictedScreen=("); pw.print(mRestrictedScreenLeft);
- pw.print(","); pw.print(mRestrictedScreenTop);
- pw.print(") "); pw.print(mRestrictedScreenWidth);
- pw.print("x"); pw.println(mRestrictedScreenHeight);
- pw.print(prefix); pw.print("mStableFullscreen=("); pw.print(mStableFullscreenLeft);
- pw.print(","); pw.print(mStableFullscreenTop);
- pw.print(")-("); pw.print(mStableFullscreenRight);
- pw.print(","); pw.print(mStableFullscreenBottom); pw.println(")");
- pw.print(prefix); pw.print("mStable=("); pw.print(mStableLeft);
- pw.print(","); pw.print(mStableTop);
- pw.print(")-("); pw.print(mStableRight);
- pw.print(","); pw.print(mStableBottom); pw.println(")");
- pw.print(prefix); pw.print("mSystem=("); pw.print(mSystemLeft);
- pw.print(","); pw.print(mSystemTop);
- pw.print(")-("); pw.print(mSystemRight);
- pw.print(","); pw.print(mSystemBottom); pw.println(")");
- pw.print(prefix); pw.print("mCur=("); pw.print(mCurLeft);
- pw.print(","); pw.print(mCurTop);
- pw.print(")-("); pw.print(mCurRight);
- pw.print(","); pw.print(mCurBottom); pw.println(")");
- pw.print(prefix); pw.print("mContent=("); pw.print(mContentLeft);
- pw.print(","); pw.print(mContentTop);
- pw.print(")-("); pw.print(mContentRight);
- pw.print(","); pw.print(mContentBottom); pw.println(")");
- pw.print(prefix); pw.print("mVoiceContent=("); pw.print(mVoiceContentLeft);
- pw.print(","); pw.print(mVoiceContentTop);
- pw.print(")-("); pw.print(mVoiceContentRight);
- pw.print(","); pw.print(mVoiceContentBottom); pw.println(")");
- pw.print(prefix); pw.print("mDock=("); pw.print(mDockLeft);
- pw.print(","); pw.print(mDockTop);
- pw.print(")-("); pw.print(mDockRight);
- pw.print(","); pw.print(mDockBottom); pw.println(")");
pw.print(prefix); pw.print("mDockLayer="); pw.print(mDockLayer);
pw.print(" mStatusBarLayer="); pw.println(mStatusBarLayer);
pw.print(prefix); pw.print("mShowingDream="); pw.print(mShowingDream);
diff --git a/services/core/java/com/android/server/power/BatterySaverPolicy.java b/services/core/java/com/android/server/power/BatterySaverPolicy.java
index 273b945..0c73fe8 100644
--- a/services/core/java/com/android/server/power/BatterySaverPolicy.java
+++ b/services/core/java/com/android/server/power/BatterySaverPolicy.java
@@ -56,6 +56,9 @@
private static final String KEY_ADJUST_BRIGHTNESS_FACTOR = "adjust_brightness_factor";
private static final String KEY_FULLBACKUP_DEFERRED = "fullbackup_deferred";
private static final String KEY_KEYVALUE_DEFERRED = "keyvaluebackup_deferred";
+ private static final String KEY_FORCE_ALL_APPS_STANDBY_JOBS = "force_all_apps_standby_jobs";
+ private static final String KEY_FORCE_ALL_APPS_STANDBY_ALARMS = "force_all_apps_standby_alarms";
+ private static final String KEY_OPTIONAL_SENSORS_DISABLED = "optional_sensors_disabled";
private final KeyValueListParser mParser = new KeyValueListParser(',');
@@ -141,6 +144,21 @@
*/
private float mAdjustBrightnessFactor;
+ /**
+ * Whether to put all apps in the stand-by mode or not for job scheduler.
+ */
+ private boolean mForceAllAppsStandbyJobs;
+
+ /**
+ * Whether to put all apps in the stand-by mode or not for alarms.
+ */
+ private boolean mForceAllAppsStandbyAlarms;
+
+ /**
+ * Weather to show non-essential sensors (e.g. edge sensors) or not.
+ */
+ private boolean mOptionalSensorsDisabled;
+
private ContentResolver mContentResolver;
public BatterySaverPolicy(Handler handler) {
@@ -180,10 +198,14 @@
mAdjustBrightnessDisabled = mParser.getBoolean(KEY_ADJUST_BRIGHTNESS_DISABLED, false);
mAdjustBrightnessFactor = mParser.getFloat(KEY_ADJUST_BRIGHTNESS_FACTOR, 0.5f);
mDataSaverDisabled = mParser.getBoolean(KEY_DATASAVER_DISABLED, true);
+ mForceAllAppsStandbyJobs = mParser.getBoolean(KEY_FORCE_ALL_APPS_STANDBY_JOBS, true);
+ mForceAllAppsStandbyAlarms =
+ mParser.getBoolean(KEY_FORCE_ALL_APPS_STANDBY_ALARMS, true);
+ mOptionalSensorsDisabled = mParser.getBoolean(KEY_OPTIONAL_SENSORS_DISABLED, true);
// Get default value from Settings.Secure
final int defaultGpsMode = Settings.Secure.getInt(mContentResolver, SECURE_KEY_GPS_MODE,
- GPS_MODE_DISABLED_WHEN_SCREEN_OFF);
+ GPS_MODE_NO_CHANGE);
mGpsMode = mParser.getInt(KEY_GPS_MODE, defaultGpsMode);
}
}
@@ -235,6 +257,15 @@
case ServiceType.VIBRATION:
return builder.setBatterySaverEnabled(mVibrationDisabled)
.build();
+ case ServiceType.FORCE_ALL_APPS_STANDBY_JOBS:
+ return builder.setBatterySaverEnabled(mForceAllAppsStandbyJobs)
+ .build();
+ case ServiceType.FORCE_ALL_APPS_STANDBY_ALARMS:
+ return builder.setBatterySaverEnabled(mForceAllAppsStandbyAlarms)
+ .build();
+ case ServiceType.OPTIONAL_SENSORS:
+ return builder.setBatterySaverEnabled(mOptionalSensorsDisabled)
+ .build();
default:
return builder.setBatterySaverEnabled(realMode)
.build();
@@ -259,6 +290,8 @@
pw.println(" " + KEY_ADJUST_BRIGHTNESS_DISABLED + "=" + mAdjustBrightnessDisabled);
pw.println(" " + KEY_ADJUST_BRIGHTNESS_FACTOR + "=" + mAdjustBrightnessFactor);
pw.println(" " + KEY_GPS_MODE + "=" + mGpsMode);
-
+ pw.println(" " + KEY_FORCE_ALL_APPS_STANDBY_JOBS + "=" + mForceAllAppsStandbyJobs);
+ pw.println(" " + KEY_FORCE_ALL_APPS_STANDBY_ALARMS + "=" + mForceAllAppsStandbyAlarms);
+ pw.println(" " + KEY_OPTIONAL_SENSORS_DISABLED + "=" + mOptionalSensorsDisabled);
}
}
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index 416a606..a153fdf 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -71,6 +71,8 @@
import android.util.proto.ProtoOutputStream;
import android.view.Display;
import android.view.WindowManagerPolicy;
+import android.widget.Toast;
+
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.IAppOpsService;
import com.android.internal.app.IBatteryStats;
@@ -1022,6 +1024,13 @@
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
mContext.sendBroadcastAsUser(intent, UserHandle.ALL,
Manifest.permission.DEVICE_POWER);
+
+ // STOPSHIP Remove the toast.
+ if (mLowPowerModeEnabled) {
+ Toast.makeText(mContext,
+ com.android.internal.R.string.battery_saver_warning,
+ Toast.LENGTH_LONG).show();
+ }
}
});
}
diff --git a/services/core/java/com/android/server/stats/StatsCompanionService.java b/services/core/java/com/android/server/stats/StatsCompanionService.java
index 2488723..f41ff2c 100644
--- a/services/core/java/com/android/server/stats/StatsCompanionService.java
+++ b/services/core/java/com/android/server/stats/StatsCompanionService.java
@@ -44,6 +44,8 @@
import com.android.internal.net.NetworkStatsFactory;
import com.android.internal.os.KernelWakelockReader;
import com.android.internal.os.KernelWakelockStats;
+import com.android.internal.os.KernelCpuSpeedReader;
+import com.android.internal.os.PowerProfile;
import com.android.server.LocalServices;
import com.android.server.SystemService;
@@ -71,6 +73,9 @@
private final PendingIntent mPullingAlarmIntent;
private final BroadcastReceiver mAppUpdateReceiver;
private final BroadcastReceiver mUserUpdateReceiver;
+ private final KernelWakelockReader mKernelWakelockReader = new KernelWakelockReader();
+ private final KernelWakelockStats mTmpWakelockStats = new KernelWakelockStats();
+ private final KernelCpuSpeedReader[] mKernelCpuSpeedReaders;
public StatsCompanionService(Context context) {
super();
@@ -103,6 +108,16 @@
}
};
Slog.w(TAG, "Registered receiver for ACTION_PACKAGE_REPLACE AND ADDED.");
+ PowerProfile powerProfile = new PowerProfile(context);
+ final int numClusters = powerProfile.getNumCpuClusters();
+ mKernelCpuSpeedReaders = new KernelCpuSpeedReader[numClusters];
+ int firstCpuOfCluster = 0;
+ for (int i = 0; i < numClusters; i++) {
+ final int numSpeedSteps = powerProfile.getNumSpeedStepsInCpuCluster(i);
+ mKernelCpuSpeedReaders[i] = new KernelCpuSpeedReader(firstCpuOfCluster,
+ numSpeedSteps);
+ firstCpuOfCluster += powerProfile.getNumCoresInCpuCluster(i);
+ }
}
private final static int[] toIntArray(List<Integer> list) {
@@ -286,9 +301,6 @@
}
}
- private final KernelWakelockReader mKernelWakelockReader = new KernelWakelockReader();
- private final KernelWakelockStats mTmpWakelockStats = new KernelWakelockStats();
-
private StatsLogEventWrapper[] addNetworkStats(int tag, NetworkStats stats, boolean withFGBG) {
List<StatsLogEventWrapper> ret = new ArrayList<>();
int size = stats.size();
@@ -446,6 +458,22 @@
}
return ret.toArray(new StatsLogEventWrapper[ret.size()]);
}
+ case StatsLog.CPU_TIME_PER_FREQ_PULLED: {
+ List<StatsLogEventWrapper> ret = new ArrayList();
+ for (int cluster = 0; cluster < mKernelCpuSpeedReaders.length; cluster++) {
+ long[] clusterTimeMs = mKernelCpuSpeedReaders[cluster].readDelta();
+ if (clusterTimeMs != null) {
+ for (int speed = clusterTimeMs.length - 1; speed >= 0; --speed) {
+ StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, 3);
+ e.writeInt(tagId);
+ e.writeInt(speed);
+ e.writeLong(clusterTimeMs[speed]);
+ ret.add(e);
+ }
+ }
+ }
+ return ret.toArray(new StatsLogEventWrapper[ret.size()]);
+ }
default:
Slog.w(TAG, "No such tagId data as " + tagId);
return null;
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 67d62e1..c4b810f 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -104,6 +104,7 @@
import static com.android.server.wm.WindowSurfacePlacer.SET_WALLPAPER_MAY_CHANGE;
import static com.android.server.wm.proto.DisplayProto.ABOVE_APP_WINDOWS;
import static com.android.server.wm.proto.DisplayProto.BELOW_APP_WINDOWS;
+import static com.android.server.wm.proto.DisplayProto.DISPLAY_FRAMES;
import static com.android.server.wm.proto.DisplayProto.DISPLAY_INFO;
import static com.android.server.wm.proto.DisplayProto.DOCKED_STACK_DIVIDER_CONTROLLER;
import static com.android.server.wm.proto.DisplayProto.DPI;
@@ -147,6 +148,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.ToBooleanFunction;
import com.android.internal.view.IInputMethodClient;
+import android.view.DisplayFrames;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -222,6 +224,8 @@
private final DisplayInfo mDisplayInfo = new DisplayInfo();
private final Display mDisplay;
private final DisplayMetrics mDisplayMetrics = new DisplayMetrics();
+ DisplayFrames mDisplayFrames;
+
/**
* For default display it contains real metrics, empty for others.
* @see WindowManagerService#createWatermarkInTransaction()
@@ -285,7 +289,6 @@
private boolean mLastWallpaperVisible = false;
private Rect mBaseDisplayRect = new Rect();
- private Rect mContentRect = new Rect();
// Accessed directly by all users.
private boolean mLayoutNeeded;
@@ -545,7 +548,7 @@
w.mLayoutNeeded = false;
w.prelayout();
final boolean firstLayout = !w.isLaidOut();
- mService.mPolicy.layoutWindowLw(w, null);
+ mService.mPolicy.layoutWindowLw(w, null, mDisplayFrames);
w.mLayoutSeq = mService.mLayoutSeq;
// If this is the first layout, we need to initialize the last inset values as
@@ -586,7 +589,7 @@
}
w.mLayoutNeeded = false;
w.prelayout();
- mService.mPolicy.layoutWindowLw(w, w.getParentWindow());
+ mService.mPolicy.layoutWindowLw(w, w.getParentWindow(), mDisplayFrames);
w.mLayoutSeq = mService.mLayoutSeq;
if (DEBUG_LAYOUT) Slog.v(TAG, " LAYOUT: mFrame=" + w.mFrame
+ " mContainingFrame=" + w.mContainingFrame
@@ -758,6 +761,7 @@
display.getMetrics(mDisplayMetrics);
isDefaultDisplay = mDisplayId == DEFAULT_DISPLAY;
mService = service;
+ mDisplayFrames = new DisplayFrames(mDisplayId, mDisplayInfo);
initializeDisplayBaseInfo();
mDividerControllerLocked = new DockedStackDividerController(service, this);
mPinnedStackControllerLocked = new PinnedStackController(service, this);
@@ -1129,6 +1133,13 @@
return true;
}
+ void configureDisplayPolicy() {
+ mService.mPolicy.setInitialDisplaySize(getDisplay(),
+ mBaseDisplayWidth, mBaseDisplayHeight, mBaseDisplayDensity);
+
+ mDisplayFrames.onDisplayInfoUpdated(mDisplayInfo);
+ }
+
/**
* Update {@link #mDisplayInfo} and other internal variables when display is rotated or config
* changed.
@@ -1744,7 +1755,7 @@
}
void getContentRect(Rect out) {
- out.set(mContentRect);
+ out.set(mDisplayFrames.mContent);
}
TaskStack createStack(int stackId, boolean onTop, StackWindowController controller) {
@@ -1847,8 +1858,8 @@
mTmpRect2.setEmpty();
for (int stackNdx = mTaskStackContainers.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
final TaskStack stack = mTaskStackContainers.getChildAt(stackNdx);
- stack.setTouchExcludeRegion(
- focusedTask, delta, mTouchExcludeRegion, mContentRect, mTmpRect2);
+ stack.setTouchExcludeRegion(focusedTask, delta, mTouchExcludeRegion,
+ mDisplayFrames.mContent, mTmpRect2);
}
// If we removed the focused task above, add it back and only leave its
// outside touch area in the exclusion. TapDectector is not interested in
@@ -2025,7 +2036,7 @@
final boolean imeOnTop = (imeDockSide == DOCKED_TOP);
final boolean imeOnBottom = (imeDockSide == DOCKED_BOTTOM);
final boolean dockMinimized = mDividerControllerLocked.isMinimizedDock();
- final int imeHeight = mService.mPolicy.getInputMethodWindowVisibleHeightLw();
+ final int imeHeight = mDisplayFrames.getInputMethodWindowVisibleHeight();
final boolean imeHeightChanged = imeVisible &&
imeHeight != mDividerControllerLocked.getImeHeightAdjustedFor();
@@ -2167,6 +2178,7 @@
if (screenRotationAnimation != null) {
screenRotationAnimation.writeToProto(proto, SCREEN_ROTATION_ANIMATION);
}
+ mDisplayFrames.writeToProto(proto, DISPLAY_FRAMES);
proto.end(token);
}
@@ -2246,6 +2258,9 @@
pw.println(subPrefix
+ "mInputMethodAnimLayerAdjustment=" + mInputMethodAnimLayerAdjustment);
}
+
+ pw.println();
+ mDisplayFrames.dump(prefix, pw);
}
@Override
@@ -2871,21 +2886,22 @@
final int dw = mDisplayInfo.logicalWidth;
final int dh = mDisplayInfo.logicalHeight;
-
if (DEBUG_LAYOUT) {
Slog.v(TAG, "-------------------------------------");
Slog.v(TAG, "performLayout: needed=" + isLayoutNeeded() + " dw=" + dw + " dh=" + dh);
}
- mService.mPolicy.beginLayoutLw(mDisplayId, dw, dh, mRotation, getConfiguration().uiMode);
+ mDisplayFrames.onDisplayInfoUpdated(mDisplayInfo);
+ // TODO: Not sure if we really need to set the rotation here since we are updating from the
+ // display info above...
+ mDisplayFrames.mRotation = mRotation;
+ mService.mPolicy.beginLayoutLw(mDisplayFrames, getConfiguration().uiMode);
if (isDefaultDisplay) {
// Not needed on non-default displays.
mService.mSystemDecorLayer = mService.mPolicy.getSystemDecorLayerLw();
mService.mScreenRect.set(0, 0, dw, dh);
}
- mService.mPolicy.getContentRectLw(mContentRect);
-
int seq = mService.mLayoutSeq + 1;
if (seq < 0) seq = 0;
mService.mLayoutSeq = seq;
@@ -2915,7 +2931,6 @@
mService.mInputMonitor.updateInputWindowsLw(false /*force*/);
}
- mService.mPolicy.finishLayoutLw();
mService.mH.sendEmptyMessage(UPDATE_DOCKED_STACK_DIVIDER);
}
diff --git a/services/core/java/com/android/server/wm/DockedStackDividerController.java b/services/core/java/com/android/server/wm/DockedStackDividerController.java
index 5ea0e1d..e693e5a 100644
--- a/services/core/java/com/android/server/wm/DockedStackDividerController.java
+++ b/services/core/java/com/android/server/wm/DockedStackDividerController.java
@@ -23,6 +23,7 @@
import static android.view.Surface.ROTATION_270;
import static android.view.Surface.ROTATION_90;
import static android.view.WindowManager.DOCKED_BOTTOM;
+import static android.view.WindowManager.DOCKED_INVALID;
import static android.view.WindowManager.DOCKED_LEFT;
import static android.view.WindowManager.DOCKED_RIGHT;
import static android.view.WindowManager.DOCKED_TOP;
@@ -116,6 +117,7 @@
private final DimLayer mDimLayer;
private boolean mMinimizedDock;
+ private int mOriginalDockedSide = DOCKED_INVALID;
private boolean mAnimatingForMinimizedDockedStack;
private boolean mAnimationStarted;
private long mAnimationStartTime;
@@ -408,6 +410,31 @@
mDockedStackListeners.finishBroadcast();
}
+ /**
+ * Checks if the primary stack is allowed to dock to a specific side based on its original dock
+ * side.
+ *
+ * @param dockSide the side to see if it is valid
+ * @return true if the side provided is valid
+ */
+ boolean canPrimaryStackDockTo(int dockSide) {
+ if (mService.mPolicy.isDockSideAllowed(dockSide)) {
+ // Side is the same as original side
+ if (dockSide == mOriginalDockedSide) {
+ return true;
+ }
+ // Special rule that the top in portrait is always valid
+ if (dockSide == DOCKED_TOP) {
+ return true;
+ }
+ // Only if original docked side was top in portrait will allow left side for landscape
+ if (dockSide == DOCKED_LEFT && mOriginalDockedSide == DOCKED_TOP) {
+ return true;
+ }
+ }
+ return false;
+ }
+
void notifyDockedStackExistsChanged(boolean exists) {
// TODO(multi-display): Perform all actions only for current display.
final int size = mDockedStackListeners.beginBroadcast();
@@ -430,8 +457,11 @@
inputMethodManagerInternal.hideCurrentInputMethod();
mImeHideRequested = true;
}
+ final TaskStack stack = mDisplayContent.getSplitScreenPrimaryStackIgnoringVisibility();
+ mOriginalDockedSide = stack.getDockSide();
return;
}
+ mOriginalDockedSide = DOCKED_INVALID;
setMinimizedDockedStack(false /* minimizedDock */, false /* animate */);
}
diff --git a/services/core/java/com/android/server/wm/DragDropController.java b/services/core/java/com/android/server/wm/DragDropController.java
index 76e25ba..4567e10 100644
--- a/services/core/java/com/android/server/wm/DragDropController.java
+++ b/services/core/java/com/android/server/wm/DragDropController.java
@@ -21,6 +21,7 @@
import static com.android.server.wm.WindowManagerDebugConfig.SHOW_TRANSACTIONS;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
+import android.annotation.NonNull;
import android.content.ClipData;
import android.graphics.PixelFormat;
import android.os.Binder;
@@ -35,6 +36,8 @@
import android.view.SurfaceControl;
import android.view.SurfaceSession;
import android.view.View;
+import android.view.WindowManagerInternal.IDragDropCallback;
+import com.android.internal.util.Preconditions;
import com.android.server.input.InputWindowHandle;
/**
@@ -61,6 +64,7 @@
private WindowManagerService mService;
private final Handler mHandler;
+
/**
* Lock to preserve the order of state updates.
* The lock is used to process drag and drop state updates in order without having the window
@@ -94,6 +98,11 @@
*/
private final Object mWriteLock = new Object();
+ /**
+ * Callback which is used to sync drag state with the vendor-specific code.
+ */
+ @NonNull private IDragDropCallback mCallback = new IDragDropCallback() {};
+
boolean dragDropActiveLocked() {
return mDragState != null;
}
@@ -102,6 +111,13 @@
return mDragState.getInputWindowHandle();
}
+ void registerCallback(IDragDropCallback callback) {
+ Preconditions.checkNotNull(callback);
+ synchronized (mWriteLock) {
+ mCallback = callback;
+ }
+ }
+
DragDropController(WindowManagerService service, Looper looper) {
mService = service;
mHandler = new DragHandler(service, looper);
@@ -169,6 +185,10 @@
}
synchronized (mWriteLock) {
+ if (!mCallback.performDrag(window, dragToken, touchSource, touchX, touchY, thumbCenterX,
+ thumbCenterY, data)) {
+ return false;
+ }
synchronized (mService.mWindowMap) {
if (mDragState == null) {
Slog.w(TAG_WM, "No drag prepared");
@@ -251,6 +271,7 @@
}
synchronized (mWriteLock) {
+ mCallback.reportDropResult(window, consumed);
synchronized (mService.mWindowMap) {
if (mDragState == null) {
// Most likely the drop recipient ANRed and we ended the drag
@@ -288,6 +309,7 @@
}
synchronized (mWriteLock) {
+ mCallback.cancelDragAndDrop(dragToken);
synchronized (mService.mWindowMap) {
if (mDragState == null) {
Slog.w(TAG_WM, "cancelDragAndDrop() without prepareDrag()");
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index bcb6e673..f541926 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -247,7 +247,7 @@
if (mService.mDisplayManagerInternal != null) {
mService.mDisplayManagerInternal.setDisplayInfoOverrideFromWindowManager(
displayId, displayInfo);
- mService.configureDisplayPolicyLocked(dc);
+ dc.configureDisplayPolicy();
// Tap Listeners are supported for:
// 1. All physical displays (multi-display).
diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java
index 6e89e3e..dde7946 100644
--- a/services/core/java/com/android/server/wm/TaskStack.java
+++ b/services/core/java/com/android/server/wm/TaskStack.java
@@ -442,7 +442,7 @@
mTmpRect2.set(mBounds);
mDisplayContent.rotateBounds(mRotation, newRotation, mTmpRect2);
if (inSplitScreenPrimaryWindowingMode()) {
- repositionDockedStackAfterRotation(mTmpRect2);
+ repositionPrimarySplitScreenStackAfterRotation(mTmpRect2);
snapDockedStackAfterRotation(mTmpRect2);
final int newDockSide = getDockSide(mTmpRect2);
@@ -466,14 +466,14 @@
}
/**
- * Some dock sides are not allowed by the policy. This method queries the policy and moves
- * the docked stack around if needed.
+ * Some primary split screen sides are not allowed by the policy. This method queries the policy
+ * and moves the primary stack around if needed.
*
- * @param inOutBounds the bounds of the docked stack to adjust
+ * @param inOutBounds the bounds of the primary stack to adjust
*/
- private void repositionDockedStackAfterRotation(Rect inOutBounds) {
+ private void repositionPrimarySplitScreenStackAfterRotation(Rect inOutBounds) {
int dockSide = getDockSide(inOutBounds);
- if (mService.mPolicy.isDockSideAllowed(dockSide)) {
+ if (mDisplayContent.getDockedDividerController().canPrimaryStackDockTo(dockSide)) {
return;
}
mDisplayContent.getLogicalDisplayRect(mTmpRect);
@@ -603,6 +603,14 @@
} else {
maxPosition = computeMaxPosition(maxPosition);
}
+
+ // preserve POSITION_BOTTOM/POSITION_TOP positions if they are still valid.
+ if (targetPosition == POSITION_BOTTOM && minPosition == 0) {
+ return POSITION_BOTTOM;
+ } else if (targetPosition == POSITION_TOP
+ && maxPosition == (addingNew ? stackSize : stackSize - 1)) {
+ return POSITION_TOP;
+ }
// Reset position based on minimum/maximum possible positions.
return Math.min(Math.max(targetPosition, minPosition), maxPosition);
}
diff --git a/services/core/java/com/android/server/wm/TaskWindowContainerController.java b/services/core/java/com/android/server/wm/TaskWindowContainerController.java
index 65f8cdf..b3bb0b7 100644
--- a/services/core/java/com/android/server/wm/TaskWindowContainerController.java
+++ b/services/core/java/com/android/server/wm/TaskWindowContainerController.java
@@ -103,6 +103,10 @@
}
}
+ public void positionChildAtTop(AppWindowContainerController childController) {
+ positionChildAt(childController, POSITION_TOP);
+ }
+
public void positionChildAt(AppWindowContainerController childController, int position) {
synchronized(mService.mWindowMap) {
final AppWindowToken aToken = childController.mContainer;
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index f313f31..df51be1 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -125,6 +125,7 @@
import android.app.AppOpsManager;
import android.app.IActivityManager;
import android.content.BroadcastReceiver;
+import android.content.ClipData;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
@@ -244,6 +245,7 @@
import com.android.server.UiThread;
import com.android.server.Watchdog;
import com.android.server.input.InputManagerService;
+import android.view.DisplayFrames;
import com.android.server.power.ShutdownThread;
import com.android.server.utils.PriorityDump;
@@ -568,6 +570,8 @@
boolean mForceResizableTasks = false;
boolean mSupportsPictureInPicture = false;
+ private boolean mDisableTransitionAnimation = false;
+
int getDragLayerLocked() {
return mPolicy.getWindowLayerFromTypeLw(TYPE_DRAG) * TYPE_LAYER_MULTIPLIER + TYPE_LAYER_OFFSET;
}
@@ -965,6 +969,8 @@
com.android.internal.R.bool.config_allowAnimationsInLowPowerMode);
mMaxUiWidth = context.getResources().getInteger(
com.android.internal.R.integer.config_maxUiWidth);
+ mDisableTransitionAnimation = context.getResources().getBoolean(
+ com.android.internal.R.bool.config_disableTransitionAnimation);
mInputManager = inputManager; // Must be before createDisplayContentLocked.
mDisplayManagerInternal = LocalServices.getService(DisplayManagerInternal.class);
mDisplaySettings = new DisplaySettings();
@@ -1447,23 +1453,19 @@
prepareNoneTransitionForRelaunching(atoken);
}
- if (displayContent.isDefaultDisplay) {
- final DisplayInfo displayInfo = displayContent.getDisplayInfo();
- final Rect taskBounds;
- if (atoken != null && atoken.getTask() != null) {
- taskBounds = mTmpRect;
- atoken.getTask().getBounds(mTmpRect);
- } else {
- taskBounds = null;
- }
- if (mPolicy.getInsetHintLw(win.mAttrs, taskBounds, displayInfo.rotation,
- displayInfo.logicalWidth, displayInfo.logicalHeight, outContentInsets,
- outStableInsets, outOutsets)) {
- res |= WindowManagerGlobal.ADD_FLAG_ALWAYS_CONSUME_NAV_BAR;
- }
+ final DisplayFrames displayFrames = displayContent.mDisplayFrames;
+ // TODO: Not sure if onDisplayInfoUpdated() call is needed.
+ displayFrames.onDisplayInfoUpdated(displayContent.getDisplayInfo());
+ final Rect taskBounds;
+ if (atoken != null && atoken.getTask() != null) {
+ taskBounds = mTmpRect;
+ atoken.getTask().getBounds(mTmpRect);
} else {
- outContentInsets.setEmpty();
- outStableInsets.setEmpty();
+ taskBounds = null;
+ }
+ if (mPolicy.getInsetHintLw(win.mAttrs, taskBounds, displayFrames, outContentInsets,
+ outStableInsets, outOutsets)) {
+ res |= WindowManagerGlobal.ADD_FLAG_ALWAYS_CONSUME_NAV_BAR;
}
if (mInTouchMode) {
@@ -2278,6 +2280,14 @@
boolean applyAnimationLocked(AppWindowToken atoken, WindowManager.LayoutParams lp,
int transit, boolean enter, boolean isVoiceInteraction) {
+ if (mDisableTransitionAnimation) {
+ if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) {
+ Slog.v(TAG_WM,
+ "applyAnimation: transition animation is disabled. atoken=" + atoken);
+ }
+ atoken.mAppAnimator.clearAnimation();
+ return false;
+ }
// Only apply an animation if the display isn't frozen. If it is
// frozen, there is no reason to animate and it can cause strange
// artifacts when we unfreeze the display if some different animation
@@ -2916,10 +2926,9 @@
}
public void setKeyguardGoingAway(boolean keyguardGoingAway) {
-// TODO: Use of this can be removed. Revert ag/I8369723d6a77f2c602f1ef080371fa7cd9ee094e
-// synchronized (mWindowMap) {
-// mKeyguardGoingAway = keyguardGoingAway;
-// }
+ synchronized (mWindowMap) {
+ mKeyguardGoingAway = keyguardGoingAway;
+ }
}
// -------------------------------------------------------------
@@ -4656,7 +4665,7 @@
synchronized(mWindowMap) {
mIsTouchDevice = mContext.getPackageManager().hasSystemFeature(
PackageManager.FEATURE_TOUCHSCREEN);
- configureDisplayPolicyLocked(getDefaultDisplayContentLocked());
+ getDefaultDisplayContentLocked().configureDisplayPolicy();
}
try {
@@ -5513,7 +5522,7 @@
if (!mDisplayReady) {
return;
}
- configureDisplayPolicyLocked(displayContent);
+ displayContent.configureDisplayPolicy();
displayContent.setLayoutNeeded();
final int displayId = displayContent.getDisplayId();
@@ -5534,18 +5543,6 @@
mWindowPlacerLocked.performSurfacePlacement();
}
- void configureDisplayPolicyLocked(DisplayContent displayContent) {
- mPolicy.setInitialDisplaySize(displayContent.getDisplay(),
- displayContent.mBaseDisplayWidth,
- displayContent.mBaseDisplayHeight,
- displayContent.mBaseDisplayDensity);
-
- DisplayInfo displayInfo = displayContent.getDisplayInfo();
- mPolicy.setDisplayOverscan(displayContent.getDisplay(),
- displayInfo.overscanLeft, displayInfo.overscanTop,
- displayInfo.overscanRight, displayInfo.overscanBottom);
- }
-
/**
* Get an array with display ids ordered by focus priority - last items should be given
* focus first. Sparse array just maps position to displayId.
@@ -7278,11 +7275,6 @@
}
@Override
- public boolean isKeyguardGoingAway() {
- return WindowManagerService.this.mKeyguardGoingAway;
- }
-
- @Override
public boolean isKeyguardShowingAndNotOccluded() {
return WindowManagerService.this.isKeyguardShowingAndNotOccluded();
}
@@ -7363,7 +7355,9 @@
@Override
public int getInputMethodWindowVisibleHeight() {
synchronized (mWindowMap) {
- return mPolicy.getInputMethodWindowVisibleHeightLw();
+ // TODO(multi-display): Have caller pass in the display they are interested in.
+ final DisplayContent dc = getDefaultDisplayContentLocked();
+ return dc.mDisplayFrames.getInputMethodWindowVisibleHeight();
}
}
@@ -7447,6 +7441,11 @@
mVr2dDisplayId = vr2dDisplayId;
}
}
+
+ @Override
+ public void registerDragDropControllerCallback(IDragDropCallback callback) {
+ mDragDropController.registerCallback(callback);
+ }
}
void registerAppFreezeListener(AppFreezeListener listener) {
diff --git a/services/core/jni/com_android_server_UsbDescriptorParser.cpp b/services/core/jni/com_android_server_UsbDescriptorParser.cpp
index 35e65bc..df85e31 100644
--- a/services/core/jni/com_android_server_UsbDescriptorParser.cpp
+++ b/services/core/jni/com_android_server_UsbDescriptorParser.cpp
@@ -17,18 +17,16 @@
#define LOG_TAG "UsbHostManagerJNI"
#include "utils/Log.h"
-#include <stdlib.h>
-
#include "jni.h"
#include <nativehelper/JNIHelp.h>
#include <usbhost/usbhost.h>
-#define MAX_DESCRIPTORS_LENGTH 4096
+#define MAX_DESCRIPTORS_LENGTH 16384
// com.android.server.usb.descriptors
extern "C" {
-jbyteArray JNICALL Java_com_android_server_usb_descriptors_UsbDescriptorParser_getRawDescriptors_1native(
+jbyteArray JNICALL Java_com_android_server_usb_descriptors_UsbDescriptorParser_getRawDescriptors(
JNIEnv* env, jobject thiz, jstring deviceAddr) {
const char *deviceAddrStr = env->GetStringUTFChars(deviceAddr, NULL);
struct usb_device* device = usb_device_open(deviceAddrStr);
@@ -41,7 +39,6 @@
int fd = usb_device_get_fd(device);
if (fd < 0) {
- usb_device_close(device);
return NULL;
}
@@ -49,48 +46,17 @@
jbyte buffer[MAX_DESCRIPTORS_LENGTH];
lseek(fd, 0, SEEK_SET);
int numBytes = read(fd, buffer, sizeof(buffer));
- jbyteArray ret = NULL;
+
usb_device_close(device);
- if (numBytes > 0) {
+ jbyteArray ret = NULL;
+ if (numBytes != 0) {
ret = env->NewByteArray(numBytes);
env->SetByteArrayRegion(ret, 0, numBytes, buffer);
- } else {
- ALOGE("error reading descriptors\n");
}
-
return ret;
}
-jstring JNICALL Java_com_android_server_usb_descriptors_UsbDescriptorParser_getDescriptorString_1native(
- JNIEnv* env, jobject thiz, jstring deviceAddr, jint stringId) {
-
- const char *deviceAddrStr = env->GetStringUTFChars(deviceAddr, NULL);
- struct usb_device* device = usb_device_open(deviceAddrStr);
- env->ReleaseStringUTFChars(deviceAddr, deviceAddrStr);
-
- if (!device) {
- ALOGE("usb_device_open failed");
- return NULL;
- }
-
- int fd = usb_device_get_fd(device);
- if (fd < 0) {
- ALOGE("usb_device_get_fd failed");
- usb_device_close(device);
- return NULL;
- }
-
- char* c_str = usb_device_get_string(device, stringId, 0 /*timeout*/);
-
- jstring j_str = env->NewStringUTF(c_str);
-
- free(c_str);
- usb_device_close(device);
-
- return j_str;
-}
-
} // extern "C"
diff --git a/services/core/jni/com_android_server_UsbHostManager.cpp b/services/core/jni/com_android_server_UsbHostManager.cpp
index 24f2014..88ae824 100644
--- a/services/core/jni/com_android_server_UsbHostManager.cpp
+++ b/services/core/jni/com_android_server_UsbHostManager.cpp
@@ -22,6 +22,8 @@
#include "android_runtime/AndroidRuntime.h"
#include "android_runtime/Log.h"
+#include <usbhost/usbhost.h>
+
#include <stdio.h>
#include <asm/byteorder.h>
#include <sys/types.h>
@@ -29,20 +31,22 @@
#include <fcntl.h>
#include <sys/ioctl.h>
-#include <usbhost/usbhost.h>
-
-#define MAX_DESCRIPTORS_LENGTH 4096
-
namespace android
{
+static const int USB_CONTROL_TRANSFER_TIMEOUT_MS = 200;
+
static struct parcel_file_descriptor_offsets_t
{
jclass mClass;
jmethodID mConstructor;
} gParcelFileDescriptorOffsets;
-static jmethodID method_usbDeviceAdded;
+static jmethodID method_beginUsbDeviceAdded;
+static jmethodID method_addUsbConfiguration;
+static jmethodID method_addUsbInterface;
+static jmethodID method_addUsbEndpoint;
+static jmethodID method_endUsbDeviceAdded;
static jmethodID method_usbDeviceRemoved;
static void checkAndClearExceptionFromCallback(JNIEnv* env, const char* methodName) {
@@ -53,52 +57,101 @@
}
}
-static int usb_device_added(const char *devAddress, void* clientData) {
- struct usb_device *device = usb_device_open(devAddress);
+static int usb_device_added(const char *devname, void* client_data) {
+ struct usb_descriptor_header* desc;
+ struct usb_descriptor_iter iter;
+
+ struct usb_device *device = usb_device_open(devname);
if (!device) {
ALOGE("usb_device_open failed\n");
return 0;
}
+ JNIEnv* env = AndroidRuntime::getJNIEnv();
+ jobject thiz = (jobject)client_data;
const usb_device_descriptor* deviceDesc = usb_device_get_device_descriptor(device);
- int classID = deviceDesc->bDeviceClass;
- int subClassID = deviceDesc->bDeviceSubClass;
- // get the raw descriptors
- int numBytes = usb_device_get_descriptors_length(device);
- if (numBytes > 0) {
- JNIEnv* env = AndroidRuntime::getJNIEnv();
- jobject thiz = (jobject)clientData;
- jstring deviceAddress = env->NewStringUTF(devAddress);
+ char *manufacturer = usb_device_get_manufacturer_name(device,
+ USB_CONTROL_TRANSFER_TIMEOUT_MS);
+ char *product = usb_device_get_product_name(device,
+ USB_CONTROL_TRANSFER_TIMEOUT_MS);
+ int version = usb_device_get_version(device);
+ char *serial = usb_device_get_serial(device,
+ USB_CONTROL_TRANSFER_TIMEOUT_MS);
- jbyteArray descriptorsArray = env->NewByteArray(numBytes);
- const jbyte* rawDescriptors = (const jbyte*)usb_device_get_raw_descriptors(device);
- env->SetByteArrayRegion(descriptorsArray, 0, numBytes, rawDescriptors);
+ jstring deviceName = env->NewStringUTF(devname);
+ jstring manufacturerName = AndroidRuntime::NewStringLatin1(env, manufacturer);
+ jstring productName = AndroidRuntime::NewStringLatin1(env, product);
+ jstring serialNumber = AndroidRuntime::NewStringLatin1(env, serial);
- env->CallBooleanMethod(thiz, method_usbDeviceAdded,
- deviceAddress, classID, subClassID, descriptorsArray);
+ jboolean result = env->CallBooleanMethod(thiz, method_beginUsbDeviceAdded,
+ deviceName, usb_device_get_vendor_id(device), usb_device_get_product_id(device),
+ deviceDesc->bDeviceClass, deviceDesc->bDeviceSubClass, deviceDesc->bDeviceProtocol,
+ manufacturerName, productName, version, serialNumber);
- env->DeleteLocalRef(descriptorsArray);
- env->DeleteLocalRef(deviceAddress);
+ env->DeleteLocalRef(serialNumber);
+ env->DeleteLocalRef(productName);
+ env->DeleteLocalRef(manufacturerName);
+ env->DeleteLocalRef(deviceName);
+ free(manufacturer);
+ free(product);
+ free(serial);
- checkAndClearExceptionFromCallback(env, __FUNCTION__);
- } else {
- // TODO return an error code here?
- ALOGE("error reading descriptors\n");
+ if (!result) goto fail;
+
+ usb_descriptor_iter_init(device, &iter);
+
+ while ((desc = usb_descriptor_iter_next(&iter)) != NULL) {
+ if (desc->bDescriptorType == USB_DT_CONFIG) {
+ struct usb_config_descriptor *config = (struct usb_config_descriptor *)desc;
+ char *name = usb_device_get_string(device, config->iConfiguration,
+ USB_CONTROL_TRANSFER_TIMEOUT_MS);
+ jstring configName = AndroidRuntime::NewStringLatin1(env, name);
+
+ env->CallVoidMethod(thiz, method_addUsbConfiguration,
+ config->bConfigurationValue, configName, config->bmAttributes,
+ config->bMaxPower);
+
+ env->DeleteLocalRef(configName);
+ free(name);
+ } else if (desc->bDescriptorType == USB_DT_INTERFACE) {
+ struct usb_interface_descriptor *interface = (struct usb_interface_descriptor *)desc;
+ char *name = usb_device_get_string(device, interface->iInterface,
+ USB_CONTROL_TRANSFER_TIMEOUT_MS);
+ jstring interfaceName = AndroidRuntime::NewStringLatin1(env, name);
+
+ env->CallVoidMethod(thiz, method_addUsbInterface,
+ interface->bInterfaceNumber, interfaceName, interface->bAlternateSetting,
+ interface->bInterfaceClass, interface->bInterfaceSubClass,
+ interface->bInterfaceProtocol);
+
+ env->DeleteLocalRef(interfaceName);
+ free(name);
+ } else if (desc->bDescriptorType == USB_DT_ENDPOINT) {
+ struct usb_endpoint_descriptor *endpoint = (struct usb_endpoint_descriptor *)desc;
+
+ env->CallVoidMethod(thiz, method_addUsbEndpoint,
+ endpoint->bEndpointAddress, endpoint->bmAttributes,
+ __le16_to_cpu(endpoint->wMaxPacketSize), endpoint->bInterval);
+ }
}
+ env->CallVoidMethod(thiz, method_endUsbDeviceAdded);
+
+fail:
usb_device_close(device);
+ checkAndClearExceptionFromCallback(env, __FUNCTION__);
return 0;
}
-static int usb_device_removed(const char *devAddress, void* clientData) {
+static int usb_device_removed(const char *devname, void* client_data) {
JNIEnv* env = AndroidRuntime::getJNIEnv();
- jobject thiz = (jobject)clientData;
+ jobject thiz = (jobject)client_data;
- jstring deviceAddress = env->NewStringUTF(devAddress);
- env->CallVoidMethod(thiz, method_usbDeviceRemoved, deviceAddress);
- env->DeleteLocalRef(deviceAddress);
+ jstring deviceName = env->NewStringUTF(devname);
+ env->CallVoidMethod(thiz, method_usbDeviceRemoved, deviceName);
+ env->DeleteLocalRef(deviceName);
checkAndClearExceptionFromCallback(env, __FUNCTION__);
return 0;
}
@@ -115,11 +168,11 @@
}
static jobject android_server_UsbHostManager_openDevice(JNIEnv *env, jobject /* thiz */,
- jstring deviceAddress)
+ jstring deviceName)
{
- const char *deviceAddressStr = env->GetStringUTFChars(deviceAddress, NULL);
- struct usb_device* device = usb_device_open(deviceAddressStr);
- env->ReleaseStringUTFChars(deviceAddress, deviceAddressStr);
+ const char *deviceNameStr = env->GetStringUTFChars(deviceName, NULL);
+ struct usb_device* device = usb_device_open(deviceNameStr);
+ env->ReleaseStringUTFChars(deviceName, deviceNameStr);
if (!device)
return NULL;
@@ -153,12 +206,34 @@
ALOGE("Can't find com/android/server/usb/UsbHostManager");
return -1;
}
- method_usbDeviceAdded =
- env->GetMethodID(clazz, "usbDeviceAdded", "(Ljava/lang/String;II[B)Z");
- if (method_usbDeviceAdded == NULL) {
+ method_beginUsbDeviceAdded = env->GetMethodID(clazz, "beginUsbDeviceAdded",
+ "(Ljava/lang/String;IIIIILjava/lang/String;Ljava/lang/String;ILjava/lang/String;)Z");
+ if (method_beginUsbDeviceAdded == NULL) {
ALOGE("Can't find beginUsbDeviceAdded");
return -1;
}
+ method_addUsbConfiguration = env->GetMethodID(clazz, "addUsbConfiguration",
+ "(ILjava/lang/String;II)V");
+ if (method_addUsbConfiguration == NULL) {
+ ALOGE("Can't find addUsbConfiguration");
+ return -1;
+ }
+ method_addUsbInterface = env->GetMethodID(clazz, "addUsbInterface",
+ "(ILjava/lang/String;IIII)V");
+ if (method_addUsbInterface == NULL) {
+ ALOGE("Can't find addUsbInterface");
+ return -1;
+ }
+ method_addUsbEndpoint = env->GetMethodID(clazz, "addUsbEndpoint", "(IIII)V");
+ if (method_addUsbEndpoint == NULL) {
+ ALOGE("Can't find addUsbEndpoint");
+ return -1;
+ }
+ method_endUsbDeviceAdded = env->GetMethodID(clazz, "endUsbDeviceAdded", "()V");
+ if (method_endUsbDeviceAdded == NULL) {
+ ALOGE("Can't find endUsbDeviceAdded");
+ return -1;
+ }
method_usbDeviceRemoved = env->GetMethodID(clazz, "usbDeviceRemoved",
"(Ljava/lang/String;)V");
if (method_usbDeviceRemoved == NULL) {
diff --git a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
index 1f1324a..daf3f2f 100644
--- a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
+++ b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
@@ -1207,6 +1207,13 @@
ALOGE("Unable to initialize GNSS NI interface\n");
}
+ sp<IAGnssRilCallback> aGnssRilCbIface = new AGnssRilCallback();
+ if (agnssRilIface != nullptr) {
+ agnssRilIface->setCallback(aGnssRilCbIface);
+ } else {
+ ALOGI("Unable to Initialize AGnss Ril interface\n");
+ }
+
return JNI_TRUE;
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/NetworkLogger.java b/services/devicepolicy/java/com/android/server/devicepolicy/NetworkLogger.java
index 0085931..0aaf32c 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/NetworkLogger.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/NetworkLogger.java
@@ -107,7 +107,8 @@
return false;
}
try {
- if (mIpConnectivityMetrics.registerNetdEventCallback(mNetdEventCallback)) {
+ if (mIpConnectivityMetrics.addNetdEventCallback(
+ INetdEventCallback.CALLBACK_CALLER_DEVICE_POLICY, mNetdEventCallback)) {
mHandlerThread = new ServiceThread(TAG, Process.THREAD_PRIORITY_BACKGROUND,
/* allowIo */ false);
mHandlerThread.start();
@@ -138,7 +139,8 @@
// logging is forcefully disabled even if unregistering fails
return true;
}
- return mIpConnectivityMetrics.unregisterNetdEventCallback();
+ return mIpConnectivityMetrics.removeNetdEventCallback(
+ INetdEventCallback.CALLBACK_CALLER_DEVICE_POLICY);
} catch (RemoteException re) {
Slog.wtf(TAG, "Failed to make remote calls to unregister the callback", re);
return true;
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index f8bcb73..74a7bd4a 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -37,7 +37,6 @@
import android.os.Message;
import android.os.PowerManager;
import android.os.Process;
-import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.StrictMode;
import android.os.SystemClock;
@@ -52,7 +51,7 @@
import android.view.WindowManager;
import com.android.internal.R;
-import com.android.internal.app.NightDisplayController;
+import com.android.internal.app.ColorDisplayController;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.notification.SystemNotificationChannels;
import com.android.internal.os.BinderInternal;
@@ -69,7 +68,7 @@
import com.android.server.coverage.CoverageService;
import com.android.server.devicepolicy.DevicePolicyManagerService;
import com.android.server.display.DisplayManagerService;
-import com.android.server.display.NightDisplayService;
+import com.android.server.display.ColorDisplayService;
import com.android.server.dreams.DreamManagerService;
import com.android.server.emergency.EmergencyAffordanceService;
import com.android.server.fingerprint.FingerprintService;
@@ -83,6 +82,7 @@
import com.android.server.media.projection.MediaProjectionManagerService;
import com.android.server.net.NetworkPolicyManagerService;
import com.android.server.net.NetworkStatsService;
+import com.android.server.net.watchlist.NetworkWatchlistService;
import com.android.server.notification.NotificationManagerService;
import com.android.server.oemlock.OemLockService;
import com.android.server.om.OverlayManagerService;
@@ -95,6 +95,7 @@
import com.android.server.pm.PackageManagerService;
import com.android.server.pm.ShortcutService;
import com.android.server.pm.UserManagerService;
+import com.android.server.pm.crossprofile.CrossProfileAppsService;
import com.android.server.policy.PhoneWindowManager;
import com.android.server.power.PowerManagerService;
import com.android.server.power.ShutdownThread;
@@ -884,6 +885,10 @@
mSystemServiceManager.startService(IpConnectivityMetrics.class);
traceEnd();
+ traceBeginAndSlog("NetworkWatchlistService");
+ mSystemServiceManager.startService(NetworkWatchlistService.Lifecycle.class);
+ traceEnd();
+
traceBeginAndSlog("PinnerService");
mSystemServiceManager.startService(PinnerService.class);
traceEnd();
@@ -1277,9 +1282,9 @@
mSystemServiceManager.startService(TwilightService.class);
traceEnd();
- if (NightDisplayController.isAvailable(context)) {
+ if (ColorDisplayController.isAvailable(context)) {
traceBeginAndSlog("StartNightDisplay");
- mSystemServiceManager.startService(NightDisplayService.class);
+ mSystemServiceManager.startService(ColorDisplayService.class);
traceEnd();
}
@@ -1485,6 +1490,10 @@
traceBeginAndSlog("StartLauncherAppsService");
mSystemServiceManager.startService(LauncherAppsService.class);
traceEnd();
+
+ traceBeginAndSlog("StartCrossProfileAppsService");
+ mSystemServiceManager.startService(CrossProfileAppsService.class);
+ traceEnd();
}
if (!disableMediaProjection) {
diff --git a/services/print/java/com/android/server/print/UserState.java b/services/print/java/com/android/server/print/UserState.java
index 58833be..e8ae020 100644
--- a/services/print/java/com/android/server/print/UserState.java
+++ b/services/print/java/com/android/server/print/UserState.java
@@ -597,6 +597,7 @@
PrintJobStateChangeListenerRecord record =
mPrintJobStateChangeListenerRecords.get(i);
if (record.listener.asBinder().equals(listener.asBinder())) {
+ record.destroy();
mPrintJobStateChangeListenerRecords.remove(i);
break;
}
@@ -639,6 +640,7 @@
ListenerRecord<IPrintServicesChangeListener> record =
mPrintServicesChangeListenerRecords.get(i);
if (record.listener.asBinder().equals(listener.asBinder())) {
+ record.destroy();
mPrintServicesChangeListenerRecords.remove(i);
break;
}
@@ -686,6 +688,7 @@
ListenerRecord<IRecommendationsChangeListener> record =
mPrintServiceRecommendationsChangeListenerRecords.get(i);
if (record.listener.asBinder().equals(listener.asBinder())) {
+ record.destroy();
mPrintServiceRecommendationsChangeListenerRecords.remove(i);
break;
}
@@ -1285,6 +1288,10 @@
listener.asBinder().linkToDeath(this, 0);
}
+ public void destroy() {
+ listener.asBinder().unlinkToDeath(this, 0);
+ }
+
@Override
public void binderDied() {
listener.asBinder().unlinkToDeath(this, 0);
@@ -1302,6 +1309,10 @@
listener.asBinder().linkToDeath(this, 0);
}
+ public void destroy() {
+ listener.asBinder().unlinkToDeath(this, 0);
+ }
+
@Override
public void binderDied() {
listener.asBinder().unlinkToDeath(this, 0);
diff --git a/services/robotests/Android.mk b/services/robotests/Android.mk
index 322b891..f63d1e7 100644
--- a/services/robotests/Android.mk
+++ b/services/robotests/Android.mk
@@ -26,13 +26,8 @@
LOCAL_PRIVILEGED_MODULE := true
LOCAL_STATIC_JAVA_LIBRARIES := \
- frameworks-base-testutils \
services.backup \
- services.core \
- android-support-test \
- mockito-target-minus-junit4 \
- platform-test-annotations \
- truth-prebuilt
+ services.core
include $(BUILD_PACKAGE)
@@ -45,8 +40,12 @@
# Include the testing libraries (JUnit4 + Robolectric libs).
LOCAL_STATIC_JAVA_LIBRARIES := \
+ android-support-test \
+ mockito-robolectric-prebuilt \
+ platform-test-annotations \
truth-prebuilt
+# TODO(b/69254249): Migrate to Robolectric 3.4.2
LOCAL_JAVA_LIBRARIES := \
junit \
platform-robolectric-prebuilt
@@ -74,4 +73,5 @@
LOCAL_INSTRUMENT_SOURCE_DIRS := $(dir $(LOCAL_PATH))backup/java
-include prebuilts/misc/common/robolectric/run_robotests.mk
+# TODO(b/69254249): Migrate to Robolectric 3.4.2
+include prebuilts/misc/common/robolectric/3.1.1/run_robotests.mk
diff --git a/services/tests/notification/src/com/android/server/notification/ZenModeHelperTest.java b/services/tests/notification/src/com/android/server/notification/ZenModeHelperTest.java
index cbe9650..8ac6481 100644
--- a/services/tests/notification/src/com/android/server/notification/ZenModeHelperTest.java
+++ b/services/tests/notification/src/com/android/server/notification/ZenModeHelperTest.java
@@ -17,7 +17,7 @@
package com.android.server.notification;
import static junit.framework.Assert.assertFalse;
-import static junit.framework.Assert.assertTrue;
+import static junit.framework.TestCase.assertTrue;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
@@ -100,5 +100,34 @@
AudioAttributes.USAGE_GAME);
verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(true,
AudioAttributes.USAGE_ASSISTANCE_SONIFICATION);
+ verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(true,
+ AudioAttributes.USAGE_UNKNOWN);
+ }
+
+ @Test
+ public void testZenAllCannotBypass() {
+ // Only audio attributes with SUPPRESIBLE_NEVER can bypass
+ mZenModeHelperSpy.mZenMode = Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
+ mZenModeHelperSpy.mConfig.allowAlarms = false;
+ mZenModeHelperSpy.mConfig.allowMediaSystemOther = false;
+ mZenModeHelperSpy.mConfig.allowReminders = false;
+ mZenModeHelperSpy.mConfig.allowCalls = false;
+ mZenModeHelperSpy.mConfig.allowMessages = false;
+ mZenModeHelperSpy.mConfig.allowEvents = false;
+ mZenModeHelperSpy.mConfig.allowRepeatCallers= false;
+ assertFalse(mZenModeHelperSpy.mConfig.allowAlarms);
+ assertFalse(mZenModeHelperSpy.mConfig.allowMediaSystemOther);
+ assertFalse(mZenModeHelperSpy.mConfig.allowReminders);
+ assertFalse(mZenModeHelperSpy.mConfig.allowCalls);
+ assertFalse(mZenModeHelperSpy.mConfig.allowMessages);
+ assertFalse(mZenModeHelperSpy.mConfig.allowEvents);
+ assertFalse(mZenModeHelperSpy.mConfig.allowRepeatCallers);
+ mZenModeHelperSpy.applyRestrictions();
+
+ for (int usage : AudioAttributes.SDK_USAGES) {
+ boolean shouldMute = AudioAttributes.SUPPRESSIBLE_USAGES.get(usage)
+ != AudioAttributes.SUPPRESSIBLE_NEVER;
+ verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(shouldMute, usage);
+ }
}
}
diff --git a/services/tests/servicestests/Android.mk b/services/tests/servicestests/Android.mk
index 8e41a55..a2ec234 100644
--- a/services/tests/servicestests/Android.mk
+++ b/services/tests/servicestests/Android.mk
@@ -25,7 +25,8 @@
mockito-target-minus-junit4 \
platform-test-annotations \
ShortcutManagerTestUtils \
- truth-prebuilt
+ truth-prebuilt \
+ testng
LOCAL_AIDL_INCLUDES := $(LOCAL_PATH)/aidl
diff --git a/services/tests/servicestests/assets/NetworkWatchlistTest/watchlist_settings_test1.xml b/services/tests/servicestests/assets/NetworkWatchlistTest/watchlist_settings_test1.xml
new file mode 100644
index 0000000..bb97e94
--- /dev/null
+++ b/services/tests/servicestests/assets/NetworkWatchlistTest/watchlist_settings_test1.xml
@@ -0,0 +1,27 @@
+<?xml version='1.0'?>
+<watchlist-settings>
+ <sha256-domain>
+ <!-- test-cc-domain.com -->
+ <hash>8E7DCD2AEB4F364358242BB3F403263E61E3B4AECE4E2500FF28BF32E52FF0F1</hash>
+ <!-- test-cc-match-sha256-only.com -->
+ <hash>F0905DA7549614957B449034C281EF7BDEFDBC2B6E050AD1E78D6DE18FBD0D5F</hash>
+ </sha256-domain>
+ <sha256-ip>
+ <!-- 127.0.0.2 -->
+ <hash>1EDD62868F2767A1FFF68DF0A4CB3C23448E45100715768DB9310B5E719536A1</hash>
+ <!-- 127.0.0.3, match in sha256 only -->
+ <hash>18DD41C9F2E8E4879A1575FB780514EF33CF6E1F66578C4AE7CCA31F49B9F2ED</hash>
+ </sha256-ip>
+ <crc32-domain>
+ <!-- test-cc-domain.com -->
+ <hash>6C67059D</hash>
+ <!-- test-cc-match-crc32-only.com -->
+ <hash>3DC775F8</hash>
+ </crc32-domain>
+ <crc32-ip>
+ <!-- 127.0.0.2 -->
+ <hash>4EBEB612</hash>
+ <!-- 127.0.0.4, match in crc32 only -->
+ <hash>A7DD1327</hash>
+ </crc32-ip>
+</watchlist-settings>
diff --git a/services/tests/servicestests/src/com/android/server/NightDisplayServiceTest.java b/services/tests/servicestests/src/com/android/server/ColorDisplayServiceTest.java
similarity index 96%
rename from services/tests/servicestests/src/com/android/server/NightDisplayServiceTest.java
rename to services/tests/servicestests/src/com/android/server/ColorDisplayServiceTest.java
index 3a92d63..46b364c 100644
--- a/services/tests/servicestests/src/com/android/server/NightDisplayServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/ColorDisplayServiceTest.java
@@ -29,10 +29,10 @@
import android.support.test.runner.AndroidJUnit4;
import android.test.mock.MockContentResolver;
-import com.android.internal.app.NightDisplayController;
+import com.android.internal.app.ColorDisplayController;
import com.android.internal.util.test.FakeSettingsProvider;
import com.android.server.display.DisplayTransformManager;
-import com.android.server.display.NightDisplayService;
+import com.android.server.display.ColorDisplayService;
import com.android.server.twilight.TwilightListener;
import com.android.server.twilight.TwilightManager;
import com.android.server.twilight.TwilightState;
@@ -55,15 +55,15 @@
import static org.mockito.Mockito.doReturn;
@RunWith(AndroidJUnit4.class)
-public class NightDisplayServiceTest {
+public class ColorDisplayServiceTest {
private Context mContext;
private int mUserId;
private MockTwilightManager mTwilightManager;
- private NightDisplayController mNightDisplayController;
- private NightDisplayService mNightDisplayService;
+ private ColorDisplayController mColorDisplayController;
+ private ColorDisplayService mColorDisplayService;
@Before
public void setUp() {
@@ -85,8 +85,8 @@
mTwilightManager = new MockTwilightManager();
LocalServices.addService(TwilightManager.class, mTwilightManager);
- mNightDisplayController = new NightDisplayController(mContext, mUserId);
- mNightDisplayService = new NightDisplayService(mContext);
+ mColorDisplayController = new ColorDisplayController(mContext, mUserId);
+ mColorDisplayService = new ColorDisplayService(mContext);
}
@After
@@ -94,8 +94,8 @@
LocalServices.removeServiceForTest(DisplayTransformManager.class);
LocalServices.removeServiceForTest(TwilightManager.class);
- mNightDisplayService = null;
- mNightDisplayController = null;
+ mColorDisplayService = null;
+ mColorDisplayController = null;
mTwilightManager = null;
@@ -902,9 +902,9 @@
* @param endTimeOffset the offset relative to now to deactivate Night display (in minutes)
*/
private void setAutoModeCustom(int startTimeOffset, int endTimeOffset) {
- mNightDisplayController.setAutoMode(NightDisplayController.AUTO_MODE_CUSTOM);
- mNightDisplayController.setCustomStartTime(getLocalTimeRelativeToNow(startTimeOffset));
- mNightDisplayController.setCustomEndTime(getLocalTimeRelativeToNow(endTimeOffset));
+ mColorDisplayController.setAutoMode(ColorDisplayController.AUTO_MODE_CUSTOM);
+ mColorDisplayController.setCustomStartTime(getLocalTimeRelativeToNow(startTimeOffset));
+ mColorDisplayController.setCustomEndTime(getLocalTimeRelativeToNow(endTimeOffset));
}
/**
@@ -914,7 +914,7 @@
* @param sunriseOffset the offset relative to now for sunrise (in minutes)
*/
private void setAutoModeTwilight(int sunsetOffset, int sunriseOffset) {
- mNightDisplayController.setAutoMode(NightDisplayController.AUTO_MODE_TWILIGHT);
+ mColorDisplayController.setAutoMode(ColorDisplayController.AUTO_MODE_TWILIGHT);
mTwilightManager.setTwilightState(
getTwilightStateRelativeToNow(sunsetOffset, sunriseOffset));
}
@@ -927,7 +927,7 @@
* activated (in minutes)
*/
private void setActivated(boolean activated, int lastActivatedTimeOffset) {
- mNightDisplayController.setActivated(activated);
+ mColorDisplayController.setActivated(activated);
Secure.putStringForUser(mContext.getContentResolver(),
Secure.NIGHT_DISPLAY_LAST_ACTIVATED_TIME,
LocalDateTime.now().plusMinutes(lastActivatedTimeOffset).toString(),
@@ -935,7 +935,7 @@
}
/**
- * Convenience method to start {@link #mNightDisplayService}.
+ * Convenience method to start {@link #mColorDisplayService}.
*/
private void startService() {
Secure.putIntForUser(mContext.getContentResolver(), Secure.USER_SETUP_COMPLETE, 1, mUserId);
@@ -943,9 +943,9 @@
InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
@Override
public void run() {
- mNightDisplayService.onStart();
- mNightDisplayService.onBootPhase(SystemService.PHASE_BOOT_COMPLETED);
- mNightDisplayService.onStartUser(mUserId);
+ mColorDisplayService.onStart();
+ mColorDisplayService.onBootPhase(SystemService.PHASE_BOOT_COMPLETED);
+ mColorDisplayService.onStartUser(mUserId);
}
});
}
@@ -957,7 +957,7 @@
*/
private void assertActivated(boolean activated) {
assertWithMessage("Invalid Night display activated state")
- .that(mNightDisplayController.isActivated())
+ .that(mColorDisplayController.isActivated())
.isEqualTo(activated);
}
@@ -988,21 +988,21 @@
final LocalDateTime now = LocalDateTime.now();
final ZoneId zoneId = ZoneId.systemDefault();
- long sunsetMillis = NightDisplayService.getDateTimeBefore(sunset, now)
+ long sunsetMillis = ColorDisplayService.getDateTimeBefore(sunset, now)
.atZone(zoneId)
.toInstant()
.toEpochMilli();
- long sunriseMillis = NightDisplayService.getDateTimeBefore(sunrise, now)
+ long sunriseMillis = ColorDisplayService.getDateTimeBefore(sunrise, now)
.atZone(zoneId)
.toInstant()
.toEpochMilli();
if (sunsetMillis < sunriseMillis) {
- sunsetMillis = NightDisplayService.getDateTimeAfter(sunset, now)
+ sunsetMillis = ColorDisplayService.getDateTimeAfter(sunset, now)
.atZone(zoneId)
.toInstant()
.toEpochMilli();
} else {
- sunriseMillis = NightDisplayService.getDateTimeAfter(sunrise, now)
+ sunriseMillis = ColorDisplayService.getDateTimeAfter(sunrise, now)
.atZone(zoneId)
.toInstant()
.toEpochMilli();
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index 9d23fe9..6de3395 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -3193,7 +3193,7 @@
// setUp() adds a secondary user for CALLER_USER_HANDLE. Remove it as otherwise the
// feature is disabled because there are non-affiliated secondary users.
getServices().removeUser(DpmMockContext.CALLER_USER_HANDLE);
- when(getServices().iipConnectivityMetrics.registerNetdEventCallback(anyObject()))
+ when(getServices().iipConnectivityMetrics.addNetdEventCallback(anyInt(), anyObject()))
.thenReturn(true);
// No logs were retrieved so far.
diff --git a/services/tests/servicestests/src/com/android/server/job/JobStoreTest.java b/services/tests/servicestests/src/com/android/server/job/JobStoreTest.java
index 31ed8ba..bf912dd 100644
--- a/services/tests/servicestests/src/com/android/server/job/JobStoreTest.java
+++ b/services/tests/servicestests/src/com/android/server/job/JobStoreTest.java
@@ -1,50 +1,93 @@
package com.android.server.job;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_IMS;
+import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
-import android.content.ComponentName;
-import android.content.Context;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
import android.app.job.JobInfo;
import android.app.job.JobInfo.Builder;
+import android.content.ComponentName;
+import android.content.Context;
+import android.net.NetworkRequest;
+import android.os.Parcel;
+import android.os.Parcelable;
import android.os.PersistableBundle;
import android.os.SystemClock;
-import android.test.AndroidTestCase;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.runner.AndroidJUnit4;
import android.test.RenamingDelegatingContext;
import android.util.Log;
import android.util.Pair;
+import com.android.internal.util.HexDump;
+import com.android.server.IoThread;
import com.android.server.job.JobStore.JobSet;
import com.android.server.job.controllers.JobStatus;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.time.Clock;
+import java.time.ZoneOffset;
+import java.util.Arrays;
import java.util.Iterator;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
/**
* Test reading and writing correctly from file.
*/
-public class JobStoreTest extends AndroidTestCase {
+@RunWith(AndroidJUnit4.class)
+public class JobStoreTest {
private static final String TAG = "TaskStoreTest";
private static final String TEST_PREFIX = "_test_";
- private static final int SOME_UID = 34234;
+ private static final int SOME_UID = android.os.Process.FIRST_APPLICATION_UID;
private ComponentName mComponent;
- private static final long IO_WAIT = 1000L;
JobStore mTaskStoreUnderTest;
Context mTestContext;
- @Override
+ private Context getContext() {
+ return InstrumentationRegistry.getContext();
+ }
+
+ @Before
public void setUp() throws Exception {
mTestContext = new RenamingDelegatingContext(getContext(), TEST_PREFIX);
Log.d(TAG, "Saving tasks to '" + mTestContext.getFilesDir() + "'");
mTaskStoreUnderTest =
JobStore.initAndGetForTesting(mTestContext, mTestContext.getFilesDir());
mComponent = new ComponentName(getContext().getPackageName(), StubClass.class.getName());
+
+ // Freeze the clocks at this moment in time
+ JobSchedulerService.sSystemClock =
+ Clock.fixed(Clock.systemUTC().instant(), ZoneOffset.UTC);
+ JobSchedulerService.sUptimeMillisClock =
+ Clock.fixed(SystemClock.uptimeMillisClock().instant(), ZoneOffset.UTC);
+ JobSchedulerService.sElapsedRealtimeClock =
+ Clock.fixed(SystemClock.elapsedRealtimeClock().instant(), ZoneOffset.UTC);
}
- @Override
+ @After
public void tearDown() throws Exception {
mTaskStoreUnderTest.clear();
}
+ private void waitForPendingIo() throws Exception {
+ final CountDownLatch latch = new CountDownLatch(1);
+ IoThread.getHandler().post(() -> {
+ latch.countDown();
+ });
+ latch.await(10, TimeUnit.SECONDS);
+ }
+
+ @Test
public void testMaybeWriteStatusToDisk() throws Exception {
int taskId = 5;
long runByMillis = 20000L; // 20s
@@ -61,7 +104,8 @@
.build();
final JobStatus ts = JobStatus.createFromJobInfo(task, SOME_UID, null, -1, null);
mTaskStoreUnderTest.add(ts);
- Thread.sleep(IO_WAIT);
+ waitForPendingIo();
+
// Manually load tasks from xml file.
final JobSet jobStatusSet = new JobSet();
mTaskStoreUnderTest.readJobMapFromDisk(jobStatusSet, true);
@@ -75,9 +119,9 @@
ts.getEarliestRunTime(), loadedTaskStatus.getEarliestRunTime());
compareTimestampsSubjectToIoLatency("Late run-times not the same after read.",
ts.getLatestRunTimeElapsed(), loadedTaskStatus.getLatestRunTimeElapsed());
-
}
+ @Test
public void testWritingTwoFilesToDisk() throws Exception {
final JobInfo task1 = new Builder(8, mComponent)
.setRequiresDeviceIdle(true)
@@ -96,7 +140,7 @@
final JobStatus taskStatus2 = JobStatus.createFromJobInfo(task2, SOME_UID, null, -1, null);
mTaskStoreUnderTest.add(taskStatus1);
mTaskStoreUnderTest.add(taskStatus2);
- Thread.sleep(IO_WAIT);
+ waitForPendingIo();
final JobSet jobStatusSet = new JobSet();
mTaskStoreUnderTest.readJobMapFromDisk(jobStatusSet, true);
@@ -125,9 +169,9 @@
taskStatus2.getEarliestRunTime(), loaded2.getEarliestRunTime());
compareTimestampsSubjectToIoLatency("Late run-times not the same after read.",
taskStatus2.getLatestRunTimeElapsed(), loaded2.getLatestRunTimeElapsed());
-
}
+ @Test
public void testWritingTaskWithExtras() throws Exception {
JobInfo.Builder b = new Builder(8, mComponent)
.setRequiresDeviceIdle(true)
@@ -144,7 +188,7 @@
JobStatus taskStatus = JobStatus.createFromJobInfo(task, SOME_UID, null, -1, null);
mTaskStoreUnderTest.add(taskStatus);
- Thread.sleep(IO_WAIT);
+ waitForPendingIo();
final JobSet jobStatusSet = new JobSet();
mTaskStoreUnderTest.readJobMapFromDisk(jobStatusSet, true);
@@ -152,6 +196,8 @@
JobStatus loaded = jobStatusSet.getAllJobs().iterator().next();
assertTasksEqual(task, loaded.getJob());
}
+
+ @Test
public void testWritingTaskWithSourcePackage() throws Exception {
JobInfo.Builder b = new Builder(8, mComponent)
.setRequiresDeviceIdle(true)
@@ -162,7 +208,7 @@
"com.google.android.gms", 0, null);
mTaskStoreUnderTest.add(taskStatus);
- Thread.sleep(IO_WAIT);
+ waitForPendingIo();
final JobSet jobStatusSet = new JobSet();
mTaskStoreUnderTest.readJobMapFromDisk(jobStatusSet, true);
@@ -174,6 +220,7 @@
taskStatus.getSourceUserId());
}
+ @Test
public void testWritingTaskWithFlex() throws Exception {
JobInfo.Builder b = new Builder(8, mComponent)
.setRequiresDeviceIdle(true)
@@ -183,7 +230,7 @@
JobStatus taskStatus = JobStatus.createFromJobInfo(b.build(), SOME_UID, null, -1, null);
mTaskStoreUnderTest.add(taskStatus);
- Thread.sleep(IO_WAIT);
+ waitForPendingIo();
final JobSet jobStatusSet = new JobSet();
mTaskStoreUnderTest.readJobMapFromDisk(jobStatusSet, true);
@@ -195,6 +242,7 @@
taskStatus.getJob().getFlexMillis());
}
+ @Test
public void testMassivePeriodClampedOnRead() throws Exception {
final long ONE_HOUR = 60*60*1000L; // flex
final long TWO_HOURS = 2 * ONE_HOUR; // period
@@ -214,7 +262,7 @@
persistedExecutionTimesUTC);
mTaskStoreUnderTest.add(js);
- Thread.sleep(IO_WAIT);
+ waitForPendingIo();
final JobSet jobStatusSet = new JobSet();
mTaskStoreUnderTest.readJobMapFromDisk(jobStatusSet, true);
@@ -231,6 +279,7 @@
loaded.getEarliestRunTime() <= newNowElapsed + TWO_HOURS + ONE_HOUR);
}
+ @Test
public void testPriorityPersisted() throws Exception {
JobInfo.Builder b = new Builder(92, mComponent)
.setOverrideDeadline(5000)
@@ -238,7 +287,8 @@
.setPersisted(true);
final JobStatus js = JobStatus.createFromJobInfo(b.build(), SOME_UID, null, -1, null);
mTaskStoreUnderTest.add(js);
- Thread.sleep(IO_WAIT);
+ waitForPendingIo();
+
final JobSet jobStatusSet = new JobSet();
mTaskStoreUnderTest.readJobMapFromDisk(jobStatusSet, true);
JobStatus loaded = jobStatusSet.getAllJobs().iterator().next();
@@ -248,6 +298,7 @@
/**
* Test that non persisted job is not written to disk.
*/
+ @Test
public void testNonPersistedTaskIsNotPersisted() throws Exception {
JobInfo.Builder b = new Builder(42, mComponent)
.setOverrideDeadline(10000)
@@ -259,7 +310,8 @@
.setPersisted(true);
JobStatus jsPersisted = JobStatus.createFromJobInfo(b.build(), SOME_UID, null, -1, null);
mTaskStoreUnderTest.add(jsPersisted);
- Thread.sleep(IO_WAIT);
+ waitForPendingIo();
+
final JobSet jobStatusSet = new JobSet();
mTaskStoreUnderTest.readJobMapFromDisk(jobStatusSet, true);
assertEquals("Job count is incorrect.", 1, jobStatusSet.size());
@@ -267,6 +319,62 @@
assertEquals("Wrong job persisted.", 43, jobStatus.getJobId());
}
+ @Test
+ public void testRequiredNetworkType() throws Exception {
+ assertPersistedEquals(new JobInfo.Builder(0, mComponent)
+ .setPersisted(true)
+ .setRequiresDeviceIdle(true)
+ .setRequiredNetworkType(JobInfo.NETWORK_TYPE_NONE).build());
+ assertPersistedEquals(new JobInfo.Builder(0, mComponent)
+ .setPersisted(true)
+ .setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY).build());
+ assertPersistedEquals(new JobInfo.Builder(0, mComponent)
+ .setPersisted(true)
+ .setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED).build());
+ assertPersistedEquals(new JobInfo.Builder(0, mComponent)
+ .setPersisted(true)
+ .setRequiredNetworkType(JobInfo.NETWORK_TYPE_NOT_ROAMING).build());
+ assertPersistedEquals(new JobInfo.Builder(0, mComponent)
+ .setPersisted(true)
+ .setRequiredNetworkType(JobInfo.NETWORK_TYPE_CELLULAR).build());
+ }
+
+ @Test
+ public void testRequiredNetwork() throws Exception {
+ assertPersistedEquals(new JobInfo.Builder(0, mComponent)
+ .setPersisted(true)
+ .setRequiresDeviceIdle(true)
+ .setRequiredNetwork(null).build());
+ assertPersistedEquals(new JobInfo.Builder(0, mComponent)
+ .setPersisted(true)
+ .setRequiredNetwork(new NetworkRequest.Builder().build()).build());
+ assertPersistedEquals(new JobInfo.Builder(0, mComponent)
+ .setPersisted(true)
+ .setRequiredNetwork(new NetworkRequest.Builder()
+ .addTransportType(TRANSPORT_WIFI).build())
+ .build());
+ assertPersistedEquals(new JobInfo.Builder(0, mComponent)
+ .setPersisted(true)
+ .setRequiredNetwork(new NetworkRequest.Builder()
+ .addCapability(NET_CAPABILITY_IMS).build())
+ .build());
+ }
+
+ /**
+ * Helper function to kick a {@link JobInfo} through a persistence cycle and
+ * assert that it's unchanged.
+ */
+ private void assertPersistedEquals(JobInfo first) throws Exception {
+ mTaskStoreUnderTest.clear();
+ mTaskStoreUnderTest.add(JobStatus.createFromJobInfo(first, SOME_UID, null, -1, null));
+ waitForPendingIo();
+
+ final JobSet jobStatusSet = new JobSet();
+ mTaskStoreUnderTest.readJobMapFromDisk(jobStatusSet, true);
+ final JobStatus second = jobStatusSet.getAllJobs().iterator().next();
+ assertTasksEqual(first, second.getJob());
+ }
+
/**
* Helper function to throw an error if the provided task and TaskStatus objects are not equal.
*/
@@ -286,12 +394,10 @@
second.isRequireBatteryNotLow());
assertEquals("Invalid idle constraint.", first.isRequireDeviceIdle(),
second.isRequireDeviceIdle());
- assertEquals("Invalid unmetered constraint.",
- first.getNetworkType() == JobInfo.NETWORK_TYPE_UNMETERED,
- second.getNetworkType() == JobInfo.NETWORK_TYPE_UNMETERED);
- assertEquals("Invalid connectivity constraint.",
- first.getNetworkType() == JobInfo.NETWORK_TYPE_ANY,
- second.getNetworkType() == JobInfo.NETWORK_TYPE_ANY);
+ assertEquals("Invalid network type.",
+ first.getNetworkType(), second.getNetworkType());
+ assertEquals("Invalid network.",
+ first.getRequiredNetwork(), second.getRequiredNetwork());
assertEquals("Invalid deadline constraint.",
first.hasLateConstraint(),
second.hasLateConstraint());
@@ -302,6 +408,26 @@
first.getExtras().toString(), second.getExtras().toString());
assertEquals("Transient xtras don't match",
first.getTransientExtras().toString(), second.getTransientExtras().toString());
+
+ // Since people can forget to add tests here for new fields, do one last
+ // sanity check based on bits-on-wire equality.
+ final byte[] firstBytes = marshall(first);
+ final byte[] secondBytes = marshall(second);
+ if (!Arrays.equals(firstBytes, secondBytes)) {
+ Log.w(TAG, "First: " + HexDump.dumpHexString(firstBytes));
+ Log.w(TAG, "Second: " + HexDump.dumpHexString(secondBytes));
+ fail("Raw JobInfo aren't equal; see logs for details");
+ }
+ }
+
+ private static byte[] marshall(Parcelable p) {
+ final Parcel parcel = Parcel.obtain();
+ try {
+ p.writeToParcel(parcel, 0);
+ return parcel.marshall();
+ } finally {
+ parcel.recycle();
+ }
}
/**
@@ -312,7 +438,7 @@
*/
private void compareTimestampsSubjectToIoLatency(String error, long ts1, long ts2) {
final long DELTA_MILLIS = 700L; // We allow up to 700ms of latency for IO read/writes.
- assertTrue(error, Math.abs(ts1 - ts2) < DELTA_MILLIS + IO_WAIT);
+ assertTrue(error, Math.abs(ts1 - ts2) < DELTA_MILLIS);
}
private static class StubClass {}
diff --git a/services/tests/servicestests/src/com/android/server/net/watchlist/HarmfulDigestsTests.java b/services/tests/servicestests/src/com/android/server/net/watchlist/HarmfulDigestsTests.java
new file mode 100644
index 0000000..a34f95e
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/net/watchlist/HarmfulDigestsTests.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.net.watchlist;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import com.android.internal.util.HexDump;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Arrays;
+
+/**
+ * runtest frameworks-services -c com.android.server.net.watchlist.HarmfulDigestsTests
+ */
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class HarmfulDigestsTests {
+
+ private static final byte[] TEST_DIGEST_1 = HexDump.hexStringToByteArray("AAAAAA");
+ private static final byte[] TEST_DIGEST_2 = HexDump.hexStringToByteArray("BBBBBB");
+ private static final byte[] TEST_DIGEST_3 = HexDump.hexStringToByteArray("AAAABB");
+ private static final byte[] TEST_DIGEST_4 = HexDump.hexStringToByteArray("BBBBAA");
+
+ @Before
+ public void setUp() throws Exception {
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ }
+
+ @Test
+ public void testHarmfulDigests_setAndContains() throws Exception {
+ HarmfulDigests harmfulDigests = new HarmfulDigests(
+ Arrays.asList(new byte[][] {TEST_DIGEST_1}));
+ assertTrue(harmfulDigests.contains(TEST_DIGEST_1));
+ assertFalse(harmfulDigests.contains(TEST_DIGEST_2));
+ assertFalse(harmfulDigests.contains(TEST_DIGEST_3));
+ assertFalse(harmfulDigests.contains(TEST_DIGEST_4));
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/net/watchlist/NetworkWatchlistServiceTests.java b/services/tests/servicestests/src/com/android/server/net/watchlist/NetworkWatchlistServiceTests.java
new file mode 100644
index 0000000..ccd3cdd
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/net/watchlist/NetworkWatchlistServiceTests.java
@@ -0,0 +1,217 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.net.watchlist;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import android.net.ConnectivityMetricsEvent;
+import android.net.IIpConnectivityMetrics;
+import android.net.INetdEventCallback;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Message;
+import android.os.Process;
+import android.os.RemoteException;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.MediumTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import com.android.server.ServiceThread;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * runtest frameworks-services -c com.android.server.net.watchlist.NetworkWatchlistServiceTests
+ */
+@RunWith(AndroidJUnit4.class)
+@MediumTest
+public class NetworkWatchlistServiceTests {
+
+ private static final long NETWOR_EVENT_TIMEOUT_SEC = 1;
+ private static final String TEST_HOST = "testhost.com";
+ private static final String TEST_IP = "7.6.8.9";
+ private static final String[] TEST_IPS =
+ new String[] {"1.2.3.4", "4.6.8.9", "2001:0db8:0001:0000:0000:0ab9:C0A8:0102"};
+
+ private static class TestHandler extends Handler {
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case WatchlistLoggingHandler.LOG_WATCHLIST_EVENT_MSG:
+ onLogEvent();
+ break;
+ case WatchlistLoggingHandler.REPORT_RECORDS_IF_NECESSARY_MSG:
+ onAggregateEvent();
+ break;
+ default:
+ fail("Unexpected message: " + msg.what);
+ }
+ }
+
+ public void onLogEvent() {}
+ public void onAggregateEvent() {}
+ }
+
+ private static class TestIIpConnectivityMetrics implements IIpConnectivityMetrics {
+
+ int counter = 0;
+ INetdEventCallback callback = null;
+
+ @Override
+ public IBinder asBinder() {
+ return null;
+ }
+
+ @Override
+ public int logEvent(ConnectivityMetricsEvent connectivityMetricsEvent)
+ throws RemoteException {
+ return 0;
+ }
+
+ @Override
+ public boolean addNetdEventCallback(int callerType, INetdEventCallback callback) {
+ counter++;
+ this.callback = callback;
+ return true;
+ }
+
+ @Override
+ public boolean removeNetdEventCallback(int callerType) {
+ counter--;
+ return true;
+ }
+ };
+
+ ServiceThread mHandlerThread;
+ WatchlistLoggingHandler mWatchlistHandler;
+ NetworkWatchlistService mWatchlistService;
+
+ @Before
+ public void setUp() {
+ mHandlerThread = new ServiceThread("NetworkWatchlistServiceTests",
+ Process.THREAD_PRIORITY_BACKGROUND, /* allowIo */ false);
+ mHandlerThread.start();
+ mWatchlistHandler = new WatchlistLoggingHandler(InstrumentationRegistry.getContext(),
+ mHandlerThread.getLooper());
+ mWatchlistService = new NetworkWatchlistService(InstrumentationRegistry.getContext(),
+ mHandlerThread, mWatchlistHandler, null);
+ }
+
+ @After
+ public void tearDown() {
+ mHandlerThread.quitSafely();
+ }
+
+ @Test
+ public void testStartStopWatchlistLogging() throws Exception {
+ TestIIpConnectivityMetrics connectivityMetrics = new TestIIpConnectivityMetrics() {
+ @Override
+ public boolean addNetdEventCallback(int callerType, INetdEventCallback callback) {
+ super.addNetdEventCallback(callerType, callback);
+ assertEquals(callerType, INetdEventCallback.CALLBACK_CALLER_NETWORK_WATCHLIST);
+ return true;
+ }
+
+ @Override
+ public boolean removeNetdEventCallback(int callerType) {
+ super.removeNetdEventCallback(callerType);
+ assertEquals(callerType, INetdEventCallback.CALLBACK_CALLER_NETWORK_WATCHLIST);
+ return true;
+ }
+ };
+ assertEquals(connectivityMetrics.counter, 0);
+ mWatchlistService.mIpConnectivityMetrics = connectivityMetrics;
+ assertTrue(mWatchlistService.startWatchlistLoggingImpl());
+ assertEquals(connectivityMetrics.counter, 1);
+ assertTrue(mWatchlistService.startWatchlistLoggingImpl());
+ assertEquals(connectivityMetrics.counter, 1);
+ assertTrue(mWatchlistService.stopWatchlistLoggingImpl());
+ assertEquals(connectivityMetrics.counter, 0);
+ assertTrue(mWatchlistService.stopWatchlistLoggingImpl());
+ assertEquals(connectivityMetrics.counter, 0);
+ assertTrue(mWatchlistService.startWatchlistLoggingImpl());
+ assertEquals(connectivityMetrics.counter, 1);
+ assertTrue(mWatchlistService.stopWatchlistLoggingImpl());
+ assertEquals(connectivityMetrics.counter, 0);
+ }
+
+ @Test
+ public void testNetworkEvents() throws Exception {
+ TestIIpConnectivityMetrics connectivityMetrics = new TestIIpConnectivityMetrics();
+ mWatchlistService.mIpConnectivityMetrics = connectivityMetrics;
+ assertTrue(mWatchlistService.startWatchlistLoggingImpl());
+
+ // Test DNS events
+ final CountDownLatch testDnsLatch = new CountDownLatch(1);
+ final Object[] dnsParams = new Object[3];
+ final WatchlistLoggingHandler testDnsHandler =
+ new WatchlistLoggingHandler(InstrumentationRegistry.getContext(),
+ mHandlerThread.getLooper()) {
+ @Override
+ public void asyncNetworkEvent(String host, String[] ipAddresses, int uid) {
+ dnsParams[0] = host;
+ dnsParams[1] = ipAddresses;
+ dnsParams[2] = uid;
+ testDnsLatch.countDown();
+ }
+ };
+ mWatchlistService.mNetworkWatchlistHandler = testDnsHandler;
+ connectivityMetrics.callback.onDnsEvent(TEST_HOST, TEST_IPS, TEST_IPS.length, 123L, 456);
+ if (!testDnsLatch.await(NETWOR_EVENT_TIMEOUT_SEC, TimeUnit.SECONDS)) {
+ fail("Timed out waiting for network event");
+ }
+ assertEquals(TEST_HOST, dnsParams[0]);
+ for (int i = 0; i < TEST_IPS.length; i++) {
+ assertEquals(TEST_IPS[i], ((String[])dnsParams[1])[i]);
+ }
+ assertEquals(456, dnsParams[2]);
+
+ // Test connect events
+ final CountDownLatch testConnectLatch = new CountDownLatch(1);
+ final Object[] connectParams = new Object[3];
+ final WatchlistLoggingHandler testConnectHandler =
+ new WatchlistLoggingHandler(InstrumentationRegistry.getContext(),
+ mHandlerThread.getLooper()) {
+ @Override
+ public void asyncNetworkEvent(String host, String[] ipAddresses, int uid) {
+ connectParams[0] = host;
+ connectParams[1] = ipAddresses;
+ connectParams[2] = uid;
+ testConnectLatch.countDown();
+ }
+ };
+ mWatchlistService.mNetworkWatchlistHandler = testConnectHandler;
+ connectivityMetrics.callback.onConnectEvent(TEST_IP, 80, 123L, 456);
+ if (!testConnectLatch.await(NETWOR_EVENT_TIMEOUT_SEC, TimeUnit.SECONDS)) {
+ fail("Timed out waiting for network event");
+ }
+ assertNull(connectParams[0]);
+ assertEquals(1, ((String[]) connectParams[1]).length);
+ assertEquals(TEST_IP, ((String[]) connectParams[1])[0]);
+ assertEquals(456, connectParams[2]);
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/net/watchlist/WatchlistLoggingHandlerTests.java b/services/tests/servicestests/src/com/android/server/net/watchlist/WatchlistLoggingHandlerTests.java
new file mode 100644
index 0000000..e356b13
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/net/watchlist/WatchlistLoggingHandlerTests.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.net.watchlist;
+
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+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.util.Arrays;
+
+/**
+ * runtest frameworks-services -c com.android.server.net.watchlist.WatchlistLoggingHandlerTests
+ */
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class WatchlistLoggingHandlerTests {
+
+ @Before
+ public void setUp() throws Exception {
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ }
+
+ @Test
+ public void testWatchlistLoggingHandler_getAllSubDomains() throws Exception {
+ String[] subDomains = WatchlistLoggingHandler.getAllSubDomains("abc.def.gh.i.jkl.mm");
+ assertTrue(Arrays.equals(subDomains, new String[] {"abc.def.gh.i.jkl.mm",
+ "def.gh.i.jkl.mm", "gh.i.jkl.mm", "i.jkl.mm", "jkl.mm", "mm"}));
+ subDomains = WatchlistLoggingHandler.getAllSubDomains(null);
+ assertNull(subDomains);
+ subDomains = WatchlistLoggingHandler.getAllSubDomains("jkl.mm");
+ assertTrue(Arrays.equals(subDomains, new String[] {"jkl.mm", "mm"}));
+ subDomains = WatchlistLoggingHandler.getAllSubDomains("abc");
+ assertTrue(Arrays.equals(subDomains, new String[] {"abc"}));
+ subDomains = WatchlistLoggingHandler.getAllSubDomains("jkl.mm.");
+ assertTrue(Arrays.equals(subDomains, new String[] {"jkl.mm.", "mm."}));
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/net/watchlist/WatchlistSettingsTests.java b/services/tests/servicestests/src/com/android/server/net/watchlist/WatchlistSettingsTests.java
new file mode 100644
index 0000000..f3cb980
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/net/watchlist/WatchlistSettingsTests.java
@@ -0,0 +1,195 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.net.watchlist;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import android.content.Context;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import com.android.internal.util.HexDump;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.util.Arrays;
+
+/**
+ * runtest frameworks-services -c com.android.server.net.watchlist.WatchlistSettingsTests
+ */
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class WatchlistSettingsTests {
+
+ private static final String TEST_XML_1 = "NetworkWatchlistTest/watchlist_settings_test1.xml";
+ private static final String TEST_CC_DOMAIN = "test-cc-domain.com";
+ private static final String TEST_CC_IP = "127.0.0.2";
+ private static final String TEST_NOT_EXIST_CC_DOMAIN = "test-not-exist-cc-domain.com";
+ private static final String TEST_NOT_EXIST_CC_IP = "1.2.3.4";
+ private static final String TEST_SHA256_ONLY_DOMAIN = "test-cc-match-sha256-only.com";
+ private static final String TEST_SHA256_ONLY_IP = "127.0.0.3";
+ private static final String TEST_CRC32_ONLY_DOMAIN = "test-cc-match-crc32-only.com";
+ private static final String TEST_CRC32_ONLY_IP = "127.0.0.4";
+
+ private static final String TEST_NEW_CC_DOMAIN = "test-new-cc-domain.com";
+ private static final byte[] TEST_NEW_CC_DOMAIN_SHA256 = HexDump.hexStringToByteArray(
+ "B86F9D37425340B635F43D6BC2506630761ADA71F5E6BBDBCA4651C479F9FB43");
+ private static final byte[] TEST_NEW_CC_DOMAIN_CRC32 = HexDump.hexStringToByteArray("76795BD3");
+
+ private static final String TEST_NEW_CC_IP = "1.1.1.2";
+ private static final byte[] TEST_NEW_CC_IP_SHA256 = HexDump.hexStringToByteArray(
+ "721BAB5E313CF0CC76B10F9592F18B9D1B8996497501A3306A55B3AE9F1CC87C");
+ private static final byte[] TEST_NEW_CC_IP_CRC32 = HexDump.hexStringToByteArray("940B8BEE");
+
+ private Context mContext;
+ private File mTestXmlFile;
+
+ @Before
+ public void setUp() throws Exception {
+ mContext = InstrumentationRegistry.getContext();
+ mTestXmlFile = new File(mContext.getFilesDir(), "test_watchlist_settings.xml");
+ mTestXmlFile.delete();
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ mTestXmlFile.delete();
+ }
+
+ @Test
+ public void testWatchlistSettings_parsing() throws Exception {
+ copyWatchlistSettingsXml(mContext, TEST_XML_1, mTestXmlFile);
+ WatchlistSettings settings = new WatchlistSettings(mTestXmlFile);
+ assertTrue(settings.containsDomain(TEST_CC_DOMAIN));
+ assertTrue(settings.containsIp(TEST_CC_IP));
+ assertFalse(settings.containsDomain(TEST_NOT_EXIST_CC_DOMAIN));
+ assertFalse(settings.containsIp(TEST_NOT_EXIST_CC_IP));
+ assertFalse(settings.containsDomain(TEST_SHA256_ONLY_DOMAIN));
+ assertFalse(settings.containsIp(TEST_SHA256_ONLY_IP));
+ assertFalse(settings.containsDomain(TEST_CRC32_ONLY_DOMAIN));
+ assertFalse(settings.containsIp(TEST_CRC32_ONLY_IP));
+ }
+
+ @Test
+ public void testWatchlistSettings_writeSettingsToDisk() throws Exception {
+ copyWatchlistSettingsXml(mContext, TEST_XML_1, mTestXmlFile);
+ WatchlistSettings settings = new WatchlistSettings(mTestXmlFile);
+ settings.writeSettingsToDisk(Arrays.asList(TEST_NEW_CC_DOMAIN_CRC32),
+ Arrays.asList(TEST_NEW_CC_DOMAIN_SHA256), Arrays.asList(TEST_NEW_CC_IP_CRC32),
+ Arrays.asList(TEST_NEW_CC_IP_SHA256));
+ // Ensure old watchlist is not in memory
+ assertFalse(settings.containsDomain(TEST_CC_DOMAIN));
+ assertFalse(settings.containsIp(TEST_CC_IP));
+ assertFalse(settings.containsDomain(TEST_NOT_EXIST_CC_DOMAIN));
+ assertFalse(settings.containsIp(TEST_NOT_EXIST_CC_IP));
+ assertFalse(settings.containsDomain(TEST_SHA256_ONLY_DOMAIN));
+ assertFalse(settings.containsIp(TEST_SHA256_ONLY_IP));
+ assertFalse(settings.containsDomain(TEST_CRC32_ONLY_DOMAIN));
+ assertFalse(settings.containsIp(TEST_CRC32_ONLY_IP));
+ // Ensure new watchlist is in memory
+ assertTrue(settings.containsDomain(TEST_NEW_CC_DOMAIN));
+ assertTrue(settings.containsIp(TEST_NEW_CC_IP));
+ // Reload settings from disk and test again
+ settings = new WatchlistSettings(mTestXmlFile);
+ // Ensure old watchlist is not in memory
+ assertFalse(settings.containsDomain(TEST_CC_DOMAIN));
+ assertFalse(settings.containsIp(TEST_CC_IP));
+ assertFalse(settings.containsDomain(TEST_NOT_EXIST_CC_DOMAIN));
+ assertFalse(settings.containsIp(TEST_NOT_EXIST_CC_IP));
+ assertFalse(settings.containsDomain(TEST_SHA256_ONLY_DOMAIN));
+ assertFalse(settings.containsIp(TEST_SHA256_ONLY_IP));
+ assertFalse(settings.containsDomain(TEST_CRC32_ONLY_DOMAIN));
+ assertFalse(settings.containsIp(TEST_CRC32_ONLY_IP));
+ // Ensure new watchlist is in memory
+ assertTrue(settings.containsDomain(TEST_NEW_CC_DOMAIN));
+ assertTrue(settings.containsIp(TEST_NEW_CC_IP));
+ }
+
+ @Test
+ public void testWatchlistSettings_writeSettingsToMemory() throws Exception {
+ copyWatchlistSettingsXml(mContext, TEST_XML_1, mTestXmlFile);
+ WatchlistSettings settings = new WatchlistSettings(mTestXmlFile);
+ settings.writeSettingsToMemory(Arrays.asList(TEST_NEW_CC_DOMAIN_CRC32),
+ Arrays.asList(TEST_NEW_CC_DOMAIN_SHA256), Arrays.asList(TEST_NEW_CC_IP_CRC32),
+ Arrays.asList(TEST_NEW_CC_IP_SHA256));
+ // Ensure old watchlist is not in memory
+ assertFalse(settings.containsDomain(TEST_CC_DOMAIN));
+ assertFalse(settings.containsIp(TEST_CC_IP));
+ assertFalse(settings.containsDomain(TEST_NOT_EXIST_CC_DOMAIN));
+ assertFalse(settings.containsIp(TEST_NOT_EXIST_CC_IP));
+ assertFalse(settings.containsDomain(TEST_SHA256_ONLY_DOMAIN));
+ assertFalse(settings.containsIp(TEST_SHA256_ONLY_IP));
+ assertFalse(settings.containsDomain(TEST_CRC32_ONLY_DOMAIN));
+ assertFalse(settings.containsIp(TEST_CRC32_ONLY_IP));
+ // Ensure new watchlist is in memory
+ assertTrue(settings.containsDomain(TEST_NEW_CC_DOMAIN));
+ assertTrue(settings.containsIp(TEST_NEW_CC_IP));
+ // Reload settings from disk and test again
+ settings = new WatchlistSettings(mTestXmlFile);
+ // Ensure old watchlist is in memory
+ assertTrue(settings.containsDomain(TEST_CC_DOMAIN));
+ assertTrue(settings.containsIp(TEST_CC_IP));
+ assertFalse(settings.containsDomain(TEST_NOT_EXIST_CC_DOMAIN));
+ assertFalse(settings.containsIp(TEST_NOT_EXIST_CC_IP));
+ assertFalse(settings.containsDomain(TEST_SHA256_ONLY_DOMAIN));
+ assertFalse(settings.containsIp(TEST_SHA256_ONLY_IP));
+ assertFalse(settings.containsDomain(TEST_CRC32_ONLY_DOMAIN));
+ assertFalse(settings.containsIp(TEST_CRC32_ONLY_IP));
+ // Ensure new watchlist is not in memory
+ assertFalse(settings.containsDomain(TEST_NEW_CC_DOMAIN));
+ assertFalse(settings.containsIp(TEST_NEW_CC_IP));;
+ }
+
+ private static void copyWatchlistSettingsXml(Context context, String xmlAsset, File outFile)
+ throws IOException {
+ writeToFile(outFile, readAsset(context, xmlAsset));
+
+ }
+
+ private static String readAsset(Context context, String assetPath) throws IOException {
+ final StringBuilder sb = new StringBuilder();
+ try (BufferedReader br = new BufferedReader(
+ new InputStreamReader(
+ context.getResources().getAssets().open(assetPath)))) {
+ String line;
+ while ((line = br.readLine()) != null) {
+ sb.append(line);
+ sb.append(System.lineSeparator());
+ }
+ }
+ return sb.toString();
+ }
+
+ private static void writeToFile(File path, String content)
+ throws IOException {
+ path.getParentFile().mkdirs();
+
+ try (FileWriter writer = new FileWriter(path)) {
+ writer.write(content);
+ }
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
index b656d5e..dd9a8ab 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
@@ -309,6 +309,8 @@
@MediumTest
public void testAddRestrictedProfile() throws Exception {
+ assertFalse("There should be no associated restricted profiles before the test",
+ mUserManager.hasRestrictedProfiles());
UserInfo userInfo = createRestrictedProfile("Profile");
assertNotNull(userInfo);
@@ -324,6 +326,9 @@
userInfo.id);
assertEquals("Restricted profile should have setting LOCATION_MODE set to "
+ "LOCATION_MODE_OFF by default", locationMode, Settings.Secure.LOCATION_MODE_OFF);
+
+ assertTrue("Newly created profile should be associated with the current user",
+ mUserManager.hasRestrictedProfiles());
}
@MediumTest
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserRestrictionsUtilsTest.java b/services/tests/servicestests/src/com/android/server/pm/UserRestrictionsUtilsTest.java
index 480be2e..882bf32 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserRestrictionsUtilsTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserRestrictionsUtilsTest.java
@@ -89,6 +89,7 @@
assertFalse(UserRestrictionsUtils.canDeviceOwnerChange(UserManager.DISALLOW_RECORD_AUDIO));
assertFalse(UserRestrictionsUtils.canDeviceOwnerChange(UserManager.DISALLOW_WALLPAPER));
assertTrue(UserRestrictionsUtils.canDeviceOwnerChange(UserManager.DISALLOW_ADD_USER));
+ assertTrue(UserRestrictionsUtils.canDeviceOwnerChange(UserManager.DISALLOW_USER_SWITCH));
}
public void testCanProfileOwnerChange() {
@@ -97,6 +98,8 @@
UserManager.DISALLOW_RECORD_AUDIO, user));
assertFalse(UserRestrictionsUtils.canProfileOwnerChange(
UserManager.DISALLOW_WALLPAPER, user));
+ assertFalse(UserRestrictionsUtils.canProfileOwnerChange(
+ UserManager.DISALLOW_USER_SWITCH, user));
assertTrue(UserRestrictionsUtils.canProfileOwnerChange(
UserManager.DISALLOW_ADD_USER, user));
assertTrue(UserRestrictionsUtils.canProfileOwnerChange(
@@ -109,6 +112,8 @@
UserManager.DISALLOW_WALLPAPER, user));
assertFalse(UserRestrictionsUtils.canProfileOwnerChange(
UserManager.DISALLOW_ADD_USER, user));
+ assertFalse(UserRestrictionsUtils.canProfileOwnerChange(
+ UserManager.DISALLOW_USER_SWITCH, user));
assertTrue(UserRestrictionsUtils.canProfileOwnerChange(
UserManager.DISALLOW_ADJUST_VOLUME, user));
}
diff --git a/services/tests/servicestests/src/com/android/server/pm/crossprofile/CrossProfileAppsServiceImplTest.java b/services/tests/servicestests/src/com/android/server/pm/crossprofile/CrossProfileAppsServiceImplTest.java
new file mode 100644
index 0000000..880b77e
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/pm/crossprofile/CrossProfileAppsServiceImplTest.java
@@ -0,0 +1,449 @@
+package com.android.server.pm.crossprofile;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static junit.framework.Assert.assertEquals;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.nullable;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+import static org.testng.Assert.assertThrows;
+
+import android.app.AppOpsManager;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManagerInternal;
+import android.content.pm.ResolveInfo;
+import android.os.Bundle;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.platform.test.annotations.Presubmit;
+import android.util.SparseArray;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnitRunner;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Build/Install/Run:
+ * bit FrameworksServicesTests:com.android.server.pm.crossprofile.CrossProfileAppsServiceImplTest
+ */
+@Presubmit
+@RunWith(MockitoJUnitRunner.class)
+public class CrossProfileAppsServiceImplTest {
+ private static final String PACKAGE_ONE = "com.one";
+ private static final int PACKAGE_ONE_UID = 1111;
+ private static final ComponentName ACTIVITY_COMPONENT =
+ new ComponentName("com.one", "test");
+
+ private static final String PACKAGE_TWO = "com.two";
+ private static final int PACKAGE_TWO_UID = 2222;
+
+ private static final int PRIMARY_USER = 0;
+ private static final int PROFILE_OF_PRIMARY_USER = 10;
+ private static final int SECONDARY_USER = 11;
+
+ @Mock
+ private Context mContext;
+ @Mock
+ private UserManager mUserManager;
+ @Mock
+ private PackageManager mPackageManager;
+ @Mock
+ private PackageManagerInternal mPackageManagerInternal;
+ @Mock
+ private AppOpsManager mAppOpsManager;
+
+ private TestInjector mTestInjector;
+ private ActivityInfo mActivityInfo;
+ private CrossProfileAppsServiceImpl mCrossProfileAppsServiceImpl;
+
+ private SparseArray<Boolean> mUserEnabled = new SparseArray<>();
+
+ @Before
+ public void initCrossProfileAppsServiceImpl() {
+ mTestInjector = new TestInjector();
+ mCrossProfileAppsServiceImpl = new CrossProfileAppsServiceImpl(mContext, mTestInjector);
+ }
+
+ @Before
+ public void setupEnabledProfiles() {
+ mUserEnabled.put(PRIMARY_USER, true);
+ mUserEnabled.put(PROFILE_OF_PRIMARY_USER, true);
+ mUserEnabled.put(SECONDARY_USER, true);
+
+ when(mUserManager.getEnabledProfileIds(anyInt())).thenAnswer(
+ invocation -> {
+ List<Integer> users = new ArrayList<>();
+ final int targetUser = invocation.getArgument(0);
+ users.add(targetUser);
+
+ int profileUserId = -1;
+ if (targetUser == PRIMARY_USER) {
+ profileUserId = PROFILE_OF_PRIMARY_USER;
+ } else if (targetUser == PROFILE_OF_PRIMARY_USER) {
+ profileUserId = PRIMARY_USER;
+ }
+
+ if (profileUserId != -1 && mUserEnabled.get(profileUserId)) {
+ users.add(profileUserId);
+ }
+ return users.stream().mapToInt(i -> i).toArray();
+ });
+ }
+
+ @Before
+ public void setupCaller() {
+ mTestInjector.setCallingUid(PACKAGE_ONE_UID);
+ mTestInjector.setCallingUserId(PRIMARY_USER);
+ }
+
+ @Before
+ public void setupPackage() throws Exception {
+ // PACKAGE_ONE are installed in all users.
+ mockAppsInstalled(PACKAGE_ONE, PRIMARY_USER, true);
+ mockAppsInstalled(PACKAGE_ONE, PROFILE_OF_PRIMARY_USER, true);
+ mockAppsInstalled(PACKAGE_ONE, SECONDARY_USER, true);
+
+ // Packages are resolved to their corresponding UID.
+ doAnswer(invocation -> {
+ final int uid = invocation.getArgument(0);
+ final String packageName = invocation.getArgument(1);
+ if (uid == PACKAGE_ONE_UID && PACKAGE_ONE.equals(packageName)) {
+ return null;
+ } else if (uid ==PACKAGE_TWO_UID && PACKAGE_TWO.equals(packageName)) {
+ return null;
+ }
+ throw new SecurityException("Not matching");
+ }).when(mAppOpsManager).checkPackage(anyInt(), anyString());
+
+ // The intent is resolved to the ACTIVITY_COMPONENT.
+ mockActivityLaunchIntentResolvedTo(ACTIVITY_COMPONENT);
+ }
+
+ @Test
+ public void getTargetUserProfiles_fromPrimaryUser_installed() throws Exception {
+ List<UserHandle> targetProfiles =
+ mCrossProfileAppsServiceImpl.getTargetUserProfiles(PACKAGE_ONE);
+ assertThat(targetProfiles).containsExactly(UserHandle.of(PROFILE_OF_PRIMARY_USER));
+ }
+
+ @Test
+ public void getTargetUserProfiles_fromPrimaryUser_notInstalled() throws Exception {
+ mockAppsInstalled(PACKAGE_ONE, PROFILE_OF_PRIMARY_USER, false);
+
+ List<UserHandle> targetProfiles =
+ mCrossProfileAppsServiceImpl.getTargetUserProfiles(PACKAGE_ONE);
+ assertThat(targetProfiles).isEmpty();
+ }
+
+ @Test
+ public void getTargetUserProfiles_fromPrimaryUser_userNotEnabled() throws Exception {
+ mUserEnabled.put(PROFILE_OF_PRIMARY_USER, false);
+
+ List<UserHandle> targetProfiles =
+ mCrossProfileAppsServiceImpl.getTargetUserProfiles(PACKAGE_ONE);
+ assertThat(targetProfiles).isEmpty();
+ }
+
+ @Test
+ public void getTargetUserProfiles_fromSecondaryUser() throws Exception {
+ mTestInjector.setCallingUserId(SECONDARY_USER);
+
+ List<UserHandle> targetProfiles =
+ mCrossProfileAppsServiceImpl.getTargetUserProfiles(PACKAGE_ONE);
+ assertThat(targetProfiles).isEmpty();
+ }
+
+ @Test
+ public void getTargetUserProfiles_fromProfile_installed() throws Exception {
+ mTestInjector.setCallingUserId(PROFILE_OF_PRIMARY_USER);
+
+ List<UserHandle> targetProfiles =
+ mCrossProfileAppsServiceImpl.getTargetUserProfiles(PACKAGE_ONE);
+ assertThat(targetProfiles).containsExactly(UserHandle.of(PRIMARY_USER));
+ }
+
+ @Test
+ public void getTargetUserProfiles_fromProfile_notInstalled() throws Exception {
+ mTestInjector.setCallingUserId(PROFILE_OF_PRIMARY_USER);
+ mockAppsInstalled(PACKAGE_ONE, PRIMARY_USER, false);
+
+ List<UserHandle> targetProfiles =
+ mCrossProfileAppsServiceImpl.getTargetUserProfiles(PACKAGE_ONE);
+ assertThat(targetProfiles).isEmpty();
+ }
+
+ @Test(expected = SecurityException.class)
+ public void getTargetUserProfiles_fakeCaller() throws Exception {
+ mCrossProfileAppsServiceImpl.getTargetUserProfiles(PACKAGE_TWO);
+ }
+
+ @Test
+ public void startActivityAsUser_currentUser() throws Exception {
+ assertThrows(
+ SecurityException.class,
+ () ->
+ mCrossProfileAppsServiceImpl.startActivityAsUser(
+ PACKAGE_ONE,
+ ACTIVITY_COMPONENT,
+ null,
+ null,
+ UserHandle.of(PRIMARY_USER)));
+
+ verify(mContext, never())
+ .startActivityAsUser(
+ any(Intent.class),
+ nullable(Bundle.class),
+ any(UserHandle.class));
+ }
+
+ @Test
+ public void startActivityAsUser_profile_successWithOption() throws Exception {
+ Bundle options = Bundle.forPair("test_key", "test_value");
+
+ mCrossProfileAppsServiceImpl.startActivityAsUser(
+ PACKAGE_ONE,
+ ACTIVITY_COMPONENT,
+ null,
+ options,
+ UserHandle.of(PROFILE_OF_PRIMARY_USER));
+
+ ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
+ ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class);
+
+ verify(mContext)
+ .startActivityAsUser(
+ intentCaptor.capture(),
+ bundleCaptor.capture(),
+ eq(UserHandle.of(PROFILE_OF_PRIMARY_USER)));
+
+ Intent intent = intentCaptor.getValue();
+ assertEquals(ACTIVITY_COMPONENT, intent.getComponent());
+
+ Bundle bundle = bundleCaptor.getValue();
+ assertEquals("test_value", bundle.getString("test_key"));
+ }
+
+ @Test
+ public void startActivityAsUser_profile_notInstalled() throws Exception {
+ mockAppsInstalled(PACKAGE_ONE, PROFILE_OF_PRIMARY_USER, false);
+
+ assertThrows(
+ SecurityException.class,
+ () ->
+ mCrossProfileAppsServiceImpl.startActivityAsUser(
+ PACKAGE_ONE,
+ ACTIVITY_COMPONENT,
+ null,
+ null,
+ UserHandle.of(PROFILE_OF_PRIMARY_USER)));
+
+ verify(mContext, never())
+ .startActivityAsUser(
+ any(Intent.class),
+ nullable(Bundle.class),
+ any(UserHandle.class));
+ }
+
+ @Test
+ public void startActivityAsUser_profile_fakeCaller() throws Exception {
+ assertThrows(
+ SecurityException.class,
+ () ->
+ mCrossProfileAppsServiceImpl.startActivityAsUser(
+ PACKAGE_TWO,
+ ACTIVITY_COMPONENT,
+ null,
+ null,
+ UserHandle.of(PROFILE_OF_PRIMARY_USER)));
+
+ verify(mContext, never())
+ .startActivityAsUser(
+ any(Intent.class),
+ nullable(Bundle.class),
+ any(UserHandle.class));
+ }
+
+ @Test
+ public void startActivityAsUser_profile_notExported() throws Exception {
+ mActivityInfo.exported = false;
+
+ assertThrows(
+ SecurityException.class,
+ () ->
+ mCrossProfileAppsServiceImpl.startActivityAsUser(
+ PACKAGE_ONE,
+ ACTIVITY_COMPONENT,
+ null,
+ null,
+ UserHandle.of(PROFILE_OF_PRIMARY_USER)));
+
+ verify(mContext, never())
+ .startActivityAsUser(
+ any(Intent.class),
+ nullable(Bundle.class),
+ any(UserHandle.class));
+ }
+
+ @Test
+ public void startActivityAsUser_profile_anotherPackage() throws Exception {
+ assertThrows(
+ SecurityException.class,
+ () ->
+ mCrossProfileAppsServiceImpl.startActivityAsUser(
+ PACKAGE_ONE,
+ new ComponentName(PACKAGE_TWO, "test"),
+ null,
+ null,
+ UserHandle.of(PROFILE_OF_PRIMARY_USER)));
+
+ verify(mContext, never())
+ .startActivityAsUser(
+ any(Intent.class),
+ nullable(Bundle.class),
+ any(UserHandle.class));
+ }
+
+ @Test
+ public void startActivityAsUser_secondaryUser() throws Exception {
+ assertThrows(
+ SecurityException.class,
+ () ->
+ mCrossProfileAppsServiceImpl.startActivityAsUser(
+ PACKAGE_ONE,
+ ACTIVITY_COMPONENT,
+ null,
+ null,
+ UserHandle.of(SECONDARY_USER)));
+
+ verify(mContext, never())
+ .startActivityAsUser(
+ any(Intent.class),
+ nullable(Bundle.class),
+ any(UserHandle.class));
+ }
+
+ @Test
+ public void startActivityAsUser_fromProfile_success() throws Exception {
+ mTestInjector.setCallingUserId(PROFILE_OF_PRIMARY_USER);
+
+ mCrossProfileAppsServiceImpl.startActivityAsUser(
+ PACKAGE_ONE,
+ ACTIVITY_COMPONENT,
+ null,
+ null,
+ UserHandle.of(PRIMARY_USER));
+
+ verify(mContext)
+ .startActivityAsUser(
+ any(Intent.class),
+ nullable(Bundle.class),
+ eq(UserHandle.of(PRIMARY_USER)));
+ }
+
+ private void mockAppsInstalled(String packageName, int user, boolean installed) {
+ when(mPackageManagerInternal.getPackageInfo(
+ eq(packageName),
+ anyInt(),
+ anyInt(),
+ eq(user)))
+ .thenReturn(installed ? createInstalledPackageInfo() : null);
+ }
+
+ private PackageInfo createInstalledPackageInfo() {
+ PackageInfo packageInfo = new PackageInfo();
+ packageInfo.applicationInfo = new ApplicationInfo();
+ packageInfo.applicationInfo.enabled = true;
+ return packageInfo;
+ }
+
+ private void mockActivityLaunchIntentResolvedTo(ComponentName componentName) {
+ ResolveInfo resolveInfo = new ResolveInfo();
+ ActivityInfo activityInfo = new ActivityInfo();
+ activityInfo.packageName = componentName.getPackageName();
+ activityInfo.name = componentName.getClassName();
+ activityInfo.exported = true;
+ resolveInfo.activityInfo = activityInfo;
+ mActivityInfo = activityInfo;
+
+ when(mPackageManagerInternal.queryIntentActivities(
+ any(Intent.class), anyInt(), anyInt(), anyInt()))
+ .thenReturn(Collections.singletonList(resolveInfo));
+ }
+
+ private class TestInjector implements CrossProfileAppsServiceImpl.Injector {
+ private int mCallingUid;
+ private int mCallingUserId;
+
+ public void setCallingUid(int uid) {
+ mCallingUid = uid;
+ }
+
+ public void setCallingUserId(int userId) {
+ mCallingUserId = userId;
+ }
+
+ @Override
+ public int getCallingUid() {
+ return mCallingUid;
+ }
+
+ @Override
+ public int getCallingUserId() {
+ return mCallingUserId;
+ }
+
+ @Override
+ public UserHandle getCallingUserHandle() {
+ return UserHandle.of(mCallingUserId);
+ }
+
+ @Override
+ public long clearCallingIdentity() {
+ return 0;
+ }
+
+ @Override
+ public void restoreCallingIdentity(long token) {
+ }
+
+ @Override
+ public UserManager getUserManager() {
+ return mUserManager;
+ }
+
+ @Override
+ public PackageManagerInternal getPackageManagerInternal() {
+ return mPackageManagerInternal;
+ }
+
+ @Override
+ public PackageManager getPackageManager() {
+ return mPackageManager;
+ }
+
+ @Override
+ public AppOpsManager getAppOpsManager() {
+ return mAppOpsManager;
+ }
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/usage/AppIdleHistoryTests.java b/services/tests/servicestests/src/com/android/server/usage/AppIdleHistoryTests.java
index 67ffe58..39d256a 100644
--- a/services/tests/servicestests/src/com/android/server/usage/AppIdleHistoryTests.java
+++ b/services/tests/servicestests/src/com/android/server/usage/AppIdleHistoryTests.java
@@ -16,16 +16,14 @@
package com.android.server.usage;
-import static android.app.usage.AppStandby.REASON_TIMEOUT;
-import static android.app.usage.AppStandby.STANDBY_BUCKET_ACTIVE;
-import static android.app.usage.AppStandby.STANDBY_BUCKET_RARE;
-
import android.app.usage.AppStandby;
import android.os.FileUtils;
import android.test.AndroidTestCase;
import java.io.File;
+import static android.app.usage.AppStandby.*;
+
public class AppIdleHistoryTests extends AndroidTestCase {
File mStorageDir;
@@ -111,5 +109,9 @@
assertEquals(aih.getAppStandbyBucket(PACKAGE_1, USER_ID, 5000), STANDBY_BUCKET_RARE);
assertEquals(aih.getAppStandbyBucket(PACKAGE_2, USER_ID, 5000), STANDBY_BUCKET_ACTIVE);
assertEquals(aih.getAppStandbyReason(PACKAGE_1, USER_ID, 5000), REASON_TIMEOUT);
+
+ assertTrue(aih.shouldInformListeners(PACKAGE_1, USER_ID, 5000, STANDBY_BUCKET_RARE));
+ assertFalse(aih.shouldInformListeners(PACKAGE_1, USER_ID, 5000, STANDBY_BUCKET_RARE));
+ assertTrue(aih.shouldInformListeners(PACKAGE_1, USER_ID, 5000, STANDBY_BUCKET_FREQUENT));
}
}
\ No newline at end of file
diff --git a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
index 9846d6f..8531baf 100644
--- a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
+++ b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
@@ -16,11 +16,7 @@
package com.android.server.usage;
-import static android.app.usage.AppStandby.STANDBY_BUCKET_ACTIVE;
-import static android.app.usage.AppStandby.STANDBY_BUCKET_FREQUENT;
-import static android.app.usage.AppStandby.STANDBY_BUCKET_RARE;
-import static android.app.usage.AppStandby.STANDBY_BUCKET_WORKING_SET;
-
+import static android.app.usage.AppStandby.*;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
@@ -251,10 +247,22 @@
false));
}
+ private void reportEvent(AppStandbyController controller, long elapsedTime) {
+ // Back to ACTIVE on event
+ UsageEvents.Event ev = new UsageEvents.Event();
+ ev.mPackage = PACKAGE_1;
+ ev.mEventType = UsageEvents.Event.USER_INTERACTION;
+ controller.reportEvent(ev, elapsedTime, USER_ID);
+ }
+
@Test
public void testBuckets() throws Exception {
AppStandbyController controller = setupController();
+ assertTimeout(controller, 0, STANDBY_BUCKET_NEVER);
+
+ reportEvent(controller, 0);
+
// ACTIVE bucket
assertTimeout(controller, 11 * HOUR_MS, STANDBY_BUCKET_ACTIVE);
@@ -270,11 +278,7 @@
// RARE bucket
assertTimeout(controller, 9 * DAY_MS, STANDBY_BUCKET_RARE);
- // Back to ACTIVE on event
- UsageEvents.Event ev = new UsageEvents.Event();
- ev.mPackage = PACKAGE_1;
- ev.mEventType = UsageEvents.Event.USER_INTERACTION;
- controller.reportEvent(ev, mInjector.mElapsedRealtime, USER_ID);
+ reportEvent(controller, 9 * DAY_MS);
assertTimeout(controller, 9 * DAY_MS, STANDBY_BUCKET_ACTIVE);
@@ -287,6 +291,10 @@
AppStandbyController controller = setupController();
mInjector.setDisplayOn(false);
+ assertTimeout(controller, 0, STANDBY_BUCKET_NEVER);
+
+ reportEvent(controller, 0);
+
// ACTIVE bucket
assertTimeout(controller, 11 * HOUR_MS, STANDBY_BUCKET_ACTIVE);
diff --git a/services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java
index 27c5eab..693264c 100644
--- a/services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java
@@ -18,10 +18,14 @@
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG;
import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
+import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR;
import static android.view.WindowManager.LayoutParams.TYPE_VOICE_INTERACTION;
import static com.android.server.wm.WindowContainer.POSITION_TOP;
import static org.junit.Assert.assertEquals;
@@ -31,6 +35,7 @@
import org.junit.Test;
import org.junit.runner.RunWith;
+import android.annotation.SuppressLint;
import android.content.res.Configuration;
import android.os.SystemClock;
import android.platform.test.annotations.Presubmit;
@@ -379,6 +384,31 @@
assertEquals(-1, orderedDisplayIds.indexOfValue(dc.getDisplayId()));
}
+ @Test
+ @SuppressLint("InlinedApi")
+ public void testOrientationDefinedByKeyguard() {
+ final DisplayContent dc = createNewDisplay();
+ // Create a window that requests landscape orientation. It will define device orientation
+ // by default.
+ final WindowState window = createWindow(null /* parent */, TYPE_BASE_APPLICATION, dc, "w");
+ window.mAppToken.setOrientation(SCREEN_ORIENTATION_LANDSCAPE);
+
+ final WindowState keyguard = createWindow(null, TYPE_STATUS_BAR, dc, "keyguard");
+ keyguard.mHasSurface = true;
+ keyguard.mAttrs.screenOrientation = SCREEN_ORIENTATION_UNSPECIFIED;
+
+ assertEquals("Screen orientation must be defined by the app window by default",
+ SCREEN_ORIENTATION_LANDSCAPE, dc.getOrientation());
+
+ keyguard.mAttrs.screenOrientation = SCREEN_ORIENTATION_PORTRAIT;
+ assertEquals("Visible keyguard must influence device orientation",
+ SCREEN_ORIENTATION_PORTRAIT, dc.getOrientation());
+
+ sWm.setKeyguardGoingAway(true);
+ assertEquals("Keyguard that is going away must not influence device orientation",
+ SCREEN_ORIENTATION_LANDSCAPE, dc.getOrientation());
+ }
+
private static void verifySizes(DisplayContent displayContent, int expectedBaseWidth,
int expectedBaseHeight, int expectedBaseDensity) {
assertEquals(displayContent.mBaseDisplayWidth, expectedBaseWidth);
diff --git a/services/tests/servicestests/src/com/android/server/wm/ScreenDecorWindowTests.java b/services/tests/servicestests/src/com/android/server/wm/ScreenDecorWindowTests.java
index 9c80544..f23bd62 100644
--- a/services/tests/servicestests/src/com/android/server/wm/ScreenDecorWindowTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/ScreenDecorWindowTests.java
@@ -16,8 +16,12 @@
package com.android.server.wm;
+import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
import static android.graphics.Color.BLUE;
import static android.graphics.Color.RED;
+import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY;
+import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_PRESENTATION;
+import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.Gravity.BOTTOM;
import static android.view.Gravity.LEFT;
import static android.view.Gravity.RIGHT;
@@ -33,16 +37,23 @@
import static org.junit.Assert.assertEquals;
import android.app.Activity;
+import android.app.ActivityOptions;
import android.app.Instrumentation;
import android.content.Context;
+import android.content.Intent;
import android.graphics.PixelFormat;
import android.graphics.Point;
+import android.hardware.display.DisplayManager;
+import android.hardware.display.VirtualDisplay;
+import android.media.ImageReader;
import android.os.Handler;
import android.platform.test.annotations.Presubmit;
import android.support.test.InstrumentationRegistry;
import android.support.test.filters.SmallTest;
-import android.support.test.rule.ActivityTestRule;
import android.support.test.runner.AndroidJUnit4;
+import android.util.Pair;
+import android.view.Display;
+import android.view.DisplayInfo;
import android.view.View;
import android.view.WindowInsets;
import android.view.WindowManager;
@@ -51,7 +62,6 @@
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
-import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -65,7 +75,6 @@
*/
// TODO: Add test for FLAG_FULLSCREEN which hides the status bar and also other flags.
// TODO: Test non-Activity windows.
-// TODO: Test secondary display.
@SmallTest
// TODO(b/68957554)
//@Presubmit
@@ -78,22 +87,26 @@
private WindowManager mWm;
private ArrayList<View> mWindows = new ArrayList<>();
- @Rule
- public ActivityTestRule<TestActivity> mTestActivityRule = new ActivityTestRule<>(
- TestActivity.class, false /* initialTouchMode */, false /* launchActivity */);
private Activity mTestActivity;
+ private VirtualDisplay mDisplay;
+ private ImageReader mImageReader;
private int mDecorThickness;
private int mHalfDecorThickness;
@Before
public void setUp() {
- mWm = mContext.getSystemService(WindowManager.class);
+ final Pair<VirtualDisplay, ImageReader> result = createDisplay();
+ mDisplay = result.first;
+ mImageReader = result.second;
+ final Display display = mDisplay.getDisplay();
+ final Context dContext = mContext.createDisplayContext(display);
+ mWm = dContext.getSystemService(WindowManager.class);
+ mTestActivity = startActivityOnDisplay(TestActivity.class, display.getDisplayId());
final Point size = new Point();
- mWm.getDefaultDisplay().getSize(size);
+ mDisplay.getDisplay().getRealSize(size);
mDecorThickness = Math.min(size.x, size.y) / 3;
mHalfDecorThickness = mDecorThickness / 2;
- mTestActivity = launchActivity(mTestActivityRule);
}
@After
@@ -101,7 +114,9 @@
while (!mWindows.isEmpty()) {
removeWindow(mWindows.get(0));
}
- finishActivity(mTestActivityRule);
+ finishActivity(mTestActivity);
+ mDisplay.release();
+ mImageReader.close();
}
@Test
@@ -259,23 +274,48 @@
Assert.assertTrue("Excepted " + first + " >= " + second, first >= second);
}
- private Activity launchActivity(ActivityTestRule activityRule) {
- final Activity activity = activityRule.launchActivity(null);
- waitForIdle();
- return activity;
- }
-
- private void finishActivity(ActivityTestRule activityRule) {
- final Activity activity = activityRule.getActivity();
- if (activity != null) {
- activity.finish();
+ private void finishActivity(Activity a) {
+ if (a == null) {
+ return;
}
+ a.finish();
+ waitForIdle();
}
private void waitForIdle() {
mInstrumentation.waitForIdleSync();
}
+ private Activity startActivityOnDisplay(Class<?> cls, int displayId) {
+ final Intent intent = new Intent(mContext, cls);
+ intent.addFlags(FLAG_ACTIVITY_NEW_TASK);
+ final ActivityOptions options = ActivityOptions.makeBasic();
+ options.setLaunchDisplayId(displayId);
+ final Activity activity = mInstrumentation.startActivitySync(intent, options.toBundle());
+ waitForIdle();
+
+ assertEquals(displayId, activity.getDisplay().getDisplayId());
+ return activity;
+ }
+
+ private Pair<VirtualDisplay, ImageReader> createDisplay() {
+ final DisplayManager dm = mContext.getSystemService(DisplayManager.class);
+ final DisplayInfo displayInfo = new DisplayInfo();
+ final Display defaultDisplay = dm.getDisplay(DEFAULT_DISPLAY);
+ defaultDisplay.getDisplayInfo(displayInfo);
+ final String name = "ScreenDecorWindowTests";
+ int flags = VIRTUAL_DISPLAY_FLAG_PRESENTATION | VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY;
+
+ final ImageReader imageReader = ImageReader.newInstance(
+ displayInfo.logicalWidth, displayInfo.logicalHeight, PixelFormat.RGBA_8888, 2);
+
+ final VirtualDisplay display = dm.createVirtualDisplay(name, displayInfo.logicalWidth,
+ displayInfo.logicalHeight, displayInfo.logicalDensityDpi, imageReader.getSurface(),
+ flags);
+
+ return Pair.create(display, imageReader);
+ }
+
public static class TestActivity extends Activity {
}
}
diff --git a/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java b/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java
index 33d4721..53d0bfb 100644
--- a/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java
+++ b/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java
@@ -17,7 +17,7 @@
package com.android.server.wm;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
-import static android.view.WindowManagerPolicy.NAV_BAR_BOTTOM;
+import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
@@ -133,11 +133,6 @@
}
@Override
- public void setDisplayOverscan(Display display, int left, int top, int right, int bottom) {
-
- }
-
- @Override
public int checkAddPermission(WindowManager.LayoutParams attrs, int[] outAppOp) {
return 0;
}
@@ -189,7 +184,7 @@
@Override
public boolean isKeyguardHostWindow(WindowManager.LayoutParams attrs) {
- return false;
+ return attrs.type == TYPE_STATUS_BAR;
}
@Override
@@ -290,40 +285,11 @@
}
@Override
- public void beginLayoutLw(int displayId, int displayWidth, int displayHeight,
- int displayRotation, int uiMode) {
-
- }
-
- @Override
public int getSystemDecorLayerLw() {
return 0;
}
@Override
- public void getContentRectLw(Rect r) {
-
- }
-
- @Override
- public void layoutWindowLw(WindowState win,
- WindowState attached) {
-
- }
-
- @Override
- public boolean getInsetHintLw(WindowManager.LayoutParams attrs, Rect taskBounds,
- int displayRotation, int displayWidth, int displayHeight, Rect outContentInsets,
- Rect outStableInsets, Rect outOutsets) {
- return false;
- }
-
- @Override
- public void finishLayoutLw() {
-
- }
-
- @Override
public void beginPostLayoutPolicyLw(int displayWidth, int displayHeight) {
}
@@ -582,11 +548,6 @@
}
@Override
- public int getInputMethodWindowVisibleHeightLw() {
- return 0;
- }
-
- @Override
public void setCurrentUserLw(int newUserId) {
}
diff --git a/services/usage/java/com/android/server/usage/AppIdleHistory.java b/services/usage/java/com/android/server/usage/AppIdleHistory.java
index e5d3915..c5ca330 100644
--- a/services/usage/java/com/android/server/usage/AppIdleHistory.java
+++ b/services/usage/java/com/android/server/usage/AppIdleHistory.java
@@ -103,7 +103,7 @@
long lastUsedScreenTime;
@StandbyBuckets int currentBucket;
String bucketingReason;
- int lastInformedState;
+ int lastInformedBucket;
}
AppIdleHistory(File storageDir, long elapsedRealtime) {
@@ -333,13 +333,12 @@
}
boolean shouldInformListeners(String packageName, int userId,
- long elapsedRealtime, boolean isIdle) {
+ long elapsedRealtime, int bucket) {
ArrayMap<String, AppUsageHistory> userHistory = getUserHistory(userId);
AppUsageHistory appUsageHistory = getPackageHistory(userHistory, packageName,
elapsedRealtime, true);
- int targetState = isIdle? STATE_IDLE : STATE_ACTIVE;
- if (appUsageHistory.lastInformedState != (isIdle ? STATE_IDLE : STATE_ACTIVE)) {
- appUsageHistory.lastInformedState = targetState;
+ if (appUsageHistory.lastInformedBucket != bucket) {
+ appUsageHistory.lastInformedBucket = bucket;
return true;
}
return false;
diff --git a/services/usage/java/com/android/server/usage/AppStandbyController.java b/services/usage/java/com/android/server/usage/AppStandbyController.java
index 17fde57..5623a68 100644
--- a/services/usage/java/com/android/server/usage/AppStandbyController.java
+++ b/services/usage/java/com/android/server/usage/AppStandbyController.java
@@ -160,7 +160,6 @@
private final Context mContext;
// TODO: Provide a mechanism to set an external bucketing service
- private boolean mUseInternalBucketingHeuristics = true;
private AppWidgetManager mAppWidgetManager;
private PowerManager mPowerManager;
@@ -367,29 +366,33 @@
Slog.d(TAG, " Checking idle state for " + packageName);
}
if (isSpecial) {
- maybeInformListeners(packageName, userId, elapsedRealtime, false);
- } else if (mUseInternalBucketingHeuristics) {
+ maybeInformListeners(packageName, userId, elapsedRealtime,
+ AppStandby.STANDBY_BUCKET_ACTIVE);
+ } else {
synchronized (mAppIdleLock) {
- int oldBucket = mAppIdleHistory.getAppStandbyBucket(packageName, userId,
- elapsedRealtime);
String bucketingReason = mAppIdleHistory.getAppStandbyReason(packageName,
userId, elapsedRealtime);
- if (bucketingReason != null
- && (bucketingReason.equals(AppStandby.REASON_FORCED)
- || bucketingReason.startsWith(AppStandby.REASON_PREDICTED))) {
+ // If the bucket was forced by the developer, leave it alone
+ if (AppStandby.REASON_FORCED.equals(bucketingReason)) {
continue;
}
- int newBucket = getBucketForLocked(packageName, userId,
- elapsedRealtime);
- if (DEBUG) {
- Slog.d(TAG, " Old bucket=" + oldBucket
- + ", newBucket=" + newBucket);
- }
- if (oldBucket != newBucket) {
- mAppIdleHistory.setAppStandbyBucket(packageName, userId,
- elapsedRealtime, newBucket, AppStandby.REASON_TIMEOUT);
- maybeInformListeners(packageName, userId, elapsedRealtime,
- newBucket >= AppStandby.STANDBY_BUCKET_RARE);
+ // If the bucket was moved up due to usage, let the timeouts apply.
+ if (AppStandby.REASON_USAGE.equals(bucketingReason)
+ || AppStandby.REASON_TIMEOUT.equals(bucketingReason)) {
+ int oldBucket = mAppIdleHistory.getAppStandbyBucket(packageName, userId,
+ elapsedRealtime);
+ int newBucket = getBucketForLocked(packageName, userId,
+ elapsedRealtime);
+ if (DEBUG) {
+ Slog.d(TAG, " Old bucket=" + oldBucket
+ + ", newBucket=" + newBucket);
+ }
+ if (oldBucket < newBucket) {
+ mAppIdleHistory.setAppStandbyBucket(packageName, userId,
+ elapsedRealtime, newBucket, AppStandby.REASON_TIMEOUT);
+ maybeInformListeners(packageName, userId, elapsedRealtime,
+ newBucket);
+ }
}
}
}
@@ -403,12 +406,12 @@
}
private void maybeInformListeners(String packageName, int userId,
- long elapsedRealtime, boolean isIdle) {
+ long elapsedRealtime, int bucket) {
synchronized (mAppIdleLock) {
if (mAppIdleHistory.shouldInformListeners(packageName, userId,
- elapsedRealtime, isIdle)) {
+ elapsedRealtime, bucket)) {
mHandler.sendMessage(mHandler.obtainMessage(MSG_INFORM_LISTENERS,
- userId, isIdle ? 1 : 0, packageName));
+ userId, bucket, packageName));
}
}
}
@@ -461,11 +464,13 @@
if (DEBUG) Slog.i(TAG, "DeviceIdleMode changed to " + deviceIdle);
boolean paroled = false;
synchronized (mAppIdleLock) {
- final long timeSinceLastParole = mInjector.currentTimeMillis() - mLastAppIdleParoledTime;
+ final long timeSinceLastParole =
+ mInjector.currentTimeMillis() - mLastAppIdleParoledTime;
if (!deviceIdle
&& timeSinceLastParole >= mAppIdleParoleIntervalMillis) {
if (DEBUG) {
- Slog.i(TAG, "Bringing idle apps out of inactive state due to deviceIdleMode=false");
+ Slog.i(TAG,
+ "Bringing idle apps out of inactive state due to deviceIdleMode=false");
}
paroled = true;
} else if (deviceIdle) {
@@ -491,7 +496,8 @@
|| event.mEventType == UsageEvents.Event.USER_INTERACTION)) {
mAppIdleHistory.reportUsage(event.mPackage, userId, elapsedRealtime);
if (previouslyIdle) {
- maybeInformListeners(event.mPackage, userId, elapsedRealtime, false);
+ maybeInformListeners(event.mPackage, userId, elapsedRealtime,
+ AppStandby.STANDBY_BUCKET_ACTIVE);
notifyBatteryStats(event.mPackage, userId, false);
}
}
@@ -729,7 +735,8 @@
void setAppStandbyBucket(String packageName, int userId, @StandbyBuckets int newBucket,
String reason, long elapsedRealtime) {
- mAppIdleHistory.setAppStandbyBucket(packageName, userId, elapsedRealtime, newBucket, reason);
+ mAppIdleHistory.setAppStandbyBucket(packageName, userId, elapsedRealtime, newBucket,
+ reason);
}
private boolean isActiveDeviceAdmin(String packageName, int userId) {
@@ -786,9 +793,10 @@
return packageName != null && packageName.equals(activeScorer);
}
- void informListeners(String packageName, int userId, boolean isIdle) {
+ void informListeners(String packageName, int userId, int bucket) {
+ final boolean idle = bucket >= AppStandby.STANDBY_BUCKET_RARE;
for (AppIdleStateChangeListener listener : mPackageAccessListeners) {
- listener.onAppIdleStateChanged(packageName, userId, isIdle);
+ listener.onAppIdleStateChanged(packageName, userId, idle, bucket);
}
}
@@ -1037,7 +1045,7 @@
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_INFORM_LISTENERS:
- informListeners((String) msg.obj, msg.arg1, msg.arg2 == 1);
+ informListeners((String) msg.obj, msg.arg1, msg.arg2);
break;
case MSG_FORCE_IDLE_STATE:
@@ -1187,7 +1195,8 @@
mAppStandbyScreenThresholds = parseLongArray(screenThresholdsValue,
SCREEN_TIME_THRESHOLDS);
- String elapsedThresholdsValue = mParser.getString(KEY_ELAPSED_TIME_THRESHOLDS, null);
+ String elapsedThresholdsValue = mParser.getString(KEY_ELAPSED_TIME_THRESHOLDS,
+ null);
mAppStandbyElapsedThresholds = parseLongArray(elapsedThresholdsValue,
ELAPSED_TIME_THRESHOLDS);
}
diff --git a/services/usb/java/com/android/server/usb/UsbAlsaManager.java b/services/usb/java/com/android/server/usb/UsbAlsaManager.java
index 7bea8a1..d359b70 100644
--- a/services/usb/java/com/android/server/usb/UsbAlsaManager.java
+++ b/services/usb/java/com/android/server/usb/UsbAlsaManager.java
@@ -255,7 +255,6 @@
}
private void alsaFileAdded(String name) {
- Slog.i(TAG, "alsaFileAdded(" + name + ")");
int type = AlsaDevice.TYPE_UNKNOWN;
int card = -1, device = -1;
diff --git a/services/usb/java/com/android/server/usb/UsbHostManager.java b/services/usb/java/com/android/server/usb/UsbHostManager.java
index c29a335..095fdc6 100644
--- a/services/usb/java/com/android/server/usb/UsbHostManager.java
+++ b/services/usb/java/com/android/server/usb/UsbHostManager.java
@@ -19,8 +19,13 @@
import android.annotation.Nullable;
import android.content.ComponentName;
import android.content.Context;
+import android.hardware.usb.UsbConfiguration;
import android.hardware.usb.UsbConstants;
import android.hardware.usb.UsbDevice;
+import android.hardware.usb.UsbDeviceConnection;
+import android.hardware.usb.UsbEndpoint;
+import android.hardware.usb.UsbInterface;
+import android.hardware.usb.UsbManager;
import android.os.Bundle;
import android.os.ParcelFileDescriptor;
import android.text.TextUtils;
@@ -32,6 +37,7 @@
import com.android.server.usb.descriptors.report.TextReportCanvas;
import com.android.server.usb.descriptors.tree.UsbDescriptorsTree;
+import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
@@ -44,23 +50,28 @@
private final Context mContext;
+ // contains all connected USB devices
+ private final HashMap<String, UsbDevice> mDevices = new HashMap<>();
+
// USB busses to exclude from USB host support
private final String[] mHostBlacklist;
+ private final Object mLock = new Object();
+
+ private UsbDevice mNewDevice;
+ private UsbConfiguration mNewConfiguration;
+ private UsbInterface mNewInterface;
+ private ArrayList<UsbConfiguration> mNewConfigurations;
+ private ArrayList<UsbInterface> mNewInterfaces;
+ private ArrayList<UsbEndpoint> mNewEndpoints;
+
private final UsbAlsaManager mUsbAlsaManager;
private final UsbSettingsManager mSettingsManager;
- private final Object mLock = new Object();
@GuardedBy("mLock")
- // contains all connected USB devices
- private final HashMap<String, UsbDevice> mDevices = new HashMap<>();
-
- private Object mSettingsLock = new Object();
- @GuardedBy("mSettingsLock")
private UsbProfileGroupSettingsManager mCurrentSettings;
- private Object mHandlerLock = new Object();
- @GuardedBy("mHandlerLock")
+ @GuardedBy("mLock")
private ComponentName mUsbDeviceConnectionHandler;
public UsbHostManager(Context context, UsbAlsaManager alsaManager,
@@ -80,33 +91,33 @@
}
public void setCurrentUserSettings(UsbProfileGroupSettingsManager settings) {
- synchronized (mSettingsLock) {
+ synchronized (mLock) {
mCurrentSettings = settings;
}
}
private UsbProfileGroupSettingsManager getCurrentUserSettings() {
- synchronized (mSettingsLock) {
+ synchronized (mLock) {
return mCurrentSettings;
}
}
public void setUsbDeviceConnectionHandler(@Nullable ComponentName usbDeviceConnectionHandler) {
- synchronized (mHandlerLock) {
+ synchronized (mLock) {
mUsbDeviceConnectionHandler = usbDeviceConnectionHandler;
}
}
private @Nullable ComponentName getUsbDeviceConnectionHandler() {
- synchronized (mHandlerLock) {
+ synchronized (mLock) {
return mUsbDeviceConnectionHandler;
}
}
- private boolean isBlackListed(String deviceAddress) {
+ private boolean isBlackListed(String deviceName) {
int count = mHostBlacklist.length;
for (int i = 0; i < count; i++) {
- if (deviceAddress.startsWith(mHostBlacklist[i])) {
+ if (deviceName.startsWith(mHostBlacklist[i])) {
return true;
}
}
@@ -125,73 +136,166 @@
}
/* Called from JNI in monitorUsbHostBus() to report new USB devices
- Returns true if successful, i.e. the USB Audio device descriptors are
- correctly parsed and the unique device is added to the audio device list.
+ Returns true if successful, in which case the JNI code will continue adding configurations,
+ interfaces and endpoints, and finally call endUsbDeviceAdded after all descriptors
+ have been processed
*/
@SuppressWarnings("unused")
- private boolean usbDeviceAdded(String deviceAddress, int deviceClass, int deviceSubclass,
- byte[] descriptors) {
+ private boolean beginUsbDeviceAdded(String deviceName, int vendorID, int productID,
+ int deviceClass, int deviceSubclass, int deviceProtocol,
+ String manufacturerName, String productName, int version, String serialNumber) {
+
if (DEBUG) {
- Slog.d(TAG, "usbDeviceAdded(" + deviceAddress + ") - start");
+ Slog.d(TAG, "usb:UsbHostManager.beginUsbDeviceAdded(" + deviceName + ")");
+ // Audio Class Codes:
+ // Audio: 0x01
+ // Audio Subclass Codes:
+ // undefined: 0x00
+ // audio control: 0x01
+ // audio streaming: 0x02
+ // midi streaming: 0x03
+
+ // some useful debugging info
+ Slog.d(TAG, "usb: nm:" + deviceName + " vnd:" + vendorID + " prd:" + productID + " cls:"
+ + deviceClass + " sub:" + deviceSubclass + " proto:" + deviceProtocol);
}
- // check class/subclass first as it is more likely to be blacklisted
- if (isBlackListed(deviceClass, deviceSubclass) || isBlackListed(deviceAddress)) {
- if (DEBUG) {
- Slog.d(TAG, "device is black listed");
- }
+ // OK this is non-obvious, but true. One can't tell if the device being attached is even
+ // potentially an audio device without parsing the interface descriptors, so punt on any
+ // such test until endUsbDeviceAdded() when we have that info.
+
+ if (isBlackListed(deviceName) ||
+ isBlackListed(deviceClass, deviceSubclass)) {
return false;
}
synchronized (mLock) {
- if (mDevices.get(deviceAddress) != null) {
- Slog.w(TAG, "device already on mDevices list: " + deviceAddress);
- //TODO If this is the same peripheral as is being connected, replace
- // it with the new connection.
+ if (mDevices.get(deviceName) != null) {
+ Slog.w(TAG, "device already on mDevices list: " + deviceName);
return false;
}
- UsbDescriptorParser parser = new UsbDescriptorParser(deviceAddress);
- if (parser.parseDescriptors(descriptors)) {
-
- UsbDevice newDevice = parser.toAndroidUsbDevice();
- mDevices.put(deviceAddress, newDevice);
-
- // It is fine to call this only for the current user as all broadcasts are sent to
- // all profiles of the user and the dialogs should only show once.
- ComponentName usbDeviceConnectionHandler = getUsbDeviceConnectionHandler();
- if (usbDeviceConnectionHandler == null) {
- getCurrentUserSettings().deviceAttached(newDevice);
- } else {
- getCurrentUserSettings().deviceAttachedForFixedHandler(newDevice,
- usbDeviceConnectionHandler);
- }
-
- // Headset?
- boolean isInputHeadset = parser.isInputHeadset();
- boolean isOutputHeadset = parser.isOutputHeadset();
- Slog.i(TAG, "---- isHeadset[in: " + isInputHeadset
- + " , out: " + isOutputHeadset + "]");
-
- mUsbAlsaManager.usbDeviceAdded(newDevice, isInputHeadset, isOutputHeadset);
- } else {
- Slog.e(TAG, "Error parsing USB device descriptors for " + deviceAddress);
+ if (mNewDevice != null) {
+ Slog.e(TAG, "mNewDevice is not null in endUsbDeviceAdded");
return false;
}
- }
- if (DEBUG) {
- Slog.d(TAG, "beginUsbDeviceAdded(" + deviceAddress + ") end");
+ // Create version string in "%.%" format
+ String versionString = Integer.toString(version >> 8) + "." + (version & 0xFF);
+
+ mNewDevice = new UsbDevice(deviceName, vendorID, productID,
+ deviceClass, deviceSubclass, deviceProtocol,
+ manufacturerName, productName, versionString, serialNumber);
+
+ mNewConfigurations = new ArrayList<>();
+ mNewInterfaces = new ArrayList<>();
+ mNewEndpoints = new ArrayList<>();
}
return true;
}
+ /* Called from JNI in monitorUsbHostBus() to report new USB configuration for the device
+ currently being added. Returns true if successful, false in case of error.
+ */
+ @SuppressWarnings("unused")
+ private void addUsbConfiguration(int id, String name, int attributes, int maxPower) {
+ if (mNewConfiguration != null) {
+ mNewConfiguration.setInterfaces(
+ mNewInterfaces.toArray(new UsbInterface[mNewInterfaces.size()]));
+ mNewInterfaces.clear();
+ }
+
+ mNewConfiguration = new UsbConfiguration(id, name, attributes, maxPower);
+ mNewConfigurations.add(mNewConfiguration);
+ }
+
+ /* Called from JNI in monitorUsbHostBus() to report new USB interface for the device
+ currently being added. Returns true if successful, false in case of error.
+ */
+ @SuppressWarnings("unused")
+ private void addUsbInterface(int id, String name, int altSetting,
+ int Class, int subClass, int protocol) {
+ if (mNewInterface != null) {
+ mNewInterface.setEndpoints(
+ mNewEndpoints.toArray(new UsbEndpoint[mNewEndpoints.size()]));
+ mNewEndpoints.clear();
+ }
+
+ mNewInterface = new UsbInterface(id, altSetting, name, Class, subClass, protocol);
+ mNewInterfaces.add(mNewInterface);
+ }
+
+ /* Called from JNI in monitorUsbHostBus() to report new USB endpoint for the device
+ currently being added. Returns true if successful, false in case of error.
+ */
+ @SuppressWarnings("unused")
+ private void addUsbEndpoint(int address, int attributes, int maxPacketSize, int interval) {
+ mNewEndpoints.add(new UsbEndpoint(address, attributes, maxPacketSize, interval));
+ }
+
+ /* Called from JNI in monitorUsbHostBus() to finish adding a new device */
+ @SuppressWarnings("unused")
+ private void endUsbDeviceAdded() {
+ if (DEBUG) {
+ Slog.d(TAG, "usb:UsbHostManager.endUsbDeviceAdded()");
+ }
+ if (mNewInterface != null) {
+ mNewInterface.setEndpoints(
+ mNewEndpoints.toArray(new UsbEndpoint[mNewEndpoints.size()]));
+ }
+ if (mNewConfiguration != null) {
+ mNewConfiguration.setInterfaces(
+ mNewInterfaces.toArray(new UsbInterface[mNewInterfaces.size()]));
+ }
+
+
+ synchronized (mLock) {
+ if (mNewDevice != null) {
+ mNewDevice.setConfigurations(
+ mNewConfigurations.toArray(
+ new UsbConfiguration[mNewConfigurations.size()]));
+ mDevices.put(mNewDevice.getDeviceName(), mNewDevice);
+ Slog.d(TAG, "Added device " + mNewDevice);
+
+ // It is fine to call this only for the current user as all broadcasts are sent to
+ // all profiles of the user and the dialogs should only show once.
+ ComponentName usbDeviceConnectionHandler = getUsbDeviceConnectionHandler();
+ if (usbDeviceConnectionHandler == null) {
+ getCurrentUserSettings().deviceAttached(mNewDevice);
+ } else {
+ getCurrentUserSettings().deviceAttachedForFixedHandler(mNewDevice,
+ usbDeviceConnectionHandler);
+ }
+ // deviceName is something like: "/dev/bus/usb/001/001"
+ UsbDescriptorParser parser = new UsbDescriptorParser();
+ boolean isInputHeadset = false;
+ boolean isOutputHeadset = false;
+ if (parser.parseDevice(mNewDevice.getDeviceName())) {
+ isInputHeadset = parser.isInputHeadset();
+ isOutputHeadset = parser.isOutputHeadset();
+ Slog.i(TAG, "---- isHeadset[in: " + isInputHeadset
+ + " , out: " + isOutputHeadset + "]");
+ }
+ mUsbAlsaManager.usbDeviceAdded(mNewDevice,
+ isInputHeadset, isOutputHeadset);
+ } else {
+ Slog.e(TAG, "mNewDevice is null in endUsbDeviceAdded");
+ }
+ mNewDevice = null;
+ mNewConfigurations = null;
+ mNewInterfaces = null;
+ mNewEndpoints = null;
+ mNewConfiguration = null;
+ mNewInterface = null;
+ }
+ }
+
/* Called from JNI in monitorUsbHostBus to report USB device removal */
@SuppressWarnings("unused")
- private void usbDeviceRemoved(String deviceAddress) {
+ private void usbDeviceRemoved(String deviceName) {
synchronized (mLock) {
- UsbDevice device = mDevices.remove(deviceAddress);
+ UsbDevice device = mDevices.remove(deviceName);
if (device != null) {
mUsbAlsaManager.usbDeviceRemoved(device);
mSettingsManager.usbDeviceRemoved(device);
@@ -218,37 +322,32 @@
}
}
- /**
- * Opens the specified USB device
- * @hide
- */
- public ParcelFileDescriptor openDevice(String deviceAddress, UsbUserSettingsManager settings) {
+ /* Opens the specified USB device */
+ public ParcelFileDescriptor openDevice(String deviceName, UsbUserSettingsManager settings) {
synchronized (mLock) {
- if (isBlackListed(deviceAddress)) {
+ if (isBlackListed(deviceName)) {
throw new SecurityException("USB device is on a restricted bus");
}
- UsbDevice device = mDevices.get(deviceAddress);
+ UsbDevice device = mDevices.get(deviceName);
if (device == null) {
// if it is not in mDevices, it either does not exist or is blacklisted
throw new IllegalArgumentException(
- "device " + deviceAddress + " does not exist or is restricted");
+ "device " + deviceName + " does not exist or is restricted");
}
settings.checkPermission(device);
- return nativeOpenDevice(deviceAddress);
+ return nativeOpenDevice(deviceName);
}
}
public void dump(IndentingPrintWriter pw) {
- pw.println("USB Host State:");
- synchronized (mHandlerLock) {
- if (mUsbDeviceConnectionHandler != null) {
- pw.println("Default USB Host Connection handler: " + mUsbDeviceConnectionHandler);
- }
- }
synchronized (mLock) {
+ pw.println("USB Host State:");
for (String name : mDevices.keySet()) {
pw.println(" " + name + ": " + mDevices.get(name));
}
+ if (mUsbDeviceConnectionHandler != null) {
+ pw.println("Default USB Host Connection handler: " + mUsbDeviceConnectionHandler);
+ }
Collection<UsbDevice> devices = mDevices.values();
if (devices.size() != 0) {
@@ -256,12 +355,17 @@
for (UsbDevice device : devices) {
StringBuilder stringBuilder = new StringBuilder();
- UsbDescriptorParser parser = new UsbDescriptorParser(device.getDeviceName());
- if (parser.parseDevice()) {
+ UsbDescriptorParser parser = new UsbDescriptorParser();
+ if (parser.parseDevice(device.getDeviceName())) {
UsbDescriptorsTree descriptorTree = new UsbDescriptorsTree();
descriptorTree.parse(parser);
- descriptorTree.report(new TextReportCanvas(parser, stringBuilder));
+ UsbManager usbManager =
+ (UsbManager) mContext.getSystemService(Context.USB_SERVICE);
+ UsbDeviceConnection connection = usbManager.openDevice(device);
+
+ descriptorTree.report(new TextReportCanvas(connection, stringBuilder));
+ connection.close();
stringBuilder.append("isHeadset[in: " + parser.isInputHeadset()
+ " , out: " + parser.isOutputHeadset() + "]");
@@ -277,5 +381,5 @@
}
private native void monitorUsbHostBus();
- private native ParcelFileDescriptor nativeOpenDevice(String deviceAddress);
+ private native ParcelFileDescriptor nativeOpenDevice(String deviceName);
}
diff --git a/services/usb/java/com/android/server/usb/UsbProfileGroupSettingsManager.java b/services/usb/java/com/android/server/usb/UsbProfileGroupSettingsManager.java
index ebb5a62..917e651 100644
--- a/services/usb/java/com/android/server/usb/UsbProfileGroupSettingsManager.java
+++ b/services/usb/java/com/android/server/usb/UsbProfileGroupSettingsManager.java
@@ -32,9 +32,10 @@
import android.content.pm.ResolveInfo;
import android.content.pm.UserInfo;
import android.content.res.XmlResourceParser;
+import android.hardware.usb.AccessoryFilter;
+import android.hardware.usb.DeviceFilter;
import android.hardware.usb.UsbAccessory;
import android.hardware.usb.UsbDevice;
-import android.hardware.usb.UsbInterface;
import android.hardware.usb.UsbManager;
import android.os.AsyncTask;
import android.os.Environment;
@@ -58,7 +59,6 @@
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
-import org.xmlpull.v1.XmlSerializer;
import java.io.File;
import java.io.FileInputStream;
@@ -71,7 +71,6 @@
import java.util.Iterator;
import java.util.List;
import java.util.Map;
-import java.util.Objects;
class UsbProfileGroupSettingsManager {
private static final String TAG = UsbProfileGroupSettingsManager.class.getSimpleName();
@@ -157,404 +156,6 @@
}
}
- // This class is used to describe a USB device.
- // When used in HashMaps all values must be specified,
- // but wildcards can be used for any of the fields in
- // the package meta-data.
- private static class DeviceFilter {
- // USB Vendor ID (or -1 for unspecified)
- public final int mVendorId;
- // USB Product ID (or -1 for unspecified)
- public final int mProductId;
- // USB device or interface class (or -1 for unspecified)
- public final int mClass;
- // USB device subclass (or -1 for unspecified)
- public final int mSubclass;
- // USB device protocol (or -1 for unspecified)
- public final int mProtocol;
- // USB device manufacturer name string (or null for unspecified)
- public final String mManufacturerName;
- // USB device product name string (or null for unspecified)
- public final String mProductName;
- // USB device serial number string (or null for unspecified)
- public final String mSerialNumber;
-
- public DeviceFilter(int vid, int pid, int clasz, int subclass, int protocol,
- String manufacturer, String product, String serialnum) {
- mVendorId = vid;
- mProductId = pid;
- mClass = clasz;
- mSubclass = subclass;
- mProtocol = protocol;
- mManufacturerName = manufacturer;
- mProductName = product;
- mSerialNumber = serialnum;
- }
-
- public DeviceFilter(UsbDevice device) {
- mVendorId = device.getVendorId();
- mProductId = device.getProductId();
- mClass = device.getDeviceClass();
- mSubclass = device.getDeviceSubclass();
- mProtocol = device.getDeviceProtocol();
- mManufacturerName = device.getManufacturerName();
- mProductName = device.getProductName();
- mSerialNumber = device.getSerialNumber();
- }
-
- public static DeviceFilter read(XmlPullParser parser)
- throws XmlPullParserException, IOException {
- int vendorId = -1;
- int productId = -1;
- int deviceClass = -1;
- int deviceSubclass = -1;
- int deviceProtocol = -1;
- String manufacturerName = null;
- String productName = null;
- String serialNumber = null;
-
- int count = parser.getAttributeCount();
- for (int i = 0; i < count; i++) {
- String name = parser.getAttributeName(i);
- String value = parser.getAttributeValue(i);
- // Attribute values are ints or strings
- if ("manufacturer-name".equals(name)) {
- manufacturerName = value;
- } else if ("product-name".equals(name)) {
- productName = value;
- } else if ("serial-number".equals(name)) {
- serialNumber = value;
- } else {
- int intValue;
- int radix = 10;
- if (value != null && value.length() > 2 && value.charAt(0) == '0' &&
- (value.charAt(1) == 'x' || value.charAt(1) == 'X')) {
- // allow hex values starting with 0x or 0X
- radix = 16;
- value = value.substring(2);
- }
- try {
- intValue = Integer.parseInt(value, radix);
- } catch (NumberFormatException e) {
- Slog.e(TAG, "invalid number for field " + name, e);
- continue;
- }
- if ("vendor-id".equals(name)) {
- vendorId = intValue;
- } else if ("product-id".equals(name)) {
- productId = intValue;
- } else if ("class".equals(name)) {
- deviceClass = intValue;
- } else if ("subclass".equals(name)) {
- deviceSubclass = intValue;
- } else if ("protocol".equals(name)) {
- deviceProtocol = intValue;
- }
- }
- }
- return new DeviceFilter(vendorId, productId,
- deviceClass, deviceSubclass, deviceProtocol,
- manufacturerName, productName, serialNumber);
- }
-
- public void write(XmlSerializer serializer) throws IOException {
- serializer.startTag(null, "usb-device");
- if (mVendorId != -1) {
- serializer.attribute(null, "vendor-id", Integer.toString(mVendorId));
- }
- if (mProductId != -1) {
- serializer.attribute(null, "product-id", Integer.toString(mProductId));
- }
- if (mClass != -1) {
- serializer.attribute(null, "class", Integer.toString(mClass));
- }
- if (mSubclass != -1) {
- serializer.attribute(null, "subclass", Integer.toString(mSubclass));
- }
- if (mProtocol != -1) {
- serializer.attribute(null, "protocol", Integer.toString(mProtocol));
- }
- if (mManufacturerName != null) {
- serializer.attribute(null, "manufacturer-name", mManufacturerName);
- }
- if (mProductName != null) {
- serializer.attribute(null, "product-name", mProductName);
- }
- if (mSerialNumber != null) {
- serializer.attribute(null, "serial-number", mSerialNumber);
- }
- serializer.endTag(null, "usb-device");
- }
-
- private boolean matches(int clasz, int subclass, int protocol) {
- return ((mClass == -1 || clasz == mClass) &&
- (mSubclass == -1 || subclass == mSubclass) &&
- (mProtocol == -1 || protocol == mProtocol));
- }
-
- public boolean matches(UsbDevice device) {
- if (mVendorId != -1 && device.getVendorId() != mVendorId) return false;
- if (mProductId != -1 && device.getProductId() != mProductId) return false;
- if (mManufacturerName != null && device.getManufacturerName() == null) return false;
- if (mProductName != null && device.getProductName() == null) return false;
- if (mSerialNumber != null && device.getSerialNumber() == null) return false;
- if (mManufacturerName != null && device.getManufacturerName() != null &&
- !mManufacturerName.equals(device.getManufacturerName())) return false;
- if (mProductName != null && device.getProductName() != null &&
- !mProductName.equals(device.getProductName())) return false;
- if (mSerialNumber != null && device.getSerialNumber() != null &&
- !mSerialNumber.equals(device.getSerialNumber())) return false;
-
- // check device class/subclass/protocol
- if (matches(device.getDeviceClass(), device.getDeviceSubclass(),
- device.getDeviceProtocol())) return true;
-
- // if device doesn't match, check the interfaces
- int count = device.getInterfaceCount();
- for (int i = 0; i < count; i++) {
- UsbInterface intf = device.getInterface(i);
- if (matches(intf.getInterfaceClass(), intf.getInterfaceSubclass(),
- intf.getInterfaceProtocol())) return true;
- }
-
- return false;
- }
-
- /**
- * If the device described by {@code device} covered by this filter?
- *
- * @param device The device
- *
- * @return {@code true} iff this filter covers the {@code device}
- */
- public boolean contains(DeviceFilter device) {
- // -1 and null means "match anything"
-
- if (mVendorId != -1 && device.mVendorId != mVendorId) return false;
- if (mProductId != -1 && device.mProductId != mProductId) return false;
- if (mManufacturerName != null && !Objects.equals(mManufacturerName,
- device.mManufacturerName)) {
- return false;
- }
- if (mProductName != null && !Objects.equals(mProductName, device.mProductName)) {
- return false;
- }
- if (mSerialNumber != null
- && !Objects.equals(mSerialNumber, device.mSerialNumber)) {
- return false;
- }
-
- // check device class/subclass/protocol
- return matches(device.mClass, device.mSubclass, device.mProtocol);
- }
-
- @Override
- public boolean equals(Object obj) {
- // can't compare if we have wildcard strings
- if (mVendorId == -1 || mProductId == -1 ||
- mClass == -1 || mSubclass == -1 || mProtocol == -1) {
- return false;
- }
- if (obj instanceof DeviceFilter) {
- DeviceFilter filter = (DeviceFilter)obj;
-
- if (filter.mVendorId != mVendorId ||
- filter.mProductId != mProductId ||
- filter.mClass != mClass ||
- filter.mSubclass != mSubclass ||
- filter.mProtocol != mProtocol) {
- return(false);
- }
- if ((filter.mManufacturerName != null &&
- mManufacturerName == null) ||
- (filter.mManufacturerName == null &&
- mManufacturerName != null) ||
- (filter.mProductName != null &&
- mProductName == null) ||
- (filter.mProductName == null &&
- mProductName != null) ||
- (filter.mSerialNumber != null &&
- mSerialNumber == null) ||
- (filter.mSerialNumber == null &&
- mSerialNumber != null)) {
- return(false);
- }
- if ((filter.mManufacturerName != null &&
- mManufacturerName != null &&
- !mManufacturerName.equals(filter.mManufacturerName)) ||
- (filter.mProductName != null &&
- mProductName != null &&
- !mProductName.equals(filter.mProductName)) ||
- (filter.mSerialNumber != null &&
- mSerialNumber != null &&
- !mSerialNumber.equals(filter.mSerialNumber))) {
- return false;
- }
- return true;
- }
- if (obj instanceof UsbDevice) {
- UsbDevice device = (UsbDevice)obj;
- if (device.getVendorId() != mVendorId ||
- device.getProductId() != mProductId ||
- device.getDeviceClass() != mClass ||
- device.getDeviceSubclass() != mSubclass ||
- device.getDeviceProtocol() != mProtocol) {
- return(false);
- }
- if ((mManufacturerName != null && device.getManufacturerName() == null) ||
- (mManufacturerName == null && device.getManufacturerName() != null) ||
- (mProductName != null && device.getProductName() == null) ||
- (mProductName == null && device.getProductName() != null) ||
- (mSerialNumber != null && device.getSerialNumber() == null) ||
- (mSerialNumber == null && device.getSerialNumber() != null)) {
- return(false);
- }
- if ((device.getManufacturerName() != null &&
- !mManufacturerName.equals(device.getManufacturerName())) ||
- (device.getProductName() != null &&
- !mProductName.equals(device.getProductName())) ||
- (device.getSerialNumber() != null &&
- !mSerialNumber.equals(device.getSerialNumber()))) {
- return false;
- }
- return true;
- }
- return false;
- }
-
- @Override
- public int hashCode() {
- return (((mVendorId << 16) | mProductId) ^
- ((mClass << 16) | (mSubclass << 8) | mProtocol));
- }
-
- @Override
- public String toString() {
- return "DeviceFilter[mVendorId=" + mVendorId + ",mProductId=" + mProductId +
- ",mClass=" + mClass + ",mSubclass=" + mSubclass +
- ",mProtocol=" + mProtocol + ",mManufacturerName=" + mManufacturerName +
- ",mProductName=" + mProductName + ",mSerialNumber=" + mSerialNumber +
- "]";
- }
- }
-
- // This class is used to describe a USB accessory.
- // When used in HashMaps all values must be specified,
- // but wildcards can be used for any of the fields in
- // the package meta-data.
- private static class AccessoryFilter {
- // USB accessory manufacturer (or null for unspecified)
- public final String mManufacturer;
- // USB accessory model (or null for unspecified)
- public final String mModel;
- // USB accessory version (or null for unspecified)
- public final String mVersion;
-
- public AccessoryFilter(String manufacturer, String model, String version) {
- mManufacturer = manufacturer;
- mModel = model;
- mVersion = version;
- }
-
- public AccessoryFilter(UsbAccessory accessory) {
- mManufacturer = accessory.getManufacturer();
- mModel = accessory.getModel();
- mVersion = accessory.getVersion();
- }
-
- public static AccessoryFilter read(XmlPullParser parser)
- throws XmlPullParserException, IOException {
- String manufacturer = null;
- String model = null;
- String version = null;
-
- int count = parser.getAttributeCount();
- for (int i = 0; i < count; i++) {
- String name = parser.getAttributeName(i);
- String value = parser.getAttributeValue(i);
-
- if ("manufacturer".equals(name)) {
- manufacturer = value;
- } else if ("model".equals(name)) {
- model = value;
- } else if ("version".equals(name)) {
- version = value;
- }
- }
- return new AccessoryFilter(manufacturer, model, version);
- }
-
- public void write(XmlSerializer serializer)throws IOException {
- serializer.startTag(null, "usb-accessory");
- if (mManufacturer != null) {
- serializer.attribute(null, "manufacturer", mManufacturer);
- }
- if (mModel != null) {
- serializer.attribute(null, "model", mModel);
- }
- if (mVersion != null) {
- serializer.attribute(null, "version", mVersion);
- }
- serializer.endTag(null, "usb-accessory");
- }
-
- public boolean matches(UsbAccessory acc) {
- if (mManufacturer != null && !acc.getManufacturer().equals(mManufacturer)) return false;
- if (mModel != null && !acc.getModel().equals(mModel)) return false;
- return !(mVersion != null && !acc.getVersion().equals(mVersion));
- }
-
- /**
- * Is the accessories described {@code accessory} covered by this filter?
- *
- * @param accessory A filter describing the accessory
- *
- * @return {@code true} iff this the filter covers the accessory
- */
- public boolean contains(AccessoryFilter accessory) {
- if (mManufacturer != null && !Objects.equals(accessory.mManufacturer, mManufacturer)) {
- return false;
- }
- if (mModel != null && !Objects.equals(accessory.mModel, mModel)) return false;
- return !(mVersion != null && !Objects.equals(accessory.mVersion, mVersion));
- }
-
- @Override
- public boolean equals(Object obj) {
- // can't compare if we have wildcard strings
- if (mManufacturer == null || mModel == null || mVersion == null) {
- return false;
- }
- if (obj instanceof AccessoryFilter) {
- AccessoryFilter filter = (AccessoryFilter)obj;
- return (mManufacturer.equals(filter.mManufacturer) &&
- mModel.equals(filter.mModel) &&
- mVersion.equals(filter.mVersion));
- }
- if (obj instanceof UsbAccessory) {
- UsbAccessory accessory = (UsbAccessory)obj;
- return (mManufacturer.equals(accessory.getManufacturer()) &&
- mModel.equals(accessory.getModel()) &&
- mVersion.equals(accessory.getVersion()));
- }
- return false;
- }
-
- @Override
- public int hashCode() {
- return ((mManufacturer == null ? 0 : mManufacturer.hashCode()) ^
- (mModel == null ? 0 : mModel.hashCode()) ^
- (mVersion == null ? 0 : mVersion.hashCode()));
- }
-
- @Override
- public String toString() {
- return "AccessoryFilter[mManufacturer=\"" + mManufacturer +
- "\", mModel=\"" + mModel +
- "\", mVersion=\"" + mVersion + "\"]";
- }
- }
-
private class MyPackageMonitor extends PackageMonitor {
@Override
public void onPackageAdded(String packageName, int uid) {
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbConfigDescriptor.java b/services/usb/java/com/android/server/usb/descriptors/UsbConfigDescriptor.java
index 511e59a..75279c6 100644
--- a/services/usb/java/com/android/server/usb/descriptors/UsbConfigDescriptor.java
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbConfigDescriptor.java
@@ -15,13 +15,8 @@
*/
package com.android.server.usb.descriptors;
-import android.hardware.usb.UsbConfiguration;
-import android.hardware.usb.UsbInterface;
-
import com.android.server.usb.descriptors.report.ReportCanvas;
-import java.util.ArrayList;
-
/**
* @hide
* An USB Config Descriptor.
@@ -40,9 +35,6 @@
// D4..0 Reserved, set to 0.
private byte mMaxPower; // 8:1 Maximum Power Consumption in 2mA units
- private ArrayList<UsbInterfaceDescriptor> mInterfaceDescriptors =
- new ArrayList<UsbInterfaceDescriptor>();
-
UsbConfigDescriptor(int length, byte type) {
super(length, type);
mHierarchyLevel = 2;
@@ -72,22 +64,6 @@
return mMaxPower;
}
- void addInterfaceDescriptor(UsbInterfaceDescriptor interfaceDesc) {
- mInterfaceDescriptors.add(interfaceDesc);
- }
-
- UsbConfiguration toAndroid(UsbDescriptorParser parser) {
- String name = parser.getDescriptorString(mConfigIndex);
- UsbConfiguration config = new
- UsbConfiguration(mConfigValue, name, mAttribs, mMaxPower);
- UsbInterface[] interfaces = new UsbInterface[mInterfaceDescriptors.size()];
- for (int index = 0; index < mInterfaceDescriptors.size(); index++) {
- interfaces[index] = mInterfaceDescriptors.get(index).toAndroid(parser);
- }
- config.setInterfaces(interfaces);
- return config;
- }
-
@Override
public int parseRawDescriptors(ByteStream stream) {
mTotalLength = stream.unpackUsbShort();
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbDescriptorParser.java b/services/usb/java/com/android/server/usb/descriptors/UsbDescriptorParser.java
index c5052c8..ad7bde5c 100644
--- a/services/usb/java/com/android/server/usb/descriptors/UsbDescriptorParser.java
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbDescriptorParser.java
@@ -15,7 +15,6 @@
*/
package com.android.server.usb.descriptors;
-import android.hardware.usb.UsbDevice;
import android.util.Log;
import java.util.ArrayList;
@@ -26,16 +25,11 @@
*/
public final class UsbDescriptorParser {
private static final String TAG = "UsbDescriptorParser";
- private static final boolean DEBUG = false;
-
- private final String mDeviceAddr;
// Descriptor Objects
- private static final int DESCRIPTORS_ALLOC_SIZE = 128;
private ArrayList<UsbDescriptor> mDescriptors = new ArrayList<UsbDescriptor>();
private UsbDeviceDescriptor mDeviceDescriptor;
- private UsbConfigDescriptor mCurConfigDescriptor;
private UsbInterfaceDescriptor mCurInterfaceDescriptor;
// The AudioClass spec implemented by the AudioClass Interfaces
@@ -43,13 +37,7 @@
// Obtained from the first AudioClass Header descriptor.
private int mACInterfacesSpec = UsbDeviceDescriptor.USBSPEC_1_0;
- public UsbDescriptorParser(String deviceAddr) {
- mDeviceAddr = deviceAddr;
- }
-
- public String getDeviceAddr() {
- return mDeviceAddr;
- }
+ public UsbDescriptorParser() {}
/**
* @return the USB Spec value associated with the Device descriptor for the
@@ -72,18 +60,6 @@
public int getACInterfaceSpec() {
return mACInterfacesSpec;
}
-
- private class UsbDescriptorsStreamFormatException extends Exception {
- String mMessage;
- UsbDescriptorsStreamFormatException(String message) {
- mMessage = message;
- }
-
- public String toString() {
- return "Descriptor Stream Format Exception: " + mMessage;
- }
- }
-
/**
* The probability (as returned by getHeadsetProbability() at which we conclude
* the peripheral is a headset.
@@ -91,8 +67,7 @@
private static final float IN_HEADSET_TRIGGER = 0.75f;
private static final float OUT_HEADSET_TRIGGER = 0.75f;
- private UsbDescriptor allocDescriptor(ByteStream stream)
- throws UsbDescriptorsStreamFormatException {
+ private UsbDescriptor allocDescriptor(ByteStream stream) {
stream.resetReadCount();
int length = stream.getUnsignedByte();
@@ -108,38 +83,15 @@
break;
case UsbDescriptor.DESCRIPTORTYPE_CONFIG:
- descriptor = mCurConfigDescriptor = new UsbConfigDescriptor(length, type);
- if (mDeviceDescriptor != null) {
- mDeviceDescriptor.addConfigDescriptor(mCurConfigDescriptor);
- } else {
- Log.e(TAG, "Config Descriptor found with no associated Device Descriptor!");
- throw new UsbDescriptorsStreamFormatException(
- "Config Descriptor found with no associated Device Descriptor!");
- }
+ descriptor = new UsbConfigDescriptor(length, type);
break;
case UsbDescriptor.DESCRIPTORTYPE_INTERFACE:
descriptor = mCurInterfaceDescriptor = new UsbInterfaceDescriptor(length, type);
- if (mCurConfigDescriptor != null) {
- mCurConfigDescriptor.addInterfaceDescriptor(mCurInterfaceDescriptor);
- } else {
- Log.e(TAG, "Interface Descriptor found with no associated Config Descriptor!");
- throw new UsbDescriptorsStreamFormatException(
- "Interface Descriptor found with no associated Config Descriptor!");
- }
break;
case UsbDescriptor.DESCRIPTORTYPE_ENDPOINT:
descriptor = new UsbEndpointDescriptor(length, type);
- if (mCurInterfaceDescriptor != null) {
- mCurInterfaceDescriptor.addEndpointDescriptor(
- (UsbEndpointDescriptor) descriptor);
- } else {
- Log.e(TAG,
- "Endpoint Descriptor found with no associated Interface Descriptor!");
- throw new UsbDescriptorsStreamFormatException(
- "Endpoint Descriptor found with no associated Interface Descriptor!");
- }
break;
/*
@@ -192,12 +144,8 @@
/**
* @hide
*/
- public boolean parseDescriptors(byte[] descriptors) {
- if (DEBUG) {
- Log.d(TAG, "parseDescriptors() - start");
- }
- // This will allow us to (probably) alloc mDescriptors just once.
- mDescriptors = new ArrayList<UsbDescriptor>(DESCRIPTORS_ALLOC_SIZE);
+ public void parseDescriptors(byte[] descriptors) {
+ mDescriptors.clear();
ByteStream stream = new ByteStream(descriptors);
while (stream.available() > 0) {
@@ -225,36 +173,21 @@
}
}
}
- if (DEBUG) {
- Log.d(TAG, "parseDescriptors() - end " + mDescriptors.size() + " descriptors.");
+ }
+
+ /**
+ * @hide
+ */
+ public boolean parseDevice(String deviceAddr) {
+ byte[] rawDescriptors = getRawDescriptors(deviceAddr);
+ if (rawDescriptors != null) {
+ parseDescriptors(rawDescriptors);
+ return true;
}
- return true;
+ return false;
}
- /**
- * @hide
- */
- public boolean parseDevice() {
- byte[] rawDescriptors = getRawDescriptors();
-
- return rawDescriptors != null
- ? parseDescriptors(rawDescriptors) : false;
- }
-
- private byte[] getRawDescriptors() {
- return getRawDescriptors_native(mDeviceAddr);
- }
-
- private native byte[] getRawDescriptors_native(String deviceAddr);
-
- /**
- * @hide
- */
- public String getDescriptorString(int stringId) {
- return getDescriptorString_native(mDeviceAddr, stringId);
- }
-
- private native String getDescriptorString_native(String deviceAddr, int stringId);
+ private native byte[] getRawDescriptors(String deviceAddr);
public int getParsingSpec() {
return mDeviceDescriptor != null ? mDeviceDescriptor.getSpec() : 0;
@@ -267,17 +200,6 @@
/**
* @hide
*/
- public UsbDevice toAndroidUsbDevice() {
- if (mDeviceDescriptor == null) {
- return null;
- }
-
- return mDeviceDescriptor.toAndroid(this);
- }
-
- /**
- * @hide
- */
public ArrayList<UsbDescriptor> getDescriptors(byte type) {
ArrayList<UsbDescriptor> list = new ArrayList<UsbDescriptor>();
for (UsbDescriptor descriptor : mDescriptors) {
@@ -433,6 +355,8 @@
* to count on the peripheral being a headset.
*/
public boolean isInputHeadset() {
+ // TEMP
+ Log.i(TAG, "---- isInputHeadset() prob:" + (getInputHeadsetProbability() * 100f) + "%");
return getInputHeadsetProbability() >= IN_HEADSET_TRIGGER;
}
@@ -486,6 +410,8 @@
* to count on the peripheral being a headset.
*/
public boolean isOutputHeadset() {
+ // TEMP
+ Log.i(TAG, "---- isOutputHeadset() prob:" + (getOutputHeadsetProbability() * 100f) + "%");
return getOutputHeadsetProbability() >= OUT_HEADSET_TRIGGER;
}
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbDeviceDescriptor.java b/services/usb/java/com/android/server/usb/descriptors/UsbDeviceDescriptor.java
index c89d9b6..d5cb89e 100644
--- a/services/usb/java/com/android/server/usb/descriptors/UsbDeviceDescriptor.java
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbDeviceDescriptor.java
@@ -15,14 +15,9 @@
*/
package com.android.server.usb.descriptors;
-import android.hardware.usb.UsbConfiguration;
-import android.hardware.usb.UsbDevice;
-
import com.android.server.usb.descriptors.report.ReportCanvas;
import com.android.server.usb.descriptors.report.UsbStrings;
-import java.util.ArrayList;
-
/**
* @hide
* A USB Device Descriptor.
@@ -49,9 +44,6 @@
private byte mSerialNum; // 16:1 Index of Serial Number String Descriptor
private byte mNumConfigs; // 17:1 Number of Possible Configurations
- private ArrayList<UsbConfigDescriptor> mConfigDescriptors =
- new ArrayList<UsbConfigDescriptor>();
-
UsbDeviceDescriptor(int length, byte type) {
super(length, type);
mHierarchyLevel = 1;
@@ -105,35 +97,6 @@
return mNumConfigs;
}
- void addConfigDescriptor(UsbConfigDescriptor config) {
- mConfigDescriptors.add(config);
- }
-
- /**
- * @hide
- */
- public UsbDevice toAndroid(UsbDescriptorParser parser) {
- String mfgName = parser.getDescriptorString(mMfgIndex);
- String prodName = parser.getDescriptorString(mProductIndex);
-
- // Create version string in "%.%" format
- String versionString =
- Integer.toString(mDeviceRelease >> 8) + "." + (mDeviceRelease & 0xFF);
- String serialStr = parser.getDescriptorString(mSerialNum);
-
- UsbDevice device = new UsbDevice(parser.getDeviceAddr(), mVendorID, mProductID,
- mDevClass, mDevSubClass,
- mProtocol, mfgName, prodName,
- versionString, serialStr);
- UsbConfiguration[] configs = new UsbConfiguration[mConfigDescriptors.size()];
- for (int index = 0; index < mConfigDescriptors.size(); index++) {
- configs[index] = mConfigDescriptors.get(index).toAndroid(parser);
- }
- device.setConfigurations(configs);
-
- return device;
- }
-
@Override
public int parseRawDescriptors(ByteStream stream) {
mSpec = stream.unpackUsbShort();
@@ -171,11 +134,12 @@
+ " Product ID: " + ReportCanvas.getHexString(getProductID())
+ " Product Release: " + ReportCanvas.getBCDString(getDeviceRelease()));
- UsbDescriptorParser parser = canvas.getParser();
byte mfgIndex = getMfgIndex();
- String manufacturer = parser.getDescriptorString(mfgIndex);
+ String manufacturer =
+ UsbDescriptor.getUsbDescriptorString(canvas.getConnection(), mfgIndex);
byte productIndex = getProductIndex();
- String product = parser.getDescriptorString(productIndex);
+ String product =
+ UsbDescriptor.getUsbDescriptorString(canvas.getConnection(), productIndex);
canvas.writeListItem("Manufacturer " + mfgIndex + ": " + manufacturer
+ " Product " + productIndex + ": " + product);
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbEndpointDescriptor.java b/services/usb/java/com/android/server/usb/descriptors/UsbEndpointDescriptor.java
index 9cb8f05..6322fbe 100644
--- a/services/usb/java/com/android/server/usb/descriptors/UsbEndpointDescriptor.java
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbEndpointDescriptor.java
@@ -15,8 +15,6 @@
*/
package com.android.server.usb.descriptors;
-import android.hardware.usb.UsbEndpoint;
-
import com.android.server.usb.descriptors.report.ReportCanvas;
/**
@@ -107,10 +105,6 @@
return mSyncAddress;
}
- /* package */ UsbEndpoint toAndroid(UsbDescriptorParser parser) {
- return new UsbEndpoint(mEndpointAddress, mAttributes, mPacketSize, mInterval);
- }
-
@Override
public int parseRawDescriptors(ByteStream stream) {
mEndpointAddress = stream.getByte();
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbInterfaceDescriptor.java b/services/usb/java/com/android/server/usb/descriptors/UsbInterfaceDescriptor.java
index 645807d..4eef6ca 100644
--- a/services/usb/java/com/android/server/usb/descriptors/UsbInterfaceDescriptor.java
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbInterfaceDescriptor.java
@@ -15,14 +15,9 @@
*/
package com.android.server.usb.descriptors;
-import android.hardware.usb.UsbEndpoint;
-import android.hardware.usb.UsbInterface;
-
import com.android.server.usb.descriptors.report.ReportCanvas;
import com.android.server.usb.descriptors.report.UsbStrings;
-import java.util.ArrayList;
-
/**
* @hide
* A common super-class for all USB Interface Descritor subtypes.
@@ -39,9 +34,6 @@
protected byte mProtocol; // 7:1 Protocol Code
protected byte mDescrIndex; // 8:1 Index of String Descriptor Describing this interface
- private ArrayList<UsbEndpointDescriptor> mEndpointDescriptors =
- new ArrayList<UsbEndpointDescriptor>();
-
UsbInterfaceDescriptor(int length, byte type) {
super(length, type);
mHierarchyLevel = 3;
@@ -88,22 +80,6 @@
return mDescrIndex;
}
- void addEndpointDescriptor(UsbEndpointDescriptor endpoint) {
- mEndpointDescriptors.add(endpoint);
- }
-
- UsbInterface toAndroid(UsbDescriptorParser parser) {
- String name = parser.getDescriptorString(mDescrIndex);
- UsbInterface ntrface = new UsbInterface(
- mInterfaceNumber, mAlternateSetting, name, mUsbClass, mUsbSubclass, mProtocol);
- UsbEndpoint[] endpoints = new UsbEndpoint[mEndpointDescriptors.size()];
- for (int index = 0; index < mEndpointDescriptors.size(); index++) {
- endpoints[index] = mEndpointDescriptors.get(index).toAndroid(parser);
- }
- ntrface.setEndpoints(endpoints);
- return ntrface;
- }
-
@Override
public void report(ReportCanvas canvas) {
super.report(canvas);
diff --git a/services/usb/java/com/android/server/usb/descriptors/report/HTMLReportCanvas.java b/services/usb/java/com/android/server/usb/descriptors/report/HTMLReportCanvas.java
index adfc514..99ebcca 100644
--- a/services/usb/java/com/android/server/usb/descriptors/report/HTMLReportCanvas.java
+++ b/services/usb/java/com/android/server/usb/descriptors/report/HTMLReportCanvas.java
@@ -15,7 +15,7 @@
*/
package com.android.server.usb.descriptors.report;
-import com.android.server.usb.descriptors.UsbDescriptorParser;
+import android.hardware.usb.UsbDeviceConnection;
/**
* @hide
@@ -32,8 +32,8 @@
* from the USB device.
* @param stringBuilder Generated output gets written into this object.
*/
- public HTMLReportCanvas(UsbDescriptorParser parser, StringBuilder stringBuilder) {
- super(parser);
+ public HTMLReportCanvas(UsbDeviceConnection connection, StringBuilder stringBuilder) {
+ super(connection);
mStringBuilder = stringBuilder;
}
diff --git a/services/usb/java/com/android/server/usb/descriptors/report/ReportCanvas.java b/services/usb/java/com/android/server/usb/descriptors/report/ReportCanvas.java
index c34dc98..9e0adf5 100644
--- a/services/usb/java/com/android/server/usb/descriptors/report/ReportCanvas.java
+++ b/services/usb/java/com/android/server/usb/descriptors/report/ReportCanvas.java
@@ -15,7 +15,7 @@
*/
package com.android.server.usb.descriptors.report;
-import com.android.server.usb.descriptors.UsbDescriptorParser;
+import android.hardware.usb.UsbDeviceConnection;
/**
* @hide
@@ -24,19 +24,22 @@
public abstract class ReportCanvas {
private static final String TAG = "ReportCanvas";
- private final UsbDescriptorParser mParser;
+ private final UsbDeviceConnection mConnection;
/**
* Constructor.
* @param connection The USB connection object used to retrieve strings
* from the USB device.
*/
- public ReportCanvas(UsbDescriptorParser parser) {
- mParser = parser;
+ public ReportCanvas(UsbDeviceConnection connection) {
+ mConnection = connection;
}
- public UsbDescriptorParser getParser() {
- return mParser;
+ /**
+ * @returns the UsbDeviceConnection member (mConnection).
+ */
+ public UsbDeviceConnection getConnection() {
+ return mConnection;
}
/**
diff --git a/services/usb/java/com/android/server/usb/descriptors/report/TextReportCanvas.java b/services/usb/java/com/android/server/usb/descriptors/report/TextReportCanvas.java
index 1e19ea1..a43569d 100644
--- a/services/usb/java/com/android/server/usb/descriptors/report/TextReportCanvas.java
+++ b/services/usb/java/com/android/server/usb/descriptors/report/TextReportCanvas.java
@@ -15,7 +15,7 @@
*/
package com.android.server.usb.descriptors.report;
-import com.android.server.usb.descriptors.UsbDescriptorParser;
+import android.hardware.usb.UsbDeviceConnection;
/**
* @hide
@@ -34,8 +34,8 @@
* from the USB device.
* @param stringBuilder Generated output gets written into this object.
*/
- public TextReportCanvas(UsbDescriptorParser parser, StringBuilder stringBuilder) {
- super(parser);
+ public TextReportCanvas(UsbDeviceConnection connection, StringBuilder stringBuilder) {
+ super(connection);
mStringBuilder = stringBuilder;
}
diff --git a/telecomm/java/android/telecom/CallAudioState.java b/telecomm/java/android/telecom/CallAudioState.java
index f601d8b..4b827d2 100644
--- a/telecomm/java/android/telecom/CallAudioState.java
+++ b/telecomm/java/android/telecom/CallAudioState.java
@@ -16,16 +16,35 @@
package android.telecom;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.bluetooth.BluetoothDevice;
import android.os.Parcel;
import android.os.Parcelable;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
import java.util.Locale;
+import java.util.Objects;
+import java.util.stream.Collectors;
/**
* Encapsulates the telecom audio state, including the current audio routing, supported audio
* routing and mute.
*/
public final class CallAudioState implements Parcelable {
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(value={ROUTE_EARPIECE, ROUTE_BLUETOOTH, ROUTE_WIRED_HEADSET, ROUTE_SPEAKER},
+ flag=true)
+ public @interface CallAudioRoute {}
+
/** Direct the audio stream through the device's earpiece. */
public static final int ROUTE_EARPIECE = 0x00000001;
@@ -55,6 +74,8 @@
private final boolean isMuted;
private final int route;
private final int supportedRouteMask;
+ private final BluetoothDevice activeBluetoothDevice;
+ private final Collection<BluetoothDevice> supportedBluetoothDevices;
/**
* Constructor for a {@link CallAudioState} object.
@@ -73,10 +94,21 @@
* {@link #ROUTE_WIRED_HEADSET}
* {@link #ROUTE_SPEAKER}
*/
- public CallAudioState(boolean muted, int route, int supportedRouteMask) {
- this.isMuted = muted;
+ public CallAudioState(boolean muted, @CallAudioRoute int route,
+ @CallAudioRoute int supportedRouteMask) {
+ this(muted, route, supportedRouteMask, null, Collections.emptyList());
+ }
+
+ /** @hide */
+ public CallAudioState(boolean isMuted, @CallAudioRoute int route,
+ @CallAudioRoute int supportedRouteMask,
+ @Nullable BluetoothDevice activeBluetoothDevice,
+ @NonNull Collection<BluetoothDevice> supportedBluetoothDevices) {
+ this.isMuted = isMuted;
this.route = route;
this.supportedRouteMask = supportedRouteMask;
+ this.activeBluetoothDevice = activeBluetoothDevice;
+ this.supportedBluetoothDevices = supportedBluetoothDevices;
}
/** @hide */
@@ -84,6 +116,8 @@
isMuted = state.isMuted();
route = state.getRoute();
supportedRouteMask = state.getSupportedRouteMask();
+ activeBluetoothDevice = state.activeBluetoothDevice;
+ supportedBluetoothDevices = state.getSupportedBluetoothDevices();
}
/** @hide */
@@ -92,6 +126,8 @@
isMuted = state.isMuted();
route = state.getRoute();
supportedRouteMask = state.getSupportedRouteMask();
+ activeBluetoothDevice = null;
+ supportedBluetoothDevices = Collections.emptyList();
}
@Override
@@ -103,17 +139,32 @@
return false;
}
CallAudioState state = (CallAudioState) obj;
- return isMuted() == state.isMuted() && getRoute() == state.getRoute() &&
- getSupportedRouteMask() == state.getSupportedRouteMask();
+ if (supportedBluetoothDevices.size() != state.supportedBluetoothDevices.size()) {
+ return false;
+ }
+ for (BluetoothDevice device : supportedBluetoothDevices) {
+ if (!state.supportedBluetoothDevices.contains(device)) {
+ return false;
+ }
+ }
+ return Objects.equals(activeBluetoothDevice, state.activeBluetoothDevice) && isMuted() ==
+ state.isMuted() && getRoute() == state.getRoute() && getSupportedRouteMask() ==
+ state.getSupportedRouteMask();
}
@Override
public String toString() {
+ String bluetoothDeviceList = supportedBluetoothDevices.stream()
+ .map(BluetoothDevice::getAddress).collect(Collectors.joining(", "));
+
return String.format(Locale.US,
- "[AudioState isMuted: %b, route: %s, supportedRouteMask: %s]",
+ "[AudioState isMuted: %b, route: %s, supportedRouteMask: %s, " +
+ "activeBluetoothDevice: [%s], supportedBluetoothDevices: [%s]]",
isMuted,
audioRouteToString(route),
- audioRouteToString(supportedRouteMask));
+ audioRouteToString(supportedRouteMask),
+ activeBluetoothDevice,
+ bluetoothDeviceList);
}
/**
@@ -126,6 +177,7 @@
/**
* @return The current audio route being used.
*/
+ @CallAudioRoute
public int getRoute() {
return route;
}
@@ -133,11 +185,27 @@
/**
* @return Bit mask of all routes supported by this call.
*/
+ @CallAudioRoute
public int getSupportedRouteMask() {
return supportedRouteMask;
}
/**
+ * @return The {@link BluetoothDevice} through which audio is being routed.
+ * Will not be {@code null} if {@link #getRoute()} returns {@link #ROUTE_BLUETOOTH}.
+ */
+ public BluetoothDevice getActiveBluetoothDevice() {
+ return activeBluetoothDevice;
+ }
+
+ /**
+ * @return {@link List} of {@link BluetoothDevice}s that can be used for this call.
+ */
+ public Collection<BluetoothDevice> getSupportedBluetoothDevices() {
+ return supportedBluetoothDevices;
+ }
+
+ /**
* Converts the provided audio route into a human readable string representation.
*
* @param route to convert into a string.
@@ -177,7 +245,13 @@
boolean isMuted = source.readByte() == 0 ? false : true;
int route = source.readInt();
int supportedRouteMask = source.readInt();
- return new CallAudioState(isMuted, route, supportedRouteMask);
+ BluetoothDevice activeBluetoothDevice = source.readParcelable(
+ ClassLoader.getSystemClassLoader());
+ List<BluetoothDevice> supportedBluetoothDevices = new ArrayList<>();
+ source.readParcelableList(supportedBluetoothDevices,
+ ClassLoader.getSystemClassLoader());
+ return new CallAudioState(isMuted, route,
+ supportedRouteMask, activeBluetoothDevice, supportedBluetoothDevices);
}
@Override
@@ -202,6 +276,8 @@
destination.writeByte((byte) (isMuted ? 1 : 0));
destination.writeInt(route);
destination.writeInt(supportedRouteMask);
+ destination.writeParcelable(activeBluetoothDevice, 0);
+ destination.writeParcelableList(new ArrayList<>(supportedBluetoothDevices), 0);
}
private static void listAppend(StringBuffer buffer, String str) {
diff --git a/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java
index 8ba934c..ffb5e93 100644
--- a/telecomm/java/android/telecom/Connection.java
+++ b/telecomm/java/android/telecom/Connection.java
@@ -25,6 +25,7 @@
import android.annotation.SystemApi;
import android.annotation.TestApi;
import android.app.Notification;
+import android.bluetooth.BluetoothDevice;
import android.content.Intent;
import android.hardware.camera2.CameraManager;
import android.net.Uri;
@@ -819,7 +820,7 @@
public void onConnectionEvent(Connection c, String event, Bundle extras) {}
/** @hide */
public void onConferenceSupportedChanged(Connection c, boolean isConferenceSupported) {}
- public void onAudioRouteChanged(Connection c, int audioRoute) {}
+ public void onAudioRouteChanged(Connection c, int audioRoute, String bluetoothAddress) {}
public void onRttInitiationSuccess(Connection c) {}
public void onRttInitiationFailure(Connection c, int reason) {}
public void onRttSessionRemotelyTerminated(Connection c) {}
@@ -2576,7 +2577,29 @@
*/
public final void setAudioRoute(int route) {
for (Listener l : mListeners) {
- l.onAudioRouteChanged(this, route);
+ l.onAudioRouteChanged(this, route, null);
+ }
+ }
+
+ /**
+ *
+ * Request audio routing to a specific bluetooth device. Calling this method may result in
+ * the device routing audio to a different bluetooth device than the one specified if the
+ * bluetooth stack is unable to route audio to the requested device.
+ * A list of available devices can be obtained via
+ * {@link CallAudioState#getSupportedBluetoothDevices()}
+ *
+ * <p>
+ * Used by self-managed {@link ConnectionService}s which wish to use bluetooth audio for a
+ * self-managed {@link Connection} (see {@link PhoneAccount#CAPABILITY_SELF_MANAGED}.)
+ * <p>
+ * See also {@link InCallService#requestBluetoothAudio(String)}
+ * @param bluetoothAddress The address of the bluetooth device to connect to, as returned by
+ * {@link BluetoothDevice#getAddress()}.
+ */
+ public void requestBluetoothAudio(@NonNull String bluetoothAddress) {
+ for (Listener l : mListeners) {
+ l.onAudioRouteChanged(this, CallAudioState.ROUTE_BLUETOOTH, bluetoothAddress);
}
}
diff --git a/telecomm/java/android/telecom/ConnectionService.java b/telecomm/java/android/telecom/ConnectionService.java
index a81fba9..35804f6 100644
--- a/telecomm/java/android/telecom/ConnectionService.java
+++ b/telecomm/java/android/telecom/ConnectionService.java
@@ -1294,10 +1294,10 @@
}
@Override
- public void onAudioRouteChanged(Connection c, int audioRoute) {
+ public void onAudioRouteChanged(Connection c, int audioRoute, String bluetoothAddress) {
String id = mIdByConnection.get(c);
if (id != null) {
- mAdapter.setAudioRoute(id, audioRoute);
+ mAdapter.setAudioRoute(id, audioRoute, bluetoothAddress);
}
}
diff --git a/telecomm/java/android/telecom/ConnectionServiceAdapter.java b/telecomm/java/android/telecom/ConnectionServiceAdapter.java
index 111fcc7..92a9dc2 100644
--- a/telecomm/java/android/telecom/ConnectionServiceAdapter.java
+++ b/telecomm/java/android/telecom/ConnectionServiceAdapter.java
@@ -520,11 +520,14 @@
* @param callId The unique ID of the call.
* @param audioRoute The new audio route (see {@code CallAudioState#ROUTE_*}).
*/
- void setAudioRoute(String callId, int audioRoute) {
- Log.v(this, "setAudioRoute: %s %s", callId, CallAudioState.audioRouteToString(audioRoute));
+ void setAudioRoute(String callId, int audioRoute, String bluetoothAddress) {
+ Log.v(this, "setAudioRoute: %s %s %s", callId,
+ CallAudioState.audioRouteToString(audioRoute),
+ bluetoothAddress);
for (IConnectionServiceAdapter adapter : mAdapters) {
try {
- adapter.setAudioRoute(callId, audioRoute, Log.getExternalSession());
+ adapter.setAudioRoute(callId, audioRoute,
+ bluetoothAddress, Log.getExternalSession());
} catch (RemoteException ignored) {
}
}
diff --git a/telecomm/java/android/telecom/ConnectionServiceAdapterServant.java b/telecomm/java/android/telecom/ConnectionServiceAdapterServant.java
index b1617f4..3fbdeb1 100644
--- a/telecomm/java/android/telecom/ConnectionServiceAdapterServant.java
+++ b/telecomm/java/android/telecom/ConnectionServiceAdapterServant.java
@@ -298,8 +298,8 @@
case MSG_SET_AUDIO_ROUTE: {
SomeArgs args = (SomeArgs) msg.obj;
try {
- mDelegate.setAudioRoute((String) args.arg1, args.argi1,
- (Session.Info) args.arg2);
+ mDelegate.setAudioRoute((String) args.arg1, args.argi1, (String) args.arg2,
+ (Session.Info) args.arg3);
} finally {
args.recycle();
}
@@ -548,12 +548,12 @@
@Override
public final void setAudioRoute(String connectionId, int audioRoute,
- Session.Info sessionInfo) {
-
+ String bluetoothAddress, Session.Info sessionInfo) {
SomeArgs args = SomeArgs.obtain();
args.arg1 = connectionId;
args.argi1 = audioRoute;
- args.arg2 = sessionInfo;
+ args.arg2 = bluetoothAddress;
+ args.arg3 = sessionInfo;
mHandler.obtainMessage(MSG_SET_AUDIO_ROUTE, args).sendToTarget();
}
diff --git a/telecomm/java/android/telecom/InCallAdapter.java b/telecomm/java/android/telecom/InCallAdapter.java
index 9559a28..9bf0467 100644
--- a/telecomm/java/android/telecom/InCallAdapter.java
+++ b/telecomm/java/android/telecom/InCallAdapter.java
@@ -16,6 +16,7 @@
package android.telecom;
+import android.bluetooth.BluetoothDevice;
import android.os.Bundle;
import android.os.RemoteException;
@@ -128,7 +129,22 @@
*/
public void setAudioRoute(int route) {
try {
- mAdapter.setAudioRoute(route);
+ mAdapter.setAudioRoute(route, null);
+ } catch (RemoteException e) {
+ }
+ }
+
+ /**
+ * Request audio routing to a specific bluetooth device. Calling this method may result in
+ * the device routing audio to a different bluetooth device than the one specified. A list of
+ * available devices can be obtained via {@link CallAudioState#getSupportedBluetoothDevices()}
+ *
+ * @param bluetoothAddress The address of the bluetooth device to connect to, as returned by
+ * {@link BluetoothDevice#getAddress()}, or {@code null} if no device is preferred.
+ */
+ public void requestBluetoothAudio(String bluetoothAddress) {
+ try {
+ mAdapter.setAudioRoute(CallAudioState.ROUTE_BLUETOOTH, bluetoothAddress);
} catch (RemoteException e) {
}
}
diff --git a/telecomm/java/android/telecom/InCallService.java b/telecomm/java/android/telecom/InCallService.java
index e384d46..d558bba 100644
--- a/telecomm/java/android/telecom/InCallService.java
+++ b/telecomm/java/android/telecom/InCallService.java
@@ -16,9 +16,11 @@
package android.telecom;
+import android.annotation.NonNull;
import android.annotation.SdkConstant;
import android.annotation.SystemApi;
import android.app.Service;
+import android.bluetooth.BluetoothDevice;
import android.content.Intent;
import android.hardware.camera2.CameraManager;
import android.net.Uri;
@@ -377,6 +379,22 @@
}
/**
+ * Request audio routing to a specific bluetooth device. Calling this method may result in
+ * the device routing audio to a different bluetooth device than the one specified if the
+ * bluetooth stack is unable to route audio to the requested device.
+ * A list of available devices can be obtained via
+ * {@link CallAudioState#getSupportedBluetoothDevices()}
+ *
+ * @param bluetoothAddress The address of the bluetooth device to connect to, as returned by
+ * {@link BluetoothDevice#getAddress()}.
+ */
+ public final void requestBluetoothAudio(@NonNull String bluetoothAddress) {
+ if (mPhone != null) {
+ mPhone.requestBluetoothAudio(bluetoothAddress);
+ }
+ }
+
+ /**
* Invoked when the {@code Phone} has been created. This is a signal to the in-call experience
* to start displaying in-call information to the user. Each instance of {@code InCallService}
* will have only one {@code Phone}, and this method will be called exactly once in the lifetime
diff --git a/telecomm/java/android/telecom/Phone.java b/telecomm/java/android/telecom/Phone.java
index 066f6c2..421b1a4 100644
--- a/telecomm/java/android/telecom/Phone.java
+++ b/telecomm/java/android/telecom/Phone.java
@@ -17,7 +17,9 @@
package android.telecom;
import android.annotation.SystemApi;
+import android.bluetooth.BluetoothDevice;
import android.os.Bundle;
+import android.os.RemoteException;
import android.util.ArrayMap;
import java.util.Collections;
@@ -295,6 +297,18 @@
}
/**
+ * Request audio routing to a specific bluetooth device. Calling this method may result in
+ * the device routing audio to a different bluetooth device than the one specified. A list of
+ * available devices can be obtained via {@link CallAudioState#getSupportedBluetoothDevices()}
+ *
+ * @param bluetoothAddress The address of the bluetooth device to connect to, as returned by
+ * {@link BluetoothDevice#getAddress()}, or {@code null} if no device is preferred.
+ */
+ public void requestBluetoothAudio(String bluetoothAddress) {
+ mInCallAdapter.requestBluetoothAudio(bluetoothAddress);
+ }
+
+ /**
* Turns the proximity sensor on. When this request is made, the proximity sensor will
* become active, and the touch screen and display will be turned off when the user's face
* is detected to be in close proximity to the screen. This operation is a no-op on devices
diff --git a/telecomm/java/android/telecom/RemoteConnectionService.java b/telecomm/java/android/telecom/RemoteConnectionService.java
index 2cc4314..85906ad 100644
--- a/telecomm/java/android/telecom/RemoteConnectionService.java
+++ b/telecomm/java/android/telecom/RemoteConnectionService.java
@@ -398,7 +398,8 @@
}
@Override
- public void setAudioRoute(String callId, int audioRoute, Session.Info sessionInfo) {
+ public void setAudioRoute(String callId, int audioRoute, String bluetoothAddress,
+ Session.Info sessionInfo) {
if (hasConnection(callId)) {
// TODO(3pcalls): handle this for remote connections.
// Likely we don't want to do anything since it doesn't make sense for self-managed
diff --git a/telecomm/java/com/android/internal/telecom/IConnectionServiceAdapter.aidl b/telecomm/java/com/android/internal/telecom/IConnectionServiceAdapter.aidl
index d20da18..da2015f 100644
--- a/telecomm/java/com/android/internal/telecom/IConnectionServiceAdapter.aidl
+++ b/telecomm/java/com/android/internal/telecom/IConnectionServiceAdapter.aidl
@@ -103,7 +103,8 @@
void removeExtras(String callId, in List<String> keys, in Session.Info sessionInfo);
- void setAudioRoute(String callId, int audioRoute, in Session.Info sessionInfo);
+ void setAudioRoute(String callId, int audioRoute, String bluetoothAddress,
+ in Session.Info sessionInfo);
void onConnectionEvent(String callId, String event, in Bundle extras,
in Session.Info sessionInfo);
diff --git a/telecomm/java/com/android/internal/telecom/IInCallAdapter.aidl b/telecomm/java/com/android/internal/telecom/IInCallAdapter.aidl
index 73fa29a..bce3392 100644
--- a/telecomm/java/com/android/internal/telecom/IInCallAdapter.aidl
+++ b/telecomm/java/com/android/internal/telecom/IInCallAdapter.aidl
@@ -39,7 +39,7 @@
void mute(boolean shouldMute);
- void setAudioRoute(int route);
+ void setAudioRoute(int route, String bluetoothAddress);
void playDtmfTone(String callId, char digit);
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 061e55a..0030ab6 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -1650,6 +1650,26 @@
public static final String KEY_IDENTIFY_HIGH_DEFINITION_CALLS_IN_CALL_LOG_BOOL =
"identify_high_definition_calls_in_call_log_bool";
+ /**
+ * Flag specifying whether to use the {@link ServiceState} roaming status, which can be
+ * affected by other carrier configs (e.g.
+ * {@link #KEY_GSM_NONROAMING_NETWORKS_STRING_ARRAY}), when setting the SPN display.
+ * <p>
+ * If {@code true}, the SPN display uses {@link ServiceState#getRoaming}.
+ * If {@code false} the SPN display checks if the current MCC/MNC is different from the
+ * SIM card's MCC/MNC.
+ *
+ * @see KEY_GSM_ROAMING_NETWORKS_STRING_ARRAY
+ * @see KEY_GSM_NONROAMING_NETWORKS_STRING_ARRAY
+ * @see KEY_NON_ROAMING_OPERATOR_STRING_ARRAY
+ * @see KEY_ROAMING_OPERATOR_STRING_ARRAY
+ * @see KEY_FORCE_HOME_NETWORK_BOOL
+ *
+ * @hide
+ */
+ public static final String KEY_SPN_DISPLAY_RULE_USE_ROAMING_FROM_SERVICE_STATE_BOOL =
+ "spn_display_rule_use_roaming_from_service_state_bool";
+
/** The default value for every variable. */
private final static PersistableBundle sDefaults;
@@ -1928,6 +1948,7 @@
sDefaults.putBoolean(KEY_DISABLE_CHARGE_INDICATION_BOOL, false);
sDefaults.putStringArray(KEY_FEATURE_ACCESS_CODES_STRING_ARRAY, null);
sDefaults.putBoolean(KEY_IDENTIFY_HIGH_DEFINITION_CALLS_IN_CALL_LOG_BOOL, false);
+ sDefaults.putBoolean(KEY_SPN_DISPLAY_RULE_USE_ROAMING_FROM_SERVICE_STATE_BOOL, false);
}
/**
diff --git a/telephony/java/android/telephony/PhoneStateListener.java b/telephony/java/android/telephony/PhoneStateListener.java
index afff6d5..9ccfa94 100644
--- a/telephony/java/android/telephony/PhoneStateListener.java
+++ b/telephony/java/android/telephony/PhoneStateListener.java
@@ -204,16 +204,6 @@
public static final int LISTEN_VOLTE_STATE = 0x00004000;
/**
- * Listen for OEM hook raw event
- *
- * @see #onOemHookRawEvent
- * @hide
- * @deprecated OEM needs a vendor-extension hal and their apps should use that instead
- */
- @Deprecated
- public static final int LISTEN_OEM_HOOK_RAW_EVENT = 0x00008000;
-
- /**
* Listen for carrier network changes indicated by a carrier app.
*
* @see #onCarrierNetworkRequest
@@ -359,9 +349,6 @@
case LISTEN_DATA_ACTIVATION_STATE:
PhoneStateListener.this.onDataActivationStateChanged((int)msg.obj);
break;
- case LISTEN_OEM_HOOK_RAW_EVENT:
- PhoneStateListener.this.onOemHookRawEvent((byte[])msg.obj);
- break;
case LISTEN_CARRIER_NETWORK_CHANGE:
PhoneStateListener.this.onCarrierNetworkChange((boolean)msg.obj);
break;
@@ -556,16 +543,6 @@
}
/**
- * Callback invoked when OEM hook raw event is received. Requires
- * the READ_PRIVILEGED_PHONE_STATE permission.
- * @param rawData is the byte array of the OEM hook raw data.
- * @hide
- */
- public void onOemHookRawEvent(byte[] rawData) {
- // default implementation empty
- }
-
- /**
* Callback invoked when telephony has received notice from a carrier
* app that a network action that could result in connectivity loss
* has been requested by an app using
@@ -677,10 +654,6 @@
send(LISTEN_DATA_ACTIVATION_STATE, 0, 0, activationState);
}
- public void onOemHookRawEvent(byte[] rawData) {
- send(LISTEN_OEM_HOOK_RAW_EVENT, 0, 0, rawData);
- }
-
public void onCarrierNetworkChange(boolean active) {
send(LISTEN_CARRIER_NETWORK_CHANGE, 0, 0, active);
}
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index 88f4880..2f39ddb 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -360,6 +360,42 @@
public static final String CB_OPT_OUT_DIALOG = "show_cmas_opt_out_dialog";
/**
+ * TelephonyProvider column name for enable Volte.
+ *@hide
+ */
+ public static final String ENHANCED_4G_MODE_ENABLED = "volte_vt_enabled";
+
+ /**
+ * TelephonyProvider column name for enable VT (Video Telephony over IMS)
+ *@hide
+ */
+ public static final String VT_IMS_ENABLED = "vt_ims_enabled";
+
+ /**
+ * TelephonyProvider column name for enable Wifi calling
+ *@hide
+ */
+ public static final String WFC_IMS_ENABLED = "wfc_ims_enabled";
+
+ /**
+ * TelephonyProvider column name for Wifi calling mode
+ *@hide
+ */
+ public static final String WFC_IMS_MODE = "wfc_ims_mode";
+
+ /**
+ * TelephonyProvider column name for Wifi calling mode in roaming
+ *@hide
+ */
+ public static final String WFC_IMS_ROAMING_MODE = "wfc_ims_roaming_mode";
+
+ /**
+ * TelephonyProvider column name for enable Wifi calling in roaming
+ *@hide
+ */
+ public static final String WFC_IMS_ROAMING_ENABLED = "wfc_ims_roaming_enabled";
+
+ /**
* Broadcast Action: The user has changed one of the default subs related to
* data, phone calls, or sms</p>
*
diff --git a/telephony/java/android/telephony/Telephony.java b/telephony/java/android/telephony/Telephony.java
index e40f20f..d7b6142 100644
--- a/telephony/java/android/telephony/Telephony.java
+++ b/telephony/java/android/telephony/Telephony.java
@@ -2828,6 +2828,26 @@
* @hide
*/
public static final int CARRIER_DELETED_BUT_PRESENT_IN_XML = 6;
+
+ /**
+ * The owner of the APN.
+ * <p>Type: INTEGER</p>
+ * @hide
+ */
+ public static final String OWNED_BY = "owned_by";
+
+ /**
+ * Possible value for the OWNED_BY field.
+ * APN is owned by DPC.
+ * @hide
+ */
+ public static final int OWNED_BY_DPC = 0;
+ /**
+ * Possible value for the OWNED_BY field.
+ * APN is owned by other sources.
+ * @hide
+ */
+ public static final int OWNED_BY_OTHERS = 1;
}
/**
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 42c3de5..4ffb3c3 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -5709,29 +5709,6 @@
return retVal;
}
- /**
- * Returns the result and response from RIL for oem request
- *
- * @param oemReq the data is sent to ril.
- * @param oemResp the respose data from RIL.
- * @return negative value request was not handled or get error
- * 0 request was handled succesfully, but no response data
- * positive value success, data length of response
- * @hide
- * @deprecated OEM needs a vendor-extension hal and their apps should use that instead
- */
- @Deprecated
- public int invokeOemRilRequestRaw(byte[] oemReq, byte[] oemResp) {
- try {
- ITelephony telephony = getITelephony();
- if (telephony != null)
- return telephony.invokeOemRilRequestRaw(oemReq, oemResp);
- } catch (RemoteException ex) {
- } catch (NullPointerException ex) {
- }
- return -1;
- }
-
/** @hide */
@SystemApi
@RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
diff --git a/telephony/java/android/telephony/euicc/DownloadableSubscription.java b/telephony/java/android/telephony/euicc/DownloadableSubscription.java
index b5484e34..01041c8 100644
--- a/telephony/java/android/telephony/euicc/DownloadableSubscription.java
+++ b/telephony/java/android/telephony/euicc/DownloadableSubscription.java
@@ -53,6 +53,8 @@
@Nullable
public final String encodedActivationCode;
+ @Nullable private String confirmationCode;
+
// see getCarrierName and setCarrierName
@Nullable
private String carrierName;
@@ -66,6 +68,7 @@
private DownloadableSubscription(Parcel in) {
encodedActivationCode = in.readString();
+ confirmationCode = in.readString();
carrierName = in.readString();
accessRules = in.createTypedArray(UiccAccessRule.CREATOR);
}
@@ -83,6 +86,21 @@
}
/**
+ * Sets the confirmation code.
+ */
+ public void setConfirmationCode(String confirmationCode) {
+ this.confirmationCode = confirmationCode;
+ }
+
+ /**
+ * Returns the confirmation code.
+ */
+ @Nullable
+ public String getConfirmationCode() {
+ return confirmationCode;
+ }
+
+ /**
* Set the user-visible carrier name.
* @hide
*
@@ -134,6 +152,7 @@
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(encodedActivationCode);
+ dest.writeString(confirmationCode);
dest.writeString(carrierName);
dest.writeTypedArray(accessRules, flags);
}
diff --git a/telephony/java/android/telephony/ims/ImsServiceProxy.java b/telephony/java/android/telephony/ims/ImsServiceProxy.java
deleted file mode 100644
index 31d3db4..0000000
--- a/telephony/java/android/telephony/ims/ImsServiceProxy.java
+++ /dev/null
@@ -1,322 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package android.telephony.ims;
-
-import android.app.PendingIntent;
-import android.os.IBinder;
-import android.os.Message;
-import android.os.RemoteException;
-import android.telephony.ims.feature.IRcsFeature;
-import android.telephony.ims.feature.ImsFeature;
-import android.util.Log;
-
-import com.android.ims.ImsCallProfile;
-import com.android.ims.internal.IImsCallSession;
-import com.android.ims.internal.IImsCallSessionListener;
-import com.android.ims.internal.IImsConfig;
-import com.android.ims.internal.IImsEcbm;
-import com.android.ims.internal.IImsMultiEndpoint;
-import com.android.ims.internal.IImsRegistrationListener;
-import com.android.ims.internal.IImsServiceController;
-import com.android.ims.internal.IImsServiceFeatureListener;
-import com.android.ims.internal.IImsUt;
-
-/**
- * A container of the IImsServiceController binder, which implements all of the ImsFeatures that
- * the platform currently supports: MMTel and RCS.
- * @hide
- */
-
-public class ImsServiceProxy extends ImsServiceProxyCompat implements IRcsFeature {
-
- protected String LOG_TAG = "ImsServiceProxy";
- private final int mSupportedFeature;
-
- // Start by assuming the proxy is available for usage.
- private boolean mIsAvailable = true;
- // ImsFeature Status from the ImsService. Cached.
- private Integer mFeatureStatusCached = null;
- private ImsServiceProxy.INotifyStatusChanged mStatusCallback;
- private final Object mLock = new Object();
-
- public interface INotifyStatusChanged {
- void notifyStatusChanged();
- }
-
- private final IImsServiceFeatureListener mListenerBinder =
- new IImsServiceFeatureListener.Stub() {
-
- @Override
- public void imsFeatureCreated(int slotId, int feature) throws RemoteException {
- // The feature has been re-enabled. This may happen when the service crashes.
- synchronized (mLock) {
- if (!mIsAvailable && mSlotId == slotId && feature == mSupportedFeature) {
- Log.i(LOG_TAG, "Feature enabled on slotId: " + slotId + " for feature: " +
- feature);
- mIsAvailable = true;
- }
- }
- }
-
- @Override
- public void imsFeatureRemoved(int slotId, int feature) throws RemoteException {
- synchronized (mLock) {
- if (mIsAvailable && mSlotId == slotId && feature == mSupportedFeature) {
- Log.i(LOG_TAG, "Feature disabled on slotId: " + slotId + " for feature: " +
- feature);
- mIsAvailable = false;
- }
- }
- }
-
- @Override
- public void imsStatusChanged(int slotId, int feature, int status) throws RemoteException {
- synchronized (mLock) {
- Log.i(LOG_TAG, "imsStatusChanged: slot: " + slotId + " feature: " + feature +
- " status: " + status);
- if (mSlotId == slotId && feature == mSupportedFeature) {
- mFeatureStatusCached = status;
- if (mStatusCallback != null) {
- mStatusCallback.notifyStatusChanged();
- }
- }
- }
- }
- };
-
- public ImsServiceProxy(int slotId, IBinder binder, int featureType) {
- super(slotId, binder);
- mSupportedFeature = featureType;
- }
-
- public ImsServiceProxy(int slotId, int featureType) {
- super(slotId, null /*IBinder*/);
- mSupportedFeature = featureType;
- }
-
- public IImsServiceFeatureListener getListener() {
- return mListenerBinder;
- }
-
- public void setBinder(IBinder binder) {
- mBinder = binder;
- }
-
- @Override
- public int startSession(PendingIntent incomingCallIntent, IImsRegistrationListener listener)
- throws RemoteException {
- synchronized (mLock) {
- checkServiceIsReady();
- return getServiceInterface(mBinder).startSession(mSlotId, mSupportedFeature,
- incomingCallIntent, listener);
- }
- }
-
- @Override
- public void endSession(int sessionId) throws RemoteException {
- synchronized (mLock) {
- // Only check to make sure the binder connection still exists. This method should
- // still be able to be called when the state is STATE_NOT_AVAILABLE.
- checkBinderConnection();
- getServiceInterface(mBinder).endSession(mSlotId, mSupportedFeature, sessionId);
- }
- }
-
- @Override
- public boolean isConnected(int callServiceType, int callType)
- throws RemoteException {
- synchronized (mLock) {
- checkServiceIsReady();
- return getServiceInterface(mBinder).isConnected(mSlotId, mSupportedFeature,
- callServiceType, callType);
- }
- }
-
- @Override
- public boolean isOpened() throws RemoteException {
- synchronized (mLock) {
- checkServiceIsReady();
- return getServiceInterface(mBinder).isOpened(mSlotId, mSupportedFeature);
- }
- }
-
- @Override
- public void addRegistrationListener(IImsRegistrationListener listener)
- throws RemoteException {
- synchronized (mLock) {
- checkServiceIsReady();
- getServiceInterface(mBinder).addRegistrationListener(mSlotId, mSupportedFeature,
- listener);
- }
- }
-
- @Override
- public void removeRegistrationListener(IImsRegistrationListener listener)
- throws RemoteException {
- synchronized (mLock) {
- checkServiceIsReady();
- getServiceInterface(mBinder).removeRegistrationListener(mSlotId, mSupportedFeature,
- listener);
- }
- }
-
- @Override
- public ImsCallProfile createCallProfile(int sessionId, int callServiceType, int callType)
- throws RemoteException {
- synchronized (mLock) {
- checkServiceIsReady();
- return getServiceInterface(mBinder).createCallProfile(mSlotId, mSupportedFeature,
- sessionId, callServiceType, callType);
- }
- }
-
- @Override
- public IImsCallSession createCallSession(int sessionId, ImsCallProfile profile,
- IImsCallSessionListener listener) throws RemoteException {
- synchronized (mLock) {
- checkServiceIsReady();
- return getServiceInterface(mBinder).createCallSession(mSlotId, mSupportedFeature,
- sessionId, profile, listener);
- }
- }
-
- @Override
- public IImsCallSession getPendingCallSession(int sessionId, String callId)
- throws RemoteException {
- synchronized (mLock) {
- checkServiceIsReady();
- return getServiceInterface(mBinder).getPendingCallSession(mSlotId, mSupportedFeature,
- sessionId, callId);
- }
- }
-
- @Override
- public IImsUt getUtInterface() throws RemoteException {
- synchronized (mLock) {
- checkServiceIsReady();
- return getServiceInterface(mBinder).getUtInterface(mSlotId, mSupportedFeature);
- }
- }
-
- @Override
- public IImsConfig getConfigInterface() throws RemoteException {
- synchronized (mLock) {
- checkServiceIsReady();
- return getServiceInterface(mBinder).getConfigInterface(mSlotId, mSupportedFeature);
- }
- }
-
- @Override
- public void turnOnIms() throws RemoteException {
- synchronized (mLock) {
- checkServiceIsReady();
- getServiceInterface(mBinder).turnOnIms(mSlotId, mSupportedFeature);
- }
- }
-
- @Override
- public void turnOffIms() throws RemoteException {
- synchronized (mLock) {
- checkServiceIsReady();
- getServiceInterface(mBinder).turnOffIms(mSlotId, mSupportedFeature);
- }
- }
-
- @Override
- public IImsEcbm getEcbmInterface() throws RemoteException {
- synchronized (mLock) {
- checkServiceIsReady();
- return getServiceInterface(mBinder).getEcbmInterface(mSlotId, mSupportedFeature);
- }
- }
-
- @Override
- public void setUiTTYMode(int uiTtyMode, Message onComplete)
- throws RemoteException {
- synchronized (mLock) {
- checkServiceIsReady();
- getServiceInterface(mBinder).setUiTTYMode(mSlotId, mSupportedFeature, uiTtyMode,
- onComplete);
- }
- }
-
- @Override
- public IImsMultiEndpoint getMultiEndpointInterface() throws RemoteException {
- synchronized (mLock) {
- checkServiceIsReady();
- return getServiceInterface(mBinder).getMultiEndpointInterface(mSlotId,
- mSupportedFeature);
- }
- }
-
- @Override
- public int getFeatureStatus() {
- synchronized (mLock) {
- if (isBinderAlive() && mFeatureStatusCached != null) {
- Log.i(LOG_TAG, "getFeatureStatus - returning cached: " + mFeatureStatusCached);
- return mFeatureStatusCached;
- }
- }
- // Don't synchronize on Binder call.
- Integer status = retrieveFeatureStatus();
- synchronized (mLock) {
- if (status == null) {
- return ImsFeature.STATE_NOT_AVAILABLE;
- }
- // Cache only non-null value for feature status.
- mFeatureStatusCached = status;
- }
- Log.i(LOG_TAG, "getFeatureStatus - returning " + status);
- return status;
- }
-
- /**
- * Internal method used to retrieve the feature status from the corresponding ImsService.
- */
- private Integer retrieveFeatureStatus() {
- if (mBinder != null) {
- try {
- return getServiceInterface(mBinder).getFeatureStatus(mSlotId, mSupportedFeature);
- } catch (RemoteException e) {
- // Status check failed, don't update cache
- }
- }
- return null;
- }
-
- /**
- * @param c Callback that will fire when the feature status has changed.
- */
- public void setStatusCallback(INotifyStatusChanged c) {
- mStatusCallback = c;
- }
-
- @Override
- public boolean isBinderAlive() {
- return mIsAvailable && mBinder != null && mBinder.isBinderAlive();
- }
-
- protected void checkServiceIsReady() throws RemoteException {
- if (!isBinderReady()) {
- throw new RemoteException("ImsServiceProxy is not ready to accept commands.");
- }
- }
-
- private IImsServiceController getServiceInterface(IBinder b) {
- return IImsServiceController.Stub.asInterface(b);
- }
-}
diff --git a/telephony/java/android/telephony/ims/ImsServiceProxyCompat.java b/telephony/java/android/telephony/ims/ImsServiceProxyCompat.java
deleted file mode 100644
index 7ec9229..0000000
--- a/telephony/java/android/telephony/ims/ImsServiceProxyCompat.java
+++ /dev/null
@@ -1,196 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package android.telephony.ims;
-
-import android.app.PendingIntent;
-import android.os.IBinder;
-import android.os.Message;
-import android.os.RemoteException;
-import android.telephony.ims.feature.IMMTelFeature;
-import android.telephony.ims.feature.ImsFeature;
-
-import com.android.ims.ImsCallProfile;
-import com.android.ims.internal.IImsCallSession;
-import com.android.ims.internal.IImsCallSessionListener;
-import com.android.ims.internal.IImsConfig;
-import com.android.ims.internal.IImsEcbm;
-import com.android.ims.internal.IImsMultiEndpoint;
-import com.android.ims.internal.IImsRegistrationListener;
-import com.android.ims.internal.IImsService;
-import com.android.ims.internal.IImsUt;
-
-/**
- * Compatibility class that implements the new ImsService IMMTelFeature interface, but
- * uses the old IImsService interface to support older devices that implement the deprecated
- * opt/net/ims interface.
- * @hide
- */
-
-public class ImsServiceProxyCompat implements IMMTelFeature {
-
- private static final int SERVICE_ID = ImsFeature.MMTEL;
-
- protected final int mSlotId;
- protected IBinder mBinder;
-
- public ImsServiceProxyCompat(int slotId, IBinder binder) {
- mSlotId = slotId;
- mBinder = binder;
- }
-
- @Override
- public int startSession(PendingIntent incomingCallIntent, IImsRegistrationListener listener)
- throws RemoteException {
- checkBinderConnection();
- return getServiceInterface(mBinder).open(mSlotId, ImsFeature.MMTEL, incomingCallIntent,
- listener);
- }
-
- @Override
- public void endSession(int sessionId) throws RemoteException {
- checkBinderConnection();
- getServiceInterface(mBinder).close(sessionId);
- }
-
- @Override
- public boolean isConnected(int callServiceType, int callType)
- throws RemoteException {
- checkBinderConnection();
- return getServiceInterface(mBinder).isConnected(SERVICE_ID, callServiceType, callType);
- }
-
- @Override
- public boolean isOpened() throws RemoteException {
- checkBinderConnection();
- return getServiceInterface(mBinder).isOpened(SERVICE_ID);
- }
-
- @Override
- public void addRegistrationListener(IImsRegistrationListener listener)
- throws RemoteException {
- checkBinderConnection();
- getServiceInterface(mBinder).addRegistrationListener(mSlotId, ImsFeature.MMTEL, listener);
- }
-
- @Override
- public void removeRegistrationListener(IImsRegistrationListener listener)
- throws RemoteException {
- // Not Implemented in old ImsService. If the registration listener becomes invalid, the
- // ImsService will remove.
- }
-
- @Override
- public ImsCallProfile createCallProfile(int sessionId, int callServiceType, int callType)
- throws RemoteException {
- checkBinderConnection();
- return getServiceInterface(mBinder).createCallProfile(sessionId, callServiceType, callType);
- }
-
- @Override
- public IImsCallSession createCallSession(int sessionId, ImsCallProfile profile,
- IImsCallSessionListener listener) throws RemoteException {
- checkBinderConnection();
- return getServiceInterface(mBinder).createCallSession(sessionId, profile, listener);
- }
-
- @Override
- public IImsCallSession getPendingCallSession(int sessionId, String callId)
- throws RemoteException {
- checkBinderConnection();
- return getServiceInterface(mBinder).getPendingCallSession(sessionId, callId);
- }
-
- @Override
- public IImsUt getUtInterface() throws RemoteException {
- checkBinderConnection();
- return getServiceInterface(mBinder).getUtInterface(SERVICE_ID);
- }
-
- @Override
- public IImsConfig getConfigInterface() throws RemoteException {
- checkBinderConnection();
- return getServiceInterface(mBinder).getConfigInterface(mSlotId);
- }
-
- @Override
- public void turnOnIms() throws RemoteException {
- checkBinderConnection();
- getServiceInterface(mBinder).turnOnIms(mSlotId);
- }
-
- @Override
- public void turnOffIms() throws RemoteException {
- checkBinderConnection();
- getServiceInterface(mBinder).turnOffIms(mSlotId);
- }
-
- @Override
- public IImsEcbm getEcbmInterface() throws RemoteException {
- checkBinderConnection();
- return getServiceInterface(mBinder).getEcbmInterface(SERVICE_ID);
- }
-
- @Override
- public void setUiTTYMode(int uiTtyMode, Message onComplete)
- throws RemoteException {
- checkBinderConnection();
- getServiceInterface(mBinder).setUiTTYMode(SERVICE_ID, uiTtyMode, onComplete);
- }
-
- @Override
- public IImsMultiEndpoint getMultiEndpointInterface() throws RemoteException {
- checkBinderConnection();
- return getServiceInterface(mBinder).getMultiEndpointInterface(SERVICE_ID);
- }
-
- /**
- * Base implementation, always returns READY for compatibility with old ImsService.
- */
- public int getFeatureStatus() {
- return ImsFeature.STATE_READY;
- }
-
- /**
- * @return false if the binder connection is no longer alive.
- */
- public boolean isBinderAlive() {
- return mBinder != null && mBinder.isBinderAlive();
- }
-
- /**
- * @return Returns true if the ImsService is ready to take commands, false otherwise. If this
- * method returns false, it doesn't mean that the Binder connection is not available (use
- * {@link #isBinderReady()} to check that), but that the ImsService is not accepting commands
- * at this time.
- *
- * For example, for DSDS devices, only one slot can be {@link ImsFeature#STATE_READY} to take
- * commands at a time, so the other slot must stay at {@link ImsFeature#STATE_NOT_AVAILABLE}.
- */
- public boolean isBinderReady() {
- return isBinderAlive() && getFeatureStatus() == ImsFeature.STATE_READY;
- }
-
- private IImsService getServiceInterface(IBinder b) {
- return IImsService.Stub.asInterface(b);
- }
-
- protected void checkBinderConnection() throws RemoteException {
- if (!isBinderAlive()) {
- throw new RemoteException("ImsServiceProxy is not available for that feature.");
- }
- }
-}
diff --git a/telephony/java/android/telephony/ims/feature/IMMTelFeature.java b/telephony/java/android/telephony/ims/feature/IMMTelFeature.java
deleted file mode 100644
index d65e27e..0000000
--- a/telephony/java/android/telephony/ims/feature/IMMTelFeature.java
+++ /dev/null
@@ -1,187 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package android.telephony.ims.feature;
-
-import android.app.PendingIntent;
-import android.os.Message;
-import android.os.RemoteException;
-
-import com.android.ims.ImsCallProfile;
-import com.android.ims.internal.IImsCallSession;
-import com.android.ims.internal.IImsCallSessionListener;
-import com.android.ims.internal.IImsConfig;
-import com.android.ims.internal.IImsEcbm;
-import com.android.ims.internal.IImsMultiEndpoint;
-import com.android.ims.internal.IImsRegistrationListener;
-import com.android.ims.internal.IImsUt;
-
-/**
- * MMTel interface for an ImsService. When updating this interface, ensure that base implementations
- * of your changes are also present in MMTelFeature for compatibility with older versions of the
- * MMTel feature.
- * @hide
- */
-
-public interface IMMTelFeature {
-
- /**
- * Notifies the MMTel feature that you would like to start a session. This should always be
- * done before making/receiving IMS calls. The IMS service will register the device to the
- * operator's network with the credentials (from ISIM) periodically in order to receive calls
- * from the operator's network. When the IMS service receives a new call, it will send out an
- * intent with the provided action string. The intent contains a call ID extra
- * {@link IImsCallSession#getCallId} and it can be used to take a call.
- *
- * @param incomingCallIntent When an incoming call is received, the IMS service will call
- * {@link PendingIntent#send} to send back the intent to the caller with
- * {@link #INCOMING_CALL_RESULT_CODE} as the result code and the intent to fill in the call ID;
- * It cannot be null.
- * @param listener To listen to IMS registration events; It cannot be null
- * @return an integer (greater than 0) representing the session id associated with the session
- * that has been started.
- */
- int startSession(PendingIntent incomingCallIntent, IImsRegistrationListener listener)
- throws RemoteException;
-
- /**
- * End a previously started session using the associated sessionId.
- * @param sessionId an integer (greater than 0) representing the ongoing session. See
- * {@link #startSession}.
- */
- void endSession(int sessionId) throws RemoteException;
-
- /**
- * Checks if the IMS service has successfully registered to the IMS network with the specified
- * service & call type.
- *
- * @param callServiceType a service type that is specified in {@link ImsCallProfile}
- * {@link ImsCallProfile#SERVICE_TYPE_NORMAL}
- * {@link ImsCallProfile#SERVICE_TYPE_EMERGENCY}
- * @param callType a call type that is specified in {@link ImsCallProfile}
- * {@link ImsCallProfile#CALL_TYPE_VOICE_N_VIDEO}
- * {@link ImsCallProfile#CALL_TYPE_VOICE}
- * {@link ImsCallProfile#CALL_TYPE_VT}
- * {@link ImsCallProfile#CALL_TYPE_VS}
- * @return true if the specified service id is connected to the IMS network; false otherwise
- * @throws RemoteException
- */
- boolean isConnected(int callServiceType, int callType) throws RemoteException;
-
- /**
- * Checks if the specified IMS service is opened.
- *
- * @return true if the specified service id is opened; false otherwise
- */
- boolean isOpened() throws RemoteException;
-
- /**
- * Add a new registration listener for the client associated with the session Id.
- * @param listener An implementation of IImsRegistrationListener.
- */
- void addRegistrationListener(IImsRegistrationListener listener)
- throws RemoteException;
-
- /**
- * Remove a previously registered listener using {@link #addRegistrationListener} for the client
- * associated with the session Id.
- * @param listener A previously registered IImsRegistrationListener
- */
- void removeRegistrationListener(IImsRegistrationListener listener)
- throws RemoteException;
-
- /**
- * Creates a {@link ImsCallProfile} from the service capabilities & IMS registration state.
- *
- * @param sessionId a session id which is obtained from {@link #startSession}
- * @param callServiceType a service type that is specified in {@link ImsCallProfile}
- * {@link ImsCallProfile#SERVICE_TYPE_NONE}
- * {@link ImsCallProfile#SERVICE_TYPE_NORMAL}
- * {@link ImsCallProfile#SERVICE_TYPE_EMERGENCY}
- * @param callType a call type that is specified in {@link ImsCallProfile}
- * {@link ImsCallProfile#CALL_TYPE_VOICE}
- * {@link ImsCallProfile#CALL_TYPE_VT}
- * {@link ImsCallProfile#CALL_TYPE_VT_TX}
- * {@link ImsCallProfile#CALL_TYPE_VT_RX}
- * {@link ImsCallProfile#CALL_TYPE_VT_NODIR}
- * {@link ImsCallProfile#CALL_TYPE_VS}
- * {@link ImsCallProfile#CALL_TYPE_VS_TX}
- * {@link ImsCallProfile#CALL_TYPE_VS_RX}
- * @return a {@link ImsCallProfile} object
- */
- ImsCallProfile createCallProfile(int sessionId, int callServiceType, int callType)
- throws RemoteException;
-
- /**
- * Creates a {@link ImsCallSession} with the specified call profile.
- * Use other methods, if applicable, instead of interacting with
- * {@link ImsCallSession} directly.
- *
- * @param sessionId a session id which is obtained from {@link #startSession}
- * @param profile a call profile to make the call
- * @param listener An implementation of IImsCallSessionListener.
- */
- IImsCallSession createCallSession(int sessionId, ImsCallProfile profile,
- IImsCallSessionListener listener) throws RemoteException;
-
- /**
- * Retrieves the call session associated with a pending call.
- *
- * @param sessionId a session id which is obtained from {@link #startSession}
- * @param callId a call id to make the call
- */
- IImsCallSession getPendingCallSession(int sessionId, String callId) throws RemoteException;
-
- /**
- * @return The Ut interface for the supplementary service configuration.
- */
- IImsUt getUtInterface() throws RemoteException;
-
- /**
- * @return The config interface for IMS Configuration
- */
- IImsConfig getConfigInterface() throws RemoteException;
-
- /**
- * Signal the MMTelFeature to turn on IMS when it has been turned off using {@link #turnOffIms}
- * @param sessionId a session id which is obtained from {@link #startSession}
- */
- void turnOnIms() throws RemoteException;
-
- /**
- * Signal the MMTelFeature to turn off IMS when it has been turned on using {@link #turnOnIms}
- * @param sessionId a session id which is obtained from {@link #startSession}
- */
- void turnOffIms() throws RemoteException;
-
- /**
- * @return The Emergency call-back mode interface for emergency VoLTE calls that support it.
- */
- IImsEcbm getEcbmInterface() throws RemoteException;
-
- /**
- * Sets the current UI TTY mode for the MMTelFeature.
- * @param uiTtyMode An integer containing the new UI TTY Mode.
- * @param onComplete A {@link Message} to be used when the mode has been set.
- * @throws RemoteException
- */
- void setUiTTYMode(int uiTtyMode, Message onComplete) throws RemoteException;
-
- /**
- * @return MultiEndpoint interface for DEP notifications
- */
- IImsMultiEndpoint getMultiEndpointInterface() throws RemoteException;
-}
diff --git a/telephony/java/android/telephony/ims/feature/IRcsFeature.java b/telephony/java/android/telephony/ims/feature/IRcsFeature.java
deleted file mode 100644
index e28e1b3..0000000
--- a/telephony/java/android/telephony/ims/feature/IRcsFeature.java
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package android.telephony.ims.feature;
-
-/**
- * Feature interface that provides access to RCS APIs. Currently empty until RCS support is added
- * in the framework.
- * @hide
- */
-
-public interface IRcsFeature {
-}
diff --git a/telephony/java/android/telephony/ims/feature/MMTelFeature.java b/telephony/java/android/telephony/ims/feature/MMTelFeature.java
index a71f0bf..758c379 100644
--- a/telephony/java/android/telephony/ims/feature/MMTelFeature.java
+++ b/telephony/java/android/telephony/ims/feature/MMTelFeature.java
@@ -32,90 +32,183 @@
import java.util.List;
/**
- * Base implementation, which implements all methods in IMMTelFeature. Any class wishing to use
- * MMTelFeature should extend this class and implement all methods that the service supports.
+ * Base implementation for MMTel.
+ * Any class wishing to use MMTelFeature should extend this class and implement all methods that the
+ * service supports.
*
* @hide
*/
-public class MMTelFeature extends ImsFeature implements IMMTelFeature {
+public class MMTelFeature extends ImsFeature {
- @Override
+ /**
+ * Notifies the MMTel feature that you would like to start a session. This should always be
+ * done before making/receiving IMS calls. The IMS service will register the device to the
+ * operator's network with the credentials (from ISIM) periodically in order to receive calls
+ * from the operator's network. When the IMS service receives a new call, it will send out an
+ * intent with the provided action string. The intent contains a call ID extra
+ * {@link IImsCallSession#getCallId} and it can be used to take a call.
+ *
+ * @param incomingCallIntent When an incoming call is received, the IMS service will call
+ * {@link PendingIntent#send} to send back the intent to the caller with
+ * ImsManager#INCOMING_CALL_RESULT_CODE as the result code and the intent to fill in the call
+ * ID; It cannot be null.
+ * @param listener To listen to IMS registration events; It cannot be null
+ * @return an integer (greater than 0) representing the session id associated with the session
+ * that has been started.
+ */
public int startSession(PendingIntent incomingCallIntent, IImsRegistrationListener listener) {
return 0;
}
- @Override
+ /**
+ * End a previously started session using the associated sessionId.
+ * @param sessionId an integer (greater than 0) representing the ongoing session. See
+ * {@link #startSession}.
+ */
public void endSession(int sessionId) {
}
- @Override
+ /**
+ * Checks if the IMS service has successfully registered to the IMS network with the specified
+ * service & call type.
+ *
+ * @param callSessionType a service type that is specified in {@link ImsCallProfile}
+ * {@link ImsCallProfile#SERVICE_TYPE_NORMAL}
+ * {@link ImsCallProfile#SERVICE_TYPE_EMERGENCY}
+ * @param callType a call type that is specified in {@link ImsCallProfile}
+ * {@link ImsCallProfile#CALL_TYPE_VOICE_N_VIDEO}
+ * {@link ImsCallProfile#CALL_TYPE_VOICE}
+ * {@link ImsCallProfile#CALL_TYPE_VT}
+ * {@link ImsCallProfile#CALL_TYPE_VS}
+ * @return true if the specified service id is connected to the IMS network; false otherwise
+ */
public boolean isConnected(int callSessionType, int callType) {
return false;
}
- @Override
+ /**
+ * Checks if the specified IMS service is opened.
+ *
+ * @return true if the specified service id is opened; false otherwise
+ */
public boolean isOpened() {
return false;
}
- @Override
+ /**
+ * Add a new registration listener for the client associated with the session Id.
+ * @param listener An implementation of IImsRegistrationListener.
+ */
public void addRegistrationListener(IImsRegistrationListener listener) {
}
- @Override
+ /**
+ * Remove a previously registered listener using {@link #addRegistrationListener} for the client
+ * associated with the session Id.
+ * @param listener A previously registered IImsRegistrationListener
+ */
public void removeRegistrationListener(IImsRegistrationListener listener) {
}
- @Override
+ /**
+ * Creates a {@link ImsCallProfile} from the service capabilities & IMS registration state.
+ *
+ * @param sessionId a session id which is obtained from {@link #startSession}
+ * @param callSessionType a service type that is specified in {@link ImsCallProfile}
+ * {@link ImsCallProfile#SERVICE_TYPE_NONE}
+ * {@link ImsCallProfile#SERVICE_TYPE_NORMAL}
+ * {@link ImsCallProfile#SERVICE_TYPE_EMERGENCY}
+ * @param callType a call type that is specified in {@link ImsCallProfile}
+ * {@link ImsCallProfile#CALL_TYPE_VOICE}
+ * {@link ImsCallProfile#CALL_TYPE_VT}
+ * {@link ImsCallProfile#CALL_TYPE_VT_TX}
+ * {@link ImsCallProfile#CALL_TYPE_VT_RX}
+ * {@link ImsCallProfile#CALL_TYPE_VT_NODIR}
+ * {@link ImsCallProfile#CALL_TYPE_VS}
+ * {@link ImsCallProfile#CALL_TYPE_VS_TX}
+ * {@link ImsCallProfile#CALL_TYPE_VS_RX}
+ * @return a {@link ImsCallProfile} object
+ */
public ImsCallProfile createCallProfile(int sessionId, int callSessionType, int callType) {
return null;
}
- @Override
+ /**
+ * Creates a {@link ImsCallSession} with the specified call profile.
+ * Use other methods, if applicable, instead of interacting with
+ * {@link ImsCallSession} directly.
+ *
+ * @param sessionId a session id which is obtained from {@link #startSession}
+ * @param profile a call profile to make the call
+ * @param listener An implementation of IImsCallSessionListener.
+ */
public IImsCallSession createCallSession(int sessionId, ImsCallProfile profile,
IImsCallSessionListener listener) {
return null;
}
- @Override
+ /**
+ * Retrieves the call session associated with a pending call.
+ *
+ * @param sessionId a session id which is obtained from {@link #startSession}
+ * @param callId a call id to make the call
+ */
public IImsCallSession getPendingCallSession(int sessionId, String callId) {
return null;
}
- @Override
+ /**
+ * @return The Ut interface for the supplementary service configuration.
+ */
public IImsUt getUtInterface() {
return null;
}
- @Override
+ /**
+ * @return The config interface for IMS Configuration
+ */
public IImsConfig getConfigInterface() {
return null;
}
- @Override
+ /**
+ * Signal the MMTelFeature to turn on IMS when it has been turned off using {@link #turnOffIms}
+ */
public void turnOnIms() {
}
- @Override
+ /**
+ * Signal the MMTelFeature to turn off IMS when it has been turned on using {@link #turnOnIms}
+ */
public void turnOffIms() {
}
- @Override
+ /**
+ * @return The Emergency call-back mode interface for emergency VoLTE calls that support it.
+ */
public IImsEcbm getEcbmInterface() {
return null;
}
- @Override
+ /**
+ * Sets the current UI TTY mode for the MMTelFeature.
+ * @param uiTtyMode An integer containing the new UI TTY Mode.
+ * @param onComplete A {@link Message} to be used when the mode has been set.
+ */
public void setUiTTYMode(int uiTtyMode, Message onComplete) {
}
- @Override
+ /**
+ * @return MultiEndpoint interface for DEP notifications
+ */
public IImsMultiEndpoint getMultiEndpointInterface() {
return null;
}
- @Override
+ /**
+ * {@inheritDoc}
+ */
public void onFeatureRemoved() {
}
diff --git a/telephony/java/android/telephony/ims/feature/RcsFeature.java b/telephony/java/android/telephony/ims/feature/RcsFeature.java
index 9cddc1b..332cca3 100644
--- a/telephony/java/android/telephony/ims/feature/RcsFeature.java
+++ b/telephony/java/android/telephony/ims/feature/RcsFeature.java
@@ -18,11 +18,11 @@
/**
* Base implementation of the RcsFeature APIs. Any ImsService wishing to support RCS should extend
- * this class and provide implementations of the IRcsFeature methods that they support.
+ * this class and provide implementations of the RcsFeature methods that they support.
* @hide
*/
-public class RcsFeature extends ImsFeature implements IRcsFeature {
+public class RcsFeature extends ImsFeature {
public RcsFeature() {
super();
diff --git a/telephony/java/android/telephony/mbms/DownloadStateCallback.java b/telephony/java/android/telephony/mbms/DownloadStateCallback.java
index 892fbf0..9f60cc3 100644
--- a/telephony/java/android/telephony/mbms/DownloadStateCallback.java
+++ b/telephony/java/android/telephony/mbms/DownloadStateCallback.java
@@ -38,7 +38,7 @@
* @hide
*/
@Retention(RetentionPolicy.SOURCE)
- @IntDef({ALL_UPDATES, PROGRESS_UPDATES, STATE_UPDATES})
+ @IntDef(flag = true, value = {ALL_UPDATES, PROGRESS_UPDATES, STATE_UPDATES})
public @interface FilterFlag {}
/**
diff --git a/telephony/java/android/telephony/mbms/FileInfo.java b/telephony/java/android/telephony/mbms/FileInfo.java
index 0d737b5..e064adb 100644
--- a/telephony/java/android/telephony/mbms/FileInfo.java
+++ b/telephony/java/android/telephony/mbms/FileInfo.java
@@ -17,10 +17,13 @@
package android.telephony.mbms;
import android.annotation.SystemApi;
+import android.annotation.TestApi;
import android.net.Uri;
import android.os.Parcel;
import android.os.Parcelable;
+import java.util.Objects;
+
/**
* Describes a single file that is available over MBMS.
*/
@@ -47,6 +50,7 @@
* @hide
*/
@SystemApi
+ @TestApi
public FileInfo(Uri uri, String mimeType) {
this.uri = uri;
this.mimeType = mimeType;
@@ -82,4 +86,23 @@
public String getMimeType() {
return mimeType;
}
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+
+ FileInfo fileInfo = (FileInfo) o;
+ return Objects.equals(uri, fileInfo.uri) &&
+ Objects.equals(mimeType, fileInfo.mimeType);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(uri, mimeType);
+ }
}
diff --git a/telephony/java/android/telephony/mbms/MbmsDownloadReceiver.java b/telephony/java/android/telephony/mbms/MbmsDownloadReceiver.java
index 9af1eb9..9ef188c 100644
--- a/telephony/java/android/telephony/mbms/MbmsDownloadReceiver.java
+++ b/telephony/java/android/telephony/mbms/MbmsDownloadReceiver.java
@@ -165,16 +165,16 @@
Log.w(LOG_TAG, "Download result did not include a result code. Ignoring.");
return false;
}
+ if (!intent.hasExtra(MbmsDownloadSession.EXTRA_MBMS_DOWNLOAD_REQUEST)) {
+ Log.w(LOG_TAG, "Download result did not include the associated request. Ignoring.");
+ return false;
+ }
// We do not need to verify below extras if the result is not success.
if (MbmsDownloadSession.RESULT_SUCCESSFUL !=
intent.getIntExtra(MbmsDownloadSession.EXTRA_MBMS_DOWNLOAD_RESULT,
MbmsDownloadSession.RESULT_CANCELLED)) {
return true;
}
- if (!intent.hasExtra(MbmsDownloadSession.EXTRA_MBMS_DOWNLOAD_REQUEST)) {
- Log.w(LOG_TAG, "Download result did not include the associated request. Ignoring.");
- return false;
- }
if (!intent.hasExtra(VendorUtils.EXTRA_TEMP_FILE_ROOT)) {
Log.w(LOG_TAG, "Download result did not include the temp file root. Ignoring.");
return false;
@@ -242,10 +242,12 @@
int result = intent.getIntExtra(MbmsDownloadSession.EXTRA_MBMS_DOWNLOAD_RESULT,
MbmsDownloadSession.RESULT_CANCELLED);
intentForApp.putExtra(MbmsDownloadSession.EXTRA_MBMS_DOWNLOAD_RESULT, result);
+ intentForApp.putExtra(MbmsDownloadSession.EXTRA_MBMS_DOWNLOAD_REQUEST, request);
if (result != MbmsDownloadSession.RESULT_SUCCESSFUL) {
Log.i(LOG_TAG, "Download request indicated a failed download. Aborting.");
context.sendBroadcast(intentForApp);
+ setResultCode(RESULT_OK);
return;
}
@@ -273,7 +275,6 @@
intentForApp.putExtra(MbmsDownloadSession.EXTRA_MBMS_COMPLETED_FILE_URI,
stagedFileLocation);
intentForApp.putExtra(MbmsDownloadSession.EXTRA_MBMS_FILE_INFO, completedFileInfo);
- intentForApp.putExtra(MbmsDownloadSession.EXTRA_MBMS_DOWNLOAD_REQUEST, request);
context.sendBroadcast(intentForApp);
setResultCode(RESULT_OK);
diff --git a/telephony/java/android/telephony/mbms/UriPathPair.java b/telephony/java/android/telephony/mbms/UriPathPair.java
index 187e9ee..dd20a69 100644
--- a/telephony/java/android/telephony/mbms/UriPathPair.java
+++ b/telephony/java/android/telephony/mbms/UriPathPair.java
@@ -17,6 +17,7 @@
package android.telephony.mbms;
import android.annotation.SystemApi;
+import android.annotation.TestApi;
import android.content.ContentResolver;
import android.net.Uri;
import android.os.Parcel;
@@ -29,6 +30,7 @@
* @hide
*/
@SystemApi
+@TestApi
public final class UriPathPair implements Parcelable {
private final Uri mFilePathUri;
private final Uri mContentUri;
diff --git a/telephony/java/android/telephony/mbms/vendor/VendorUtils.java b/telephony/java/android/telephony/mbms/vendor/VendorUtils.java
index a43f122..f1cac8c 100644
--- a/telephony/java/android/telephony/mbms/vendor/VendorUtils.java
+++ b/telephony/java/android/telephony/mbms/vendor/VendorUtils.java
@@ -17,6 +17,7 @@
package android.telephony.mbms.vendor;
import android.annotation.SystemApi;
+import android.annotation.TestApi;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -34,6 +35,7 @@
* @hide
*/
@SystemApi
+@TestApi
public class VendorUtils {
/**
diff --git a/telephony/java/com/android/ims/internal/IImsServiceController.aidl b/telephony/java/com/android/ims/internal/IImsServiceController.aidl
index bb06d7e..f1e2262 100644
--- a/telephony/java/com/android/ims/internal/IImsServiceController.aidl
+++ b/telephony/java/com/android/ims/internal/IImsServiceController.aidl
@@ -31,7 +31,7 @@
import android.os.Message;
/**
- * See ImsService and IMMTelFeature for more information.
+ * See ImsService and MMTelFeature for more information.
* {@hide}
*/
interface IImsServiceController {
diff --git a/telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl b/telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl
index e9c5461..ac16139 100644
--- a/telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl
+++ b/telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl
@@ -45,7 +45,6 @@
void onVoLteServiceStateChanged(in VoLteServiceState lteState);
void onVoiceActivationStateChanged(int activationState);
void onDataActivationStateChanged(int activationState);
- void onOemHookRawEvent(in byte[] rawData);
void onCarrierNetworkChange(in boolean active);
}
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index 2ac11b5..3cc9bde 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -1000,17 +1000,6 @@
in List<String> cdmaNonRoamingList);
/**
- * Returns the result and response from RIL for oem request
- *
- * @param oemReq the data is sent to ril.
- * @param oemResp the respose data from RIL.
- * @return negative value request was not handled or get error
- * 0 request was handled succesfully, but no response data
- * positive value success, data length of response
- */
- int invokeOemRilRequestRaw(in byte[] oemReq, out byte[] oemResp);
-
- /**
* Check if any mobile Radios need to be shutdown.
*
* @return true is any mobile radio needs to be shutdown
diff --git a/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl b/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl
index 2c2206c..75d8f3f 100644
--- a/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl
@@ -67,7 +67,6 @@
void notifyVoLteServiceStateChanged(in VoLteServiceState lteState);
void notifySimActivationStateChangedForPhoneId(in int phoneId, in int subId,
int activationState, int activationType);
- void notifyOemHookRawEventForSubscriber(in int subId, in byte[] rawData);
void notifySubscriptionInfoChanged();
void notifyCarrierNetworkChange(in boolean active);
}
diff --git a/tests/FeatureSplit/feature1/Android.mk b/tests/FeatureSplit/feature1/Android.mk
index b3ea97b..e6ba5c2 100644
--- a/tests/FeatureSplit/feature1/Android.mk
+++ b/tests/FeatureSplit/feature1/Android.mk
@@ -26,6 +26,6 @@
LOCAL_RES_LIBRARIES := FeatureSplitBase
LOCAL_AAPT_FLAGS += --package-id 0x80
-LOCAL_AAPT_FLAGS += --rename-manifest-package com.android.test.split.feature
+LOCAL_AAPT_FLAGS += --custom-package com.android.test.split.feature.one
include $(BUILD_PACKAGE)
diff --git a/tests/FeatureSplit/feature1/AndroidManifest.xml b/tests/FeatureSplit/feature1/AndroidManifest.xml
index 4e7d151..b87361f 100644
--- a/tests/FeatureSplit/feature1/AndroidManifest.xml
+++ b/tests/FeatureSplit/feature1/AndroidManifest.xml
@@ -15,13 +15,13 @@
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.test.split.feature.one"
+ package="com.android.test.split.feature"
featureSplit="feature1">
<uses-sdk android:minSdkVersion="21" />
<application>
- <activity android:name=".One" android:label="Feature One">
+ <activity android:name=".one.One" android:label="Feature One">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
diff --git a/tests/FeatureSplit/feature1/res/values/values.xml b/tests/FeatureSplit/feature1/res/values/values.xml
index 0e3e73c..7d58865 100644
--- a/tests/FeatureSplit/feature1/res/values/values.xml
+++ b/tests/FeatureSplit/feature1/res/values/values.xml
@@ -20,7 +20,7 @@
<integer name="test_integer2">200</integer>
<color name="test_color2">#00ff00</color>
<string-array name="string_array2">
- <item>@*com.android.test.split.feature:string/app_title</item>
+ <item>@string/app_title</item>
</string-array>
</resources>
diff --git a/tests/FeatureSplit/feature2/Android.mk b/tests/FeatureSplit/feature2/Android.mk
index e2fd903..c8e8609 100644
--- a/tests/FeatureSplit/feature2/Android.mk
+++ b/tests/FeatureSplit/feature2/Android.mk
@@ -26,6 +26,6 @@
LOCAL_RES_LIBRARIES := FeatureSplitBase
LOCAL_AAPT_FLAGS += --package-id 0x81
-LOCAL_AAPT_FLAGS += --rename-manifest-package com.android.test.split.feature
+LOCAL_AAPT_FLAGS += --custom-package com.android.test.split.feature.two
include $(BUILD_PACKAGE)
diff --git a/tests/FeatureSplit/feature2/AndroidManifest.xml b/tests/FeatureSplit/feature2/AndroidManifest.xml
index bfe6f38..abd0b5e 100644
--- a/tests/FeatureSplit/feature2/AndroidManifest.xml
+++ b/tests/FeatureSplit/feature2/AndroidManifest.xml
@@ -15,7 +15,7 @@
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.test.split.feature.two"
+ package="com.android.test.split.feature"
featureSplit="feature2">
<uses-sdk android:minSdkVersion="21" />
diff --git a/tests/FeatureSplit/feature2/res/values/values.xml b/tests/FeatureSplit/feature2/res/values/values.xml
index 2fa6f90..af5ed1b 100644
--- a/tests/FeatureSplit/feature2/res/values/values.xml
+++ b/tests/FeatureSplit/feature2/res/values/values.xml
@@ -18,7 +18,7 @@
<integer name="test_integer3">300</integer>
<color name="test_color3">#0000ff</color>
<string-array name="string_array3">
- <item>@*com.android.test.split.feature:string/app_title</item>
+ <item>@string/app_title</item>
</string-array>
</resources>
diff --git a/tests/UiBench/Android.mk b/tests/UiBench/Android.mk
index 0824c26..60327e5 100644
--- a/tests/UiBench/Android.mk
+++ b/tests/UiBench/Android.mk
@@ -10,25 +10,11 @@
# use appcompat/support lib from the tree, so improvements/
# regressions are reflected in test data
-LOCAL_RESOURCE_DIR := \
- $(LOCAL_PATH)/res \
- frameworks/support/core-ui/res \
- frameworks/support/design/res \
- frameworks/support/v7/appcompat/res \
- frameworks/support/v7/cardview/res \
- frameworks/support/v7/recyclerview/res \
- frameworks/support/v17/leanback/res
+LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
-LOCAL_AAPT_FLAGS := \
- --auto-add-overlay \
- --extra-packages android.support.coreui \
- --extra-packages android.support.design \
- --extra-packages android.support.v7.appcompat \
- --extra-packages android.support.v7.cardview \
- --extra-packages android.support.v7.recyclerview \
- --extra-packages android.support.v17.leanback
+LOCAL_USE_AAPT2 := true
-LOCAL_STATIC_JAVA_LIBRARIES := \
+LOCAL_STATIC_ANDROID_LIBRARIES := \
android-support-design \
android-support-v4 \
android-support-v7-appcompat \
diff --git a/tests/net/java/android/net/MacAddressTest.java b/tests/net/java/android/net/MacAddressTest.java
index 3fa9c3a..fcbb9da 100644
--- a/tests/net/java/android/net/MacAddressTest.java
+++ b/tests/net/java/android/net/MacAddressTest.java
@@ -19,12 +19,14 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.fail;
import android.net.MacAddress.MacAddressType;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
import java.util.Arrays;
+import java.util.Random;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -64,10 +66,139 @@
String msg = String.format("expected type of %s to be %s, but got %s",
Arrays.toString(t.addr), t.expected, got);
assertEquals(msg, t.expected, got);
+
+ if (got != null) {
+ assertEquals(got, new MacAddress(t.addr).addressType());
+ }
}
}
- static byte[] toByteArray(int[] in) {
+ @Test
+ public void testIsMulticastAddress() {
+ MacAddress[] multicastAddresses = {
+ MacAddress.BROADCAST_ADDRESS,
+ new MacAddress("07:00:d3:56:8a:c4"),
+ new MacAddress("33:33:aa:bb:cc:dd"),
+ };
+ MacAddress[] unicastAddresses = {
+ MacAddress.ALL_ZEROS_ADDRESS,
+ new MacAddress("00:01:44:55:66:77"),
+ new MacAddress("08:00:22:33:44:55"),
+ new MacAddress("06:00:00:00:00:00"),
+ };
+
+ for (MacAddress mac : multicastAddresses) {
+ String msg = mac.toString() + " expected to be a multicast address";
+ assertTrue(msg, mac.isMulticastAddress());
+ }
+ for (MacAddress mac : unicastAddresses) {
+ String msg = mac.toString() + " expected not to be a multicast address";
+ assertFalse(msg, mac.isMulticastAddress());
+ }
+ }
+
+ @Test
+ public void testIsLocallyAssignedAddress() {
+ MacAddress[] localAddresses = {
+ new MacAddress("06:00:00:00:00:00"),
+ new MacAddress("07:00:d3:56:8a:c4"),
+ new MacAddress("33:33:aa:bb:cc:dd"),
+ };
+ MacAddress[] universalAddresses = {
+ new MacAddress("00:01:44:55:66:77"),
+ new MacAddress("08:00:22:33:44:55"),
+ };
+
+ for (MacAddress mac : localAddresses) {
+ String msg = mac.toString() + " expected to be a locally assigned address";
+ assertTrue(msg, mac.isLocallyAssigned());
+ }
+ for (MacAddress mac : universalAddresses) {
+ String msg = mac.toString() + " expected not to be globally unique address";
+ assertFalse(msg, mac.isLocallyAssigned());
+ }
+ }
+
+ @Test
+ public void testMacAddressConversions() {
+ final int iterations = 10000;
+ for (int i = 0; i < iterations; i++) {
+ MacAddress mac = MacAddress.getRandomAddress();
+
+ String stringRepr = mac.toString();
+ byte[] bytesRepr = mac.toByteArray();
+
+ assertEquals(mac, new MacAddress(stringRepr));
+ assertEquals(mac, new MacAddress(bytesRepr));
+ }
+ }
+
+ @Test
+ public void testMacAddressRandomGeneration() {
+ final int iterations = 1000;
+ final String expectedAndroidOui = "da:a1:19";
+ for (int i = 0; i < iterations; i++) {
+ MacAddress mac = MacAddress.getRandomAddress();
+ String stringRepr = mac.toString();
+
+ assertTrue(stringRepr + " expected to be a locally assigned address",
+ mac.isLocallyAssigned());
+ assertTrue(stringRepr + " expected to begin with " + expectedAndroidOui,
+ stringRepr.startsWith(expectedAndroidOui));
+ }
+
+ final Random r = new Random();
+ final String anotherOui = "24:5f:78";
+ final String expectedLocalOui = "26:5f:78";
+ final MacAddress base = new MacAddress(anotherOui + ":0:0:0");
+ for (int i = 0; i < iterations; i++) {
+ MacAddress mac = MacAddress.getRandomAddress(base, r);
+ String stringRepr = mac.toString();
+
+ assertTrue(stringRepr + " expected to be a locally assigned address",
+ mac.isLocallyAssigned());
+ assertTrue(stringRepr + " expected to begin with " + expectedLocalOui,
+ stringRepr.startsWith(expectedLocalOui));
+ }
+ }
+
+ @Test
+ public void testConstructorInputValidation() {
+ String[] invalidStringAddresses = {
+ null,
+ "",
+ "abcd",
+ "1:2:3:4:5",
+ "1:2:3:4:5:6:7",
+ "10000:2:3:4:5:6",
+ };
+
+ for (String s : invalidStringAddresses) {
+ try {
+ MacAddress mac = new MacAddress(s);
+ fail("new MacAddress(" + s + ") should have failed, but returned " + mac);
+ } catch (IllegalArgumentException excepted) {
+ }
+ }
+
+ byte[][] invalidBytesAddresses = {
+ null,
+ {},
+ {1,2,3,4,5},
+ {1,2,3,4,5,6,7},
+ };
+
+ for (byte[] b : invalidBytesAddresses) {
+ try {
+ MacAddress mac = new MacAddress(b);
+ fail("new MacAddress(" + Arrays.toString(b)
+ + ") should have failed, but returned " + mac);
+ } catch (IllegalArgumentException excepted) {
+ }
+ }
+ }
+
+ static byte[] toByteArray(int... in) {
byte[] out = new byte[in.length];
for (int i = 0; i < in.length; i++) {
out[i] = (byte) in[i];
diff --git a/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java b/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java
index 9e97d84b..5c031eb 100644
--- a/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java
+++ b/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java
@@ -64,6 +64,13 @@
return Arrays.asList(new Object[][] {{"8.8.4.4"}, {"2601::10"}});
}
+ private static final byte[] AEAD_KEY = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+ 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F,
+ 0x73, 0x61, 0x6C, 0x74
+ };
private static final byte[] CRYPT_KEY = {
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
@@ -87,7 +94,7 @@
private static final IpSecAlgorithm CRYPT_ALGO =
new IpSecAlgorithm(IpSecAlgorithm.CRYPT_AES_CBC, CRYPT_KEY);
private static final IpSecAlgorithm AEAD_ALGO =
- new IpSecAlgorithm(IpSecAlgorithm.AUTH_CRYPT_AES_GCM, CRYPT_KEY, CRYPT_KEY.length * 4);
+ new IpSecAlgorithm(IpSecAlgorithm.AUTH_CRYPT_AES_GCM, AEAD_KEY, 128);
private static final int[] DIRECTIONS =
new int[] {IpSecTransform.DIRECTION_IN, IpSecTransform.DIRECTION_OUT};
@@ -262,7 +269,7 @@
eq(new byte[] {}),
eq(0),
eq(IpSecAlgorithm.AUTH_CRYPT_AES_GCM),
- eq(CRYPT_KEY),
+ eq(AEAD_KEY),
anyInt(),
anyInt(),
anyInt(),
@@ -283,7 +290,7 @@
eq(new byte[] {}),
eq(0),
eq(IpSecAlgorithm.AUTH_CRYPT_AES_GCM),
- eq(CRYPT_KEY),
+ eq(AEAD_KEY),
anyInt(),
anyInt(),
anyInt(),
diff --git a/tests/net/java/com/android/server/connectivity/IpConnectivityEventBuilderTest.java b/tests/net/java/com/android/server/connectivity/IpConnectivityEventBuilderTest.java
index a65bb24..0656c5f 100644
--- a/tests/net/java/com/android/server/connectivity/IpConnectivityEventBuilderTest.java
+++ b/tests/net/java/com/android/server/connectivity/IpConnectivityEventBuilderTest.java
@@ -337,7 +337,6 @@
public void testNetworkEventSerialization() {
ConnectivityMetricsEvent ev = describeIpEvent(
aType(NetworkEvent.class),
- anInt(100),
anInt(5),
aLong(20410));
@@ -352,9 +351,6 @@
" network_event <",
" event_type: 5",
" latency_ms: 20410",
- " network_id <",
- " network_id: 100",
- " >",
" >",
">",
"version: 2\n");
@@ -508,6 +504,13 @@
stats.rootWakeups = 2;
stats.systemWakeups = 3;
stats.noUidWakeups = 3;
+ stats.l2UnicastCount = 5;
+ stats.l2MulticastCount = 1;
+ stats.l2BroadcastCount = 2;
+ stats.ethertypes.put(0x800, 3);
+ stats.ethertypes.put(0x86dd, 3);
+ stats.ipNextHeaders.put(6, 5);
+
IpConnectivityEvent got = IpConnectivityEventBuilder.toProto(stats);
String want = String.join("\n",
@@ -521,6 +524,21 @@
" wakeup_stats <",
" application_wakeups: 5",
" duration_sec: 0",
+ " ethertype_counts <",
+ " key: 2048",
+ " value: 3",
+ " >",
+ " ethertype_counts <",
+ " key: 34525",
+ " value: 3",
+ " >",
+ " ip_next_header_counts <",
+ " key: 6",
+ " value: 5",
+ " >",
+ " l2_broadcast_count: 2",
+ " l2_multicast_count: 1",
+ " l2_unicast_count: 5",
" no_uid_wakeups: 3",
" non_application_wakeups: 1",
" root_wakeups: 2",
diff --git a/tests/net/java/com/android/server/connectivity/IpConnectivityMetricsTest.java b/tests/net/java/com/android/server/connectivity/IpConnectivityMetricsTest.java
index b48ff8d..10d6deb 100644
--- a/tests/net/java/com/android/server/connectivity/IpConnectivityMetricsTest.java
+++ b/tests/net/java/com/android/server/connectivity/IpConnectivityMetricsTest.java
@@ -78,6 +78,9 @@
private static final String EXAMPLE_IPV4 = "192.0.2.1";
private static final String EXAMPLE_IPV6 = "2001:db8:1200::2:1";
+ private static final byte[] MAC_ADDR =
+ {(byte)0x84, (byte)0xc9, (byte)0xb2, (byte)0x6a, (byte)0xed, (byte)0x4b};
+
@Mock Context mCtx;
@Mock IIpConnectivityMetrics mMockService;
@Mock ConnectivityManager mCm;
@@ -351,13 +354,21 @@
dnsEvent(101, EVENT_GETHOSTBYNAME, 0, 34);
// iface, uid
- wakeupEvent("wlan0", 1000);
- wakeupEvent("rmnet0", 10123);
- wakeupEvent("wlan0", 1000);
- wakeupEvent("rmnet0", 10008);
- wakeupEvent("wlan0", -1);
- wakeupEvent("wlan0", 10008);
- wakeupEvent("rmnet0", 1000);
+ final byte[] mac = {0x48, 0x7c, 0x2b, 0x6a, 0x3e, 0x4b};
+ final String srcIp = "192.168.2.1";
+ final String dstIp = "192.168.2.23";
+ final int sport = 2356;
+ final int dport = 13489;
+ final long now = 1001L;
+ final int v4 = 0x800;
+ final int tcp = 6;
+ final int udp = 17;
+ wakeupEvent("wlan0", 1000, v4, tcp, mac, srcIp, dstIp, sport, dport, 1001L);
+ wakeupEvent("wlan0", 10123, v4, tcp, mac, srcIp, dstIp, sport, dport, 1001L);
+ wakeupEvent("wlan0", 1000, v4, udp, mac, srcIp, dstIp, sport, dport, 1001L);
+ wakeupEvent("wlan0", 10008, v4, udp, mac, srcIp, dstIp, sport, dport, 1001L);
+ wakeupEvent("wlan0", -1, v4, udp, mac, srcIp, dstIp, sport, dport, 1001L);
+ wakeupEvent("wlan0", 10008, v4, tcp, mac, srcIp, dstIp, sport, dport, 1001L);
long timeMs = mService.mDefaultNetworkMetrics.creationTimeMs;
final long cell = BitUtils.packBits(new int[]{NetworkCapabilities.TRANSPORT_CELLULAR});
@@ -560,34 +571,33 @@
">",
"events <",
" if_name: \"\"",
- " link_layer: 2",
- " network_id: 0",
- " time_ms: 0",
- " transports: 0",
- " wakeup_stats <",
- " application_wakeups: 2",
- " duration_sec: 0",
- " no_uid_wakeups: 0",
- " non_application_wakeups: 0",
- " root_wakeups: 0",
- " system_wakeups: 1",
- " total_wakeups: 3",
- " >",
- ">",
- "events <",
- " if_name: \"\"",
" link_layer: 4",
" network_id: 0",
" time_ms: 0",
" transports: 0",
" wakeup_stats <",
- " application_wakeups: 1",
+ " application_wakeups: 3",
" duration_sec: 0",
+ " ethertype_counts <",
+ " key: 2048",
+ " value: 6",
+ " >",
+ " ip_next_header_counts <",
+ " key: 6",
+ " value: 3",
+ " >",
+ " ip_next_header_counts <",
+ " key: 17",
+ " value: 3",
+ " >",
+ " l2_broadcast_count: 0",
+ " l2_multicast_count: 0",
+ " l2_unicast_count: 6",
" no_uid_wakeups: 1",
" non_application_wakeups: 0",
" root_wakeups: 0",
" system_wakeups: 2",
- " total_wakeups: 4",
+ " total_wakeups: 6",
" >",
">",
"version: 2\n");
@@ -610,9 +620,10 @@
mNetdListener.onDnsEvent(netId, type, result, latency, "", null, 0, 0);
}
- void wakeupEvent(String iface, int uid) throws Exception {
+ void wakeupEvent(String iface, int uid, int ether, int ip, byte[] mac, String srcIp,
+ String dstIp, int sport, int dport, long now) throws Exception {
String prefix = NetdEventListenerService.WAKEUP_EVENT_IFACE_PREFIX + iface;
- mNetdListener.onWakeupEvent(prefix, uid, uid, 0);
+ mNetdListener.onWakeupEvent(prefix, uid, ether, ip, mac, srcIp, dstIp, sport, dport, now);
}
NetworkAgentInfo makeNai(int netId, int score, boolean ipv4, boolean ipv6, long transports) {
diff --git a/tests/net/java/com/android/server/connectivity/NetdEventListenerServiceTest.java b/tests/net/java/com/android/server/connectivity/NetdEventListenerServiceTest.java
index 83194d9..67805c9 100644
--- a/tests/net/java/com/android/server/connectivity/NetdEventListenerServiceTest.java
+++ b/tests/net/java/com/android/server/connectivity/NetdEventListenerServiceTest.java
@@ -61,7 +61,10 @@
private static final String EXAMPLE_IPV4 = "192.0.2.1";
private static final String EXAMPLE_IPV6 = "2001:db8:1200::2:1";
- NetdEventListenerService mNetdEventListenerService;
+ private static final byte[] MAC_ADDR =
+ {(byte)0x84, (byte)0xc9, (byte)0xb2, (byte)0x6a, (byte)0xed, (byte)0x4b};
+
+ NetdEventListenerService mService;
ConnectivityManager mCm;
@Before
@@ -75,28 +78,49 @@
when(mCm.getNetworkCapabilities(new Network(100))).thenReturn(ncWifi);
when(mCm.getNetworkCapabilities(new Network(101))).thenReturn(ncCell);
- mNetdEventListenerService = new NetdEventListenerService(mCm);
+ mService = new NetdEventListenerService(mCm);
}
@Test
public void testWakeupEventLogging() throws Exception {
final int BUFFER_LENGTH = NetdEventListenerService.WAKEUP_EVENT_BUFFER_LENGTH;
+ final long now = System.currentTimeMillis();
+ final String iface = "wlan0";
+ final byte[] mac = MAC_ADDR;
+ final String srcIp = "192.168.2.1";
+ final String dstIp = "192.168.2.23";
+ final String srcIp6 = "2001:db8:4:fd00:a585:13d1:6a23:4fb4";
+ final String dstIp6 = "2001:db8:4006:807::200a";
+ final int sport = 2356;
+ final int dport = 13489;
+
+ final int v4 = 0x800;
+ final int v6 = 0x86dd;
+ final int tcp = 6;
+ final int udp = 17;
+ final int icmp6 = 58;
// Baseline without any event
String[] baseline = listNetdEvent();
- long now = System.currentTimeMillis();
- String prefix = "iface:wlan0";
- int[] uids = { 10001, 10002, 10004, 1000, 10052, 10023, 10002, 10123, 10004 };
- for (int uid : uids) {
- mNetdEventListenerService.onWakeupEvent(prefix, uid, uid, now);
- }
+ int[] uids = {10001, 10002, 10004, 1000, 10052, 10023, 10002, 10123, 10004};
+ wakeupEvent(iface, uids[0], v4, tcp, mac, srcIp, dstIp, sport, dport, now);
+ wakeupEvent(iface, uids[1], v6, udp, mac, srcIp6, dstIp6, sport, dport, now);
+ wakeupEvent(iface, uids[2], v6, udp, mac, srcIp6, dstIp6, sport, dport, now);
+ wakeupEvent(iface, uids[3], v4, icmp6, mac, srcIp, dstIp, sport, dport, now);
+ wakeupEvent(iface, uids[4], v6, tcp, mac, srcIp6, dstIp6, sport, dport, now);
+ wakeupEvent(iface, uids[5], v4, tcp, mac, srcIp, dstIp, sport, dport, now);
+ wakeupEvent(iface, uids[6], v6, udp, mac, srcIp6, dstIp6, sport, dport, now);
+ wakeupEvent(iface, uids[7], v6, tcp, mac, srcIp6, dstIp6, sport, dport, now);
+ wakeupEvent(iface, uids[8], v6, udp, mac, srcIp6, dstIp6, sport, dport, now);
String[] events2 = remove(listNetdEvent(), baseline);
int expectedLength2 = uids.length + 1; // +1 for the WakeupStats line
assertEquals(expectedLength2, events2.length);
assertContains(events2[0], "WakeupStats");
assertContains(events2[0], "wlan0");
+ assertContains(events2[0], "0x800");
+ assertContains(events2[0], "0x86dd");
for (int i = 0; i < uids.length; i++) {
String got = events2[i+1];
assertContains(got, "WakeupEvent");
@@ -107,7 +131,7 @@
int uid = 20000;
for (int i = 0; i < BUFFER_LENGTH * 2; i++) {
long ts = now + 10;
- mNetdEventListenerService.onWakeupEvent(prefix, uid, uid, ts);
+ wakeupEvent(iface, uid, 0x800, 6, mac, srcIp, dstIp, 23, 24, ts);
}
String[] events3 = remove(listNetdEvent(), baseline);
@@ -123,7 +147,7 @@
}
uid = 45678;
- mNetdEventListenerService.onWakeupEvent(prefix, uid, uid, now);
+ wakeupEvent(iface, uid, 0x800, 6, mac, srcIp, dstIp, 23, 24, now);
String[] events4 = remove(listNetdEvent(), baseline);
String lastEvent = events4[events4.length - 1];
@@ -134,21 +158,36 @@
@Test
public void testWakeupStatsLogging() throws Exception {
- wakeupEvent("wlan0", 1000);
- wakeupEvent("rmnet0", 10123);
- wakeupEvent("wlan0", 1000);
- wakeupEvent("rmnet0", 10008);
- wakeupEvent("wlan0", -1);
- wakeupEvent("wlan0", 10008);
- wakeupEvent("rmnet0", 1000);
- wakeupEvent("wlan0", 10004);
- wakeupEvent("wlan0", 1000);
- wakeupEvent("wlan0", 0);
- wakeupEvent("wlan0", -1);
- wakeupEvent("rmnet0", 10052);
- wakeupEvent("wlan0", 0);
- wakeupEvent("rmnet0", 1000);
- wakeupEvent("wlan0", 1010);
+ final byte[] mac = MAC_ADDR;
+ final String srcIp = "192.168.2.1";
+ final String dstIp = "192.168.2.23";
+ final String srcIp6 = "2401:fa00:4:fd00:a585:13d1:6a23:4fb4";
+ final String dstIp6 = "2404:6800:4006:807::200a";
+ final int sport = 2356;
+ final int dport = 13489;
+ final long now = 1001L;
+
+ final int v4 = 0x800;
+ final int v6 = 0x86dd;
+ final int tcp = 6;
+ final int udp = 17;
+ final int icmp6 = 58;
+
+ wakeupEvent("wlan0", 1000, v4, tcp, mac, srcIp, dstIp, sport, dport, now);
+ wakeupEvent("rmnet0", 10123, v4, tcp, mac, srcIp, dstIp, sport, dport, now);
+ wakeupEvent("wlan0", 1000, v4, udp, mac, srcIp, dstIp, sport, dport, now);
+ wakeupEvent("rmnet0", 10008, v4, tcp, mac, srcIp, dstIp, sport, dport, now);
+ wakeupEvent("wlan0", -1, v6, icmp6, mac, srcIp6, dstIp6, sport, dport, now);
+ wakeupEvent("wlan0", 10008, v4, tcp, mac, srcIp, dstIp, sport, dport, now);
+ wakeupEvent("rmnet0", 1000, v4, tcp, mac, srcIp, dstIp, sport, dport, now);
+ wakeupEvent("wlan0", 10004, v4, udp, mac, srcIp, dstIp, sport, dport, now);
+ wakeupEvent("wlan0", 1000, v6, tcp, mac, srcIp6, dstIp6, sport, dport, now);
+ wakeupEvent("wlan0", 0, v6, udp, mac, srcIp6, dstIp6, sport, dport, now);
+ wakeupEvent("wlan0", -1, v6, icmp6, mac, srcIp6, dstIp6, sport, dport, now);
+ wakeupEvent("rmnet0", 10052, v4, tcp, mac, srcIp, dstIp, sport, dport, now);
+ wakeupEvent("wlan0", 0, v6, udp, mac, srcIp6, dstIp6, sport, dport, now);
+ wakeupEvent("rmnet0", 1000, v6, tcp, mac, srcIp6, dstIp6, sport, dport, now);
+ wakeupEvent("wlan0", 1010, v4, udp, mac, srcIp, dstIp, sport, dport, now);
String got = flushStatistics();
String want = String.join("\n",
@@ -162,6 +201,21 @@
" wakeup_stats <",
" application_wakeups: 3",
" duration_sec: 0",
+ " ethertype_counts <",
+ " key: 2048",
+ " value: 4",
+ " >",
+ " ethertype_counts <",
+ " key: 34525",
+ " value: 1",
+ " >",
+ " ip_next_header_counts <",
+ " key: 6",
+ " value: 5",
+ " >",
+ " l2_broadcast_count: 0",
+ " l2_multicast_count: 0",
+ " l2_unicast_count: 5",
" no_uid_wakeups: 0",
" non_application_wakeups: 0",
" root_wakeups: 0",
@@ -178,6 +232,29 @@
" wakeup_stats <",
" application_wakeups: 2",
" duration_sec: 0",
+ " ethertype_counts <",
+ " key: 2048",
+ " value: 5",
+ " >",
+ " ethertype_counts <",
+ " key: 34525",
+ " value: 5",
+ " >",
+ " ip_next_header_counts <",
+ " key: 6",
+ " value: 3",
+ " >",
+ " ip_next_header_counts <",
+ " key: 17",
+ " value: 5",
+ " >",
+ " ip_next_header_counts <",
+ " key: 58",
+ " value: 2",
+ " >",
+ " l2_broadcast_count: 0",
+ " l2_multicast_count: 0",
+ " l2_unicast_count: 10",
" no_uid_wakeups: 2",
" non_application_wakeups: 1",
" root_wakeups: 2",
@@ -401,7 +478,7 @@
Thread connectEventAction(int netId, int error, int latencyMs, String ipAddr) {
return new Thread(() -> {
try {
- mNetdEventListenerService.onConnectEvent(netId, error, latencyMs, ipAddr, 80, 1);
+ mService.onConnectEvent(netId, error, latencyMs, ipAddr, 80, 1);
} catch (Exception e) {
fail(e.toString());
}
@@ -409,12 +486,13 @@
}
void dnsEvent(int netId, int type, int result, int latency) throws Exception {
- mNetdEventListenerService.onDnsEvent(netId, type, result, latency, "", null, 0, 0);
+ mService.onDnsEvent(netId, type, result, latency, "", null, 0, 0);
}
- void wakeupEvent(String iface, int uid) throws Exception {
+ void wakeupEvent(String iface, int uid, int ether, int ip, byte[] mac, String srcIp,
+ String dstIp, int sport, int dport, long now) throws Exception {
String prefix = NetdEventListenerService.WAKEUP_EVENT_IFACE_PREFIX + iface;
- mNetdEventListenerService.onWakeupEvent(prefix, uid, uid, 0);
+ mService.onWakeupEvent(prefix, uid, ether, ip, mac, srcIp, dstIp, sport, dport, now);
}
void asyncDump(long durationMs) throws Exception {
@@ -422,7 +500,7 @@
final PrintWriter pw = new PrintWriter(new FileOutputStream("/dev/null"));
new Thread(() -> {
while (System.currentTimeMillis() < stop) {
- mNetdEventListenerService.list(pw);
+ mService.list(pw);
}
}).start();
}
@@ -431,7 +509,7 @@
String flushStatistics() throws Exception {
IpConnectivityMetrics metricsService =
new IpConnectivityMetrics(mock(Context.class), (ctx) -> 2000);
- metricsService.mNetdListener = mNetdEventListenerService;
+ metricsService.mNetdListener = mService;
StringWriter buffer = new StringWriter();
PrintWriter writer = new PrintWriter(buffer);
@@ -453,7 +531,7 @@
String[] listNetdEvent() throws Exception {
StringWriter buffer = new StringWriter();
PrintWriter writer = new PrintWriter(buffer);
- mNetdEventListenerService.list(writer);
+ mService.list(writer);
return buffer.toString().split("\\n");
}
diff --git a/tools/aapt2/LoadedApk.cpp b/tools/aapt2/LoadedApk.cpp
index 921d853..5981401 100644
--- a/tools/aapt2/LoadedApk.cpp
+++ b/tools/aapt2/LoadedApk.cpp
@@ -235,8 +235,7 @@
return false;
}
} else {
- uint32_t compression_flags = file->WasCompressed() ? ArchiveEntry::kCompress : 0u;
- if (!io::CopyFileToArchive(context, file, path, compression_flags, writer)) {
+ if (!io::CopyFileToArchivePreserveCompression(context, file, path, writer)) {
return false;
}
}
diff --git a/tools/aapt2/ResourceValues.cpp b/tools/aapt2/ResourceValues.cpp
index b38d259..a782cd3 100644
--- a/tools/aapt2/ResourceValues.cpp
+++ b/tools/aapt2/ResourceValues.cpp
@@ -118,7 +118,7 @@
bool Reference::Flatten(android::Res_value* out_value) const {
const ResourceId resid = id.value_or_default(ResourceId(0));
const bool dynamic = resid.is_valid_dynamic() && resid.package_id() != kFrameworkPackageId &&
- resid.package_id() != kAppPackageId;
+ resid.package_id() < kAppPackageId;
if (reference_type == Reference::Type::kResource) {
if (dynamic) {
diff --git a/tools/aapt2/cmd/Convert.cpp b/tools/aapt2/cmd/Convert.cpp
index 89ae9e8..2bd2405 100644
--- a/tools/aapt2/cmd/Convert.cpp
+++ b/tools/aapt2/cmd/Convert.cpp
@@ -26,49 +26,52 @@
#include "format/binary/TableFlattener.h"
#include "format/binary/XmlFlattener.h"
#include "format/proto/ProtoDeserialize.h"
+#include "format/proto/ProtoSerialize.h"
#include "io/BigBufferStream.h"
#include "io/Util.h"
#include "process/IResourceTableConsumer.h"
#include "process/SymbolTable.h"
+#include "util/Util.h"
using ::android::StringPiece;
+using ::android::base::StringPrintf;
using ::std::unique_ptr;
using ::std::vector;
namespace aapt {
-static bool FlattenXml(IAaptContext* context, const xml::XmlResource& xml,
- const std::string& entry_path, bool utf16, IArchiveWriter* writer) {
- BigBuffer buffer(4096);
- XmlFlattenerOptions options = {};
- options.use_utf16 = utf16;
- XmlFlattener flattener(&buffer, options);
- if (!flattener.Consume(context, &xml)) {
- return false;
- }
- io::BigBufferInputStream input_stream(&buffer);
- return io::CopyInputStreamToArchive(context, &input_stream, entry_path, ArchiveEntry::kCompress,
- writer);
-}
+class IApkSerializer {
+ public:
+ IApkSerializer(IAaptContext* context, const Source& source) : context_(context), source_(source) {}
-bool ConvertProtoApkToBinaryApk(IAaptContext* context, unique_ptr<LoadedApk> apk,
- const TableFlattenerOptions& options, IArchiveWriter* writer) {
- if (!FlattenXml(context, *apk->GetManifest(), kAndroidManifestPath, true /*utf16*/, writer)) {
+ virtual bool SerializeXml(const xml::XmlResource* xml, const std::string& path, bool utf16,
+ IArchiveWriter* writer) = 0;
+ virtual bool SerializeTable(ResourceTable* table, IArchiveWriter* writer) = 0;
+ virtual bool SerializeFile(const FileReference* file, IArchiveWriter* writer) = 0;
+
+ virtual ~IApkSerializer() = default;
+
+ protected:
+ IAaptContext* context_;
+ Source source_;
+};
+
+bool ConvertApk(IAaptContext* context, unique_ptr<LoadedApk> apk, IApkSerializer* serializer,
+ IArchiveWriter* writer) {
+ if (!serializer->SerializeXml(apk->GetManifest(), kAndroidManifestPath, true /*utf16*/, writer)) {
+ context->GetDiagnostics()->Error(DiagMessage(apk->GetSource())
+ << "failed to serialize AndroidManifest.xml");
return false;
}
- BigBuffer buffer(4096);
- TableFlattener table_flattener(options, &buffer);
- if (!table_flattener.Consume(context, apk->GetResourceTable())) {
+ // Resource table
+ if (!serializer->SerializeTable(apk->GetResourceTable(), writer)) {
+ context->GetDiagnostics()->Error(DiagMessage(apk->GetSource())
+ << "failed to serialize the resource table");
return false;
}
- io::BigBufferInputStream input_stream(&buffer);
- if (!io::CopyInputStreamToArchive(context, &input_stream, kApkResourceTablePath,
- ArchiveEntry::kAlign, writer)) {
- return false;
- }
-
+ // Resources
for (const auto& package : apk->GetResourceTable()->packages) {
for (const auto& type : package->types) {
for (const auto& entry : type->entries) {
@@ -76,59 +79,187 @@
const FileReference* file = ValueCast<FileReference>(config_value->value.get());
if (file != nullptr) {
if (file->file == nullptr) {
- context->GetDiagnostics()->Warn(DiagMessage(apk->GetSource())
- << "no file associated with " << *file);
+ context->GetDiagnostics()->Error(DiagMessage(apk->GetSource())
+ << "no file associated with " << *file);
return false;
}
- if (file->type == ResourceFile::Type::kProtoXml) {
- unique_ptr<io::InputStream> in = file->file->OpenInputStream();
- if (in == nullptr) {
- context->GetDiagnostics()->Error(DiagMessage(apk->GetSource())
- << "failed to open file " << *file->path);
- return false;
- }
-
- pb::XmlNode pb_node;
- io::ZeroCopyInputAdaptor adaptor(in.get());
- if (!pb_node.ParseFromZeroCopyStream(&adaptor)) {
- context->GetDiagnostics()->Error(DiagMessage(apk->GetSource())
- << "failed to parse proto XML " << *file->path);
- return false;
- }
-
- std::string error;
- unique_ptr<xml::XmlResource> xml = DeserializeXmlResourceFromPb(pb_node, &error);
- if (xml == nullptr) {
- context->GetDiagnostics()->Error(DiagMessage(apk->GetSource())
- << "failed to deserialize proto XML "
- << *file->path << ": " << error);
- return false;
- }
-
- if (!FlattenXml(context, *xml, *file->path, false /*utf16*/, writer)) {
- context->GetDiagnostics()->Error(DiagMessage(apk->GetSource())
- << "failed to serialize XML " << *file->path);
- return false;
- }
- } else {
- if (!io::CopyFileToArchive(context, file->file, *file->path,
- file->file->WasCompressed() ? ArchiveEntry::kCompress : 0u,
- writer)) {
- context->GetDiagnostics()->Error(DiagMessage(apk->GetSource())
- << "failed to copy file " << *file->path);
- return false;
- }
+ if (!serializer->SerializeFile(file, writer)) {
+ context->GetDiagnostics()->Error(DiagMessage(apk->GetSource())
+ << "failed to serialize file " << *file->path);
+ return false;
}
-
} // file
} // config_value
} // entry
} // type
} // package
+
+ // Other files
+ std::unique_ptr<io::IFileCollectionIterator> iterator = apk->GetFileCollection()->Iterator();
+ while (iterator->HasNext()) {
+ io::IFile* file = iterator->Next();
+
+ std::string path = file->GetSource().path;
+ // The name of the path has the format "<zip-file-name>@<path-to-file>".
+ path = path.substr(path.find('@') + 1);
+
+ // Manifest, resource table and resources have already been taken care of.
+ if (path == kAndroidManifestPath ||
+ path == kApkResourceTablePath ||
+ path == kProtoResourceTablePath ||
+ path.find("res/") == 0) {
+ continue;
+ }
+
+ if (!io::CopyFileToArchivePreserveCompression(context, file, path, writer)) {
+ context->GetDiagnostics()->Error(DiagMessage(apk->GetSource())
+ << "failed to copy file " << path);
+ return false;
+ }
+ }
+
return true;
}
+
+class BinaryApkSerializer : public IApkSerializer {
+ public:
+ BinaryApkSerializer(IAaptContext* context, const Source& source,
+ const TableFlattenerOptions& options)
+ : IApkSerializer(context, source), tableFlattenerOptions_(options) {}
+
+ bool SerializeXml(const xml::XmlResource* xml, const std::string& path, bool utf16,
+ IArchiveWriter* writer) override {
+ BigBuffer buffer(4096);
+ XmlFlattenerOptions options = {};
+ options.use_utf16 = utf16;
+ XmlFlattener flattener(&buffer, options);
+ if (!flattener.Consume(context_, xml)) {
+ return false;
+ }
+
+ io::BigBufferInputStream input_stream(&buffer);
+ return io::CopyInputStreamToArchive(context_, &input_stream, path, ArchiveEntry::kCompress,
+ writer);
+ }
+
+ bool SerializeTable(ResourceTable* table, IArchiveWriter* writer) override {
+ BigBuffer buffer(4096);
+ TableFlattener table_flattener(tableFlattenerOptions_, &buffer);
+ if (!table_flattener.Consume(context_, table)) {
+ return false;
+ }
+
+ io::BigBufferInputStream input_stream(&buffer);
+ return io::CopyInputStreamToArchive(context_, &input_stream, kApkResourceTablePath,
+ ArchiveEntry::kAlign, writer);
+ }
+
+ bool SerializeFile(const FileReference* file, IArchiveWriter* writer) override {
+ if (file->type == ResourceFile::Type::kProtoXml) {
+ unique_ptr<io::InputStream> in = file->file->OpenInputStream();
+ if (in == nullptr) {
+ context_->GetDiagnostics()->Error(DiagMessage(source_)
+ << "failed to open file " << *file->path);
+ return false;
+ }
+
+ pb::XmlNode pb_node;
+ io::ZeroCopyInputAdaptor adaptor(in.get());
+ if (!pb_node.ParseFromZeroCopyStream(&adaptor)) {
+ context_->GetDiagnostics()->Error(DiagMessage(source_)
+ << "failed to parse proto XML " << *file->path);
+ return false;
+ }
+
+ std::string error;
+ unique_ptr<xml::XmlResource> xml = DeserializeXmlResourceFromPb(pb_node, &error);
+ if (xml == nullptr) {
+ context_->GetDiagnostics()->Error(DiagMessage(source_)
+ << "failed to deserialize proto XML "
+ << *file->path << ": " << error);
+ return false;
+ }
+
+ if (!SerializeXml(xml.get(), *file->path, false /*utf16*/, writer)) {
+ context_->GetDiagnostics()->Error(DiagMessage(source_)
+ << "failed to serialize to binary XML: " << *file->path);
+ return false;
+ }
+ } else {
+ if (!io::CopyFileToArchivePreserveCompression(context_, file->file, *file->path, writer)) {
+ context_->GetDiagnostics()->Error(DiagMessage(source_)
+ << "failed to copy file " << *file->path);
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ private:
+ TableFlattenerOptions tableFlattenerOptions_;
+
+ DISALLOW_COPY_AND_ASSIGN(BinaryApkSerializer);
+};
+
+class ProtoApkSerializer : public IApkSerializer {
+ public:
+ ProtoApkSerializer(IAaptContext* context, const Source& source)
+ : IApkSerializer(context, source) {}
+
+ bool SerializeXml(const xml::XmlResource* xml, const std::string& path, bool utf16,
+ IArchiveWriter* writer) override {
+ pb::XmlNode pb_node;
+ SerializeXmlResourceToPb(*xml, &pb_node);
+ return io::CopyProtoToArchive(context_, &pb_node, path, ArchiveEntry::kCompress, writer);
+ }
+
+ bool SerializeTable(ResourceTable* table, IArchiveWriter* writer) override {
+ pb::ResourceTable pb_table;
+ SerializeTableToPb(*table, &pb_table);
+ return io::CopyProtoToArchive(context_, &pb_table, kProtoResourceTablePath,
+ ArchiveEntry::kCompress, writer);
+ }
+
+ bool SerializeFile(const FileReference* file, IArchiveWriter* writer) override {
+ if (file->type == ResourceFile::Type::kBinaryXml) {
+ std::unique_ptr<io::IData> data = file->file->OpenAsData();
+ if (!data) {
+ context_->GetDiagnostics()->Error(DiagMessage(source_)
+ << "failed to open file " << *file->path);
+ return false;
+ }
+
+ std::string error;
+ std::unique_ptr<xml::XmlResource> xml = xml::Inflate(data->data(), data->size(), &error);
+ if (xml == nullptr) {
+ context_->GetDiagnostics()->Error(DiagMessage(source_) << "failed to parse binary XML: "
+ << error);
+ return false;
+ }
+
+ if (!SerializeXml(xml.get(), *file->path, false /*utf16*/, writer)) {
+ context_->GetDiagnostics()->Error(DiagMessage(source_)
+ << "failed to serialize to proto XML: " << *file->path);
+ return false;
+ }
+ } else {
+ if (!io::CopyFileToArchivePreserveCompression(context_, file->file, *file->path, writer)) {
+ context_->GetDiagnostics()->Error(DiagMessage(source_)
+ << "failed to copy file " << *file->path);
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ProtoApkSerializer);
+};
+
class Context : public IAaptContext {
public:
Context() : mangler_({}), symbols_(&mangler_) {
@@ -181,12 +312,20 @@
};
int Convert(const vector<StringPiece>& args) {
+
+ static const char* kOutputFormatProto = "proto";
+ static const char* kOutputFormatBinary = "binary";
+
Context context;
std::string output_path;
+ Maybe<std::string> output_format;
TableFlattenerOptions options;
Flags flags =
Flags()
.RequiredFlag("-o", "Output path", &output_path)
+ .OptionalFlag("--output-format", StringPrintf("Format of the output. Accepted values are "
+ "'%s' and '%s'. When not set, defaults to '%s'.", kOutputFormatProto,
+ kOutputFormatBinary, kOutputFormatBinary), &output_format)
.OptionalSwitch("--enable-sparse-encoding",
"Enables encoding sparse entries using a binary search tree.\n"
"This decreases APK size at the cost of resource retrieval performance.",
@@ -222,7 +361,21 @@
if (writer == nullptr) {
return 1;
}
- return ConvertProtoApkToBinaryApk(&context, std::move(apk), options, writer.get()) ? 0 : 1;
+
+ unique_ptr<IApkSerializer> serializer;
+ if (!output_format || output_format.value() == kOutputFormatBinary) {
+ serializer.reset(new BinaryApkSerializer(&context, apk->GetSource(), options));
+ } else if (output_format.value() == kOutputFormatProto) {
+ serializer.reset(new ProtoApkSerializer(&context, apk->GetSource()));
+ } else {
+ context.GetDiagnostics()->Error(DiagMessage(path)
+ << "Invalid value for flag --output-format: "
+ << output_format.value());
+ return 1;
+ }
+
+
+ return ConvertApk(&context, std::move(apk), serializer.get(), writer.get()) ? 0 : 1;
}
} // namespace aapt
diff --git a/tools/aapt2/cmd/Link.cpp b/tools/aapt2/cmd/Link.cpp
index b372923..66b5a7a 100644
--- a/tools/aapt2/cmd/Link.cpp
+++ b/tools/aapt2/cmd/Link.cpp
@@ -795,6 +795,9 @@
for (auto& entry : asset_source->GetAssignedPackageIds()) {
if (entry.first > kFrameworkPackageId && entry.first < kAppPackageId) {
final_table_.included_packages_[entry.first] = entry.second;
+ } else if (entry.first == kAppPackageId) {
+ // Capture the included base feature package.
+ included_feature_base_ = entry.second;
}
}
@@ -1465,11 +1468,48 @@
return false;
}
- if (!FlattenTable(table, options_.output_format, writer)) {
- context_->GetDiagnostics()->Error(DiagMessage() << "failed to write resource table");
- return false;
+ // Hack to fix b/68820737.
+ // We need to modify the ResourceTable's package name, but that should NOT affect
+ // anything else being generated, which includes the Java classes.
+ // If required, the package name is modifed before flattening, and then modified back
+ // to its original name.
+ ResourceTablePackage* package_to_rewrite = nullptr;
+ if (context_->GetPackageId() > kAppPackageId &&
+ included_feature_base_ == make_value(context_->GetCompilationPackage())) {
+ // The base APK is included, and this is a feature split. If the base package is
+ // the same as this package, then we are building an old style Android Instant Apps feature
+ // split and must apply this workaround to avoid requiring namespaces support.
+ package_to_rewrite = table->FindPackage(context_->GetCompilationPackage());
+ if (package_to_rewrite != nullptr) {
+ CHECK_EQ(1u, table->packages.size()) << "can't change name of package when > 1 package";
+
+ std::string new_package_name =
+ StringPrintf("%s.%s", package_to_rewrite->name.c_str(),
+ app_info_.split_name.value_or_default("feature").c_str());
+
+ if (context_->IsVerbose()) {
+ context_->GetDiagnostics()->Note(
+ DiagMessage() << "rewriting resource package name for feature split to '"
+ << new_package_name << "'");
+ }
+ package_to_rewrite->name = new_package_name;
+ }
}
- return true;
+
+ bool success = FlattenTable(table, options_.output_format, writer);
+
+ if (package_to_rewrite != nullptr) {
+ // Change the name back.
+ package_to_rewrite->name = context_->GetCompilationPackage();
+ if (package_to_rewrite->id) {
+ table->included_packages_.erase(package_to_rewrite->id.value());
+ }
+ }
+
+ if (!success) {
+ context_->GetDiagnostics()->Error(DiagMessage() << "failed to write resource table");
+ }
+ return success;
}
int Run(const std::vector<std::string>& input_files) {
@@ -1498,8 +1538,8 @@
return 1;
}
- const AppInfo& app_info = maybe_app_info.value();
- context_->SetMinSdkVersion(app_info.min_sdk_version.value_or_default(0));
+ app_info_ = maybe_app_info.value();
+ context_->SetMinSdkVersion(app_info_.min_sdk_version.value_or_default(0));
context_->SetNameManglerPolicy(NameManglerPolicy{context_->GetCompilationPackage()});
@@ -1699,7 +1739,7 @@
// Generate an AndroidManifest.xml for each split.
std::unique_ptr<xml::XmlResource> split_manifest =
- GenerateSplitManifest(app_info, *split_constraints_iter);
+ GenerateSplitManifest(app_info_, *split_constraints_iter);
XmlReferenceLinker linker;
if (!linker.Consume(context_, split_manifest.get())) {
@@ -1799,6 +1839,8 @@
LinkContext* context_;
ResourceTable final_table_;
+ AppInfo app_info_;
+
std::unique_ptr<TableMerger> table_merger_;
// A pointer to the FileCollection representing the filesystem (not archives).
@@ -1816,6 +1858,9 @@
// The set of shared libraries being used, mapping their assigned package ID to package name.
std::map<size_t, std::string> shared_libs_;
+
+ // The package name of the base application, if it is included.
+ Maybe<std::string> included_feature_base_;
};
int Link(const std::vector<StringPiece>& args, IDiagnostics* diagnostics) {
diff --git a/tools/aapt2/cmd/Optimize.cpp b/tools/aapt2/cmd/Optimize.cpp
index 688b6a7..2bf91a5 100644
--- a/tools/aapt2/cmd/Optimize.cpp
+++ b/tools/aapt2/cmd/Optimize.cpp
@@ -256,10 +256,8 @@
for (auto& entry : config_sorted_files) {
FileReference* file_ref = entry.second;
- uint32_t compression_flags =
- file_ref->file->WasCompressed() ? ArchiveEntry::kCompress : 0u;
- if (!io::CopyFileToArchive(context_, file_ref->file, *file_ref->path, compression_flags,
- writer)) {
+ if (!io::CopyFileToArchivePreserveCompression(context_, file_ref->file, *file_ref->path,
+ writer)) {
return false;
}
}
diff --git a/tools/aapt2/cmd/Util.cpp b/tools/aapt2/cmd/Util.cpp
index d39f43e..8b3a670 100644
--- a/tools/aapt2/cmd/Util.cpp
+++ b/tools/aapt2/cmd/Util.cpp
@@ -140,12 +140,27 @@
return decl;
}
+// Returns a copy of 'name' which conforms to the regex '[a-zA-Z]+[a-zA-Z0-9_]*' by
+// replacing nonconforming characters with underscores.
+//
+// See frameworks/base/core/java/android/content/pm/PackageParser.java which
+// checks this at runtime.
static std::string MakePackageSafeName(const std::string &name) {
std::string result(name);
+ bool first = true;
for (char &c : result) {
- if (c == '-') {
- c = '_';
+ if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) {
+ first = false;
+ continue;
}
+ if (!first) {
+ if (c >= '0' && c <= '9') {
+ continue;
+ }
+ }
+
+ c = '_';
+ first = false;
}
return result;
}
diff --git a/tools/aapt2/cmd/Util_test.cpp b/tools/aapt2/cmd/Util_test.cpp
index 9c33135..0c527f6 100644
--- a/tools/aapt2/cmd/Util_test.cpp
+++ b/tools/aapt2/cmd/Util_test.cpp
@@ -24,15 +24,16 @@
TEST(UtilTest, SplitNamesAreSanitized) {
AppInfo app_info{"com.pkg"};
- SplitConstraints split_constraints{{test::ParseConfigOrDie("en-rUS-land")}};
+ SplitConstraints split_constraints{
+ {test::ParseConfigOrDie("en-rUS-land"), test::ParseConfigOrDie("b+sr+Latn")}};
const auto doc = GenerateSplitManifest(app_info, split_constraints);
const auto &root = doc->root;
EXPECT_EQ(root->name, "manifest");
- // split names cannot contain hyphens
- EXPECT_EQ(root->FindAttribute("", "split")->value, "config.en_rUS_land");
+ // split names cannot contain hyphens or plus signs.
+ EXPECT_EQ(root->FindAttribute("", "split")->value, "config.b_sr_Latn_en_rUS_land");
// but we should use resource qualifiers verbatim in 'targetConfig'.
- EXPECT_EQ(root->FindAttribute("", "targetConfig")->value, "en-rUS-land");
+ EXPECT_EQ(root->FindAttribute("", "targetConfig")->value, "b+sr+Latn,en-rUS-land");
}
} // namespace aapt
diff --git a/tools/aapt2/format/binary/TableFlattener.cpp b/tools/aapt2/format/binary/TableFlattener.cpp
index 4a1b46c..2a51df3 100644
--- a/tools/aapt2/format/binary/TableFlattener.cpp
+++ b/tools/aapt2/format/binary/TableFlattener.cpp
@@ -23,6 +23,7 @@
#include "android-base/logging.h"
#include "android-base/macros.h"
+#include "android-base/stringprintf.h"
#include "ResourceTable.h"
#include "ResourceValues.h"
@@ -579,7 +580,15 @@
// Write a self mapping entry for this package if the ID is non-standard (0x7f).
const uint8_t package_id = package->id.value();
if (package_id != kFrameworkPackageId && package_id != kAppPackageId) {
- table->included_packages_[package_id] = package->name;
+ auto result = table->included_packages_.insert({package_id, package->name});
+ if (!result.second && result.first->second != package->name) {
+ // A mapping for this package ID already exists, and is a different package. Error!
+ context->GetDiagnostics()->Error(
+ DiagMessage() << android::base::StringPrintf(
+ "can't map package ID %02x to '%s'. Already mapped to '%s'", package_id,
+ package->name.c_str(), result.first->second.c_str()));
+ return false;
+ }
}
}
diff --git a/tools/aapt2/io/Util.cpp b/tools/aapt2/io/Util.cpp
index 7ee1016..9751632 100644
--- a/tools/aapt2/io/Util.cpp
+++ b/tools/aapt2/io/Util.cpp
@@ -48,6 +48,12 @@
return CopyInputStreamToArchive(context, data.get(), out_path, compression_flags, writer);
}
+bool CopyFileToArchivePreserveCompression(IAaptContext* context, io::IFile* file,
+ const std::string& out_path, IArchiveWriter* writer) {
+ uint32_t compression_flags = file->WasCompressed() ? ArchiveEntry::kCompress : 0u;
+ return CopyFileToArchive(context, file, out_path, compression_flags, writer);
+}
+
bool CopyProtoToArchive(IAaptContext* context, ::google::protobuf::MessageLite* proto_msg,
const std::string& out_path, uint32_t compression_flags,
IArchiveWriter* writer) {
diff --git a/tools/aapt2/io/Util.h b/tools/aapt2/io/Util.h
index de2ab39..b07fb53 100644
--- a/tools/aapt2/io/Util.h
+++ b/tools/aapt2/io/Util.h
@@ -35,6 +35,9 @@
bool CopyFileToArchive(IAaptContext* context, IFile* file, const std::string& out_path,
uint32_t compression_flags, IArchiveWriter* writer);
+bool CopyFileToArchivePreserveCompression(IAaptContext* context, IFile* file,
+ const std::string& out_path, IArchiveWriter* writer);
+
bool CopyProtoToArchive(IAaptContext* context, ::google::protobuf::MessageLite* proto_msg,
const std::string& out_path, uint32_t compression_flags,
IArchiveWriter* writer);
diff --git a/tools/aapt2/java/ProguardRules.cpp b/tools/aapt2/java/ProguardRules.cpp
index b9ae654..b214d21 100644
--- a/tools/aapt2/java/ProguardRules.cpp
+++ b/tools/aapt2/java/ProguardRules.cpp
@@ -345,15 +345,14 @@
*out << "# Referenced at " << location.source << "\n";
}
if (keep_set.conditional_keep_rules_ && can_be_conditional) {
- *out << "-keep class " << entry.first << " {\n ifused class **.R$layout {\n";
+ *out << "-if class **.R$layout {\n";
for (const UsageLocation& location : locations) {
auto transformed_name = JavaClassGenerator::TransformToFieldName(location.name.entry);
- *out << " int " << transformed_name << ";\n";
+ *out << " int " << transformed_name << ";\n";
}
- *out << " };\n <init>(...);\n}\n" << std::endl;
- } else {
- *out << "-keep class " << entry.first << " { <init>(...); }\n" << std::endl;
+ *out << "}\n";
}
+ *out << "-keep class " << entry.first << " { <init>(...); }\n" << std::endl;
}
for (const auto& entry : keep_set.method_set_) {
diff --git a/tools/aapt2/java/ProguardRules_test.cpp b/tools/aapt2/java/ProguardRules_test.cpp
index df3ac8b..802c56a 100644
--- a/tools/aapt2/java/ProguardRules_test.cpp
+++ b/tools/aapt2/java/ProguardRules_test.cpp
@@ -130,7 +130,7 @@
ASSERT_TRUE(proguard::WriteKeepSet(&out, set));
std::string actual = out.str();
- EXPECT_THAT(actual, HasSubstr("ifused class **.R$layout"));
+ EXPECT_THAT(actual, HasSubstr("-if class **.R$layout"));
EXPECT_THAT(actual, HasSubstr("int foo"));
EXPECT_THAT(actual, HasSubstr("int bar"));
EXPECT_THAT(actual, HasSubstr("com.foo.Bar"));
@@ -152,7 +152,7 @@
ASSERT_TRUE(proguard::WriteKeepSet(&out, set));
std::string actual = out.str();
- EXPECT_THAT(actual, HasSubstr("ifused class **.R$layout"));
+ EXPECT_THAT(actual, HasSubstr("-if class **.R$layout"));
EXPECT_THAT(actual, HasSubstr("int foo"));
EXPECT_THAT(actual, HasSubstr("int bar"));
EXPECT_THAT(actual, HasSubstr("com.foo.Bar"));
@@ -174,7 +174,7 @@
ASSERT_TRUE(proguard::WriteKeepSet(&out, set));
std::string actual = out.str();
- EXPECT_THAT(actual, Not(HasSubstr("ifused")));
+ EXPECT_THAT(actual, Not(HasSubstr("-if")));
}
TEST(ProguardRulesTest, ViewOnClickRuleIsEmitted) {
diff --git a/tools/aapt2/link/ManifestFixer.cpp b/tools/aapt2/link/ManifestFixer.cpp
index 6fb1793..de4fb73 100644
--- a/tools/aapt2/link/ManifestFixer.cpp
+++ b/tools/aapt2/link/ManifestFixer.cpp
@@ -127,9 +127,9 @@
diag->Error(DiagMessage(el->line_number)
<< "attribute 'package' in <manifest> tag must not be a reference");
return false;
- } else if (!util::IsJavaPackageName(attr->value)) {
+ } else if (!util::IsAndroidPackageName(attr->value)) {
diag->Error(DiagMessage(el->line_number)
- << "attribute 'package' in <manifest> tag is not a valid Java package name: '"
+ << "attribute 'package' in <manifest> tag is not a valid Android package name: '"
<< attr->value << "'");
return false;
}
diff --git a/tools/aapt2/text/Unicode.cpp b/tools/aapt2/text/Unicode.cpp
index 75eeb46..3735b3e 100644
--- a/tools/aapt2/text/Unicode.cpp
+++ b/tools/aapt2/text/Unicode.cpp
@@ -85,7 +85,8 @@
return false;
}
- if (!IsXidStart(iter.Next())) {
+ const char32_t first_codepoint = iter.Next();
+ if (!IsXidStart(first_codepoint) && first_codepoint != U'_' && first_codepoint != U'$') {
return false;
}
diff --git a/tools/aapt2/text/Unicode_test.cpp b/tools/aapt2/text/Unicode_test.cpp
index d47fb28..a8e797c 100644
--- a/tools/aapt2/text/Unicode_test.cpp
+++ b/tools/aapt2/text/Unicode_test.cpp
@@ -44,10 +44,11 @@
TEST(UnicodeTest, IsJavaIdentifier) {
EXPECT_TRUE(IsJavaIdentifier("FøøBar_12"));
EXPECT_TRUE(IsJavaIdentifier("Føø$Bar"));
+ EXPECT_TRUE(IsJavaIdentifier("_FøøBar"));
+ EXPECT_TRUE(IsJavaIdentifier("$Føø$Bar"));
EXPECT_FALSE(IsJavaIdentifier("12FøøBar"));
- EXPECT_FALSE(IsJavaIdentifier("_FøøBar"));
- EXPECT_FALSE(IsJavaIdentifier("$Føø$Bar"));
+ EXPECT_FALSE(IsJavaIdentifier(".Hello"));
}
TEST(UnicodeTest, IsValidResourceEntryName) {
diff --git a/tools/aapt2/util/Util.cpp b/tools/aapt2/util/Util.cpp
index a9b49d9..e42145d 100644
--- a/tools/aapt2/util/Util.cpp
+++ b/tools/aapt2/util/Util.cpp
@@ -24,6 +24,7 @@
#include "androidfw/StringPiece.h"
#include "utils/Unicode.h"
+#include "text/Unicode.h"
#include "text/Utf8Iterator.h"
#include "util/BigBuffer.h"
#include "util/Maybe.h"
@@ -94,72 +95,55 @@
return StringPiece(start, end - start);
}
-StringPiece::const_iterator FindNonAlphaNumericAndNotInSet(
- const StringPiece& str, const StringPiece& allowed_chars) {
- const auto end_iter = str.end();
- for (auto iter = str.begin(); iter != end_iter; ++iter) {
- char c = *iter;
- if ((c >= u'a' && c <= u'z') || (c >= u'A' && c <= u'Z') ||
- (c >= u'0' && c <= u'9')) {
- continue;
- }
-
- bool match = false;
- for (char i : allowed_chars) {
- if (c == i) {
- match = true;
- break;
- }
- }
-
- if (!match) {
- return iter;
+static int IsJavaNameImpl(const StringPiece& str) {
+ int pieces = 0;
+ for (const StringPiece& piece : Tokenize(str, '.')) {
+ pieces++;
+ if (!text::IsJavaIdentifier(piece)) {
+ return -1;
}
}
- return end_iter;
+ return pieces;
}
bool IsJavaClassName(const StringPiece& str) {
- size_t pieces = 0;
- for (const StringPiece& piece : Tokenize(str, '.')) {
- pieces++;
- if (piece.empty()) {
- return false;
- }
-
- // Can't have starting or trailing $ character.
- if (piece.data()[0] == '$' || piece.data()[piece.size() - 1] == '$') {
- return false;
- }
-
- if (FindNonAlphaNumericAndNotInSet(piece, "$_") != piece.end()) {
- return false;
- }
- }
- return pieces >= 2;
+ return IsJavaNameImpl(str) >= 2;
}
bool IsJavaPackageName(const StringPiece& str) {
- if (str.empty()) {
- return false;
- }
+ return IsJavaNameImpl(str) >= 1;
+}
- size_t pieces = 0;
+static int IsAndroidNameImpl(const StringPiece& str) {
+ int pieces = 0;
for (const StringPiece& piece : Tokenize(str, '.')) {
- pieces++;
if (piece.empty()) {
- return false;
+ return -1;
}
- if (piece.data()[0] == '_' || piece.data()[piece.size() - 1] == '_') {
- return false;
+ const char first_character = piece.data()[0];
+ if (!::isalpha(first_character)) {
+ return -1;
}
- if (FindNonAlphaNumericAndNotInSet(piece, "_") != piece.end()) {
- return false;
+ bool valid = std::all_of(piece.begin() + 1, piece.end(), [](const char c) -> bool {
+ return ::isalnum(c) || c == '_';
+ });
+
+ if (!valid) {
+ return -1;
}
+ pieces++;
}
- return pieces >= 1;
+ return pieces;
+}
+
+bool IsAndroidPackageName(const StringPiece& str) {
+ return IsAndroidNameImpl(str) > 1 || str == "android";
+}
+
+bool IsAndroidSplitName(const StringPiece& str) {
+ return IsAndroidNameImpl(str) > 0;
}
Maybe<std::string> GetFullyQualifiedClassName(const StringPiece& package,
@@ -176,7 +160,7 @@
return {};
}
- std::string result(package.data(), package.size());
+ std::string result = package.to_string();
if (classname.data()[0] != '.') {
result += '.';
}
diff --git a/tools/aapt2/util/Util.h b/tools/aapt2/util/Util.h
index c928458..7c949b90 100644
--- a/tools/aapt2/util/Util.h
+++ b/tools/aapt2/util/Util.h
@@ -53,48 +53,40 @@
std::vector<std::string> Split(const android::StringPiece& str, char sep);
std::vector<std::string> SplitAndLowercase(const android::StringPiece& str, char sep);
-/**
- * Returns true if the string starts with prefix.
- */
+// Returns true if the string starts with prefix.
bool StartsWith(const android::StringPiece& str, const android::StringPiece& prefix);
-/**
- * Returns true if the string ends with suffix.
- */
+// Returns true if the string ends with suffix.
bool EndsWith(const android::StringPiece& str, const android::StringPiece& suffix);
-/**
- * Creates a new StringPiece16 that points to a substring
- * of the original string without leading or trailing whitespace.
- */
+// Creates a new StringPiece16 that points to a substring of the original string without leading or
+// trailing whitespace.
android::StringPiece TrimWhitespace(const android::StringPiece& str);
-/**
- * Returns an iterator to the first character that is not alpha-numeric and that
- * is not in the allowedChars set.
- */
-android::StringPiece::const_iterator FindNonAlphaNumericAndNotInSet(
- const android::StringPiece& str, const android::StringPiece& allowed_chars);
-
-/**
- * Tests that the string is a valid Java class name.
- */
+// Tests that the string is a valid Java class name.
bool IsJavaClassName(const android::StringPiece& str);
-/**
- * Tests that the string is a valid Java package name.
- */
+// Tests that the string is a valid Java package name.
bool IsJavaPackageName(const android::StringPiece& str);
-/**
- * Converts the class name to a fully qualified class name from the given
- * `package`. Ex:
- *
- * asdf --> package.asdf
- * .asdf --> package.asdf
- * .a.b --> package.a.b
- * asdf.adsf --> asdf.adsf
- */
+// Tests that the string is a valid Android package name. More strict than a Java package name.
+// - First character of each component (separated by '.') must be an ASCII letter.
+// - Subsequent characters of a component can be ASCII alphanumeric or an underscore.
+// - Package must contain at least two components, unless it is 'android'.
+bool IsAndroidPackageName(const android::StringPiece& str);
+
+// Tests that the string is a valid Android split name.
+// - First character of each component (separated by '.') must be an ASCII letter.
+// - Subsequent characters of a component can be ASCII alphanumeric or an underscore.
+bool IsAndroidSplitName(const android::StringPiece& str);
+
+// Converts the class name to a fully qualified class name from the given
+// `package`. Ex:
+//
+// asdf --> package.asdf
+// .asdf --> package.asdf
+// .a.b --> package.a.b
+// asdf.adsf --> asdf.adsf
Maybe<std::string> GetFullyQualifiedClassName(const android::StringPiece& package,
const android::StringPiece& class_name);
@@ -108,23 +100,17 @@
return 0;
}
-/**
- * Makes a std::unique_ptr<> with the template parameter inferred by the compiler.
- * This will be present in C++14 and can be removed then.
- */
+// Makes a std::unique_ptr<> with the template parameter inferred by the compiler.
+// This will be present in C++14 and can be removed then.
template <typename T, class... Args>
std::unique_ptr<T> make_unique(Args&&... args) {
return std::unique_ptr<T>(new T{std::forward<Args>(args)...});
}
-/**
- * Writes a set of items to the std::ostream, joining the times with the
- * provided
- * separator.
- */
+// Writes a set of items to the std::ostream, joining the times with the provided separator.
template <typename Container>
-::std::function<::std::ostream&(::std::ostream&)> Joiner(
- const Container& container, const char* sep) {
+::std::function<::std::ostream&(::std::ostream&)> Joiner(const Container& container,
+ const char* sep) {
using std::begin;
using std::end;
const auto begin_iter = begin(container);
@@ -140,32 +126,19 @@
};
}
-/**
- * Helper method to extract a UTF-16 string from a StringPool. If the string is
- * stored as UTF-8,
- * the conversion to UTF-16 happens within ResStringPool.
- */
+// Helper method to extract a UTF-16 string from a StringPool. If the string is stored as UTF-8,
+// the conversion to UTF-16 happens within ResStringPool.
android::StringPiece16 GetString16(const android::ResStringPool& pool, size_t idx);
-/**
- * Helper method to extract a UTF-8 string from a StringPool. If the string is
- * stored as UTF-16,
- * the conversion from UTF-16 to UTF-8 does not happen in ResStringPool and is
- * done by this method,
- * which maintains no state or cache. This means we must return an std::string
- * copy.
- */
+// Helper method to extract a UTF-8 string from a StringPool. If the string is stored as UTF-16,
+// the conversion from UTF-16 to UTF-8 does not happen in ResStringPool and is done by this method,
+// which maintains no state or cache. This means we must return an std::string copy.
std::string GetString(const android::ResStringPool& pool, size_t idx);
-/**
- * Checks that the Java string format contains no non-positional arguments
- * (arguments without
- * explicitly specifying an index) when there are more than one argument. This
- * is an error
- * because translations may rearrange the order of the arguments in the string,
- * which will
- * break the string interpolation.
- */
+// Checks that the Java string format contains no non-positional arguments (arguments without
+// explicitly specifying an index) when there are more than one argument. This is an error
+// because translations may rearrange the order of the arguments in the string, which will
+// break the string interpolation.
bool VerifyJavaStringFormat(const android::StringPiece& str);
class StringBuilder {
@@ -194,36 +167,38 @@
std::string error_;
};
-inline const std::string& StringBuilder::ToString() const { return str_; }
+inline const std::string& StringBuilder::ToString() const {
+ return str_;
+}
-inline const std::string& StringBuilder::Error() const { return error_; }
+inline const std::string& StringBuilder::Error() const {
+ return error_;
+}
-inline bool StringBuilder::IsEmpty() const { return str_.empty(); }
+inline bool StringBuilder::IsEmpty() const {
+ return str_.empty();
+}
-inline size_t StringBuilder::Utf16Len() const { return utf16_len_; }
+inline size_t StringBuilder::Utf16Len() const {
+ return utf16_len_;
+}
-inline StringBuilder::operator bool() const { return error_.empty(); }
+inline StringBuilder::operator bool() const {
+ return error_.empty();
+}
-/**
- * Converts a UTF8 string to a UTF16 string.
- */
+// Converts a UTF8 string to a UTF16 string.
std::u16string Utf8ToUtf16(const android::StringPiece& utf8);
std::string Utf16ToUtf8(const android::StringPiece16& utf16);
-/**
- * Writes the entire BigBuffer to the output stream.
- */
+// Writes the entire BigBuffer to the output stream.
bool WriteAll(std::ostream& out, const BigBuffer& buffer);
-/*
- * Copies the entire BigBuffer into a single buffer.
- */
+// Copies the entire BigBuffer into a single buffer.
std::unique_ptr<uint8_t[]> Copy(const BigBuffer& buffer);
-/**
- * A Tokenizer implemented as an iterable collection. It does not allocate
- * any memory on the heap nor use standard containers.
- */
+// A Tokenizer implemented as an iterable collection. It does not allocate any memory on the heap
+// nor use standard containers.
class Tokenizer {
public:
class iterator {
@@ -269,38 +244,42 @@
const iterator end_;
};
-inline Tokenizer Tokenize(const android::StringPiece& str, char sep) { return Tokenizer(str, sep); }
+inline Tokenizer Tokenize(const android::StringPiece& str, char sep) {
+ return Tokenizer(str, sep);
+}
-inline uint16_t HostToDevice16(uint16_t value) { return htods(value); }
+inline uint16_t HostToDevice16(uint16_t value) {
+ return htods(value);
+}
-inline uint32_t HostToDevice32(uint32_t value) { return htodl(value); }
+inline uint32_t HostToDevice32(uint32_t value) {
+ return htodl(value);
+}
-inline uint16_t DeviceToHost16(uint16_t value) { return dtohs(value); }
+inline uint16_t DeviceToHost16(uint16_t value) {
+ return dtohs(value);
+}
-inline uint32_t DeviceToHost32(uint32_t value) { return dtohl(value); }
+inline uint32_t DeviceToHost32(uint32_t value) {
+ return dtohl(value);
+}
-/**
- * Given a path like: res/xml-sw600dp/foo.xml
- *
- * Extracts "res/xml-sw600dp/" into outPrefix.
- * Extracts "foo" into outEntry.
- * Extracts ".xml" into outSuffix.
- *
- * Returns true if successful.
- */
+// Given a path like: res/xml-sw600dp/foo.xml
+//
+// Extracts "res/xml-sw600dp/" into outPrefix.
+// Extracts "foo" into outEntry.
+// Extracts ".xml" into outSuffix.
+//
+// Returns true if successful.
bool ExtractResFilePathParts(const android::StringPiece& path, android::StringPiece* out_prefix,
android::StringPiece* out_entry, android::StringPiece* out_suffix);
} // namespace util
-/**
- * Stream operator for functions. Calls the function with the stream as an
- * argument.
- * In the aapt namespace for lookup.
- */
-inline ::std::ostream& operator<<(
- ::std::ostream& out,
- const ::std::function<::std::ostream&(::std::ostream&)>& f) {
+// Stream operator for functions. Calls the function with the stream as an argument.
+// In the aapt namespace for lookup.
+inline ::std::ostream& operator<<(::std::ostream& out,
+ const ::std::function<::std::ostream&(::std::ostream&)>& f) {
return f(out);
}
diff --git a/tools/aapt2/util/Util_test.cpp b/tools/aapt2/util/Util_test.cpp
index adb5291..2d1242a 100644
--- a/tools/aapt2/util/Util_test.cpp
+++ b/tools/aapt2/util/Util_test.cpp
@@ -117,24 +117,46 @@
EXPECT_TRUE(util::IsJavaClassName("android.test.Class$Inner"));
EXPECT_TRUE(util::IsJavaClassName("android_test.test.Class"));
EXPECT_TRUE(util::IsJavaClassName("_android_.test._Class_"));
- EXPECT_FALSE(util::IsJavaClassName("android.test.$Inner"));
- EXPECT_FALSE(util::IsJavaClassName("android.test.Inner$"));
+ EXPECT_TRUE(util::IsJavaClassName("android.test.$Inner"));
+ EXPECT_TRUE(util::IsJavaClassName("android.test.Inner$"));
+ EXPECT_TRUE(util::IsJavaClassName("com.foo.FøøBar"));
+
EXPECT_FALSE(util::IsJavaClassName(".test.Class"));
EXPECT_FALSE(util::IsJavaClassName("android"));
+ EXPECT_FALSE(util::IsJavaClassName("FooBar"));
}
TEST(UtilTest, IsJavaPackageName) {
EXPECT_TRUE(util::IsJavaPackageName("android"));
EXPECT_TRUE(util::IsJavaPackageName("android.test"));
EXPECT_TRUE(util::IsJavaPackageName("android.test_thing"));
- EXPECT_FALSE(util::IsJavaPackageName("_android"));
- EXPECT_FALSE(util::IsJavaPackageName("android_"));
+ EXPECT_TRUE(util::IsJavaPackageName("_android"));
+ EXPECT_TRUE(util::IsJavaPackageName("android_"));
+ EXPECT_TRUE(util::IsJavaPackageName("android._test"));
+ EXPECT_TRUE(util::IsJavaPackageName("cøm.foo"));
+
EXPECT_FALSE(util::IsJavaPackageName("android."));
EXPECT_FALSE(util::IsJavaPackageName(".android"));
- EXPECT_FALSE(util::IsJavaPackageName("android._test"));
EXPECT_FALSE(util::IsJavaPackageName(".."));
}
+TEST(UtilTest, IsAndroidPackageName) {
+ EXPECT_TRUE(util::IsAndroidPackageName("android"));
+ EXPECT_TRUE(util::IsAndroidPackageName("android.test"));
+ EXPECT_TRUE(util::IsAndroidPackageName("com.foo"));
+ EXPECT_TRUE(util::IsAndroidPackageName("com.foo.test_thing"));
+ EXPECT_TRUE(util::IsAndroidPackageName("com.foo.testing_thing_"));
+ EXPECT_TRUE(util::IsAndroidPackageName("com.foo.test_99_"));
+
+ EXPECT_FALSE(util::IsAndroidPackageName("android._test"));
+ EXPECT_FALSE(util::IsAndroidPackageName("com"));
+ EXPECT_FALSE(util::IsAndroidPackageName("_android"));
+ EXPECT_FALSE(util::IsAndroidPackageName("android."));
+ EXPECT_FALSE(util::IsAndroidPackageName(".android"));
+ EXPECT_FALSE(util::IsAndroidPackageName(".."));
+ EXPECT_FALSE(util::IsAndroidPackageName("cøm.foo"));
+}
+
TEST(UtilTest, FullyQualifiedClassName) {
EXPECT_THAT(util::GetFullyQualifiedClassName("android", ".asdf"), Eq("android.asdf"));
EXPECT_THAT(util::GetFullyQualifiedClassName("android", ".a.b"), Eq("android.a.b"));
diff --git a/tools/apilint/apilint.py b/tools/apilint/apilint.py
index 2eab22e..c9f3199 100644
--- a/tools/apilint/apilint.py
+++ b/tools/apilint/apilint.py
@@ -1371,7 +1371,9 @@
print
"""
- print "%s API style issues %s\n" % ((format(fg=WHITE, bg=BLUE, bold=True), format(reset=True)))
- for f in sorted(cur_fail):
- print cur_fail[f]
- print
+ if len(cur_fail) != 0:
+ print "%s API style issues %s\n" % ((format(fg=WHITE, bg=BLUE, bold=True), format(reset=True)))
+ for f in sorted(cur_fail):
+ print cur_fail[f]
+ print
+ sys.exit(77)
diff --git a/tools/apilint/apilint_sha.sh b/tools/apilint/apilint_sha.sh
new file mode 100755
index 0000000..2a45b10
--- /dev/null
+++ b/tools/apilint/apilint_sha.sh
@@ -0,0 +1,4 @@
+#!/bin/bash
+if git show --name-only --pretty=format: $1 | grep api/ > /dev/null; then
+ python tools/apilint/apilint.py <(git show $1:api/current.txt) <(git show $1^:api/current.txt)
+fi
diff --git a/tools/streaming_proto/cpp/main.cpp b/tools/streaming_proto/cpp/main.cpp
index 4816984..9aef562 100644
--- a/tools/streaming_proto/cpp/main.cpp
+++ b/tools/streaming_proto/cpp/main.cpp
@@ -18,6 +18,12 @@
return file_descriptor.name() + ".h";
}
+static inline bool
+should_generate_enums_mapping(const EnumDescriptorProto& enu)
+{
+ return enu.options().GetExtension(stream_enum).enable_enums_mapping();
+}
+
static void
write_enum(stringstream& text, const EnumDescriptorProto& enu, const string& indent)
{
@@ -29,6 +35,23 @@
<< make_constant_name(value.name())
<< " = " << value.number() << ";" << endl;
}
+
+ if (should_generate_enums_mapping(enu)) {
+ string name = make_constant_name(enu.name());
+ string prefix = name + "_";
+ text << indent << "const int _ENUM_" << name << "_COUNT = " << N << ";" << endl;
+ text << indent << "const char* _ENUM_" << name << "_NAMES[" << N << "] = {" << endl;
+ for (int i=0; i<N; i++) {
+ text << indent << INDENT << "\"" << stripPrefix(enu.value(i).name(), prefix) << "\"," << endl;
+ }
+ text << indent << "};" << endl;
+ text << indent << "const uint32_t _ENUM_" << name << "_VALUES[" << N << "] = {" << endl;
+ for (int i=0; i<N; i++) {
+ text << indent << INDENT << make_constant_name(enu.value(i).name()) << "," << endl;
+ }
+ text << indent << "};" << endl;
+ }
+
text << endl;
}
@@ -59,7 +82,7 @@
static inline bool
should_generate_fields_mapping(const DescriptorProto& message)
{
- return message.options().GetExtension(stream).enable_fields_mapping();
+ return message.options().GetExtension(stream_msg).enable_fields_mapping();
}
static void
diff --git a/tools/streaming_proto/stream.proto b/tools/streaming_proto/stream.proto
index 123506c..c081209 100644
--- a/tools/streaming_proto/stream.proto
+++ b/tools/streaming_proto/stream.proto
@@ -21,12 +21,22 @@
package android.stream_proto;
// This option tells streaming proto plugin to compile .proto files with extra features.
-message StreamFlags {
+message MessageOptions {
// creates a mapping of field names of the message to its field ids
optional bool enable_fields_mapping = 1;
}
extend google.protobuf.MessageOptions {
// Flags used by streaming proto plugins
- optional StreamFlags stream = 126856794;
+ optional MessageOptions stream_msg = 126856794;
+}
+
+message EnumOptions {
+ // creates a mapping of enum names to its values, strip its prefix enum type for each value
+ optional bool enable_enums_mapping = 1;
+}
+
+extend google.protobuf.EnumOptions {
+ // Flags used by streaming proto plugins
+ optional EnumOptions stream_enum = 126856794;
}
diff --git a/tools/streaming_proto/string_utils.cpp b/tools/streaming_proto/string_utils.cpp
index bd34ab7..607d820 100644
--- a/tools/streaming_proto/string_utils.cpp
+++ b/tools/streaming_proto/string_utils.cpp
@@ -108,6 +108,17 @@
return result;
}
+string
+stripPrefix(const string& str, const string& prefix)
+{
+ if (str.size() <= prefix.size()) return str;
+ size_t i = 0, len = prefix.size();
+ for (; i<len; i++) {
+ if (str[i] != prefix[i]) return str;
+ }
+ return str.substr(i);
+}
+
} // namespace stream_proto
} // namespace android
diff --git a/tools/streaming_proto/string_utils.h b/tools/streaming_proto/string_utils.h
index d6f195f..315b275 100644
--- a/tools/streaming_proto/string_utils.h
+++ b/tools/streaming_proto/string_utils.h
@@ -26,15 +26,20 @@
string file_base_name(const string& str);
/**
- * Replace all occurances of 'replace' with 'with'.
+ * Replaces all occurances of 'replace' with 'with'.
*/
string replace_string(const string& str, const char replace, const char with);
/**
- * Split a string to parts by delimiter.
+ * Splits a string to parts by delimiter.
*/
vector<string> split(const string& str, const char delimiter);
+/**
+ * Returns the rest of str if it has prefix, otherwise return all.
+ */
+string stripPrefix(const string& str, const string& prefix);
+
} // namespace stream_proto
} // namespace android
diff --git a/wifi/java/android/net/wifi/WifiInfo.java b/wifi/java/android/net/wifi/WifiInfo.java
index a367b23..bf8fed1 100644
--- a/wifi/java/android/net/wifi/WifiInfo.java
+++ b/wifi/java/android/net/wifi/WifiInfo.java
@@ -348,6 +348,9 @@
* quotation marks. Otherwise, it is returned as a string of hex digits. The
* SSID may be <unknown ssid> if there is no network currently connected,
* or if the caller has insufficient permissions to access the SSID.
+ *
+ * Prior to {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR1}, this method
+ * always returned the SSID with no quotes around it.
* @return the SSID
*/
public String getSSID() {