Merge "Add support for audio-focused notifications."
diff --git a/api/current.txt b/api/current.txt
index dd2aa48..8d281a2 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -989,6 +989,7 @@
field public static final int preferenceStyle = 16842894; // 0x101008e
field public static final int presentationTheme = 16843712; // 0x10103c0
field public static final int previewImage = 16843482; // 0x10102da
+ field public static final int primaryContentAlpha = 16843367; // 0x1010267
field public static final int priority = 16842780; // 0x101001c
field public static final int privateImeOptions = 16843299; // 0x1010223
field public static final int process = 16842769; // 0x1010011
@@ -4591,6 +4592,7 @@
public abstract class FragmentContainer {
ctor public FragmentContainer();
+ method public android.app.Fragment instantiate(android.content.Context, java.lang.String, android.os.Bundle);
method public abstract android.view.View onFindViewById(int);
method public abstract boolean onHasView();
}
@@ -5437,6 +5439,7 @@
method public boolean removeAutomaticZenRule(java.lang.String);
method public final void setInterruptionFilter(int);
method public void setNotificationPolicy(android.app.NotificationManager.Policy);
+ method public android.content.ComponentName startServiceInForeground(android.content.Intent, int, android.app.Notification);
method public boolean updateAutomaticZenRule(java.lang.String, android.app.AutomaticZenRule);
field public static final java.lang.String ACTION_INTERRUPTION_FILTER_CHANGED = "android.app.action.INTERRUPTION_FILTER_CHANGED";
field public static final java.lang.String ACTION_NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED = "android.app.action.NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED";
@@ -35461,7 +35464,6 @@
field public static final java.lang.String EXTRA_OFFLINE = "android.service.media.extra.OFFLINE";
field public static final java.lang.String EXTRA_RECENT = "android.service.media.extra.RECENT";
field public static final java.lang.String EXTRA_SUGGESTED = "android.service.media.extra.SUGGESTED";
- field public static final java.lang.String EXTRA_SUGGESTION_KEYWORDS = "android.service.media.extra.SUGGESTION_KEYWORDS";
}
public class MediaBrowserService.Result<T> {
@@ -38978,6 +38980,7 @@
method public void startIntentSender(android.content.IntentSender, android.content.Intent, int, int, int) throws android.content.IntentSender.SendIntentException;
method public void startIntentSender(android.content.IntentSender, android.content.Intent, int, int, int, android.os.Bundle) throws android.content.IntentSender.SendIntentException;
method public android.content.ComponentName startService(android.content.Intent);
+ method public android.content.ComponentName startServiceInForeground(android.content.Intent, int, android.app.Notification);
method public boolean stopService(android.content.Intent);
method public void unbindService(android.content.ServiceConnection);
method public void unregisterReceiver(android.content.BroadcastReceiver);
diff --git a/api/system-current.txt b/api/system-current.txt
index 4e8b4d5..d7a8fc5 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -1098,6 +1098,7 @@
field public static final int preferenceStyle = 16842894; // 0x101008e
field public static final int presentationTheme = 16843712; // 0x10103c0
field public static final int previewImage = 16843482; // 0x10102da
+ field public static final int primaryContentAlpha = 16843367; // 0x1010267
field public static final int priority = 16842780; // 0x101001c
field public static final int privateImeOptions = 16843299; // 0x1010223
field public static final int process = 16842769; // 0x1010011
@@ -4748,6 +4749,7 @@
public abstract class FragmentContainer {
ctor public FragmentContainer();
+ method public android.app.Fragment instantiate(android.content.Context, java.lang.String, android.os.Bundle);
method public abstract android.view.View onFindViewById(int);
method public abstract boolean onHasView();
}
@@ -5622,6 +5624,7 @@
method public boolean removeAutomaticZenRule(java.lang.String);
method public final void setInterruptionFilter(int);
method public void setNotificationPolicy(android.app.NotificationManager.Policy);
+ method public android.content.ComponentName startServiceInForeground(android.content.Intent, int, android.app.Notification);
method public boolean updateAutomaticZenRule(java.lang.String, android.app.AutomaticZenRule);
field public static final java.lang.String ACTION_INTERRUPTION_FILTER_CHANGED = "android.app.action.INTERRUPTION_FILTER_CHANGED";
field public static final java.lang.String ACTION_NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED = "android.app.action.NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED";
@@ -21914,6 +21917,7 @@
method public int getClientPid();
method public int getClientUid();
method public int getPlayerInterfaceId();
+ method public android.media.PlayerProxy getPlayerProxy();
method public int getPlayerState();
method public int getPlayerType();
method public void writeToParcel(android.os.Parcel, int);
@@ -23742,6 +23746,13 @@
field public static final android.os.Parcelable.Creator<android.media.PlaybackParams> CREATOR;
}
+ public class PlayerProxy {
+ method public void pause() throws java.lang.IllegalStateException;
+ method public void setVolume(float) throws java.lang.IllegalStateException;
+ method public void start() throws java.lang.IllegalStateException;
+ method public void stop() throws java.lang.IllegalStateException;
+ }
+
public final class Rating implements android.os.Parcelable {
method public int describeContents();
method public float getPercentRating();
@@ -38352,7 +38363,6 @@
field public static final java.lang.String EXTRA_OFFLINE = "android.service.media.extra.OFFLINE";
field public static final java.lang.String EXTRA_RECENT = "android.service.media.extra.RECENT";
field public static final java.lang.String EXTRA_SUGGESTED = "android.service.media.extra.SUGGESTED";
- field public static final java.lang.String EXTRA_SUGGESTION_KEYWORDS = "android.service.media.extra.SUGGESTION_KEYWORDS";
}
public class MediaBrowserService.Result<T> {
@@ -42218,6 +42228,7 @@
method public void startIntentSender(android.content.IntentSender, android.content.Intent, int, int, int) throws android.content.IntentSender.SendIntentException;
method public void startIntentSender(android.content.IntentSender, android.content.Intent, int, int, int, android.os.Bundle) throws android.content.IntentSender.SendIntentException;
method public android.content.ComponentName startService(android.content.Intent);
+ method public android.content.ComponentName startServiceInForeground(android.content.Intent, int, android.app.Notification);
method public boolean stopService(android.content.Intent);
method public void unbindService(android.content.ServiceConnection);
method public void unregisterReceiver(android.content.BroadcastReceiver);
diff --git a/api/test-current.txt b/api/test-current.txt
index 3227ac7..807d6bd 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -989,6 +989,7 @@
field public static final int preferenceStyle = 16842894; // 0x101008e
field public static final int presentationTheme = 16843712; // 0x10103c0
field public static final int previewImage = 16843482; // 0x10102da
+ field public static final int primaryContentAlpha = 16843367; // 0x1010267
field public static final int priority = 16842780; // 0x101001c
field public static final int privateImeOptions = 16843299; // 0x1010223
field public static final int process = 16842769; // 0x1010011
@@ -4601,6 +4602,7 @@
public abstract class FragmentContainer {
ctor public FragmentContainer();
+ method public android.app.Fragment instantiate(android.content.Context, java.lang.String, android.os.Bundle);
method public abstract android.view.View onFindViewById(int);
method public abstract boolean onHasView();
}
@@ -5448,6 +5450,7 @@
method public boolean removeAutomaticZenRule(java.lang.String);
method public final void setInterruptionFilter(int);
method public void setNotificationPolicy(android.app.NotificationManager.Policy);
+ method public android.content.ComponentName startServiceInForeground(android.content.Intent, int, android.app.Notification);
method public boolean updateAutomaticZenRule(java.lang.String, android.app.AutomaticZenRule);
field public static final java.lang.String ACTION_INTERRUPTION_FILTER_CHANGED = "android.app.action.INTERRUPTION_FILTER_CHANGED";
field public static final java.lang.String ACTION_NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED = "android.app.action.NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED";
@@ -35580,7 +35583,6 @@
field public static final java.lang.String EXTRA_OFFLINE = "android.service.media.extra.OFFLINE";
field public static final java.lang.String EXTRA_RECENT = "android.service.media.extra.RECENT";
field public static final java.lang.String EXTRA_SUGGESTED = "android.service.media.extra.SUGGESTED";
- field public static final java.lang.String EXTRA_SUGGESTION_KEYWORDS = "android.service.media.extra.SUGGESTION_KEYWORDS";
}
public class MediaBrowserService.Result<T> {
@@ -39098,6 +39100,7 @@
method public void startIntentSender(android.content.IntentSender, android.content.Intent, int, int, int) throws android.content.IntentSender.SendIntentException;
method public void startIntentSender(android.content.IntentSender, android.content.Intent, int, int, int, android.os.Bundle) throws android.content.IntentSender.SendIntentException;
method public android.content.ComponentName startService(android.content.Intent);
+ method public android.content.ComponentName startServiceInForeground(android.content.Intent, int, android.app.Notification);
method public boolean stopService(android.content.Intent);
method public void unbindService(android.content.ServiceConnection);
method public void unregisterReceiver(android.content.BroadcastReceiver);
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index d08bee5..b67e193 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -1385,7 +1385,14 @@
@Override
public ComponentName startService(Intent service) {
warnIfCallingFromSystemProcess();
- return startServiceCommon(service, mUser);
+ return startServiceCommon(service, -1, null, mUser);
+ }
+
+ @Override
+ public ComponentName startServiceInForeground(Intent service,
+ int id, Notification notification) {
+ warnIfCallingFromSystemProcess();
+ return startServiceCommon(service, id, notification, mUser);
}
@Override
@@ -1396,16 +1403,24 @@
@Override
public ComponentName startServiceAsUser(Intent service, UserHandle user) {
- return startServiceCommon(service, user);
+ return startServiceCommon(service, -1, null, user);
}
- private ComponentName startServiceCommon(Intent service, UserHandle user) {
+ @Override
+ public ComponentName startServiceInForegroundAsUser(Intent service,
+ int id, Notification notification, UserHandle user) {
+ return startServiceCommon(service, id, notification, user);
+ }
+
+ private ComponentName startServiceCommon(Intent service, int id, Notification notification,
+ UserHandle user) {
try {
validateServiceIntent(service);
service.prepareToLeaveProcess(this);
ComponentName cn = ActivityManager.getService().startService(
mMainThread.getApplicationThread(), service, service.resolveTypeIfNeeded(
- getContentResolver()), getOpPackageName(), user.getIdentifier());
+ getContentResolver()), id, notification, getOpPackageName(),
+ user.getIdentifier());
if (cn != null) {
if (cn.getPackageName().equals("!")) {
throw new SecurityException(
diff --git a/core/java/android/app/Fragment.java b/core/java/android/app/Fragment.java
index 10ab2bc..62d6898 100644
--- a/core/java/android/app/Fragment.java
+++ b/core/java/android/app/Fragment.java
@@ -102,15 +102,19 @@
mSavedFragmentState = in.readBundle();
}
- public Fragment instantiate(FragmentHostCallback host, Fragment parent,
- FragmentManagerNonConfig childNonConfig) {
+ public Fragment instantiate(FragmentHostCallback host, FragmentContainer container,
+ Fragment parent, FragmentManagerNonConfig childNonConfig) {
if (mInstance == null) {
final Context context = host.getContext();
if (mArguments != null) {
mArguments.setClassLoader(context.getClassLoader());
}
- mInstance = Fragment.instantiate(context, mClassName, mArguments);
+ if (container != null) {
+ mInstance = container.instantiate(context, mClassName, mArguments);
+ } else {
+ mInstance = Fragment.instantiate(context, mClassName, mArguments);
+ }
if (mSavedFragmentState != null) {
mSavedFragmentState.setClassLoader(context.getClassLoader());
diff --git a/core/java/android/app/FragmentContainer.java b/core/java/android/app/FragmentContainer.java
index b2e0300..6ed54dc 100644
--- a/core/java/android/app/FragmentContainer.java
+++ b/core/java/android/app/FragmentContainer.java
@@ -18,6 +18,8 @@
import android.annotation.IdRes;
import android.annotation.Nullable;
+import android.content.Context;
+import android.os.Bundle;
import android.view.View;
/**
@@ -35,4 +37,13 @@
* Return {@code true} if the container holds any view.
*/
public abstract boolean onHasView();
+
+ /**
+ * Creates an instance of the specified fragment, can be overridden to construct fragments
+ * with dependencies, or change the fragment being constructed. By default just calls
+ * {@link Fragment#instantiate(Context, String, Bundle)}.
+ */
+ public Fragment instantiate(Context context, String className, Bundle arguments) {
+ return Fragment.instantiate(context, className, arguments);
+ }
}
diff --git a/core/java/android/app/FragmentManager.java b/core/java/android/app/FragmentManager.java
index efd2b69..44f1322 100644
--- a/core/java/android/app/FragmentManager.java
+++ b/core/java/android/app/FragmentManager.java
@@ -1984,11 +1984,13 @@
if (startIndex != recordNum) {
executeOpsTogether(records, isRecordPop, startIndex, recordNum);
}
- // execute all unoptimized together
- int optimizeEnd;
- for (optimizeEnd = recordNum + 1; optimizeEnd < numRecords; optimizeEnd++) {
- if (records.get(optimizeEnd).mAllowOptimization) {
- break;
+ // execute all unoptimized pop operations together or one add operation
+ int optimizeEnd = recordNum + 1;
+ if (isRecordPop.get(recordNum)) {
+ while (optimizeEnd < numRecords
+ && isRecordPop.get(optimizeEnd)
+ && !records.get(optimizeEnd).mAllowOptimization) {
+ optimizeEnd++;
}
}
executeOpsTogether(records, isRecordPop, recordNum, optimizeEnd);
@@ -2651,7 +2653,7 @@
if (childNonConfigs != null && i < childNonConfigs.size()) {
childNonConfig = childNonConfigs.get(i);
}
- Fragment f = fs.instantiate(mHost, mParent, childNonConfig);
+ Fragment f = fs.instantiate(mHost, mContainer, mParent, childNonConfig);
if (DEBUG) Log.v(TAG, "restoreAllState: active #" + i + ": " + f);
mActive.add(f);
// Now that the fragment is instantiated (or came from being
@@ -3270,7 +3272,7 @@
+ Integer.toHexString(id) + " fname=" + fname
+ " existing=" + fragment);
if (fragment == null) {
- fragment = Fragment.instantiate(context, fname);
+ fragment = mContainer.instantiate(context, fname, null);
fragment.mFromLayout = true;
fragment.mFragmentId = id != 0 ? id : containerId;
fragment.mContainerId = containerId;
diff --git a/core/java/android/app/FragmentTransition.java b/core/java/android/app/FragmentTransition.java
index 6d57cd4..80a5aac 100644
--- a/core/java/android/app/FragmentTransition.java
+++ b/core/java/android/app/FragmentTransition.java
@@ -188,7 +188,10 @@
private static void configureTransitionsOptimized(FragmentManagerImpl fragmentManager,
int containerId, FragmentContainerTransition fragments,
View nonExistentView, ArrayMap<String, String> nameOverrides) {
- ViewGroup sceneRoot = (ViewGroup) fragmentManager.mContainer.onFindViewById(containerId);
+ ViewGroup sceneRoot = null;
+ if (fragmentManager.mContainer.onHasView()) {
+ sceneRoot = (ViewGroup) fragmentManager.mContainer.onFindViewById(containerId);
+ }
if (sceneRoot == null) {
return;
}
@@ -257,7 +260,10 @@
private static void configureTransitionsUnoptimized(FragmentManagerImpl fragmentManager,
int containerId, FragmentContainerTransition fragments,
View nonExistentView, ArrayMap<String, String> nameOverrides) {
- ViewGroup sceneRoot = (ViewGroup) fragmentManager.mContainer.onFindViewById(containerId);
+ ViewGroup sceneRoot = null;
+ if (fragmentManager.mContainer.onHasView()) {
+ sceneRoot = (ViewGroup) fragmentManager.mContainer.onFindViewById(containerId);
+ }
if (sceneRoot == null) {
return;
}
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index 5824c32..236eb18 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -128,7 +128,8 @@
void finishSubActivity(in IBinder token, in String resultWho, int requestCode);
PendingIntent getRunningServiceControlPanel(in ComponentName service);
ComponentName startService(in IApplicationThread caller, in Intent service,
- in String resolvedType, in String callingPackage, int userId);
+ in String resolvedType, int id, in Notification notification,
+ in String callingPackage, int userId);
int stopService(in IApplicationThread caller, in Intent service,
in String resolvedType, int userId);
int bindService(in IApplicationThread caller, in IBinder token, in Intent service,
diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java
index 3551691..c0aae6d 100644
--- a/core/java/android/app/NotificationManager.java
+++ b/core/java/android/app/NotificationManager.java
@@ -24,6 +24,7 @@
import android.app.Notification.Builder;
import android.content.ComponentName;
import android.content.Context;
+import android.content.Intent;
import android.content.pm.ParceledListSlice;
import android.graphics.drawable.Icon;
import android.net.Uri;
@@ -1085,4 +1086,38 @@
default: return defValue;
}
}
+
+ /**
+ * Start a service directly into the "foreground service" state. Unlike
+ * {@link android.content.Context#startService(Intent)}, this method
+ * can be used from within background operations like broadcast receivers
+ * or scheduled jobs.
+ *
+ * @param service Description of the service to be stopped. The Intent must be either
+ * fully explicit (supplying a component name) or specify a specific package
+ * name it is targeted to.
+ * @param id The identifier for this notification as per
+ * {@link #notify(int, Notification) NotificationManager.notify(int, Notification)};
+ * must not be 0.
+ * @param notification The Notification to be displayed.
+ * @return If the service is being started or is already running, the
+ * {@link ComponentName} of the actual service that was started is
+ * returned; else if the service does not exist null is returned.
+ */
+ @Nullable
+ public ComponentName startServiceInForeground(Intent service,
+ int id, Notification notification) {
+ return mContext.startServiceInForeground(service, id, notification);
+ }
+
+ /**
+ * @hide like {@link #startServiceInForeground(Intent, int, Notification)}
+ * but for a specific user.
+ */
+ @Nullable
+ public ComponentName startServiceInForegroundAsUser(Intent service,
+ int id, Notification notification, UserHandle user) {
+ return mContext.startServiceInForegroundAsUser(service, id, notification, user);
+ }
+
}
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index af5e643..b196c64 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -35,6 +35,7 @@
import android.app.IApplicationThread;
import android.app.IServiceConnection;
import android.app.LoadedApk;
+import android.app.Notification;
import android.app.admin.DevicePolicyManager;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
@@ -2517,6 +2518,17 @@
public abstract ComponentName startService(Intent service);
/**
+ * Start a service directly into the "foreground service" state. Unlike {@link #startService},
+ * this method can be used from within background operations like broadcast receivers
+ * or scheduled jobs. The API entry point for this is in NotificationManager in order to
+ * preserve appropriate public package layering.
+ * @hide
+ */
+ @Nullable
+ public abstract ComponentName startServiceInForeground(Intent service,
+ int id, Notification notification);
+
+ /**
* Request that a given application service be stopped. If the service is
* not running, nothing happens. Otherwise it is stopped. Note that calls
* to startService() are not counted -- this stops the service no matter
@@ -2547,9 +2559,18 @@
/**
* @hide like {@link #startService(Intent)} but for a specific user.
*/
+ @Nullable
public abstract ComponentName startServiceAsUser(Intent service, UserHandle user);
/**
+ * @hide like {@link #startServiceInForeground(Intent, int, Notification)}
+ * but for a specific user.
+ */
+ @Nullable
+ public abstract ComponentName startServiceInForegroundAsUser(Intent service,
+ int id, Notification notification, UserHandle user);
+
+ /**
* @hide like {@link #stopService(Intent)} but for a specific user.
*/
public abstract boolean stopServiceAsUser(Intent service, UserHandle user);
diff --git a/core/java/android/content/ContextWrapper.java b/core/java/android/content/ContextWrapper.java
index 4b6076b..c449b6a 100644
--- a/core/java/android/content/ContextWrapper.java
+++ b/core/java/android/content/ContextWrapper.java
@@ -20,6 +20,7 @@
import android.annotation.SystemApi;
import android.app.IApplicationThread;
import android.app.IServiceConnection;
+import android.app.Notification;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.res.AssetManager;
@@ -623,6 +624,13 @@
return mBase.startService(service);
}
+ /** @hide */
+ @Override
+ public ComponentName startServiceInForeground(Intent service,
+ int id, Notification notification) {
+ return mBase.startServiceInForeground(service, id, notification);
+ }
+
@Override
public boolean stopService(Intent name) {
return mBase.stopService(name);
@@ -636,6 +644,13 @@
/** @hide */
@Override
+ public ComponentName startServiceInForegroundAsUser(Intent service,
+ int id, Notification notification, UserHandle user) {
+ return mBase.startServiceInForegroundAsUser(service, id, notification, user);
+ }
+
+ /** @hide */
+ @Override
public boolean stopServiceAsUser(Intent name, UserHandle user) {
return mBase.stopServiceAsUser(name, user);
}
diff --git a/core/java/android/content/pm/PackageManagerInternal.java b/core/java/android/content/pm/PackageManagerInternal.java
index 2590a6b..bf3aa70 100644
--- a/core/java/android/content/pm/PackageManagerInternal.java
+++ b/core/java/android/content/pm/PackageManagerInternal.java
@@ -138,9 +138,8 @@
* @param userId The user under which to check.
*
* @return An {@link ApplicationInfo} containing information about the
- * package.
- * @throws NameNotFoundException if a package with the given name cannot be
- * found on the system.
+ * package, or {@code null} if no application exists with that
+ * package name.
*/
public abstract ApplicationInfo getApplicationInfo(String packageName, int userId);
diff --git a/core/java/android/net/NetworkIdentity.java b/core/java/android/net/NetworkIdentity.java
index c704ef0..0775bda 100644
--- a/core/java/android/net/NetworkIdentity.java
+++ b/core/java/android/net/NetworkIdentity.java
@@ -24,8 +24,10 @@
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
import android.os.Build;
+import android.service.NetworkIdentityProto;
import android.telephony.TelephonyManager;
import android.util.Slog;
+import android.util.proto.ProtoOutputStream;
import java.util.Objects;
@@ -110,6 +112,23 @@
return builder.append("}").toString();
}
+ public void writeToProto(ProtoOutputStream proto, long tag) {
+ final long start = proto.start(tag);
+
+ proto.write(NetworkIdentityProto.TYPE, mType);
+
+ // Not dumping mSubType, subtypes are no longer supported.
+
+ if (mSubscriberId != null) {
+ proto.write(NetworkIdentityProto.SUBSCRIBER_ID, scrubSubscriberId(mSubscriberId));
+ }
+ proto.write(NetworkIdentityProto.NETWORK_ID, mNetworkId);
+ proto.write(NetworkIdentityProto.ROAMING, mRoaming);
+ proto.write(NetworkIdentityProto.METERED, mMetered);
+
+ proto.end(start);
+ }
+
public int getType() {
return mType;
}
diff --git a/core/java/android/net/NetworkScoreManager.java b/core/java/android/net/NetworkScoreManager.java
index 7a832d1..57cf1a5 100644
--- a/core/java/android/net/NetworkScoreManager.java
+++ b/core/java/android/net/NetworkScoreManager.java
@@ -21,6 +21,7 @@
import android.Manifest;
import android.annotation.IntDef;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
import android.annotation.SystemApi;
@@ -36,6 +37,7 @@
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.concurrent.CompletableFuture;
/**
* Class that manages communication between network subsystems and a network scorer.
@@ -370,25 +372,26 @@
*
* @param request a {@link RecommendationRequest} instance containing additional
* request details
- * @param callback a {@link RecommendationCallback} instance that will be invoked when
- * the {@link RecommendationResult} is available
- * @param handler a {@link Handler} instance representing the thread to run the callback on.
+ * @param handler a {@link Handler} instance representing the thread to complete the future on.
+ * If null the responding binder thread will be used.
+ * @return a {@link CompletableFuture} instance that will eventually receive the
+ * {@link RecommendationResult}.
* @throws SecurityException
* @hide
*/
- public void requestRecommendation(
+ public CompletableFuture<RecommendationResult> requestRecommendation(
final @NonNull RecommendationRequest request,
- final @NonNull RecommendationCallback callback,
- final @NonNull Handler handler) {
+ final @Nullable Handler handler) {
Preconditions.checkNotNull(request, "RecommendationRequest cannot be null.");
- Preconditions.checkNotNull(callback, "RecommendationCallback cannot be null.");
- Preconditions.checkNotNull(handler, "Handler cannot be null.");
+
+ final CompletableFuture<RecommendationResult> futureResult =
+ new CompletableFuture<>();
RemoteCallback remoteCallback = new RemoteCallback(new RemoteCallback.OnResultListener() {
@Override
public void onResult(Bundle data) {
RecommendationResult result = data.getParcelable(EXTRA_RECOMMENDATION_RESULT);
- callback.onRecommendationAvailable(result);
+ futureResult.complete(result);
}
}, handler);
@@ -397,18 +400,7 @@
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
- }
- /**
- * Callers of {@link #requestRecommendation(RecommendationRequest, RecommendationCallback, Handler)}
- * must pass in an implementation of this class.
- * @hide
- */
- public abstract static class RecommendationCallback {
- /**
- * Invoked when a {@link RecommendationResult} is available.
- * @param result a {@link RecommendationResult} instance.
- */
- public abstract void onRecommendationAvailable(RecommendationResult result);
+ return futureResult;
}
}
diff --git a/core/java/android/net/NetworkStatsHistory.java b/core/java/android/net/NetworkStatsHistory.java
index 4a4accb..5f521de 100644
--- a/core/java/android/net/NetworkStatsHistory.java
+++ b/core/java/android/net/NetworkStatsHistory.java
@@ -31,7 +31,10 @@
import android.os.Parcel;
import android.os.Parcelable;
+import android.service.NetworkStatsHistoryBucketProto;
+import android.service.NetworkStatsHistoryProto;
import android.util.MathUtils;
+import android.util.proto.ProtoOutputStream;
import com.android.internal.util.IndentingPrintWriter;
@@ -628,6 +631,33 @@
}
}
+ public void writeToProto(ProtoOutputStream proto, long tag) {
+ final long start = proto.start(tag);
+
+ proto.write(NetworkStatsHistoryProto.BUCKET_DURATION_MS, bucketDuration);
+
+ for (int i = 0; i < bucketCount; i++) {
+ final long startBucket = proto.start(NetworkStatsHistoryProto.BUCKETS);
+
+ proto.write(NetworkStatsHistoryBucketProto.BUCKET_START_MS, bucketStart[i]);
+ writeToProto(proto, NetworkStatsHistoryBucketProto.RX_BYTES, rxBytes, i);
+ writeToProto(proto, NetworkStatsHistoryBucketProto.RX_PACKETS, rxPackets, i);
+ writeToProto(proto, NetworkStatsHistoryBucketProto.TX_BYTES, txBytes, i);
+ writeToProto(proto, NetworkStatsHistoryBucketProto.TX_PACKETS, txPackets, i);
+ writeToProto(proto, NetworkStatsHistoryBucketProto.OPERATIONS, operations, i);
+
+ proto.end(startBucket);
+ }
+
+ proto.end(start);
+ }
+
+ private static void writeToProto(ProtoOutputStream proto, long tag, long[] array, int index) {
+ if (array != null) {
+ proto.write(tag, array[index]);
+ }
+ }
+
@Override
public String toString() {
final CharArrayWriter writer = new CharArrayWriter();
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 9d1af50..37dfdb9 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -6934,8 +6934,7 @@
& (View.AUTO_FILL_FLAG_TYPE_FILL
| View.AUTO_FILL_FLAG_TYPE_SAVE)) != 0;
final int id = mID;
- if (id > 0 && (id&0xff000000) != 0 && (id&0x00ff0000) != 0
- && (id&0x0000ffff) != 0) {
+ if (id != NO_ID && !isViewIdGenerated(id)) {
String pkg, type, entry;
try {
final Resources res = getResources();
@@ -22640,6 +22639,10 @@
}
}
+ private static boolean isViewIdGenerated(int id) {
+ return (id & 0xFF000000) == 0 && (id & 0x00FFFFFF) != 0;
+ }
+
/**
* Gets the Views in the hierarchy affected by entering and exiting Activity Scene transitions.
* @param transitioningViews This View will be added to transitioningViews if it is VISIBLE and
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 1b42a90..a479bb3 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -1788,15 +1788,15 @@
for (int i = childrenCount - 1; i >= 0; i--) {
final int childIndex = getAndVerifyPreorderedIndex(childrenCount, i, customOrder);
final View child = getAndVerifyPreorderedView(preorderedList, children, childIndex);
- final PointF point = getLocalPoint();
- if (isTransformedTouchPointInView(x, y, child, point)) {
- final PointerIcon pointerIcon =
- dispatchResolvePointerIcon(event, pointerIndex, child);
- if (pointerIcon != null) {
- if (preorderedList != null) preorderedList.clear();
- return pointerIcon;
- }
- break;
+ if (!canViewReceivePointerEvents(child)
+ || !isTransformedTouchPointInView(x, y, child, null)) {
+ continue;
+ }
+ final PointerIcon pointerIcon =
+ dispatchResolvePointerIcon(event, pointerIndex, child);
+ if (pointerIcon != null) {
+ if (preorderedList != null) preorderedList.clear();
+ return pointerIcon;
}
}
if (preorderedList != null) preorderedList.clear();
@@ -2091,11 +2091,12 @@
getAndVerifyPreorderedIndex(childrenCount, i, customOrder);
final View child =
getAndVerifyPreorderedView(preorderedList, children, childIndex);
- final PointF point = getLocalPoint();
- if (isTransformedTouchPointInView(x, y, child, point)) {
- if (dispatchTooltipHoverEvent(event, child)) {
- newTarget = child;
- }
+ if (!canViewReceivePointerEvents(child)
+ || !isTransformedTouchPointInView(x, y, child, null)) {
+ continue;
+ }
+ if (dispatchTooltipHoverEvent(event, child)) {
+ newTarget = child;
break;
}
}
diff --git a/core/java/com/android/server/SystemConfig.java b/core/java/com/android/server/SystemConfig.java
index 168da5f..fcb4c7b 100644
--- a/core/java/com/android/server/SystemConfig.java
+++ b/core/java/com/android/server/SystemConfig.java
@@ -109,6 +109,10 @@
// background while in data-usage save mode, as read from the configuration files.
final ArraySet<String> mAllowInDataUsageSave = new ArraySet<>();
+ // These are the action strings of broadcasts which are whitelisted to
+ // be delivered anonymously even to apps which target O+.
+ final ArraySet<String> mAllowImplicitBroadcasts = new ArraySet<>();
+
// These are the package names of apps which should be in the 'always'
// URL-handling state upon factory reset.
final ArraySet<String> mLinkedApps = new ArraySet<>();
@@ -162,6 +166,10 @@
return mPermissions;
}
+ public ArraySet<String> getAllowImplicitBroadcasts() {
+ return mAllowImplicitBroadcasts;
+ }
+
public ArraySet<String> getAllowInPowerSaveExceptIdle() {
return mAllowInPowerSaveExceptIdle;
}
@@ -438,6 +446,17 @@
XmlUtils.skipCurrentTag(parser);
continue;
+ } else if ("allow-implicit-broadcast".equals(name) && allowAll) {
+ String action = parser.getAttributeValue(null, "action");
+ if (action == null) {
+ Slog.w(TAG, "<allow-implicit-broadcast> without action in " + permFile
+ + " at " + parser.getPositionDescription());
+ } else {
+ mAllowImplicitBroadcasts.add(action);
+ }
+ XmlUtils.skipCurrentTag(parser);
+ continue;
+
} else if ("app-link".equals(name) && allowAppConfigs) {
String pkgname = parser.getAttributeValue(null, "package");
if (pkgname == null) {
diff --git a/core/jni/android_view_PointerIcon.cpp b/core/jni/android_view_PointerIcon.cpp
index 6b634df..4150636 100644
--- a/core/jni/android_view_PointerIcon.cpp
+++ b/core/jni/android_view_PointerIcon.cpp
@@ -78,6 +78,9 @@
status_t android_view_PointerIcon_getLoadedIcon(JNIEnv* env, jobject pointerIconObj,
PointerIcon* outPointerIcon) {
+ if (!pointerIconObj) {
+ return BAD_VALUE;
+ }
outPointerIcon->style = env->GetIntField(pointerIconObj, gPointerIconClassInfo.mType);
outPointerIcon->hotSpotX = env->GetFloatField(pointerIconObj, gPointerIconClassInfo.mHotSpotX);
outPointerIcon->hotSpotY = env->GetFloatField(pointerIconObj, gPointerIconClassInfo.mHotSpotY);
diff --git a/core/proto/android/os/incident.proto b/core/proto/android/os/incident.proto
index ec2f32b..c3b0ff1 100644
--- a/core/proto/android/os/incident.proto
+++ b/core/proto/android/os/incident.proto
@@ -21,6 +21,7 @@
import "frameworks/base/libs/incident/proto/android/privacy.proto";
import "frameworks/base/core/proto/android/service/fingerprint.proto";
+import "frameworks/base/core/proto/android/service/netstats.proto";
package android.os;
@@ -49,4 +50,5 @@
// System Services
android.service.fingerprint.FingerprintServiceDumpProto fingerprint = 3000;
+ android.service.NetworkStatsServiceDumpProto netstats = 3001;
}
diff --git a/core/proto/android/service/netstats.proto b/core/proto/android/service/netstats.proto
new file mode 100644
index 0000000..5cca6ab
--- /dev/null
+++ b/core/proto/android/service/netstats.proto
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+syntax = "proto3";
+
+package android.service;
+
+option java_multiple_files = true;
+option java_outer_classname = "NetworkStatsServiceProto";
+
+// Represents dumpsys from NetworkStatsService (netstats).
+message NetworkStatsServiceDumpProto {
+ repeated NetworkInterfaceProto active_interfaces = 1;
+
+ repeated NetworkInterfaceProto active_uid_interfaces = 2;
+
+ NetworkStatsRecorderProto dev_stats = 3;
+
+ NetworkStatsRecorderProto xt_stats = 4;
+
+ NetworkStatsRecorderProto uid_stats = 5;
+
+ NetworkStatsRecorderProto uid_tag_stats = 6;
+}
+
+// Corresponds to NetworkStatsService.mActiveIfaces/mActiveUidIfaces.
+message NetworkInterfaceProto {
+ string interface = 1;
+
+ NetworkIdentitySetProto identities = 2;
+}
+
+// Corresponds to NetworkIdentitySet.
+message NetworkIdentitySetProto {
+ repeated NetworkIdentityProto identities = 1;
+}
+
+// Corresponds to NetworkIdentity.
+message NetworkIdentityProto {
+ // Constats from ConnectivityManager.TYPE_*.
+ int32 type = 1;
+
+ string subscriber_id = 2;
+
+ string network_id = 3;
+
+ bool roaming = 4;
+
+ bool metered = 5;
+}
+
+// Corresponds to NetworkStatsRecorder.
+message NetworkStatsRecorderProto {
+ int64 pending_total_bytes = 1;
+
+ NetworkStatsCollectionProto complete_history = 2;
+}
+
+// Corresponds to NetworkStatsCollection.
+message NetworkStatsCollectionProto {
+ repeated NetworkStatsCollectionStatsProto stats = 1;
+}
+
+// Corresponds to NetworkStatsCollection.mStats.
+message NetworkStatsCollectionStatsProto {
+ NetworkStatsCollectionKeyProto key = 1;
+
+ NetworkStatsHistoryProto history = 2;
+}
+
+// Corresponds to NetworkStatsCollection.Key.
+message NetworkStatsCollectionKeyProto {
+ NetworkIdentitySetProto identity = 1;
+
+ int32 uid = 2;
+
+ int32 set = 3;
+
+ int32 tag = 4;
+}
+
+// Corresponds to NetworkStatsHistory.
+message NetworkStatsHistoryProto {
+ // Duration for this bucket in milliseconds.
+ int64 bucket_duration_ms = 1;
+
+ repeated NetworkStatsHistoryBucketProto buckets = 2;
+}
+
+// Corresponds to each bucket in NetworkStatsHistory.
+message NetworkStatsHistoryBucketProto {
+ // Bucket start time in milliseconds since epoch.
+ int64 bucket_start_ms = 1;
+
+ int64 rx_bytes = 2;
+
+ int64 rx_packets = 3;
+
+ int64 tx_bytes = 4;
+
+ int64 tx_packets = 5;
+
+ int64 operations = 6;
+}
diff --git a/core/res/res/color/text_color_primary.xml b/core/res/res/color/text_color_primary.xml
new file mode 100644
index 0000000..831a9c4
--- /dev/null
+++ b/core/res/res/color/text_color_primary.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:state_enabled="false"
+ android:alpha="?attr/disabledAlpha"
+ android:color="?attr/colorForeground"/>
+ <item android:alpha="?attr/primaryContentAlpha"
+ android:color="?attr/colorForeground"/>
+</selector>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index dd33718..8e9959f 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -62,6 +62,8 @@
<!-- Default disabled alpha for widgets that set enabled/disabled alpha programmatically. -->
<attr name="disabledAlpha" format="float" />
+ <!-- The alpha applied to the foreground color to create the primary text color. -->
+ <attr name="primaryContentAlpha" format="float" />
<!-- Default background dim amount when a menu, dialog, or something similar pops up. -->
<attr name="backgroundDimAmount" format="float" />
<!-- Control whether dimming behind the window is enabled. The default
diff --git a/core/res/res/values/colors_material.xml b/core/res/res/values/colors_material.xml
index 40e7341..835b8b60 100644
--- a/core/res/res/values/colors_material.xml
+++ b/core/res/res/values/colors_material.xml
@@ -73,6 +73,8 @@
<item name="disabled_alpha_material_light" format="float" type="dimen">0.26</item>
<item name="disabled_alpha_material_dark" format="float" type="dimen">0.30</item>
+ <item name="primary_content_alpha_material_light" format="float" type="dimen">1</item>
+ <item name="primary_content_alpha_material_dark" format="float" type="dimen">0.87</item>
<item name="highlight_alpha_material_light" format="float" type="dimen">0.12</item>
<item name="highlight_alpha_material_dark" format="float" type="dimen">0.20</item>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 064d31e..099fe08 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2793,6 +2793,8 @@
<public-group type="id" first-id="0x01020041">
</public-group>
+ <public type="attr" name="primaryContentAlpha" />
+
<!-- ===============================================================
DO NOT ADD UN-GROUPED ITEMS HERE
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index b923dff..0a75744 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2797,4 +2797,5 @@
<java-symbol type="raw" name="fallback_categories" />
+ <java-symbol type="attr" name="primaryContentAlpha" />
</resources>
diff --git a/core/res/res/values/themes_material.xml b/core/res/res/values/themes_material.xml
index 5f0ad8e..d0f202b 100644
--- a/core/res/res/values/themes_material.xml
+++ b/core/res/res/values/themes_material.xml
@@ -48,13 +48,14 @@
<item name="colorBackgroundFloating">@color/background_floating_material_dark</item>
<item name="colorBackgroundCacheHint">@color/background_cache_hint_selector_material_dark</item>
<item name="disabledAlpha">@dimen/disabled_alpha_material_dark</item>
+ <item name="primaryContentAlpha">@dimen/primary_content_alpha_material_dark</item>
<item name="backgroundDimAmount">0.6</item>
<!-- Text styles -->
<item name="textAppearance">@style/TextAppearance.Material</item>
<item name="textAppearanceInverse">@style/TextAppearance.Material.Inverse</item>
- <item name="textColorPrimary">@color/primary_text_material_dark</item>
+ <item name="textColorPrimary">@color/text_color_primary</item>
<item name="textColorPrimaryInverse">@color/primary_text_material_light</item>
<item name="textColorPrimaryActivated">@color/primary_text_inverse_when_activated_material</item>
<item name="textColorPrimaryDisableOnly">@color/primary_text_disable_only_material_dark</item>
@@ -413,13 +414,14 @@
<item name="colorBackgroundFloating">@color/background_floating_material_light</item>
<item name="colorBackgroundCacheHint">@color/background_cache_hint_selector_material_light</item>
<item name="disabledAlpha">@dimen/disabled_alpha_material_light</item>
+ <item name="primaryContentAlpha">@dimen/primary_content_alpha_material_light</item>
<item name="backgroundDimAmount">0.6</item>
<!-- Text styles -->
<item name="textAppearance">@style/TextAppearance.Material</item>
<item name="textAppearanceInverse">@style/TextAppearance.Material.Inverse</item>
- <item name="textColorPrimary">@color/primary_text_material_light</item>
+ <item name="textColorPrimary">@color/text_color_primary</item>
<item name="textColorPrimaryInverse">@color/primary_text_material_dark</item>
<item name="textColorPrimaryActivated">@color/primary_text_inverse_when_activated_material</item>
<item name="textColorSecondary">@color/secondary_text_material_light</item>
@@ -805,7 +807,7 @@
<item name="colorBackgroundFloating">@color/background_floating_material_light</item>
<item name="colorBackgroundCacheHint">@color/background_cache_hint_selector_material_light</item>
- <item name="textColorPrimary">@color/primary_text_material_light</item>
+ <item name="textColorPrimary">@color/text_color_primary</item>
<item name="textColorPrimaryInverse">@color/primary_text_material_dark</item>
<item name="textColorSecondary">@color/secondary_text_material_light</item>
<item name="textColorSecondaryInverse">@color/secondary_text_material_dark</item>
@@ -839,7 +841,7 @@
<item name="colorBackgroundFloating">@color/background_floating_material_dark</item>
<item name="colorBackgroundCacheHint">@color/background_cache_hint_selector_material_dark</item>
- <item name="textColorPrimary">@color/primary_text_material_dark</item>
+ <item name="textColorPrimary">@color/text_color_primary</item>
<item name="textColorPrimaryInverse">@color/primary_text_material_light</item>
<item name="textColorPrimaryDisableOnly">@color/primary_text_disable_only_material_dark</item>
<item name="textColorSecondary">@color/secondary_text_material_dark</item>
diff --git a/data/etc/Android.mk b/data/etc/Android.mk
index 6718259..b2c6840 100644
--- a/data/etc/Android.mk
+++ b/data/etc/Android.mk
@@ -18,6 +18,14 @@
########################
include $(CLEAR_VARS)
+LOCAL_MODULE := framework-sysconfig.xml
+LOCAL_MODULE_CLASS := ETC
+LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)/sysconfig
+LOCAL_SRC_FILES := $(LOCAL_MODULE)
+include $(BUILD_PREBUILT)
+
+########################
+include $(CLEAR_VARS)
LOCAL_MODULE := platform.xml
LOCAL_MODULE_CLASS := ETC
LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)/permissions
@@ -31,4 +39,3 @@
LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)/permissions
LOCAL_SRC_FILES := $(LOCAL_MODULE)
include $(BUILD_PREBUILT)
-
diff --git a/data/etc/framework-sysconfig.xml b/data/etc/framework-sysconfig.xml
new file mode 100644
index 0000000..2f18de0
--- /dev/null
+++ b/data/etc/framework-sysconfig.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<!-- These are configurations that must exist on all Android devices. -->
+<config>
+
+ <!-- Broadcast actions that are currently exempted from O+ background
+ delivery restrictions. -->
+ <allow-implicit-broadcast action="android.intent.action.SIM_STATE_CHANGED" />
+ <allow-implicit-broadcast action="android.intent.action.PACKAGE_CHANGED" />
+
+</config>
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index 05db9b0..f1be4a9 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -110,11 +110,21 @@
<permission name="android.permission.WRITE_SECURE_SETTINGS"/>
</privapp-permissions>
+ <privapp-permissions package="com.android.omadm.service">
+ <permission name="android.permission.CHANGE_CONFIGURATION"/>
+ <permission name="android.permission.CONNECTIVITY_INTERNAL"/>
+ <permission name="android.permission.MODIFY_PHONE_STATE"/>
+ <permission name="android.permission.READ_PRIVILEGED_PHONE_STATE"/>
+ <permission name="android.permission.WRITE_APN_SETTINGS"/>
+ <permission name="android.permission.WRITE_SECURE_SETTINGS"/>
+ </privapp-permissions>
+
<privapp-permissions package="com.android.packageinstaller">
<permission name="android.permission.CLEAR_APP_CACHE"/>
<permission name="android.permission.DELETE_PACKAGES"/>
<permission name="android.permission.INSTALL_PACKAGES"/>
<permission name="android.permission.MANAGE_USERS"/>
+ <permission name="android.permission.OBSERVE_GRANT_REVOKE_PERMISSIONS"/>
<permission name="android.permission.UPDATE_APP_OPS_STATS"/>
</privapp-permissions>
@@ -122,6 +132,7 @@
<permission name="android.permission.ACCESS_IMS_CALL_SERVICE"/>
<permission name="android.permission.BIND_CARRIER_MESSAGING_SERVICE"/>
<permission name="android.permission.BIND_CARRIER_SERVICES"/>
+ <permission name="android.permission.BIND_VISUAL_VOICEMAIL_SERVICE"/>
<permission name="android.permission.CALL_PRIVILEGED"/>
<permission name="android.permission.CHANGE_COMPONENT_ENABLED_STATE"/>
<permission name="android.permission.CHANGE_CONFIGURATION"/>
@@ -327,4 +338,4 @@
<permission name="android.permission.CONTROL_VPN"/>
</privapp-permissions>
-</permissions>
+</permissions>
\ No newline at end of file
diff --git a/media/java/android/media/AudioPlaybackConfiguration.java b/media/java/android/media/AudioPlaybackConfiguration.java
index b38a07f..dd9c6a7 100644
--- a/media/java/android/media/AudioPlaybackConfiguration.java
+++ b/media/java/android/media/AudioPlaybackConfiguration.java
@@ -283,10 +283,19 @@
/**
* @hide
- * FIXME return a player proxy instead, make systemApi
- * @return
+ * Return a proxy for the player associated with this playback configuration
+ * @return a proxy player
*/
- public IPlayer getPlayerProxy() {
+ @SystemApi
+ public PlayerProxy getPlayerProxy() {
+ return mIPlayerShell == null ? null : new PlayerProxy(this);
+ }
+
+ /**
+ * @hide
+ * @return the IPlayer interface for the associated player
+ */
+ IPlayer getIPlayer() {
return mIPlayerShell == null ? null : mIPlayerShell.getIPlayer();
}
diff --git a/media/java/android/media/PlayerProxy.java b/media/java/android/media/PlayerProxy.java
new file mode 100644
index 0000000..171be27
--- /dev/null
+++ b/media/java/android/media/PlayerProxy.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.os.RemoteException;
+import android.util.Log;
+
+import java.lang.IllegalArgumentException;
+import java.util.Objects;
+
+/**
+ * Class to remotely control a player.
+ * @hide
+ */
+@SystemApi
+public class PlayerProxy {
+
+ private final static String TAG = "PlayerProxy";
+ private final static boolean DEBUG = false;
+
+ private final AudioPlaybackConfiguration mConf; // never null
+
+ /**
+ * @hide
+ * Constructor. Proxy for this player associated with this AudioPlaybackConfiguration
+ * @param conf the configuration being proxied.
+ */
+ PlayerProxy(@NonNull AudioPlaybackConfiguration apc) {
+ if (apc == null) {
+ throw new IllegalArgumentException("Illegal null AudioPlaybackConfiguration");
+ }
+ mConf = apc;
+ };
+
+ //=====================================================================
+ // Methods matching the IPlayer interface
+ /**
+ * @hide
+ * @throws IllegalStateException
+ */
+ @SystemApi
+ public void start() throws IllegalStateException {
+ try {
+ mConf.getIPlayer().start();
+ } catch (NullPointerException|RemoteException e) {
+ throw new IllegalStateException(
+ "No player to proxy for start operation, player already released?", e);
+ }
+ }
+
+ /**
+ * @hide
+ * @throws IllegalStateException
+ */
+ @SystemApi
+ public void pause() throws IllegalStateException {
+ try {
+ mConf.getIPlayer().pause();
+ } catch (NullPointerException|RemoteException e) {
+ throw new IllegalStateException(
+ "No player to proxy for pause operation, player already released?", e);
+ }
+ }
+
+ /**
+ * @hide
+ * @throws IllegalStateException
+ */
+ @SystemApi
+ public void stop() throws IllegalStateException {
+ try {
+ mConf.getIPlayer().stop();
+ } catch (NullPointerException|RemoteException e) {
+ throw new IllegalStateException(
+ "No player to proxy for stop operation, player already released?", e);
+ }
+ }
+
+ /**
+ * @hide
+ * @throws IllegalStateException
+ */
+ @SystemApi
+ public void setVolume(float vol) throws IllegalStateException {
+ try {
+ mConf.getIPlayer().setVolume(vol);
+ } catch (NullPointerException|RemoteException e) {
+ throw new IllegalStateException(
+ "No player to proxy for setVolume operation, player already released?", e);
+ }
+ }
+
+}
diff --git a/media/java/android/service/media/MediaBrowserService.java b/media/java/android/service/media/MediaBrowserService.java
index 16847c1..eaec493 100644
--- a/media/java/android/service/media/MediaBrowserService.java
+++ b/media/java/android/service/media/MediaBrowserService.java
@@ -387,7 +387,6 @@
* @see BrowserRoot#EXTRA_RECENT
* @see BrowserRoot#EXTRA_OFFLINE
* @see BrowserRoot#EXTRA_SUGGESTED
- * @see BrowserRoot#EXTRA_SUGGESTION_KEYWORDS
*/
public abstract @Nullable BrowserRoot onGetRoot(@NonNull String clientPackageName,
int clientUid, @Nullable Bundle rootHints);
@@ -550,7 +549,6 @@
* @see MediaBrowserService.BrowserRoot#EXTRA_RECENT
* @see MediaBrowserService.BrowserRoot#EXTRA_OFFLINE
* @see MediaBrowserService.BrowserRoot#EXTRA_SUGGESTED
- * @see MediaBrowserService.BrowserRoot#EXTRA_SUGGESTION_KEYWORDS
*/
public final Bundle getBrowserRootHints() {
if (mCurConnection == null) {
@@ -818,7 +816,6 @@
*
* @see #EXTRA_OFFLINE
* @see #EXTRA_SUGGESTED
- * @see #EXTRA_SUGGESTION_KEYWORDS
*/
public static final String EXTRA_RECENT = "android.service.media.extra.RECENT";
@@ -836,7 +833,6 @@
*
* @see #EXTRA_RECENT
* @see #EXTRA_SUGGESTED
- * @see #EXTRA_SUGGESTION_KEYWORDS
*/
public static final String EXTRA_OFFLINE = "android.service.media.extra.OFFLINE";
@@ -855,31 +851,9 @@
*
* @see #EXTRA_RECENT
* @see #EXTRA_OFFLINE
- * @see #EXTRA_SUGGESTION_KEYWORDS
*/
public static final String EXTRA_SUGGESTED = "android.service.media.extra.SUGGESTED";
- /**
- * The lookup key for a string that indicates specific keywords which will be considered
- * when the browser service suggests media items.
- *
- * <p>When creating a media browser for a given media browser service, this key can be
- * supplied as a root hint together with {@link #EXTRA_SUGGESTED} for retrieving suggested
- * media items related with the keywords. The list of media items passed in
- * {@link android.media.browse.MediaBrowser.SubscriptionCallback#onChildrenLoaded(String, List)}
- * is considered ordered by relevance, first being the top suggestion.
- * If the media browser service can provide such media items, the implementation must return
- * the key in the root hint when {@link #onGetRoot(String, int, Bundle)} is called back.
- *
- * <p>The root hint may contain multiple keys.
- *
- * @see #EXTRA_RECENT
- * @see #EXTRA_OFFLINE
- * @see #EXTRA_SUGGESTED
- */
- public static final String EXTRA_SUGGESTION_KEYWORDS
- = "android.service.media.extra.SUGGESTION_KEYWORDS";
-
final private String mRootId;
final private Bundle mExtras;
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardStatusView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardStatusView.java
index e1657c7..f8f4f2a 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardStatusView.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardStatusView.java
@@ -29,6 +29,7 @@
import android.util.Slog;
import android.util.TypedValue;
import android.view.View;
+import android.view.ViewGroup;
import android.widget.GridLayout;
import android.widget.TextClock;
import android.widget.TextView;
@@ -48,6 +49,7 @@
private TextClock mDateView;
private TextClock mClockView;
private TextView mOwnerInfo;
+ private ViewGroup mClockContainer;
private KeyguardUpdateMonitorCallback mInfoCallback = new KeyguardUpdateMonitorCallback() {
@@ -105,6 +107,7 @@
@Override
protected void onFinishInflate() {
super.onFinishInflate();
+ mClockContainer = (ViewGroup) findViewById(R.id.keyguard_clock_container);
mAlarmStatusView = (TextView) findViewById(R.id.alarm_status);
mDateView = (TextClock) findViewById(R.id.date_view);
mClockView = (TextClock) findViewById(R.id.clock_view);
@@ -167,6 +170,11 @@
}
}
+ public int getClockBottom() {
+ return mClockView.getBottom() +
+ ((MarginLayoutParams) mClockView.getLayoutParams()).bottomMargin;
+ }
+
public static String formatNextAlarm(Context context, AlarmManager.AlarmClockInfo info) {
if (info == null) {
return "";
@@ -260,4 +268,15 @@
cacheKey = key;
}
}
+
+ public void setDark(boolean dark) {
+ final int N = mClockContainer.getChildCount();
+ for (int i = 0; i < N; i++) {
+ View child = mClockContainer.getChildAt(i);
+ if (child == mClockView) {
+ continue;
+ }
+ child.setAlpha(dark ? 0 : 1);
+ }
+ }
}
diff --git a/packages/SettingsLib/res/values/arrays.xml b/packages/SettingsLib/res/values/arrays.xml
index 5c00985..cfb990e 100644
--- a/packages/SettingsLib/res/values/arrays.xml
+++ b/packages/SettingsLib/res/values/arrays.xml
@@ -105,10 +105,10 @@
<!-- Titles for Bluetooth Audio Codec selection preference. [CHAR LIMIT=40] -->
<string-array name="bluetooth_a2dp_codec_titles">
- <item>Default</item>
+ <item>Use System Selection (Default)</item>
<item>SBC</item>
<item>aptX</item>
- <item>aptX-HD</item>
+ <item>aptX HD</item>
<item>LDAC</item>
</string-array>
@@ -123,16 +123,16 @@
<!-- Summaries for Bluetooth Audio Codec selection preference. [CHAR LIMIT=40]-->
<string-array name="bluetooth_a2dp_codec_summaries" >
- <item>Default</item>
+ <item>Use System Selection (Default)</item>
<item>SBC</item>
<item>aptX</item>
- <item>aptX-HD</item>
+ <item>aptX HD</item>
<item>LDAC</item>
</string-array>
<!-- Titles for Bluetooth Audio Codec Sample Rate selection preference. [CHAR LIMIT=40] -->
<string-array name="bluetooth_a2dp_codec_sample_rate_titles">
- <item>Default</item>
+ <item>Use System Selection (Default)</item>
<item>44.1 kHz</item>
<item>48.0 kHz</item>
<item>88.2 kHz</item>
@@ -150,7 +150,7 @@
<!-- Summaries for Bluetooth Audio Codec Sample Rate selection preference. [CHAR LIMIT=40]-->
<string-array name="bluetooth_a2dp_codec_sample_rate_summaries" >
- <item>Default</item>
+ <item>Use System Selection (Default)</item>
<item>44.1 kHz</item>
<item>48.0 kHz</item>
<item>88.2 kHz</item>
@@ -159,7 +159,7 @@
<!-- Titles for Bluetooth Audio Codec Bits Per Sample selection preference. [CHAR LIMIT=40] -->
<string-array name="bluetooth_a2dp_codec_bits_per_sample_titles">
- <item>Default</item>
+ <item>Use System Selection (Default)</item>
<item>16 bits/sample</item>
<item>24 bits/sample</item>
<item>32 bits/sample</item>
@@ -175,7 +175,7 @@
<!-- Summaries for Bluetooth Audio Codec Bits Per Sample selection preference. [CHAR LIMIT=40]-->
<string-array name="bluetooth_a2dp_codec_bits_per_sample_summaries" >
- <item>Default</item>
+ <item>Use System Selection (Default)</item>
<item>16 bits/sample</item>
<item>24 bits/sample</item>
<item>32 bits/sample</item>
@@ -183,7 +183,7 @@
<!-- Titles for Bluetooth Audio Codec Channel Mode selection preference. [CHAR LIMIT=40] -->
<string-array name="bluetooth_a2dp_codec_channel_mode_titles">
- <item>Default</item>
+ <item>Use System Selection (Default)</item>
<item>Mono</item>
<item>Stereo</item>
</string-array>
@@ -197,16 +197,16 @@
<!-- Summaries for Bluetooth Audio Codec Channel Mode selection preference. [CHAR LIMIT=40]-->
<string-array name="bluetooth_a2dp_codec_channel_mode_summaries" >
- <item>Default</item>
+ <item>Use System Selection (Default)</item>
<item>Mono</item>
<item>Stereo</item>
</string-array>
- <!-- Titles for Bluetooth Audio Codec LDAC Playback Quality selection preference. [CHAR LIMIT=40] -->
+ <!-- Titles for Bluetooth Audio Codec LDAC Playback Quality selection preference. [CHAR LIMIT=70] -->
<string-array name="bluetooth_a2dp_codec_ldac_playback_quality_titles">
- <item>Sound quality preferred (990kbps/909kbps)</item>
- <item>Standard (660kbps/606kbps)</item>
- <item>Connection preferred (330kbps/303kbps)</item>
+ <item>Optimize for Audio Quality (990kbps/909kbps)</item>
+ <item>Balanced Audio And Connection Quality (660kbps/606kbps)</item>
+ <item>Optimize for Connection Quality (330kbps/303kbps)</item>
</string-array>
<!-- Values for Bluetooth Audio Codec LDAC Playback Quaility selection preference. -->
@@ -216,11 +216,11 @@
<item>1002</item>
</string-array>
- <!-- Summaries for Bluetooth Audio Codec LDAC Playback Quality selection preference. [CHAR LIMIT=40]-->
+ <!-- Summaries for Bluetooth Audio Codec LDAC Playback Quality selection preference. [CHAR LIMIT=70]-->
<string-array name="bluetooth_a2dp_codec_ldac_playback_quality_summaries" >
- <item>Sound quality preferred (990kbps/909kbps)</item>
- <item>Standard (660kbps/606kbps)</item>
- <item>Connection preferred (330kbps/303kbps)</item>
+ <item>Optimize for Audio Quality</item>
+ <item>Balanced Audio And Connection Quality</item>
+ <item>Optimize for Connection Quality</item>
</string-array>
<!-- Titles for logd limit size selection preference. [CHAR LIMIT=14] -->
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index 93bd5dc..a1b2bdf 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -430,28 +430,31 @@
<!-- UI debug setting: Select Bluetooth Audio Codec -->
<string name="bluetooth_select_a2dp_codec_type">Bluetooth Audio Codec</string>
- <!-- UI debug setting: Select Preferred Bluetooth A2DP Codec -->
- <string name="bluetooth_select_a2dp_codec_type_dialog_title">Select Preferred Bluetooth A2DP Codec</string>
+ <!-- UI debug setting: Select Bluetooth Audio Codec -->
+ <string name="bluetooth_select_a2dp_codec_type_dialog_title">Select Bluetooth Audio Codec</string>
<!-- UI debug setting: Select Bluetooth Audio Sample Rate -->
<string name="bluetooth_select_a2dp_codec_sample_rate">Bluetooth Audio Sample Rate</string>
- <!-- UI debug setting: Select Preferred Bluetooth A2DP Codec Sample Rate -->
- <string name="bluetooth_select_a2dp_codec_sample_rate_dialog_title">Select Preferred Bluetooth A2DP Codec Sample Rate</string>
+ <!-- UI debug setting: Select Bluetooth Audio Codec: Sample Rate -->
+ <string name="bluetooth_select_a2dp_codec_sample_rate_dialog_title">Select Bluetooth Audio Codec:\u000ASample Rate</string>
<!-- UI debug setting: Select Bluetooth Audio Bits Per Sample -->
<string name="bluetooth_select_a2dp_codec_bits_per_sample">Bluetooth Audio Bits Per Sample</string>
- <!-- UI debug setting: Select Preferred Bluetooth A2DP Codec Bits Per Sample -->
- <string name="bluetooth_select_a2dp_codec_bits_per_sample_dialog_title">Select Preferred Bluetooth A2DP Codec Bits Per Sample</string>
+ <!-- UI debug setting: Select Bluetooth Audio Codec: Bits Per Sample -->
+ <string name="bluetooth_select_a2dp_codec_bits_per_sample_dialog_title">Select Bluetooth Audio Codec:\u000ABits Per Sample</string>
<!-- UI debug setting: Select Bluetooth Audio Channel Mode -->
<string name="bluetooth_select_a2dp_codec_channel_mode">Bluetooth Audio Channel Mode</string>
- <!-- UI debug setting: Select Preferred Bluetooth A2DP Codec Channel Mode -->
- <string name="bluetooth_select_a2dp_codec_channel_mode_dialog_title">Select Preferred Bluetooth A2DP Codec Channel Mode</string>
+ <!-- UI debug setting: Select Bluetooth Audio Codec: Channel Mode -->
+ <string name="bluetooth_select_a2dp_codec_channel_mode_dialog_title">Select Bluetooth Audio Codec:\u000AChannel Mode</string>
<!-- UI debug setting: Select Bluetooth Audio LDAC Playback Quality -->
- <string name="bluetooth_select_a2dp_codec_ldac_playback_quality">Bluetooth Audio LDAC Playback Quality</string>
- <!-- UI debug setting: Select Preferred Bluetooth A2DP Codec LDAC Playback Quality -->
- <string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title">Select Preferred Bluetooth A2DP Codec LDAC Playback Quality</string>
+ <string name="bluetooth_select_a2dp_codec_ldac_playback_quality">Bluetooth Audio LDAC Codec: Playback Quality</string>
+ <!-- UI debug setting: Select Bluetooth Audio LDAC Codec: LDAC Playback Quality -->
+ <string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title">Select Bluetooth Audio LDAC Codec:\u000APlayback Quality</string>
+
+ <!-- [CHAR LIMIT=NONE] Label for displaying Bluetooth Audio Codec Parameters while streaming -->
+ <string name="bluetooth_select_a2dp_codec_streaming_label">Streaming: <xliff:g id="streaming_parameter">%1$s</xliff:g></string>
<!-- setting Checkbox summary whether to show options for wireless display certification -->
<string name="wifi_display_certification_summary">Show options for wireless display certification</string>
diff --git a/packages/SystemUI/res/drawable/ic_qs_nfc_disabled.xml b/packages/SystemUI/res/drawable/ic_qs_nfc_disabled.xml
new file mode 100644
index 0000000..558f3d0
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_qs_nfc_disabled.xml
@@ -0,0 +1,31 @@
+<!--
+ Copyright (C) 2016 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+
+ <path
+ android:pathData="M4 20h16V4H4v16z" />
+ <path
+ android:fillColor="#4DFFFFFF"
+ android:pathData="M20 2H4c-1.1 0-2 .9-2 2v16c0 1.1 .9 2 2 2h16c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm0
+18H4V4h16v16zM18 6h-5c-1.1 0-2 .9-2 2v2.28c-.6 .35 -1 .98-1 1.72 0 1.1 .9 2 2
+2s2-.9 2-2c0-.74-.4-1.38-1-1.72V8h3v8H8V8h2V6H6v12h12V6z" />
+ <path
+ android:pathData="M0 0h24v24H0z" />
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_qs_nfc_enabled.xml b/packages/SystemUI/res/drawable/ic_qs_nfc_enabled.xml
new file mode 100644
index 0000000..becb18a
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_qs_nfc_enabled.xml
@@ -0,0 +1,31 @@
+<!--
+ Copyright (C) 2016 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+
+ <path
+ android:pathData="M4 20h16V4H4v16z" />
+ <path
+ android:fillColor="#FFFFFFFF"
+ android:pathData="M20 2H4c-1.1 0-2 .9-2 2v16c0 1.1 .9 2 2 2h16c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm0
+18H4V4h16v16zM18 6h-5c-1.1 0-2 .9-2 2v2.28c-.6 .35 -1 .98-1 1.72 0 1.1 .9 2 2
+2s2-.9 2-2c0-.74-.4-1.38-1-1.72V8h3v8H8V8h2V6H6v12h12V6z" />
+ <path
+ android:pathData="M0 0h24v24H0z" />
+</vector>
diff --git a/packages/SystemUI/res/drawable/recents_grid_task_view_focus_frame_background.xml b/packages/SystemUI/res/drawable/recents_grid_task_view_focus_frame_background.xml
new file mode 100644
index 0000000..a85beb8
--- /dev/null
+++ b/packages/SystemUI/res/drawable/recents_grid_task_view_focus_frame_background.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
+ <solid android:color="#61FFFFFF" />
+ <corners android:radius="8dp"/>
+</shape>
\ No newline at end of file
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 78d211f..6ffdbcb 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -75,6 +75,9 @@
<!-- Height of a large notification in the status bar -->
<dimen name="notification_max_height">284dp</dimen>
+ <!-- Height of an ambient notification on ambient display -->
+ <dimen name="notification_ambient_height">400dp</dimen>
+
<!-- Height of a heads up notification in the status bar for legacy custom views -->
<dimen name="notification_max_heads_up_height_legacy">128dp</dimen>
diff --git a/packages/SystemUI/res/values/dimens_grid.xml b/packages/SystemUI/res/values/dimens_grid.xml
index 94ffdb1..0b9836ff 100644
--- a/packages/SystemUI/res/values/dimens_grid.xml
+++ b/packages/SystemUI/res/values/dimens_grid.xml
@@ -21,5 +21,6 @@
<dimen name="recents_grid_padding_task_view">20dp</dimen>
<dimen name="recents_grid_task_view_header_height">44dp</dimen>
<dimen name="recents_grid_task_view_header_button_padding">8dp</dimen>
+ <dimen name="recents_grid_task_view_focused_frame_thickness">8dp</dimen>
</resources>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 69515fa..a17430a 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -760,6 +760,12 @@
<string name="quick_settings_work_mode_label">Work mode</string>
<!-- QuickSettings: Label for the toggle to activate Night display (renamed "Night Light" with title caps). [CHAR LIMIT=20] -->
<string name="quick_settings_night_display_label">Night Light</string>
+ <!-- QuickSettings: NFC tile [CHAR LIMIT=NONE] -->
+ <string name="quick_settings_nfc_label">NFC</string>
+ <!-- QuickSettings: NFC (off) [CHAR LIMIT=NONE] -->
+ <string name="quick_settings_nfc_off">NFC is disabled</string>
+ <!-- QuickSettings: NFC (on) [CHAR LIMIT=NONE] -->
+ <string name="quick_settings_nfc_on">NFC is enabled</string>
<!-- Recents: The empty recents string. [CHAR LIMIT=NONE] -->
<string name="recents_empty_message">No recent items</string>
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java b/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java
index e081b53..00d2298 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java
@@ -37,7 +37,7 @@
interface Callback {
default void onNewNotifications() {}
- default void onBuzzBeepBlinked() {}
+ default void onNotificationHeadsUp() {}
default void onNotificationLight(boolean on) {}
default void onPowerSaveChanged(boolean active) {}
}
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
index 84b22ea..db5a392 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
@@ -309,7 +309,7 @@
private DozeHost.Callback mHostCallback = new DozeHost.Callback() {
@Override
- public void onBuzzBeepBlinked() {
+ public void onNotificationHeadsUp() {
onNotification();
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/NfcTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/NfcTile.java
new file mode 100644
index 0000000..967c922
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/NfcTile.java
@@ -0,0 +1,136 @@
+/*
+ * Copyright (c) 2016, The Android Open Source Project
+ * Contributed by the Paranoid Android Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.tiles;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.PackageManager;
+import android.graphics.drawable.Drawable;
+import android.nfc.NfcAdapter;
+import android.provider.Settings;
+import android.widget.Switch;
+
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.systemui.R;
+import com.android.systemui.qs.QSTile;
+
+/** Quick settings tile: Enable/Disable NFC **/
+public class NfcTile extends QSTile<QSTile.BooleanState> {
+
+ private NfcAdapter mAdapter;
+
+ private boolean mListening;
+
+ public NfcTile(Host host) {
+ super(host);
+ }
+
+ @Override
+ public BooleanState newTileState() {
+ return new BooleanState();
+ }
+
+ @Override
+ public void setListening(boolean listening) {
+ mListening = listening;
+ if (mListening) {
+ mContext.registerReceiver(mNfcReceiver,
+ new IntentFilter(NfcAdapter.ACTION_ADAPTER_STATE_CHANGED));
+ if (mAdapter == null) {
+ try {
+ mAdapter = NfcAdapter.getNfcAdapter(mContext);
+ } catch (UnsupportedOperationException e) {
+ mAdapter = null;
+ }
+ }
+ } else {
+ mContext.unregisterReceiver(mNfcReceiver);
+ }
+ }
+
+ @Override
+ public boolean isAvailable() {
+ return mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_NFC);
+ }
+
+ @Override
+ protected void handleUserSwitch(int newUserId) {
+ }
+
+ @Override
+ public Intent getLongClickIntent() {
+ return new Intent(Settings.ACTION_NFC_SETTINGS);
+ }
+
+ @Override
+ protected void handleClick() {
+ if (mAdapter == null) return;
+ MetricsLogger.action(mContext, getMetricsCategory(), !mState.value);
+ if (!mAdapter.isEnabled()) {
+ mAdapter.enable();
+ } else {
+ mAdapter.disable();
+ }
+ }
+
+ @Override
+ protected void handleSecondaryClick() {
+ handleClick();
+ }
+
+ @Override
+ public CharSequence getTileLabel() {
+ return mContext.getString(R.string.quick_settings_nfc_label);
+ }
+
+ @Override
+ protected void handleUpdateState(BooleanState state, Object arg) {
+ final Drawable mEnable = mContext.getDrawable(R.drawable.ic_qs_nfc_enabled);
+ final Drawable mDisable = mContext.getDrawable(R.drawable.ic_qs_nfc_disabled);
+ state.value = mAdapter == null ? false : mAdapter.isEnabled();
+ state.label = mContext.getString(R.string.quick_settings_nfc_label);
+ state.icon = new DrawableIcon(state.value ? mEnable : mDisable);
+ state.minimalAccessibilityClassName = state.expandedAccessibilityClassName
+ = Switch.class.getName();
+ state.contentDescription = state.label;
+ }
+
+ @Override
+ public int getMetricsCategory() {
+ return MetricsEvent.QS_NFC;
+ }
+
+ @Override
+ protected String composeChangeAnnouncement() {
+ if (mState.value) {
+ return mContext.getString(R.string.quick_settings_nfc_on);
+ } else {
+ return mContext.getString(R.string.quick_settings_nfc_off);
+ }
+ }
+
+ private BroadcastReceiver mNfcReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ refreshState();
+ }
+ };
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivityLaunchState.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivityLaunchState.java
index 914035b..a7f6b70 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivityLaunchState.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivityLaunchState.java
@@ -50,7 +50,7 @@
/**
* Returns the task to focus given the current launch state.
*/
- public int getInitialFocusTaskIndex(int numTasks) {
+ public int getInitialFocusTaskIndex(int numTasks, boolean useGridLayout) {
RecentsDebugFlags debugFlags = Recents.getDebugFlags();
RecentsActivityLaunchState launchState = Recents.getConfiguration().getLaunchState();
if (launchedFromApp) {
@@ -66,6 +66,11 @@
return numTasks - 1;
}
+ if (useGridLayout) {
+ // If coming from another app to the grid layout, focus the front most task
+ return numTasks - 1;
+ }
+
// If coming from another app, focus the next task
return Math.max(0, numTasks - 2);
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java b/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java
index 178cb9f..9b25ef8 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java
@@ -247,6 +247,9 @@
*/
public static class DockState implements DropTarget {
+ public static final int DOCK_AREA_BG_COLOR = 0xFFffffff;
+ public static final int DOCK_AREA_GRID_BG_COLOR = 0xFF000000;
+
// The rotation to apply to the hint text
@Retention(RetentionPolicy.SOURCE)
@IntDef({HORIZONTAL, VERTICAL})
@@ -319,7 +322,8 @@
private ViewState(int areaAlpha, int hintAlpha, @TextOrientation int hintOrientation,
int hintTextResId) {
dockAreaAlpha = areaAlpha;
- dockAreaOverlay = new ColorDrawable(0xFFffffff);
+ dockAreaOverlay = new ColorDrawable(Recents.getConfiguration().isGridEnabled
+ ? DOCK_AREA_GRID_BG_COLOR : DOCK_AREA_BG_COLOR);
dockAreaOverlay.setAlpha(0);
hintTextAlpha = hintAlpha;
hintTextOrientation = hintOrientation;
@@ -435,7 +439,7 @@
* @param createMode used to pass to ActivityManager to dock the task
* @param touchArea the area in which touch will initiate this dock state
* @param dockArea the visible dock area
- * @param expandedTouchDockArea the areain which touch will continue to dock after entering
+ * @param expandedTouchDockArea the area in which touch will continue to dock after entering
* the initial touch area. This is also the new dock area to
* draw.
*/
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackAnimationHelper.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackAnimationHelper.java
index ed86d4c..a2ee4c5 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackAnimationHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackAnimationHelper.java
@@ -105,6 +105,7 @@
private static final Interpolator ENTER_WHILE_DOCKING_INTERPOLATOR =
Interpolators.LINEAR_OUT_SLOW_IN;
+ private final int mEnterAndExitFromHomeTranslationOffset;
private TaskStackView mStackView;
private TaskViewTransform mTmpTransform = new TaskViewTransform();
@@ -113,6 +114,8 @@
public TaskStackAnimationHelper(Context context, TaskStackView stackView) {
mStackView = stackView;
+ mEnterAndExitFromHomeTranslationOffset = Recents.getConfiguration().isGridEnabled
+ ? 0 : DOUBLE_FRAME_OFFSET_MS;
}
/**
@@ -260,7 +263,7 @@
AnimationProps taskAnimation = new AnimationProps()
.setInitialPlayTime(AnimationProps.BOUNDS,
Math.min(ENTER_EXIT_NUM_ANIMATING_TASKS, taskIndexFromFront) *
- DOUBLE_FRAME_OFFSET_MS)
+ mEnterAndExitFromHomeTranslationOffset)
.setStartDelay(AnimationProps.ALPHA,
Math.min(ENTER_EXIT_NUM_ANIMATING_TASKS, taskIndexFromFront) *
FRAME_OFFSET_MS)
@@ -321,7 +324,7 @@
AnimationProps taskAnimation;
if (animated) {
int delay = Math.min(ENTER_EXIT_NUM_ANIMATING_TASKS , taskIndexFromFront) *
- DOUBLE_FRAME_OFFSET_MS;
+ mEnterAndExitFromHomeTranslationOffset;
taskAnimation = new AnimationProps()
.setStartDelay(AnimationProps.BOUNDS, delay)
.setDuration(AnimationProps.BOUNDS, EXIT_TO_HOME_TRANSLATION_DURATION)
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
index b7686ce..fc2550a 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
@@ -70,6 +70,7 @@
import com.android.systemui.recents.events.activity.MultiWindowStateChangedEvent;
import com.android.systemui.recents.events.activity.PackagesChangedEvent;
import com.android.systemui.recents.events.activity.ShowStackActionButtonEvent;
+import com.android.systemui.recents.events.component.RecentsVisibilityChangedEvent;
import com.android.systemui.recents.events.ui.AllTaskViewsDismissedEvent;
import com.android.systemui.recents.events.ui.DeleteTaskDataEvent;
import com.android.systemui.recents.events.ui.DismissAllTaskViewsEvent;
@@ -93,6 +94,7 @@
import com.android.systemui.recents.model.TaskStack;
import com.android.systemui.recents.views.grid.GridTaskView;
+import com.android.systemui.recents.views.grid.TaskViewFocusFrame;
import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -206,6 +208,10 @@
private int mLastWidth;
private int mLastHeight;
+ // We keep track of the task view focused by user interaction and draw a frame around it in the
+ // grid layout.
+ private TaskViewFocusFrame mTaskViewFocusFrame;
+
// A convenience update listener to request updating clipping of tasks
private ValueAnimator.AnimatorUpdateListener mRequestUpdateClippingListener =
new ValueAnimator.AnimatorUpdateListener() {
@@ -265,6 +271,14 @@
mDisplayOrientation = Utilities.getAppConfiguration(mContext).orientation;
mDisplayRect = ssp.getDisplayRect();
+ // Create a frame to draw around the focused task view
+ if (Recents.getConfiguration().isGridEnabled) {
+ mTaskViewFocusFrame = new TaskViewFocusFrame(mContext, this,
+ mLayoutAlgorithm.mTaskGridLayoutAlgorithm);
+ addView(mTaskViewFocusFrame);
+ getViewTreeObserver().addOnGlobalFocusChangeListener(mTaskViewFocusFrame);
+ }
+
int taskBarDismissDozeDelaySeconds = getResources().getInteger(
R.integer.recents_task_bar_dismiss_delay_seconds);
mUIDozeTrigger = new DozeTrigger(taskBarDismissDozeDelaySeconds, new Runnable() {
@@ -878,7 +892,7 @@
*
* @return whether or not the stack will scroll as a part of this focus change
*/
- private boolean setFocusedTask(int taskIndex, boolean scrollToTask,
+ public boolean setFocusedTask(int taskIndex, boolean scrollToTask,
final boolean requestViewFocus) {
return setFocusedTask(taskIndex, scrollToTask, requestViewFocus, 0);
}
@@ -888,7 +902,7 @@
*
* @return whether or not the stack will scroll as a part of this focus change
*/
- private boolean setFocusedTask(int focusTaskIndex, boolean scrollToTask,
+ public boolean setFocusedTask(int focusTaskIndex, boolean scrollToTask,
boolean requestViewFocus, int timerIndicatorDuration) {
// Find the next task to focus
int newFocusedTaskIndex = mStack.getTaskCount() > 0 ?
@@ -940,6 +954,10 @@
newFocusedTaskView.setFocusedState(true, requestViewFocus);
}
}
+ // Any time a task view gets the focus, we move the focus frame around it.
+ if (mTaskViewFocusFrame != null) {
+ mTaskViewFocusFrame.moveGridTaskViewFocus(getChildViewForTask(newFocusedTask));
+ }
}
return willScroll;
}
@@ -1005,20 +1023,28 @@
float stackScroll = mStackScroller.getStackScroll();
ArrayList<Task> tasks = mStack.getStackTasks();
int taskCount = tasks.size();
- if (forward) {
- // Walk backwards and focus the next task smaller than the current stack scroll
- for (newIndex = taskCount - 1; newIndex >= 0; newIndex--) {
- float taskP = mLayoutAlgorithm.getStackScrollForTask(tasks.get(newIndex));
- if (Float.compare(taskP, stackScroll) <= 0) {
- break;
- }
- }
+ if (useGridLayout()) {
+ // For the grid layout, we directly set focus to the most recently used task
+ // no matter we're moving forwards or backwards.
+ newIndex = taskCount - 1;
} else {
- // Walk forwards and focus the next task larger than the current stack scroll
- for (newIndex = 0; newIndex < taskCount; newIndex++) {
- float taskP = mLayoutAlgorithm.getStackScrollForTask(tasks.get(newIndex));
- if (Float.compare(taskP, stackScroll) >= 0) {
- break;
+ // For the grid layout we pick a proper task to focus, according to the current
+ // stack scroll.
+ if (forward) {
+ // Walk backwards and focus the next task smaller than the current stack scroll
+ for (newIndex = taskCount - 1; newIndex >= 0; newIndex--) {
+ float taskP = mLayoutAlgorithm.getStackScrollForTask(tasks.get(newIndex));
+ if (Float.compare(taskP, stackScroll) <= 0) {
+ break;
+ }
+ }
+ } else {
+ // Walk forwards and focus the next task larger than the current stack scroll
+ for (newIndex = 0; newIndex < taskCount; newIndex++) {
+ float taskP = mLayoutAlgorithm.getStackScrollForTask(tasks.get(newIndex));
+ if (Float.compare(taskP, stackScroll) >= 0) {
+ break;
+ }
}
}
}
@@ -1037,20 +1063,23 @@
/**
* Resets the focused task.
*/
- void resetFocusedTask(Task task) {
+ public void resetFocusedTask(Task task) {
if (task != null) {
TaskView tv = getChildViewForTask(task);
if (tv != null) {
tv.setFocusedState(false, false /* requestViewFocus */);
}
}
+ if (mTaskViewFocusFrame != null) {
+ mTaskViewFocusFrame.moveGridTaskViewFocus(null);
+ }
mFocusedTask = null;
}
/**
* Returns the focused task.
*/
- Task getFocusedTask() {
+ public Task getFocusedTask() {
return mFocusedTask;
}
@@ -1253,6 +1282,9 @@
for (int i = 0; i < taskViewCount; i++) {
measureTaskView(mTmpTaskViews.get(i));
}
+ if (mTaskViewFocusFrame != null) {
+ mTaskViewFocusFrame.measure();
+ }
setMeasuredDimension(width, height);
mLastWidth = width;
@@ -1287,6 +1319,9 @@
for (int i = 0; i < taskViewCount; i++) {
layoutTaskView(changed, mTmpTaskViews.get(i));
}
+ if (mTaskViewFocusFrame != null) {
+ mTaskViewFocusFrame.layout();
+ }
if (changed) {
if (mStackScroller.isScrollOutOfBounds()) {
@@ -1339,10 +1374,19 @@
// until after the enter-animation
RecentsConfiguration config = Recents.getConfiguration();
RecentsActivityLaunchState launchState = config.getLaunchState();
- int focusedTaskIndex = launchState.getInitialFocusTaskIndex(mStack.getTaskCount());
- if (focusedTaskIndex != -1) {
- setFocusedTask(focusedTaskIndex, false /* scrollToTask */,
- false /* requestViewFocus */);
+
+ // We set the initial focused task view iff the following conditions are satisfied:
+ // 1. Recents is showing task views in stack layout.
+ // 2. Recents is launched with ALT + TAB.
+ boolean setFocusOnFirstLayout = !useGridLayout() ||
+ Recents.getConfiguration().getLaunchState().launchedWithAltTab;
+ if (setFocusOnFirstLayout) {
+ int focusedTaskIndex = launchState.getInitialFocusTaskIndex(mStack.getTaskCount(),
+ useGridLayout());
+ if (focusedTaskIndex != -1) {
+ setFocusedTask(focusedTaskIndex, false /* scrollToTask */,
+ false /* requestViewFocus */);
+ }
}
updateStackActionButtonVisibility();
}
@@ -1443,6 +1487,11 @@
// Remove the task from the ignored set
removeIgnoreTask(removedTask);
+ // Resize the grid layout task view focus frame
+ if (mTaskViewFocusFrame != null) {
+ mTaskViewFocusFrame.resize();
+ }
+
// If requested, relayout with the given animation
if (animation != null) {
updateLayoutAlgorithm(true /* boundScroll */);
@@ -1740,10 +1789,18 @@
int taskViewExitToHomeDuration = TaskStackAnimationHelper.EXIT_TO_HOME_TRANSLATION_DURATION;
animateFreeformWorkspaceBackgroundAlpha(0, new AnimationProps(taskViewExitToHomeDuration,
Interpolators.FAST_OUT_SLOW_IN));
+
+ // Dismiss the grid task view focus frame
+ if (mTaskViewFocusFrame != null) {
+ mTaskViewFocusFrame.moveGridTaskViewFocus(null);
+ }
}
public final void onBusEvent(DismissFocusedTaskViewEvent event) {
if (mFocusedTask != null) {
+ if (mTaskViewFocusFrame != null) {
+ mTaskViewFocusFrame.moveGridTaskViewFocus(null);
+ }
TaskView tv = getChildViewForTask(mFocusedTask);
if (tv != null) {
tv.dismissTask();
@@ -2073,6 +2130,12 @@
mResetToInitialStateWhenResized = true;
}
+ public final void onBusEvent(RecentsVisibilityChangedEvent event) {
+ if (!event.visible && mTaskViewFocusFrame != null) {
+ mTaskViewFocusFrame.moveGridTaskViewFocus(null);
+ }
+ }
+
public void reloadOnConfigurationChange() {
mStableLayoutAlgorithm.reloadOnConfigurationChange(getContext());
mLayoutAlgorithm.reloadOnConfigurationChange(getContext());
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java
index 33fa3b0..5817e92 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java
@@ -342,8 +342,9 @@
mSv.invalidate();
}
- // Reset the focused task after the user has scrolled
- if (!mSv.mTouchExplorationEnabled) {
+ // Reset the focused task after the user has scrolled, but we have no scrolling
+ // in grid layout and therefore we don't want to reset the focus there.
+ if (!mSv.mTouchExplorationEnabled && !mSv.useGridLayout()) {
mSv.resetFocusedTask(mSv.getFocusedTask());
}
} else if (mActiveTaskView == null) {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/grid/TaskGridLayoutAlgorithm.java b/packages/SystemUI/src/com/android/systemui/recents/views/grid/TaskGridLayoutAlgorithm.java
index 6fc4ad7..70536b1 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/grid/TaskGridLayoutAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/grid/TaskGridLayoutAlgorithm.java
@@ -51,6 +51,9 @@
private float mAppAspectRatio;
private Rect mSystemInsets = new Rect();
+ /** The thickness of the focused task view frame. */
+ private int mFocusedFrameThickness;
+
/**
* When the amount of tasks is determined, the size and position of every task view can be
* decided. Each instance of TaskGridRectInfo store the task view information for a certain
@@ -137,6 +140,9 @@
public void reloadOnConfigurationChange(Context context) {
Resources res = context.getResources();
mPaddingTaskView = res.getDimensionPixelSize(R.dimen.recents_grid_padding_task_view);
+ mFocusedFrameThickness = res.getDimensionPixelSize(
+ R.dimen.recents_grid_task_view_focused_frame_thickness);
+
mTaskGridRect = new Rect();
mTitleBarHeight = res.getDimensionPixelSize(R.dimen.recents_grid_task_view_header_height);
@@ -159,6 +165,10 @@
*/
public TaskViewTransform getTransform(int taskIndex, int taskCount,
TaskViewTransform transformOut, TaskStackLayoutAlgorithm stackLayout) {
+ if (taskCount == 0) {
+ transformOut.reset();
+ return transformOut;
+ }
TaskGridRectInfo gridInfo = mTaskGridRectInfoList[taskCount - 1];
mTaskGridRect.set(gridInfo.size);
@@ -174,7 +184,7 @@
// We also need to invert the index in order to display the most recent tasks first.
int taskLayoutIndex = taskCount - taskIndex - 1;
- boolean isTaskViewVisible = (taskLayoutIndex < MAX_LAYOUT_TASK_COUNT);
+ boolean isTaskViewVisible = taskLayoutIndex < MAX_LAYOUT_TASK_COUNT;
// Fill out the transform
transformOut.scale = 1f;
@@ -223,7 +233,18 @@
return buttonRect;
}
+ public void updateTaskGridRect(int taskCount) {
+ if (taskCount > 0) {
+ TaskGridRectInfo gridInfo = mTaskGridRectInfoList[taskCount - 1];
+ mTaskGridRect.set(gridInfo.size);
+ }
+ }
+
public Rect getTaskGridRect() {
return mTaskGridRect;
}
+
+ public int getFocusFrameThickness() {
+ return mFocusedFrameThickness;
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/grid/TaskViewFocusFrame.java b/packages/SystemUI/src/com/android/systemui/recents/views/grid/TaskViewFocusFrame.java
new file mode 100644
index 0000000..86ed583
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/grid/TaskViewFocusFrame.java
@@ -0,0 +1,141 @@
+/*
+ * 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.views.grid;
+
+import android.content.Context;
+import android.graphics.Rect;
+import android.util.AttributeSet;
+import android.view.View;
+
+import android.view.ViewTreeObserver.OnGlobalFocusChangeListener;
+import com.android.systemui.R;
+import com.android.systemui.recents.model.TaskStack;
+import com.android.systemui.recents.views.TaskStackView;
+
+public class TaskViewFocusFrame extends View implements OnGlobalFocusChangeListener {
+
+ private TaskStackView mSv;
+ private TaskGridLayoutAlgorithm mTaskGridLayoutAlgorithm;
+ public TaskViewFocusFrame(Context context) {
+ this(context, null);
+ }
+
+ public TaskViewFocusFrame(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public TaskViewFocusFrame(Context context, AttributeSet attrs, int defStyleAttr) {
+ this(context, attrs, defStyleAttr, 0);
+ }
+
+ public TaskViewFocusFrame(Context context, AttributeSet attrs, int defStyleAttr,
+ int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+ setBackground(mContext.getDrawable(
+ R.drawable.recents_grid_task_view_focus_frame_background));
+ setFocusable(false);
+ hide();
+ }
+
+ public TaskViewFocusFrame(Context context, TaskStackView stackView,
+ TaskGridLayoutAlgorithm taskGridLayoutAlgorithm) {
+ this(context);
+ mSv = stackView;
+ mTaskGridLayoutAlgorithm = taskGridLayoutAlgorithm;
+ }
+
+ /**
+ * Measure the width and height of the focus frame according to the current grid task view size.
+ */
+ public void measure() {
+ int thickness = mTaskGridLayoutAlgorithm.getFocusFrameThickness();
+ Rect rect = mTaskGridLayoutAlgorithm.getTaskGridRect();
+ measure(
+ MeasureSpec.makeMeasureSpec(rect.width() + thickness * 2, MeasureSpec.EXACTLY),
+ MeasureSpec.makeMeasureSpec(rect.height() + thickness * 2, MeasureSpec.EXACTLY));
+ }
+
+ /**
+ * Layout the focus frame with its size.
+ */
+ public void layout() {
+ layout(0, 0, getMeasuredWidth(), getMeasuredHeight());
+ }
+
+ /**
+ * Update the current size of grid task view and the focus frame.
+ */
+ public void resize() {
+ if (mSv.useGridLayout()) {
+ mTaskGridLayoutAlgorithm.updateTaskGridRect(mSv.getStack().getTaskCount());
+ measure();
+ requestLayout();
+ }
+ }
+
+ /**
+ * Move the task view focus frame to surround the newly focused view. If it's {@code null} or
+ * it's not an instance of GridTaskView, we hide the focus frame.
+ * @param newFocus The newly focused view.
+ */
+ public void moveGridTaskViewFocus(View newFocus) {
+ if (mSv.useGridLayout()) {
+ // The frame only shows up in the grid layout. It shouldn't show up in the stack
+ // layout including when we're in the split screen.
+ if (newFocus instanceof GridTaskView) {
+ // If the focus goes to a GridTaskView, we show the frame and layout it.
+ int[] location = new int[2];
+ newFocus.getLocationInWindow(location);
+ int thickness = mTaskGridLayoutAlgorithm.getFocusFrameThickness();
+ setTranslationX(location[0] - thickness);
+ setTranslationY(location[1] - thickness);
+ show();
+ } else {
+ // If focus goes to other views, we hide the frame.
+ hide();
+ }
+ }
+ }
+
+ @Override
+ public void onGlobalFocusChanged(View oldFocus, View newFocus) {
+ if (!mSv.useGridLayout()) {
+ return;
+ }
+ if (newFocus == null) {
+ // We're going to touch mode, unset the focus.
+ moveGridTaskViewFocus(null);
+ return;
+ }
+ if (oldFocus == null) {
+ // We're returning from touch mode, set the focus to the previously focused task.
+ final TaskStack stack = mSv.getStack();
+ final int taskCount = stack.getTaskCount();
+ final int k = stack.indexOfStackTask(mSv.getFocusedTask());
+ final int taskIndexToFocus = k == -1 ? (taskCount - 1) : (k % taskCount);
+ mSv.setFocusedTask(taskIndexToFocus, false, true);
+ }
+ }
+
+ private void show() {
+ setAlpha(1f);
+ }
+
+ private void hide() {
+ setAlpha(0f);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
index 19034f8..faf143e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
@@ -1132,6 +1132,11 @@
// Post to ensure the the guts are properly laid out.
guts.post(new Runnable() {
public void run() {
+ if (row.getWindowToken() == null) {
+ Log.e(TAG, "Trying to show notification guts, but not attached to "
+ + "window");
+ return;
+ }
dismissPopups(-1 /* x */, -1 /* y */, false /* resetGear */,
false /* animate */);
guts.setVisibility(View.VISIBLE);
@@ -1519,6 +1524,7 @@
final RemoteViews bigContentView = entry.cachedBigContentView;
final RemoteViews headsUpContentView = entry.cachedHeadsUpContentView;
final RemoteViews publicContentView = entry.cachedPublicContentView;
+ final RemoteViews ambientContentView = entry.cachedAmbientContentView;
if (contentView == null) {
Log.v(TAG, "no contentView for: " + sbn.getNotification());
@@ -1599,6 +1605,7 @@
View bigContentViewLocal = null;
View headsUpContentViewLocal = null;
View publicViewLocal = null;
+ View ambientViewLocal = null;
try {
contentViewLocal = contentView.apply(
sbn.getPackageContext(mContext),
@@ -1621,6 +1628,11 @@
sbn.getPackageContext(mContext),
contentContainerPublic, mOnClickHandler);
}
+ if (ambientContentView != null) {
+ ambientViewLocal = ambientContentView.apply(
+ sbn.getPackageContext(mContext),
+ contentContainer, mOnClickHandler);
+ }
if (contentViewLocal != null) {
contentViewLocal.setIsRootNamespace(true);
@@ -1638,6 +1650,11 @@
publicViewLocal.setIsRootNamespace(true);
contentContainerPublic.setContractedChild(publicViewLocal);
}
+
+ if (ambientViewLocal != null) {
+ ambientViewLocal.setIsRootNamespace(true);
+ contentContainer.setAmbientChild(ambientViewLocal);
+ }
}
catch (RuntimeException e) {
final String ident = sbn.getPackageName() + "/0x" + Integer.toHexString(sbn.getId());
@@ -2134,6 +2151,7 @@
row.setOnKeyguard(false);
row.setSystemExpanded(visibleNotifications == 0 && !childNotification);
}
+ entry.row.setShowAmbient(isDozing());
int userId = entry.notification.getUserId();
boolean suppressedSummary = mGroupManager.isSummaryOfSuppressedGroup(
entry.notification) && !entry.row.isRemoved();
@@ -2171,6 +2189,10 @@
mStackScroller.changeViewPosition(mNotificationShelf, mStackScroller.getChildCount() - 3);
}
+ public boolean isDozing() {
+ return false;
+ }
+
public boolean shouldShowOnKeyguard(StatusBarNotification sbn) {
return mShowLockscreenNotifications && !mNotificationData.isAmbient(sbn.getKey());
}
@@ -2383,7 +2405,7 @@
Log.d(TAG, "failed to query dream manager", e);
}
- if (!inUse) {
+ if (!inUse && !isDozing()) {
if (DEBUG) {
Log.d(TAG, "No peeking: not in use: " + sbn.getKey());
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
index 173a110..93c48f8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
@@ -74,6 +74,7 @@
private int mMaxHeadsUpHeight;
private int mNotificationMinHeight;
private int mNotificationMaxHeight;
+ private int mNotificationAmbientHeight;
private int mIncreasedPaddingBetweenElements;
/** Does this row contain layouts that can adapt to row expansion */
@@ -197,6 +198,7 @@
private float mContentTransformationAmount;
private boolean mIconsVisible = true;
private boolean mAboveShelf;
+ private boolean mShowAmbient;
private boolean mIsLastChild;
private Runnable mOnDismissRunnable;
@@ -326,7 +328,8 @@
!= com.android.internal.R.id.status_bar_latest_event_content;
int headsUpheight = headsUpCustom && beforeN ? mMaxHeadsUpHeightLegacy
: mMaxHeadsUpHeight;
- layout.setHeights(minHeight, headsUpheight, mNotificationMaxHeight);
+ layout.setHeights(minHeight, headsUpheight, mNotificationMaxHeight,
+ mNotificationAmbientHeight);
}
public StatusBarNotification getStatusBarNotification() {
@@ -954,6 +957,7 @@
mNotificationMinHeightLegacy = getFontScaledHeight(R.dimen.notification_min_height_legacy);
mNotificationMinHeight = getFontScaledHeight(R.dimen.notification_min_height);
mNotificationMaxHeight = getFontScaledHeight(R.dimen.notification_max_height);
+ mNotificationAmbientHeight = getFontScaledHeight(R.dimen.notification_ambient_height);
mMaxHeadsUpHeightLegacy = getFontScaledHeight(
R.dimen.notification_max_heads_up_height_legacy);
mMaxHeadsUpHeight = getFontScaledHeight(R.dimen.notification_max_heads_up_height);
@@ -1353,6 +1357,8 @@
return mGuts.getHeight();
} else if ((isChildInGroup() && !isGroupExpanded())) {
return mPrivateLayout.getMinHeight();
+ } else if (mShowAmbient) {
+ return getAmbientHeight();
} else if (mSensitive && mHideSensitiveForIntrinsicHeight) {
return getMinHeight();
} else if (mIsSummaryWithChildren && !mOnKeyguard) {
@@ -1683,6 +1689,13 @@
return showingLayout.getMinHeight();
}
+ private int getAmbientHeight() {
+ NotificationContentView showingLayout = getShowingLayout();
+ return showingLayout.getAmbientChild() != null
+ ? showingLayout.getAmbientChild().getHeight()
+ : getCollapsedHeight();
+ }
+
@Override
public int getCollapsedHeight() {
if (mIsSummaryWithChildren && !mShowingPublic) {
@@ -1879,6 +1892,13 @@
return mIsPinned || mHeadsupDisappearRunning || (mIsHeadsUp && mAboveShelf);
}
+ public void setShowAmbient(boolean showAmbient) {
+ if (showAmbient != mShowAmbient) {
+ mShowAmbient = showAmbient;
+ notifyHeightChanged(false /* needsAnimation */);
+ }
+ }
+
public void setAboveShelf(boolean aboveShelf) {
mAboveShelf = aboveShelf;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
index 9ce2115..b45cde8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
@@ -31,6 +31,7 @@
import android.widget.FrameLayout;
import android.widget.ImageView;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.NotificationColorUtil;
import com.android.systemui.R;
import com.android.systemui.statusbar.notification.HybridNotificationView;
@@ -52,6 +53,7 @@
private static final int VISIBLE_TYPE_EXPANDED = 1;
private static final int VISIBLE_TYPE_HEADSUP = 2;
private static final int VISIBLE_TYPE_SINGLELINE = 3;
+ private static final int VISIBLE_TYPE_AMBIENT = 4;
public static final int UNDEFINED = -1;
private final Rect mClipBounds = new Rect();
@@ -62,6 +64,7 @@
private View mExpandedChild;
private View mHeadsUpChild;
private HybridNotificationView mSingleLineView;
+ private View mAmbientChild;
private RemoteInputView mExpandedRemoteInput;
private RemoteInputView mHeadsUpRemoteInput;
@@ -69,6 +72,7 @@
private NotificationViewWrapper mContractedWrapper;
private NotificationViewWrapper mExpandedWrapper;
private NotificationViewWrapper mHeadsUpWrapper;
+ private NotificationViewWrapper mAmbientWrapper;
private HybridGroupManager mHybridGroupManager;
private int mClipTopAmount;
private int mContentHeight;
@@ -81,6 +85,7 @@
private int mSmallHeight;
private int mHeadsUpHeight;
private int mNotificationMaxHeight;
+ private int mNotificationAmbientHeight;
private StatusBarNotification mStatusBarNotification;
private NotificationGroupManager mGroupManager;
private RemoteInputController mRemoteInputController;
@@ -136,10 +141,12 @@
reset();
}
- public void setHeights(int smallHeight, int headsUpMaxHeight, int maxHeight) {
+ public void setHeights(int smallHeight, int headsUpMaxHeight, int maxHeight,
+ int ambientHeight) {
mSmallHeight = smallHeight;
mHeadsUpHeight = headsUpMaxHeight;
mNotificationMaxHeight = maxHeight;
+ mNotificationAmbientHeight = ambientHeight;
}
@Override
@@ -215,6 +222,17 @@
MeasureSpec.makeMeasureSpec(maxSize, MeasureSpec.AT_MOST));
maxChildHeight = Math.max(maxChildHeight, mSingleLineView.getMeasuredHeight());
}
+ if (mAmbientChild != null) {
+ int size = Math.min(maxSize, mNotificationAmbientHeight);
+ ViewGroup.LayoutParams layoutParams = mAmbientChild.getLayoutParams();
+ if (layoutParams.height >= 0) {
+ // An actual height is set
+ size = Math.min(size, layoutParams.height);
+ }
+ mAmbientChild.measure(widthMeasureSpec,
+ MeasureSpec.makeMeasureSpec(size, MeasureSpec.AT_MOST));
+ maxChildHeight = Math.max(maxChildHeight, mAmbientChild.getMeasuredHeight());
+ }
int ownHeight = Math.min(maxChildHeight, maxSize);
setMeasuredDimension(width, ownHeight);
}
@@ -339,6 +357,10 @@
return mHeadsUpChild;
}
+ public View getAmbientChild() {
+ return mAmbientChild;
+ }
+
public void setContractedChild(View child) {
if (mContractedChild != null) {
mContractedChild.animate().cancel();
@@ -373,6 +395,17 @@
mContainingNotification);
}
+ public void setAmbientChild(View child) {
+ if (mAmbientChild != null) {
+ mAmbientChild.animate().cancel();
+ removeView(mAmbientChild);
+ }
+ addView(child);
+ mAmbientChild = child;
+ mAmbientWrapper = NotificationViewWrapper.wrap(getContext(), child,
+ mContainingNotification);
+ }
+
@Override
protected void onVisibilityChanged(View changedView, int visibility) {
super.onVisibilityChanged(changedView, visibility);
@@ -447,6 +480,11 @@
com.android.internal.R.dimen.notification_action_list_height);
}
+ if (isVisibleOrTransitioning(VISIBLE_TYPE_AMBIENT)) {
+ return mContractedChild.getHeight() + mContext.getResources().getDimensionPixelSize(
+ com.android.internal.R.dimen.notification_action_list_height);
+ }
+
// Transition between heads-up & expanded, or pinned.
if (mHeadsUpChild != null && mExpandedChild != null) {
boolean transitioningBetweenHunAndExpanded =
@@ -651,39 +689,26 @@
}
private void forceUpdateVisibilities() {
- boolean contractedVisible = mVisibleType == VISIBLE_TYPE_CONTRACTED
- || mTransformationStartVisibleType == VISIBLE_TYPE_CONTRACTED;
- boolean expandedVisible = mVisibleType == VISIBLE_TYPE_EXPANDED
- || mTransformationStartVisibleType == VISIBLE_TYPE_EXPANDED;
- boolean headsUpVisible = mVisibleType == VISIBLE_TYPE_HEADSUP
- || mTransformationStartVisibleType == VISIBLE_TYPE_HEADSUP;
- boolean singleLineVisible = mVisibleType == VISIBLE_TYPE_SINGLELINE
- || mTransformationStartVisibleType == VISIBLE_TYPE_SINGLELINE;
- if (!contractedVisible) {
- mContractedChild.setVisibility(View.INVISIBLE);
+ forceUpdateVisibility(VISIBLE_TYPE_CONTRACTED, mContractedChild, mContractedWrapper);
+ forceUpdateVisibility(VISIBLE_TYPE_EXPANDED, mExpandedChild, mExpandedWrapper);
+ forceUpdateVisibility(VISIBLE_TYPE_HEADSUP, mHeadsUpChild, mHeadsUpWrapper);
+ forceUpdateVisibility(VISIBLE_TYPE_SINGLELINE, mSingleLineView, mSingleLineView);
+ forceUpdateVisibility(VISIBLE_TYPE_AMBIENT, mAmbientChild, mAmbientWrapper);
+ // forceUpdateVisibilities cancels outstanding animations without updating the
+ // mAnimationStartVisibleType. Do so here instead.
+ mAnimationStartVisibleType = UNDEFINED;
+ }
+
+ private void forceUpdateVisibility(int type, View view, TransformableView wrapper) {
+ if (view == null) {
+ return;
+ }
+ boolean visible = mVisibleType == type
+ || mTransformationStartVisibleType == type;
+ if (!visible) {
+ view.setVisibility(INVISIBLE);
} else {
- mContractedWrapper.setVisible(true);
- }
- if (mExpandedChild != null) {
- if (!expandedVisible) {
- mExpandedChild.setVisibility(View.INVISIBLE);
- } else {
- mExpandedWrapper.setVisible(true);
- }
- }
- if (mHeadsUpChild != null) {
- if (!headsUpVisible) {
- mHeadsUpChild.setVisibility(View.INVISIBLE);
- } else {
- mHeadsUpWrapper.setVisible(true);
- }
- }
- if (mSingleLineView != null) {
- if (!singleLineVisible) {
- mSingleLineView.setVisibility(View.INVISIBLE);
- } else {
- mSingleLineView.setVisible(true);
- }
+ wrapper.setVisible(true);
}
}
@@ -717,19 +742,25 @@
}
private void updateViewVisibilities(int visibleType) {
- boolean contractedVisible = visibleType == VISIBLE_TYPE_CONTRACTED;
- mContractedWrapper.setVisible(contractedVisible);
- if (mExpandedChild != null) {
- boolean expandedVisible = visibleType == VISIBLE_TYPE_EXPANDED;
- mExpandedWrapper.setVisible(expandedVisible);
- }
- if (mHeadsUpChild != null) {
- boolean headsUpVisible = visibleType == VISIBLE_TYPE_HEADSUP;
- mHeadsUpWrapper.setVisible(headsUpVisible);
- }
- if (mSingleLineView != null) {
- boolean singleLineVisible = visibleType == VISIBLE_TYPE_SINGLELINE;
- mSingleLineView.setVisible(singleLineVisible);
+ updateViewVisibility(visibleType, VISIBLE_TYPE_CONTRACTED,
+ mContractedChild, mContractedWrapper);
+ updateViewVisibility(visibleType, VISIBLE_TYPE_EXPANDED,
+ mExpandedChild, mExpandedWrapper);
+ updateViewVisibility(visibleType, VISIBLE_TYPE_HEADSUP,
+ mHeadsUpChild, mHeadsUpWrapper);
+ updateViewVisibility(visibleType, VISIBLE_TYPE_SINGLELINE,
+ mSingleLineView, mSingleLineView);
+ updateViewVisibility(visibleType, VISIBLE_TYPE_AMBIENT,
+ mAmbientChild, mAmbientWrapper);
+ // updateViewVisibilities cancels outstanding animations without updating the
+ // mAnimationStartVisibleType. Do so here instead.
+ mAnimationStartVisibleType = UNDEFINED;
+ }
+
+ private void updateViewVisibility(int visibleType, int type, View view,
+ TransformableView wrapper) {
+ if (view != null) {
+ wrapper.setVisible(visibleType == type);
}
}
@@ -779,6 +810,8 @@
return mHeadsUpWrapper;
case VISIBLE_TYPE_SINGLELINE:
return mSingleLineView;
+ case VISIBLE_TYPE_AMBIENT:
+ return mAmbientWrapper;
default:
return mContractedWrapper;
}
@@ -796,6 +829,8 @@
return mHeadsUpChild;
case VISIBLE_TYPE_SINGLELINE:
return mSingleLineView;
+ case VISIBLE_TYPE_AMBIENT:
+ return mAmbientChild;
default:
return mContractedChild;
}
@@ -809,6 +844,8 @@
return mHeadsUpWrapper;
case VISIBLE_TYPE_CONTRACTED:
return mContractedWrapper;
+ case VISIBLE_TYPE_AMBIENT:
+ return mAmbientWrapper;
default:
return null;
}
@@ -818,6 +855,10 @@
* @return one of the static enum types in this view, calculated form the current state
*/
public int calculateVisibleType() {
+ if (mDark && !mIsChildInGroup) {
+ // TODO: Handle notification groups
+ return VISIBLE_TYPE_AMBIENT;
+ }
if (mUserExpanding) {
int height = !mIsChildInGroup || isGroupExpanded()
|| mContainingNotification.isExpanded(true /* allowOnKeyguard */)
@@ -890,6 +931,7 @@
if (mSingleLineView != null && (mVisibleType == VISIBLE_TYPE_SINGLELINE || !dark)) {
mSingleLineView.setDark(dark, fade, delay);
}
+ selectLayout(!dark && fade /* animate */, false /* force */);
}
public void setHeadsUp(boolean headsUp) {
@@ -942,6 +984,9 @@
if (mHeadsUpChild != null) {
mHeadsUpWrapper.notifyContentUpdated(entry.notification);
}
+ if (mAmbientChild != null) {
+ mAmbientWrapper.notifyContentUpdated(entry.notification);
+ }
updateShowingLegacyBackground();
mForceSelectNextLayout = true;
setDark(mDark, false /* animate */, 0 /* delay */);
@@ -1128,6 +1173,9 @@
if (header == null && mHeadsUpChild != null) {
header = mHeadsUpWrapper.getNotificationHeader();
}
+ if (header == null && mAmbientChild != null) {
+ header = mAmbientWrapper.getNotificationHeader();
+ }
return header;
}
@@ -1195,6 +1243,11 @@
}
}
+ @VisibleForTesting
+ boolean isAnimatingVisibleType() {
+ return mAnimationStartVisibleType != UNDEFINED;
+ }
+
public void setHeadsUpAnimatingAway(boolean headsUpAnimatingAway) {
mHeadsUpAnimatingAway = headsUpAnimatingAway;
selectLayout(false /* animate */, true /* force */);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
index c06e639..458daf1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
@@ -67,6 +67,7 @@
public RemoteViews cachedBigContentView;
public RemoteViews cachedHeadsUpContentView;
public RemoteViews cachedPublicContentView;
+ public RemoteViews cachedAmbientContentView;
public CharSequence remoteInputText;
private int mCachedContrastColor = COLOR_INVALID;
private int mCachedContrastColorIsFor = COLOR_INVALID;
@@ -126,6 +127,8 @@
updatedNotificationBuilder.createHeadsUpContentView();
final RemoteViews newPublicNotification
= updatedNotificationBuilder.makePublicContentView();
+ final RemoteViews newAmbientNotification
+ = updatedNotificationBuilder.makeAmbientNotification();
boolean sameCustomView = Objects.equals(
notification.getNotification().extras.getBoolean(
@@ -136,11 +139,13 @@
&& compareRemoteViews(cachedBigContentView, newBigContentView)
&& compareRemoteViews(cachedHeadsUpContentView, newHeadsUpContentView)
&& compareRemoteViews(cachedPublicContentView, newPublicNotification)
+ && compareRemoteViews(cachedAmbientContentView, newAmbientNotification)
&& sameCustomView;
cachedPublicContentView = newPublicNotification;
cachedHeadsUpContentView = newHeadsUpContentView;
cachedBigContentView = newBigContentView;
cachedContentView = newContentView;
+ cachedAmbientContentView = newAmbientNotification;
} else {
final Notification.Builder builder
= Notification.Builder.recoverBuilder(ctx, notification.getNotification());
@@ -149,6 +154,7 @@
cachedBigContentView = builder.createBigContentView();
cachedHeadsUpContentView = builder.createHeadsUpContentView();
cachedPublicContentView = builder.makePublicContentView();
+ cachedAmbientContentView = builder.makeAmbientNotification();
applyInPlace = false;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
index 64c4949..e8e9d4e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
@@ -129,6 +129,7 @@
} else {
mViewInvertHelper.update(dark);
}
+ mShelfIcons.setAmbient(dark);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationTemplateViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationTemplateViewWrapper.java
index 7ca2df9..b984c0b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationTemplateViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationTemplateViewWrapper.java
@@ -116,8 +116,10 @@
private void resolveTemplateViews(StatusBarNotification notification) {
mPicture = (ImageView) mView.findViewById(com.android.internal.R.id.right_icon);
- mPicture.setTag(ImageTransformState.ICON_TAG,
- notification.getNotification().getLargeIcon());
+ if (mPicture != null) {
+ mPicture.setTag(ImageTransformState.ICON_TAG,
+ notification.getNotification().getLargeIcon());
+ }
mTitle = (TextView) mView.findViewById(com.android.internal.R.id.title);
mText = (TextView) mView.findViewById(com.android.internal.R.id.text);
final View progress = mView.findViewById(com.android.internal.R.id.progress);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisualStabilityManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisualStabilityManager.java
index 5047041..a4e5916 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisualStabilityManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisualStabilityManager.java
@@ -39,6 +39,7 @@
private VisibilityLocationProvider mVisibilityLocationProvider;
private ArraySet<View> mAllowedReorderViews = new ArraySet<>();
private ArraySet<View> mAddedChildren = new ArraySet<>();
+ private boolean mPulsing;
/**
* Add a callback to invoke when reordering is allowed again.
@@ -67,8 +68,16 @@
updateReorderingAllowed();
}
+ /**
+ * @param pulsing whether we are currently pulsing for ambient display.
+ */
+ public void setPulsing(boolean pulsing) {
+ mPulsing = pulsing;
+ updateReorderingAllowed();
+ }
+
private void updateReorderingAllowed() {
- boolean reorderingAllowed = !mScreenOn || !mPanelExpanded;
+ boolean reorderingAllowed = (!mScreenOn || !mPanelExpanded) && !mPulsing;
boolean changed = reorderingAllowed && !mReorderingAllowed;
mReorderingAllowed = reorderingAllowed;
if (changed) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java
index 01ffe01..b78f748 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java
@@ -26,6 +26,7 @@
import android.view.View;
import android.view.animation.Interpolator;
+import com.android.keyguard.KeyguardStatusView;
import com.android.systemui.Interpolators;
import com.android.systemui.doze.DozeHost;
import com.android.systemui.doze.DozeLog;
@@ -41,7 +42,9 @@
private final Handler mHandler = new Handler();
private final ScrimController mScrimController;
+ private final Context mContext;
private final View mStackScroller;
+ private final NotificationPanelView mNotificationPanelView;
private boolean mDozing;
private DozeHost.PulseCallback mPulseCallback;
@@ -52,10 +55,12 @@
private float mBehindTarget;
public DozeScrimController(ScrimController scrimController, Context context,
- View stackScroller) {
+ View stackScroller, NotificationPanelView notificationPanelView) {
+ mContext = context;
mStackScroller = stackScroller;
mScrimController = scrimController;
mDozeParameters = new DozeParameters(context);
+ mNotificationPanelView = notificationPanelView;
}
public void setDozing(boolean dozing, boolean animate) {
@@ -65,10 +70,7 @@
abortAnimations();
mScrimController.setDozeBehindAlpha(1f);
mScrimController.setDozeInFrontAlpha(mDozeParameters.getAlwaysOn() ? 0f : 1f);
- if (mDozeParameters.getAlwaysOn()) {
- mStackScroller.setAlpha(0f);
- mHandler.postDelayed(() -> mStackScroller.setAlpha(0f), 30);
- }
+ mNotificationPanelView.setDark(true);
} else {
cancelPulsing();
if (animate) {
@@ -83,9 +85,8 @@
mScrimController.setDozeBehindAlpha(0f);
mScrimController.setDozeInFrontAlpha(0f);
}
- if (mDozeParameters.getAlwaysOn()) {
- mStackScroller.setAlpha(1f);
- }
+ // TODO: animate
+ mNotificationPanelView.setDark(false);
}
}
@@ -123,9 +124,6 @@
if (isPulsing()) {
final boolean pickupOrDoubleTap = mPulseReason == DozeLog.PULSE_REASON_SENSOR_PICKUP
|| mPulseReason == DozeLog.PULSE_REASON_SENSOR_DOUBLE_TAP;
- if (mDozeParameters.getAlwaysOn()) {
- mStackScroller.setAlpha(1f);
- }
startScrimAnimation(true /* inFront */, 0f,
mDozeParameters.getPulseInDuration(pickupOrDoubleTap),
pickupOrDoubleTap ? Interpolators.LINEAR_OUT_SLOW_IN : Interpolators.ALPHA_OUT,
@@ -291,9 +289,6 @@
@Override
public void run() {
if (DEBUG) Log.d(TAG, "Pulse out finished");
- if (mDozeParameters.getAlwaysOn()) {
- mStackScroller.setAlpha(0f);
- }
DozeLog.tracePulseFinish();
// Signal that the pulse is all finished so we can turn the screen off now.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
index 70beac8ea..c78ec83 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
@@ -68,6 +68,8 @@
}
private AccelerateInterpolator mAccelerateInterpolator = new AccelerateInterpolator();
+ private int mClockBottom;
+ private boolean mDark;
/**
* Refreshes the dimension values.
@@ -86,7 +88,8 @@
}
public void setup(int maxKeyguardNotifications, int maxPanelHeight, float expandedHeight,
- int notificationCount, int height, int keyguardStatusHeight, float emptyDragAmount) {
+ int notificationCount, int height, int keyguardStatusHeight, float emptyDragAmount,
+ int clockBottom, boolean dark) {
mMaxKeyguardNotifications = maxKeyguardNotifications;
mMaxPanelHeight = maxPanelHeight;
mExpandedHeight = expandedHeight;
@@ -94,6 +97,8 @@
mHeight = height;
mKeyguardStatusHeight = keyguardStatusHeight;
mEmptyDragAmount = emptyDragAmount;
+ mClockBottom = clockBottom;
+ mDark = dark;
}
public float getMinStackScrollerPadding(int height, int keyguardStatusHeight) {
@@ -115,6 +120,9 @@
result.clockY,
y + getClockNotificationsPadding() + mKeyguardStatusHeight);
result.clockAlpha = getClockAlpha(result.clockScale);
+ if (mDark) {
+ result.stackScrollerPadding = mClockBottom + y;
+ }
}
private float getClockScale(int notificationPadding, int clockY, int startPadding) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
index 9fb5980..c25a45c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
@@ -22,9 +22,7 @@
import android.graphics.Color;
import android.graphics.Paint;
import android.util.AttributeSet;
-import android.util.Property;
import android.view.View;
-import android.view.animation.Interpolator;
import com.android.systemui.Interpolators;
import com.android.systemui.R;
@@ -32,7 +30,6 @@
import com.android.systemui.statusbar.StatusBarIconView;
import com.android.systemui.statusbar.stack.AnimationFilter;
import com.android.systemui.statusbar.stack.AnimationProperties;
-import com.android.systemui.statusbar.stack.HeadsUpAppearInterpolator;
import com.android.systemui.statusbar.stack.ViewState;
import java.util.HashMap;
@@ -98,6 +95,7 @@
private int mActualLayoutWidth = NO_VALUE;
private float mActualPaddingEnd = NO_VALUE;
private float mActualPaddingStart = NO_VALUE;
+ private boolean mCentered;
private boolean mChangingViewPositions;
private int mAddAnimationStartIndex = -1;
private int mCannedAnimationStartIndex = -1;
@@ -105,6 +103,7 @@
private int mIconSize;
private float mOpenedAmount = 0.0f;
private float mVisualOverflowAdaption;
+ private boolean mDisallowNextAnimation;
public NotificationIconContainer(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -165,6 +164,7 @@
}
mAddAnimationStartIndex = -1;
mCannedAnimationStartIndex = -1;
+ mDisallowNextAnimation = false;
}
@Override
@@ -310,6 +310,15 @@
numDots++;
}
}
+ if (mCentered && translationX < getLayoutEnd()) {
+ float delta = (getLayoutEnd() - translationX) / 2;
+ for (int i = 0; i < childCount; i++) {
+ View view = getChildAt(i);
+ IconState iconState = mIconStates.get(view);
+ iconState.xTranslation += delta;
+ }
+ }
+
if (isLayoutRtl()) {
for (int i = 0; i < childCount; i++) {
View view = getChildAt(i);
@@ -379,6 +388,11 @@
mChangingViewPositions = changingViewPositions;
}
+ public void setAmbient(boolean ambient) {
+ mCentered = ambient;
+ mDisallowNextAnimation = true;
+ }
+
public IconState getIconState(StatusBarIconView icon) {
return mIconStates.get(icon);
}
@@ -469,7 +483,7 @@
animate = true;
}
icon.setVisibleState(visibleState);
- if (animate) {
+ if (animate && !mDisallowNextAnimation) {
animateTo(icon, animationProperties);
} else {
super.applyToView(view);
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 d48819a..3bdd5e5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -211,6 +211,7 @@
private boolean mOpening;
private int mIndicationBottomPadding;
private boolean mIsFullWidth;
+ private boolean mDark;
public NotificationPanelView(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -391,7 +392,9 @@
mNotificationStackScroller.getNotGoneChildCount(),
getHeight(),
mKeyguardStatusView.getHeight(),
- mEmptyDragAmount);
+ mEmptyDragAmount,
+ mKeyguardStatusView.getClockBottom(),
+ mDark);
mClockPositionAlgorithm.run(mClockPositionResult);
if (animate || mClockAnimator != null) {
startClockAnimation(mClockPositionResult.clockY);
@@ -2453,4 +2456,10 @@
}
}
};
+
+ public void setDark(boolean dark) {
+ mDark = dark;
+ mKeyguardStatusView.setDark(dark);
+ positionClockAndNotifications();
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
index 5998ed2..191718e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -74,7 +74,6 @@
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
-import android.os.Looper;
import android.os.Message;
import android.os.PowerManager;
import android.os.Process;
@@ -879,7 +878,8 @@
mHeadsUpManager.addListener(mScrimController);
mStackScroller.setScrimController(mScrimController);
mStatusBarView.setScrimController(mScrimController);
- mDozeScrimController = new DozeScrimController(mScrimController, context, mStackScroller);
+ mDozeScrimController = new DozeScrimController(mScrimController, context, mStackScroller,
+ mNotificationPanel);
// Other icons
mLocationController = new LocationControllerImpl(mContext,
@@ -2401,6 +2401,7 @@
return getBarState() == StatusBarState.KEYGUARD;
}
+ @Override
public boolean isDozing() {
return mDozing;
}
@@ -2487,6 +2488,9 @@
}
} else {
updateNotificationRanking(null);
+ if (isHeadsUp) {
+ mDozeServiceHost.fireNotificationHeadsUp();
+ }
}
}
@@ -2886,9 +2890,6 @@
@Override // CommandQueue
public void buzzBeepBlinked() {
- if (mDozeServiceHost != null) {
- mDozeServiceHost.fireBuzzBeepBlinked();
- }
}
@Override
@@ -3518,6 +3519,9 @@
if (mSecurityController != null) {
mSecurityController.onUserSwitched(mCurrentUserId);
}
+ if (mNetworkController != null) {
+ mNetworkController.onUserSwitched(mCurrentUserId);
+ }
}
private void resetUserSetupObserver() {
@@ -4234,6 +4238,7 @@
mDozeScrimController.setDozing(mDozing &&
mFingerprintUnlockController.getMode()
!= FingerprintUnlockController.MODE_WAKE_AND_UNLOCK_PULSING, animate);
+ updateRowStates();
Trace.endSection();
}
@@ -4904,9 +4909,9 @@
}
}
- public void fireBuzzBeepBlinked() {
+ public void fireNotificationHeadsUp() {
for (Callback callback : mCallbacks) {
- callback.onBuzzBeepBlinked();
+ callback.onNotificationHeadsUp();
}
}
@@ -4950,12 +4955,14 @@
public void onPulseStarted() {
callback.onPulseStarted();
mStackScroller.setPulsing(true);
+ mVisualStabilityManager.setPulsing(true);
}
@Override
public void onPulseFinished() {
callback.onPulseFinished();
mStackScroller.setPulsing(false);
+ mVisualStabilityManager.setPulsing(false);
}
}, reason);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java
index 227ebdf..d4cf533 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java
@@ -52,6 +52,7 @@
import com.android.systemui.qs.tiles.HotspotTile;
import com.android.systemui.qs.tiles.IntentTile;
import com.android.systemui.qs.tiles.LocationTile;
+import com.android.systemui.qs.tiles.NfcTile;
import com.android.systemui.qs.tiles.NightDisplayTile;
import com.android.systemui.qs.tiles.RotationLockTile;
import com.android.systemui.qs.tiles.UserTile;
@@ -440,6 +441,7 @@
else if (tileSpec.equals("battery")) return new BatteryTile(this);
else if (tileSpec.equals("saver")) return new DataSaverTile(this);
else if (tileSpec.equals("night")) return new NightDisplayTile(this);
+ else if (tileSpec.equals("nfc")) return new NfcTile(this);
// Intent tiles.
else if (tileSpec.startsWith(IntentTile.PREFIX)) return IntentTile.create(this,tileSpec);
else if (tileSpec.startsWith(CustomTile.PREFIX)) return CustomTile.create(this,tileSpec);
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 9545fd8..395e8f2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
@@ -1883,12 +1883,16 @@
float previousIncreasedAmount = 0.0f;
int numShownItems = 0;
boolean finish = false;
+ int maxDisplayedNotifications = mAmbientState.isDark()
+ ? (mPulsing ? 1 : 0)
+ : mMaxDisplayedNotifications;
+
for (int i = 0; i < getChildCount(); i++) {
ExpandableView expandableView = (ExpandableView) getChildAt(i);
if (expandableView.getVisibility() != View.GONE
&& !expandableView.hasNoContentHeight()) {
- if (mMaxDisplayedNotifications != -1
- && numShownItems >= mMaxDisplayedNotifications) {
+ if (maxDisplayedNotifications != -1
+ && numShownItems >= maxDisplayedNotifications) {
expandableView = mShelf;
finish = true;
}
@@ -3486,6 +3490,8 @@
updateBackground();
setWillNotDraw(false);
}
+ updateContentHeight();
+ notifyHeightChangeListener(mShelf);
}
private void setBackgroundFadeAmount(float fadeAmount) {
@@ -3921,6 +3927,8 @@
public void setPulsing(boolean pulsing) {
mPulsing = pulsing;
updateNotificationAnimationStates();
+ updateContentHeight();
+ notifyHeightChangeListener(mShelf);
}
public void setFadingOut(boolean fadingOut) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/notification/VisualStabilityManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/notification/VisualStabilityManagerTest.java
index be6290b..76bb6c0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/notification/VisualStabilityManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/notification/VisualStabilityManagerTest.java
@@ -150,4 +150,28 @@
mVisualStabilityManager.onReorderingFinished();
assertEquals(mVisualStabilityManager.canReorderNotification(mRow), false);
}
+
+ @Test
+ public void testPulsing() {
+ mVisualStabilityManager.setPulsing(true);
+ assertEquals(mVisualStabilityManager.canReorderNotification(mRow), false);
+ mVisualStabilityManager.setPulsing(false);
+ assertEquals(mVisualStabilityManager.canReorderNotification(mRow), true);
+ }
+
+ @Test
+ public void testReorderingAllowedChanges_Pulsing() {
+ mVisualStabilityManager.setPulsing(true);
+ assertEquals(mVisualStabilityManager.isReorderingAllowed(), false);
+ mVisualStabilityManager.setPulsing(false);
+ assertEquals(mVisualStabilityManager.isReorderingAllowed(), true);
+ }
+
+ @Test
+ public void testCallBackCalled_Pulsing() {
+ mVisualStabilityManager.setPulsing(true);
+ mVisualStabilityManager.addReorderingAllowedCallback(mCallback);
+ mVisualStabilityManager.setPulsing(false);
+ verify(mCallback).onReorderingAllowed();
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationContentViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationContentViewTest.java
new file mode 100644
index 0000000..3bb9f5f
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationContentViewTest.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.statusbar;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.annotation.UiThreadTest;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.view.View;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class NotificationContentViewTest {
+
+ NotificationContentView mView;
+ Context mContext;
+
+ @Before
+ public void setup() {
+ ExpandableNotificationRow rowMock = mock(ExpandableNotificationRow.class);
+ when(rowMock.getIntrinsicHeight()).thenReturn(10);
+
+ mContext = InstrumentationRegistry.getTargetContext();
+ mView = new NotificationContentView(mContext, null);
+ mView.setContainingNotification(rowMock);
+ mView.setHeights(10, 20, 30, 40);
+
+ mView.setContractedChild(createViewWithHeight(10));
+ mView.setExpandedChild(createViewWithHeight(20));
+ mView.setHeadsUpChild(createViewWithHeight(30));
+ mView.setAmbientChild(createViewWithHeight(40));
+
+ mView.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);
+ mView.layout(0, 0, mView.getMeasuredWidth(), mView.getMeasuredHeight());
+ }
+
+ private View createViewWithHeight(int height) {
+ View view = new View(mContext, null);
+ view.setMinimumHeight(height);
+ return view;
+ }
+
+ @Test
+ @UiThreadTest
+ public void animationStartType_getsClearedAfterUpdatingVisibilitiesWithoutAnimation() {
+ mView.setHeadsUp(true);
+ mView.setDark(true, false, 0);
+ mView.setDark(false, true, 0);
+ mView.setHeadsUpAnimatingAway(true);
+ Assert.assertFalse(mView.isAnimatingVisibleType());
+ }
+}
diff --git a/proto/src/metrics_constants.proto b/proto/src/metrics_constants.proto
index dc92f56..ac0a4a28 100644
--- a/proto/src/metrics_constants.proto
+++ b/proto/src/metrics_constants.proto
@@ -3191,8 +3191,76 @@
// ACTION: Clicking on any search result in Settings.
ACTION_CLICK_SETTINGS_SEARCH_RESULT = 763;
+ // ACTION: Allow Battery optimization for an app
+ APP_SPECIAL_PERMISSION_BATTERY_ALLOW = 764;
+
+ // ACTION: Deny Battery optimization for an app
+ APP_SPECIAL_PERMISSION_BATTERY_DENY = 765;
+
+ // ACTION: Enable Device Admin app
+ APP_SPECIAL_PERMISSION_ADMIN_ALLOW = 766;
+
+ // ACTION: Disable Device Admin app
+ APP_SPECIAL_PERMISSION_ADMIN_DENY = 767;
+
+ // ACTION: Allow "Do Not Disturb access" for an app
+ APP_SPECIAL_PERMISSION_DND_ALLOW = 768;
+
+ // ACTION: Deny "Do Not Disturb access" for an app
+ APP_SPECIAL_PERMISSION_DND_DENY = 769;
+
+ // ACTION: Allow "Draw over other apps" for an app
+ APP_SPECIAL_PERMISSION_APPDRAW_ALLOW = 770;
+
+ // ACTION: Deny "Draw over other apps" for an app
+ APP_SPECIAL_PERMISSION_APPDRAW_DENY = 771;
+
+ // ACTION: Allow "VR helper services" for an app
+ APP_SPECIAL_PERMISSION_VRHELPER_ALLOW = 772;
+
+ // ACTION: Deny "VR helper services" for an app
+ APP_SPECIAL_PERMISSION_VRHELPER_DENY = 773;
+
+ // ACTION: Allow "Modify system settings" for an app
+ APP_SPECIAL_PERMISSION_SETTINGS_CHANGE_ALLOW = 774;
+
+ // ACTION: Deny "Modify system settings" for an app
+ APP_SPECIAL_PERMISSION_SETTINGS_CHANGE_DENY = 775;
+
+ // ACTION: Allow "Notification access" for an app
+ APP_SPECIAL_PERMISSION_NOTIVIEW_ALLOW = 776;
+
+ // ACTION: Deny "Notification access" for an app
+ APP_SPECIAL_PERMISSION_NOTIVIEW_DENY = 777;
+
+ // ACTION: "Premium SMS access" for an app - "ask user" option
+ APP_SPECIAL_PERMISSION_PREMIUM_SMS_ASK = 778;
+
+ // ACTION: "Premium SMS access" for an app - "never allow" option
+ APP_SPECIAL_PERMISSION_PREMIUM_SMS_DENY = 779;
+
+ // ACTION: "Premium SMS access" for an app - "always allow" option
+ APP_SPECIAL_PERMISSION_PREMIUM_SMS_ALWAYS_ALLOW = 780;
+
+ // ACTION: Allow "Unrestricted data access" for an app
+ APP_SPECIAL_PERMISSION_UNL_DATA_ALLOW = 781;
+
+ // ACTION: Deny "Unrestricted data access" for an app
+ APP_SPECIAL_PERMISSION_UNL_DATA_DENY = 782;
+
+ // ACTION: Allow "Usage access" for an app
+ APP_SPECIAL_PERMISSION_USAGE_VIEW_ALLOW = 783;
+
+ // ACTION: Deny "Usage access" for an app
+ APP_SPECIAL_PERMISSION_USAGE_VIEW_DENY = 784;
+
// ---- End O Constants, all O constants go above this line ----
+ // OPEN: QS NFC tile shown
+ // ACTION: QS NFC tile tapped
+ // CATEGORY: QUICK_SETTINGS
+ QS_NFC = 800;
+
// Add new aosp constants above this line.
// END OF AOSP CONSTANTS
}
diff --git a/services/core/java/com/android/server/DeviceIdleController.java b/services/core/java/com/android/server/DeviceIdleController.java
index 9f63e30..69a2c55 100644
--- a/services/core/java/com/android/server/DeviceIdleController.java
+++ b/services/core/java/com/android/server/DeviceIdleController.java
@@ -1272,6 +1272,11 @@
DeviceIdleController.this.setAlarmsActive(active);
}
+ /** Is the app on any of the power save whitelists, whether system or user? */
+ public boolean isAppOnWhitelist(int appid) {
+ return DeviceIdleController.this.isAppOnWhitelistInternal(appid);
+ }
+
/**
* Returns the array of app ids whitelisted by user. Take care not to
* modify this, as it is a reference to the original copy. But the reference
@@ -1289,6 +1294,12 @@
mHandler = new MyHandler(BackgroundThread.getHandler().getLooper());
}
+ boolean isAppOnWhitelistInternal(int appid) {
+ synchronized (this) {
+ return Arrays.binarySearch(mPowerSaveWhitelistAllAppIdArray, appid) >= 0;
+ }
+ }
+
int[] getPowerSaveWhitelistUserAppIds() {
synchronized (this) {
return mPowerSaveWhitelistUserAppIdArray;
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index 62f4f19..0834eb8 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -1360,6 +1360,7 @@
}
Intent intent = new Intent(TelephonyIntents.ACTION_SERVICE_STATE_CHANGED);
+ intent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
Bundle data = new Bundle();
state.fillInNotifierBundle(data);
intent.putExtras(data);
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 7661127..7406ff8 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -305,6 +305,7 @@
}
ComponentName startServiceLocked(IApplicationThread caller, Intent service, String resolvedType,
+ int id, Notification notification,
int callingPid, int callingUid, String callingPackage, final int userId)
throws TransactionTooLargeException {
if (DEBUG_DELAYED_STARTS) Slog.v(TAG_SERVICE, "startService: " + service
@@ -324,7 +325,6 @@
callerFg = true;
}
-
ServiceLookupResult res =
retrieveServiceLocked(service, resolvedType, callingPackage,
callingPid, callingUid, userId, true, callerFg, false);
@@ -343,10 +343,11 @@
return null;
}
- if (!r.startRequested) {
+ // Non-null notification means this is a start directly into the foreground
+ if (!r.startRequested && notification == null) {
final long token = Binder.clearCallingIdentity();
try {
- // Before going further -- if this app is not allowed to run in the
+ // Before going further -- if this app is not allowed to start services in the
// background, then at this point we aren't going to let it period.
final int allowed = mAm.checkAllowBackgroundLocked(
r.appInfo.uid, r.packageName, callingPid, false);
@@ -450,7 +451,11 @@
}
}
- return startServiceInnerLocked(smap, service, r, callerFg, addToStarting);
+ ComponentName cmp = startServiceInnerLocked(smap, service, r, callerFg, addToStarting);
+ if (notification != null) {
+ setServiceForegroundInnerLocked(r, callingUid, notification, 0);
+ }
+ return cmp;
}
private boolean requestStartTargetPermissionsReviewIfNeededLocked(ServiceRecord r,
@@ -595,9 +600,8 @@
for (int i=services.mServicesByName.size()-1; i>=0; i--) {
ServiceRecord service = services.mServicesByName.valueAt(i);
if (service.appInfo.uid == uid && service.startRequested) {
- if (service.appInfo.isEphemeralApp() ||
- mAm.mAppOpsService.noteOperation(AppOpsManager.OP_RUN_IN_BACKGROUND,
- uid, service.packageName) != AppOpsManager.MODE_ALLOWED) {
+ if (mAm.checkAllowBackgroundLocked(service.appInfo.uid, service.packageName,
+ -1, false) != ActivityManager.APP_START_MODE_NORMAL) {
if (stopping == null) {
stopping = new ArrayList<>();
stopping.add(service);
@@ -696,50 +700,55 @@
try {
ServiceRecord r = findServiceLocked(className, token, userId);
if (r != null) {
- if (id != 0) {
- if (notification == null) {
- throw new IllegalArgumentException("null notification");
- }
- if (r.foregroundId != id) {
- cancelForegroudNotificationLocked(r);
- r.foregroundId = id;
- }
- notification.flags |= Notification.FLAG_FOREGROUND_SERVICE;
- r.foregroundNoti = notification;
- r.isForeground = true;
- r.postNotification();
- if (r.app != null) {
- updateServiceForegroundLocked(r.app, true);
- }
- getServiceMapLocked(r.userId).ensureNotStartingBackgroundLocked(r);
- mAm.notifyPackageUse(r.serviceInfo.packageName,
- PackageManager.NOTIFY_PACKAGE_USE_FOREGROUND_SERVICE);
- } else {
- if (r.isForeground) {
- r.isForeground = false;
- if (r.app != null) {
- mAm.updateLruProcessLocked(r.app, false, null);
- updateServiceForegroundLocked(r.app, true);
- }
- }
- if ((flags & Service.STOP_FOREGROUND_REMOVE) != 0) {
- cancelForegroudNotificationLocked(r);
- r.foregroundId = 0;
- r.foregroundNoti = null;
- } else if (r.appInfo.targetSdkVersion >= Build.VERSION_CODES.LOLLIPOP) {
- r.stripForegroundServiceFlagFromNotification();
- if ((flags & Service.STOP_FOREGROUND_DETACH) != 0) {
- r.foregroundId = 0;
- r.foregroundNoti = null;
- }
- }
- }
+ setServiceForegroundInnerLocked(r, userId, notification, flags);
}
} finally {
Binder.restoreCallingIdentity(origId);
}
}
+ private void setServiceForegroundInnerLocked(ServiceRecord r, int id,
+ Notification notification, int flags) {
+ if (id != 0) {
+ if (notification == null) {
+ throw new IllegalArgumentException("null notification");
+ }
+ if (r.foregroundId != id) {
+ cancelForegroudNotificationLocked(r);
+ r.foregroundId = id;
+ }
+ notification.flags |= Notification.FLAG_FOREGROUND_SERVICE;
+ r.foregroundNoti = notification;
+ r.isForeground = true;
+ r.postNotification();
+ if (r.app != null) {
+ updateServiceForegroundLocked(r.app, true);
+ }
+ getServiceMapLocked(r.userId).ensureNotStartingBackgroundLocked(r);
+ mAm.notifyPackageUse(r.serviceInfo.packageName,
+ PackageManager.NOTIFY_PACKAGE_USE_FOREGROUND_SERVICE);
+ } else {
+ if (r.isForeground) {
+ r.isForeground = false;
+ if (r.app != null) {
+ mAm.updateLruProcessLocked(r.app, false, null);
+ updateServiceForegroundLocked(r.app, true);
+ }
+ }
+ if ((flags & Service.STOP_FOREGROUND_REMOVE) != 0) {
+ cancelForegroudNotificationLocked(r);
+ r.foregroundId = 0;
+ r.foregroundNoti = null;
+ } else if (r.appInfo.targetSdkVersion >= Build.VERSION_CODES.LOLLIPOP) {
+ r.stripForegroundServiceFlagFromNotification();
+ if ((flags & Service.STOP_FOREGROUND_DETACH) != 0) {
+ r.foregroundId = 0;
+ r.foregroundNoti = null;
+ }
+ }
+ }
+ }
+
private void cancelForegroudNotificationLocked(ServiceRecord r) {
if (r.foregroundId != 0) {
// First check to see if this app has any other active foreground services
diff --git a/services/core/java/com/android/server/am/ActivityManagerDebugConfig.java b/services/core/java/com/android/server/am/ActivityManagerDebugConfig.java
index 43bb5ee..63187e4 100644
--- a/services/core/java/com/android/server/am/ActivityManagerDebugConfig.java
+++ b/services/core/java/com/android/server/am/ActivityManagerDebugConfig.java
@@ -48,6 +48,7 @@
static final boolean DEBUG_ADD_REMOVE = DEBUG_ALL_ACTIVITIES || false;
static final boolean DEBUG_ANR = false;
static final boolean DEBUG_APP = DEBUG_ALL_ACTIVITIES || false;
+ static final boolean DEBUG_BACKGROUND = DEBUG_ALL || true;
static final boolean DEBUG_BACKUP = DEBUG_ALL || false;
static final boolean DEBUG_BROADCAST = DEBUG_ALL || false;
static final boolean DEBUG_BROADCAST_BACKGROUND = DEBUG_BROADCAST || false;
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 7fd91cb..c8e1aab 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -62,6 +62,7 @@
import static com.android.internal.util.XmlUtils.writeLongAttribute;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_ALL;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_ANR;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_BACKGROUND;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_BACKUP;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_BROADCAST;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_BROADCAST_BACKGROUND;
@@ -119,6 +120,7 @@
import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.am.ActivityStackSupervisor.ActivityContainer.FORCE_NEW_TASK_FLAGS;
+import static com.android.server.am.ActivityStackSupervisor.CREATE_IF_NEEDED;
import static com.android.server.am.ActivityStackSupervisor.DEFER_RESUME;
import static com.android.server.am.ActivityStackSupervisor.FORCE_FOCUS;
import static com.android.server.am.ActivityStackSupervisor.ON_TOP;
@@ -310,7 +312,6 @@
import com.google.android.collect.Lists;
import com.google.android.collect.Maps;
-
import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.app.AssistUtils;
@@ -341,6 +342,7 @@
import com.android.server.LocalServices;
import com.android.server.LockGuard;
import com.android.server.ServiceThread;
+import com.android.server.SystemConfig;
import com.android.server.SystemService;
import com.android.server.SystemServiceManager;
import com.android.server.Watchdog;
@@ -384,10 +386,10 @@
import java.util.concurrent.atomic.AtomicLong;
import dalvik.system.VMRuntime;
+
import libcore.io.IoUtils;
import libcore.util.EmptyArray;
-import static com.android.server.am.ActivityStackSupervisor.CREATE_IF_NEEDED;
public class ActivityManagerService extends IActivityManager.Stub
implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback {
@@ -758,6 +760,18 @@
ProcessRecord mHeavyWeightProcess = null;
/**
+ * Non-persistent app uid whitelist for background restrictions
+ */
+ int[] mBackgroundUidWhitelist = new int[] {
+ Process.BLUETOOTH_UID
+ };
+
+ /**
+ * Broadcast actions that will always be deliverable to unlaunched/background apps
+ */
+ final ArraySet<String> mBackgroundLaunchBroadcasts;
+
+ /**
* All of the processes we currently have running organized by pid.
* The keys are the pid running the application.
*
@@ -2590,6 +2604,17 @@
mPermissionReviewRequired = mContext.getResources().getBoolean(
com.android.internal.R.bool.config_permissionReviewRequired);
+ mBackgroundLaunchBroadcasts = SystemConfig.getInstance().getAllowImplicitBroadcasts();
+ if (DEBUG_BACKGROUND) {
+ StringBuilder sb = new StringBuilder(200);
+ sb.append(" ");
+ for (String a : mBackgroundLaunchBroadcasts) {
+ sb.append(' '); sb.append(a);
+ }
+ Slog.d(TAG, "Background implicit broadcasts:");
+ Slog.d(TAG, sb.toString());
+ }
+
mHandlerThread = new ServiceThread(TAG,
android.os.Process.THREAD_PRIORITY_FOREGROUND, false /*allowIo*/);
mHandlerThread.start();
@@ -4190,7 +4215,7 @@
validateUid = mValidateUids.get(item.uid);
if (validateUid == null && change != UidRecord.CHANGE_GONE
&& change != UidRecord.CHANGE_GONE_IDLE) {
- validateUid = new UidRecord(item.uid);
+ validateUid = new UidRecord(item.uid, false);
mValidateUids.put(item.uid, validateUid);
}
}
@@ -5504,6 +5529,7 @@
final Intent intent = new Intent(Intent.ACTION_PACKAGE_DATA_CLEARED,
Uri.fromParts("package", packageName, null));
+ intent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
intent.putExtra(Intent.EXTRA_UID, pkgUidF);
intent.putExtra(Intent.EXTRA_USER_HANDLE, UserHandle.getUserId(pkgUidF));
broadcastIntentInPackage("android", Process.SYSTEM_UID, intent,
@@ -6279,7 +6305,7 @@
}
UidRecord uidRec = mActiveUids.get(proc.uid);
if (uidRec == null) {
- uidRec = new UidRecord(proc.uid);
+ uidRec = new UidRecord(proc.uid, proc.persistent);
// This is the first appearance of the uid, report it now!
if (DEBUG_UID_OBSERVERS) Slog.i(TAG_UID_OBSERVERS,
"Creating new process uid: " + uidRec);
@@ -8026,6 +8052,84 @@
}
}
+ // Unified app-op and target sdk check
+ boolean appRestrictedInBackgroundLocked(int uid, String packageName) {
+ if (packageName == null) {
+ packageName = mPackageManagerInt.getNameForUid(uid);
+ if (packageName == null) {
+ Slog.w(TAG, "No package known for uid " + uid);
+ return false;
+ }
+ }
+
+ // !!! TODO: cache the package/versionCode lookups to fast path this
+ ApplicationInfo app = getPackageManagerInternalLocked().getApplicationInfo(packageName,
+ UserHandle.getUserId(uid));
+ if (app != null) {
+ // Apps that target O+ are always subject to background check
+ if (app.targetSdkVersion >= Build.VERSION_CODES.O) {
+ if (DEBUG_BACKGROUND) {
+ Slog.i(TAG, "App " + uid + "/" + packageName + " targets O+, restricted");
+ }
+ return true;
+ }
+ // ...and legacy apps get an AppOp check
+ int appop = mAppOpsService.noteOperation(AppOpsManager.OP_RUN_IN_BACKGROUND,
+ uid, packageName);
+ if (DEBUG_BACKGROUND) {
+ Slog.i(TAG, "Legacy app " + uid + "/" + packageName + " bg appop " + appop);
+ }
+ return (appop != AppOpsManager.MODE_ALLOWED);
+ } else {
+ Slog.w(TAG, "Unknown app " + packageName + " / " + uid);
+ }
+ return false;
+ }
+
+ // Service launch is available to apps with run-in-background exemptions but
+ // some other background operations are not. If we're doing a check
+ // of service-launch policy, allow those callers to proceed unrestricted.
+ boolean appServicesRestrictedInBackgroundLocked(int uid, String packageName) {
+ if (packageName == null) {
+ packageName = mPackageManagerInt.getNameForUid(uid);
+ if (packageName == null) {
+ Slog.w(TAG, "No package known for uid " + uid);
+ return false;
+ }
+ }
+
+ // Persistent app? NB: expects that persistent uids are always active.
+ final UidRecord uidRec = mActiveUids.get(uid);
+ if (uidRec != null && uidRec.persistent) {
+ if (DEBUG_BACKGROUND) {
+ Slog.i(TAG, "App " + uid + "/" + packageName
+ + " is persistent; not restricted in background");
+ }
+ return false;
+ }
+
+ // Non-persistent but background whitelisted?
+ if (uidOnBackgroundWhitelist(uid)) {
+ if (DEBUG_BACKGROUND) {
+ Slog.i(TAG, "App " + uid + "/" + packageName
+ + " on background whitelist; not restricted in background");
+ }
+ return false;
+ }
+
+ // Is this app on the battery whitelist?
+ if (mLocalDeviceIdleController.isAppOnWhitelist(UserHandle.getAppId(uid))) {
+ if (DEBUG_BACKGROUND) {
+ Slog.i(TAG, "App " + uid + "/" + packageName
+ + " on idle whitelist; not restricted in background");
+ }
+ return false;
+ }
+
+ // None of the service-policy criteria apply, so we apply the common criteria
+ return appRestrictedInBackgroundLocked(uid, packageName);
+ }
+
int checkAllowBackgroundLocked(int uid, String packageName, int callingPid,
boolean alwaysRestrict) {
UidRecord uidRec = mActiveUids.get(uid);
@@ -8054,8 +8158,13 @@
return ActivityManager.APP_START_MODE_NORMAL;
}
}
- if (mAppOpsService.noteOperation(AppOpsManager.OP_RUN_IN_BACKGROUND, uid,
- packageName) != AppOpsManager.MODE_ALLOWED) {
+ final boolean restricted = (alwaysRestrict)
+ ? appRestrictedInBackgroundLocked(uid, packageName)
+ : appServicesRestrictedInBackgroundLocked(uid, packageName);
+ if (restricted) {
+ if (DEBUG_BACKGROUND) {
+ Slog.i(TAG, "App " + uid + "/" + packageName + " restricted in background");
+ }
return ActivityManager.APP_START_MODE_DELAYED;
}
}
@@ -11651,6 +11760,16 @@
return r;
}
+ private boolean uidOnBackgroundWhitelist(final int uid) {
+ final int N = mBackgroundUidWhitelist.length;
+ for (int i = 0; i < N; i++) {
+ if (uid == mBackgroundUidWhitelist[i]) {
+ return true;
+ }
+ }
+ return false;
+ }
+
final ProcessRecord addAppLocked(ApplicationInfo info, boolean isolated,
String abiOverride) {
ProcessRecord app;
@@ -17284,7 +17403,8 @@
@Override
public ComponentName startService(IApplicationThread caller, Intent service,
- String resolvedType, String callingPackage, int userId)
+ String resolvedType, int id, Notification notification,
+ String callingPackage, int userId)
throws TransactionTooLargeException {
enforceNotIsolatedCaller("startService");
// Refuse possible leaked file descriptors
@@ -17303,7 +17423,8 @@
final int callingUid = Binder.getCallingUid();
final long origId = Binder.clearCallingIdentity();
ComponentName res = mServices.startServiceLocked(caller, service,
- resolvedType, callingPid, callingUid, callingPackage, userId);
+ resolvedType, id, notification,
+ callingPid, callingUid, callingPackage, userId);
Binder.restoreCallingIdentity(origId);
return res;
}
@@ -17317,7 +17438,7 @@
"startServiceInPackage: " + service + " type=" + resolvedType);
final long origId = Binder.clearCallingIdentity();
ComponentName res = mServices.startServiceLocked(null, service,
- resolvedType, -1, uid, callingPackage, userId);
+ resolvedType, 0, null, -1, uid, callingPackage, userId);
Binder.restoreCallingIdentity(origId);
return res;
}
@@ -18176,6 +18297,13 @@
}
if (action != null) {
+ if (mBackgroundLaunchBroadcasts.contains(action)) {
+ if (DEBUG_BACKGROUND) {
+ Slog.i(TAG, "Broadcast action " + action + " forcing include-background");
+ }
+ intent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
+ }
+
switch (action) {
case Intent.ACTION_UID_REMOVED:
case Intent.ACTION_PACKAGE_REMOVED:
@@ -19300,7 +19428,8 @@
UserHandle.USER_ALL);
if ((changes & ActivityInfo.CONFIG_LOCALE) != 0) {
intent = new Intent(Intent.ACTION_LOCALE_CHANGED);
- intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
+ intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND
+ | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
if (initLocale || !mProcessesReady) {
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index 29a4781..414488e 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -490,7 +490,7 @@
pw.println("Starting service: " + intent);
pw.flush();
ComponentName cn = mInterface.startService(null, intent, intent.getType(),
- SHELL_PACKAGE_NAME, mUserId);
+ -1, null, SHELL_PACKAGE_NAME, mUserId);
if (cn == null) {
err.println("Error: Not found; no service started.");
return -1;
diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java
index 47c3e6f..a2fb9f9 100644
--- a/services/core/java/com/android/server/am/ActivityRecord.java
+++ b/services/core/java/com/android/server/am/ActivityRecord.java
@@ -1802,6 +1802,9 @@
}
void showStartingWindow(ActivityRecord prev, boolean newTask, boolean taskSwitch) {
+ if (mWindowContainerController == null) {
+ return;
+ }
final CompatibilityInfo compatInfo =
service.compatibilityInfoForPackageLocked(info.applicationInfo);
final boolean shown = mWindowContainerController.addStartingWindow(packageName, theme,
diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java
index 8104a43..61e555b 100644
--- a/services/core/java/com/android/server/am/BroadcastQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastQueue.java
@@ -1157,8 +1157,7 @@
if (!skip) {
final int allowed = mService.checkAllowBackgroundLocked(
- info.activityInfo.applicationInfo.uid, info.activityInfo.packageName, -1,
- true);
+ info.activityInfo.applicationInfo.uid, info.activityInfo.packageName, -1, true);
if (allowed != ActivityManager.APP_START_MODE_NORMAL) {
// We won't allow this receiver to be launched if the app has been
// completely disabled from launches, or it was not explicitly sent
diff --git a/services/core/java/com/android/server/am/UidRecord.java b/services/core/java/com/android/server/am/UidRecord.java
index d1a15bd..4ebd068 100644
--- a/services/core/java/com/android/server/am/UidRecord.java
+++ b/services/core/java/com/android/server/am/UidRecord.java
@@ -26,6 +26,7 @@
*/
public final class UidRecord {
final int uid;
+ final boolean persistent;
int curProcState;
int setProcState = ActivityManager.PROCESS_STATE_NONEXISTENT;
long lastBackgroundTime;
@@ -49,8 +50,9 @@
ChangeItem pendingChange;
- public UidRecord(int _uid) {
+ public UidRecord(int _uid, boolean _persist) {
uid = _uid;
+ persistent = _persist;
reset();
}
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index 71ebad9..31b7ca7 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -414,7 +414,8 @@
if (userId != UserHandle.USER_SYSTEM) {
Slog.d(TAG, "Initializing user #" + userId);
Intent intent = new Intent(Intent.ACTION_USER_INITIALIZE);
- intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
+ intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND
+ | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
mInjector.broadcastIntentLocked(intent, null,
new IIntentReceiver.Stub() {
@Override
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index 87f4030..fbb39384 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -25,6 +25,7 @@
import com.android.internal.inputmethod.InputMethodSubtypeHandle;
import com.android.internal.os.SomeArgs;
import com.android.internal.R;
+import com.android.internal.util.Preconditions;
import com.android.internal.util.XmlUtils;
import com.android.server.DisplayThread;
import com.android.server.LocalServices;
@@ -1700,6 +1701,7 @@
// Binder call
@Override
public void setCustomPointerIcon(PointerIcon icon) {
+ Preconditions.checkNotNull(icon);
nativeSetCustomPointerIcon(mPtr, icon);
}
diff --git a/services/core/java/com/android/server/net/NetworkIdentitySet.java b/services/core/java/com/android/server/net/NetworkIdentitySet.java
index c48f430..ee00fdc 100644
--- a/services/core/java/com/android/server/net/NetworkIdentitySet.java
+++ b/services/core/java/com/android/server/net/NetworkIdentitySet.java
@@ -17,6 +17,8 @@
package com.android.server.net;
import android.net.NetworkIdentity;
+import android.service.NetworkIdentitySetProto;
+import android.util.proto.ProtoOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
@@ -143,4 +145,14 @@
final NetworkIdentity anotherIdent = another.iterator().next();
return ident.compareTo(anotherIdent);
}
+
+ public void writeToProto(ProtoOutputStream proto, long tag) {
+ final long start = proto.start(tag);
+
+ for (NetworkIdentity ident : this) {
+ ident.writeToProto(proto, NetworkIdentitySetProto.IDENTITIES);
+ }
+
+ proto.end(start);
+ }
}
diff --git a/services/core/java/com/android/server/net/NetworkStatsCollection.java b/services/core/java/com/android/server/net/NetworkStatsCollection.java
index c45b416..0354300 100644
--- a/services/core/java/com/android/server/net/NetworkStatsCollection.java
+++ b/services/core/java/com/android/server/net/NetworkStatsCollection.java
@@ -34,9 +34,13 @@
import android.net.NetworkTemplate;
import android.net.TrafficStats;
import android.os.Binder;
+import android.service.NetworkStatsCollectionKeyProto;
+import android.service.NetworkStatsCollectionProto;
+import android.service.NetworkStatsCollectionStatsProto;
import android.util.ArrayMap;
import android.util.AtomicFile;
import android.util.IntArray;
+import android.util.proto.ProtoOutputStream;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.FileRotator;
@@ -532,12 +536,15 @@
/ mBucketDuration);
}
- public void dump(IndentingPrintWriter pw) {
+ private ArrayList<Key> getSortedKeys() {
final ArrayList<Key> keys = Lists.newArrayList();
keys.addAll(mStats.keySet());
Collections.sort(keys);
+ return keys;
+ }
- for (Key key : keys) {
+ public void dump(IndentingPrintWriter pw) {
+ for (Key key : getSortedKeys()) {
pw.print("ident="); pw.print(key.ident.toString());
pw.print(" uid="); pw.print(key.uid);
pw.print(" set="); pw.print(NetworkStats.setToString(key.set));
@@ -550,6 +557,29 @@
}
}
+ public void writeToProto(ProtoOutputStream proto, long tag) {
+ final long start = proto.start(tag);
+
+ for (Key key : getSortedKeys()) {
+ final long startStats = proto.start(NetworkStatsCollectionProto.STATS);
+
+ // Key
+ final long startKey = proto.start(NetworkStatsCollectionStatsProto.KEY);
+ key.ident.writeToProto(proto, NetworkStatsCollectionKeyProto.IDENTITY);
+ proto.write(NetworkStatsCollectionKeyProto.UID, key.uid);
+ proto.write(NetworkStatsCollectionKeyProto.SET, key.set);
+ proto.write(NetworkStatsCollectionKeyProto.TAG, key.tag);
+ proto.end(startKey);
+
+ // Value
+ final NetworkStatsHistory history = mStats.get(key);
+ history.writeToProto(proto, NetworkStatsCollectionStatsProto.HISTORY);
+ proto.end(startStats);
+ }
+
+ proto.end(start);
+ }
+
public void dumpCheckin(PrintWriter pw, long start, long end) {
dumpCheckin(pw, start, end, NetworkTemplate.buildTemplateMobileWildcard(), "cell");
dumpCheckin(pw, start, end, NetworkTemplate.buildTemplateWifiWildcard(), "wifi");
diff --git a/services/core/java/com/android/server/net/NetworkStatsRecorder.java b/services/core/java/com/android/server/net/NetworkStatsRecorder.java
index 090a076..80309e1 100644
--- a/services/core/java/com/android/server/net/NetworkStatsRecorder.java
+++ b/services/core/java/com/android/server/net/NetworkStatsRecorder.java
@@ -29,9 +29,11 @@
import android.net.NetworkTemplate;
import android.net.TrafficStats;
import android.os.DropBoxManager;
+import android.service.NetworkStatsRecorderProto;
import android.util.Log;
import android.util.MathUtils;
import android.util.Slog;
+import android.util.proto.ProtoOutputStream;
import com.android.internal.net.VpnInfo;
import com.android.internal.util.FileRotator;
@@ -465,6 +467,15 @@
}
}
+ public void writeToProtoLocked(ProtoOutputStream proto, long tag) {
+ final long start = proto.start(tag);
+ if (mPending != null) {
+ proto.write(NetworkStatsRecorderProto.PENDING_TOTAL_BYTES, mPending.getTotalBytes());
+ }
+ getOrLoadCompleteLocked().writeToProto(proto, NetworkStatsRecorderProto.COMPLETE_HISTORY);
+ proto.end(start);
+ }
+
public void dumpCheckin(PrintWriter pw, long start, long end) {
// Only load and dump stats from the requested window
getOrLoadPartialLocked(start, end).dumpCheckin(pw, start, end);
diff --git a/services/core/java/com/android/server/net/NetworkStatsService.java b/services/core/java/com/android/server/net/NetworkStatsService.java
index 386e78b..104c296 100644
--- a/services/core/java/com/android/server/net/NetworkStatsService.java
+++ b/services/core/java/com/android/server/net/NetworkStatsService.java
@@ -104,6 +104,8 @@
import android.os.UserHandle;
import android.provider.Settings;
import android.provider.Settings.Global;
+import android.service.NetworkInterfaceProto;
+import android.service.NetworkStatsServiceDumpProto;
import android.telephony.TelephonyManager;
import android.text.format.DateUtils;
import android.util.ArrayMap;
@@ -115,6 +117,7 @@
import android.util.Slog;
import android.util.SparseIntArray;
import android.util.TrustedTime;
+import android.util.proto.ProtoOutputStream;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.net.VpnInfo;
@@ -1255,6 +1258,12 @@
final IndentingPrintWriter pw = new IndentingPrintWriter(rawWriter, " ");
synchronized (mStatsLock) {
+ if (args.length > 0 && "--proto".equals(args[0])) {
+ // In this case ignore all other arguments.
+ dumpProto(fd);
+ return;
+ }
+
if (poll) {
performPollLocked(FLAG_PERSIST_ALL | FLAG_PERSIST_FORCE);
pw.println("Forced poll");
@@ -1327,6 +1336,33 @@
}
}
+ private void dumpProto(FileDescriptor fd) {
+ final ProtoOutputStream proto = new ProtoOutputStream(fd);
+
+ // TODO Right now it writes all history. Should it limit to the "since-boot" log?
+
+ dumpInterfaces(proto, NetworkStatsServiceDumpProto.ACTIVE_INTERFACES, mActiveIfaces);
+ dumpInterfaces(proto, NetworkStatsServiceDumpProto.ACTIVE_UID_INTERFACES, mActiveUidIfaces);
+ mDevRecorder.writeToProtoLocked(proto, NetworkStatsServiceDumpProto.DEV_STATS);
+ mXtRecorder.writeToProtoLocked(proto, NetworkStatsServiceDumpProto.XT_STATS);
+ mUidRecorder.writeToProtoLocked(proto, NetworkStatsServiceDumpProto.UID_STATS);
+ mUidTagRecorder.writeToProtoLocked(proto, NetworkStatsServiceDumpProto.UID_TAG_STATS);
+
+ proto.flush();
+ }
+
+ private static void dumpInterfaces(ProtoOutputStream proto, long tag,
+ ArrayMap<String, NetworkIdentitySet> ifaces) {
+ for (int i = 0; i < ifaces.size(); i++) {
+ final long start = proto.start(tag);
+
+ proto.write(NetworkInterfaceProto.INTERFACE, ifaces.keyAt(i));
+ ifaces.valueAt(i).writeToProto(proto, NetworkInterfaceProto.IDENTITIES);
+
+ proto.end(start);
+ }
+ }
+
/**
* Return snapshot of current UID statistics, including any
* {@link TrafficStats#UID_TETHERING} and {@link #mUidOperations} values.
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index d25abbf..d516acf 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -145,6 +145,7 @@
private static final String ATTR_ABI_OVERRIDE = "abiOverride";
private static final String ATTR_VOLUME_UUID = "volumeUuid";
private static final String ATTR_NAME = "name";
+ private static final String ATTR_INSTALL_REASON = "installRason";
/** Automatically destroy sessions older than this */
private static final long MAX_AGE_MILLIS = 3 * DateUtils.DAY_IN_MILLIS;
@@ -412,6 +413,7 @@
params.abiOverride = readStringAttribute(in, ATTR_ABI_OVERRIDE);
params.volumeUuid = readStringAttribute(in, ATTR_VOLUME_UUID);
params.grantedRuntimePermissions = readGrantedRuntimePermissions(in);
+ params.installReason = readIntAttribute(in, ATTR_INSTALL_REASON);
final File appIconFile = buildAppIconFile(sessionId);
if (appIconFile.exists()) {
@@ -484,6 +486,7 @@
writeUriAttribute(out, ATTR_REFERRER_URI, params.referrerUri);
writeStringAttribute(out, ATTR_ABI_OVERRIDE, params.abiOverride);
writeStringAttribute(out, ATTR_VOLUME_UUID, params.volumeUuid);
+ writeIntAttribute(out, ATTR_INSTALL_REASON, params.installReason);
// Persist app icon if changed since last written
final File appIconFile = buildAppIconFile(session.sessionId);
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 66c3d44..c793f49 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -80,7 +80,6 @@
import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER;
import static android.system.OsConstants.O_CREAT;
import static android.system.OsConstants.O_RDWR;
-
import static com.android.internal.app.IntentForwarderActivity.FORWARD_INTENT_TO_MANAGED_PROFILE;
import static com.android.internal.app.IntentForwarderActivity.FORWARD_INTENT_TO_PARENT;
import static com.android.internal.content.NativeLibraryHelper.LIB64_DIR_NAME;
@@ -5965,7 +5964,7 @@
int status = (int)(packedStatus >> 32);
int linkGeneration = (int)(packedStatus & 0xFFFFFFFF);
if (status == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS) {
- if (DEBUG_DOMAIN_VERIFICATION) {
+ if (DEBUG_DOMAIN_VERIFICATION || debug) {
Slog.i(TAG, " + always: " + info.activityInfo.packageName
+ " : linkgen=" + linkGeneration);
}
@@ -5974,18 +5973,18 @@
info.preferredOrder = linkGeneration;
alwaysList.add(info);
} else if (status == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER) {
- if (DEBUG_DOMAIN_VERIFICATION) {
+ if (DEBUG_DOMAIN_VERIFICATION || debug) {
Slog.i(TAG, " + never: " + info.activityInfo.packageName);
}
neverList.add(info);
} else if (status == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS_ASK) {
- if (DEBUG_DOMAIN_VERIFICATION) {
+ if (DEBUG_DOMAIN_VERIFICATION || debug) {
Slog.i(TAG, " + always-ask: " + info.activityInfo.packageName);
}
alwaysAskList.add(info);
} else if (status == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED ||
status == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ASK) {
- if (DEBUG_DOMAIN_VERIFICATION) {
+ if (DEBUG_DOMAIN_VERIFICATION || debug) {
Slog.i(TAG, " + ask: " + info.activityInfo.packageName);
}
undefinedList.add(info);
@@ -12143,7 +12142,7 @@
IActivityManager am = ActivityManager.getService();
if (am != null) {
try {
- am.startService(null, intent, null, mContext.getOpPackageName(),
+ am.startService(null, intent, null, -1, null, mContext.getOpPackageName(),
UserHandle.USER_SYSTEM);
} catch (RemoteException e) {
}
@@ -12217,6 +12216,67 @@
mHandler.sendMessage(msg);
}
+
+ /**
+ * Ensure that the install reason matches what we know about the package installer (e.g. whether
+ * it is acting on behalf on an enterprise or the user).
+ *
+ * Note that the ordering of the conditionals in this method is important. The checks we perform
+ * are as follows, in this order:
+ *
+ * 1) If the install is being performed by a system app, we can trust the app to have set the
+ * install reason correctly. Thus, we pass through the install reason unchanged, no matter
+ * what it is.
+ * 2) If the install is being performed by a device or profile owner app, the install reason
+ * should be enterprise policy. However, we cannot be sure that the device or profile owner
+ * set the install reason correctly. If the app targets an older SDK version where install
+ * reasons did not exist yet, or if the app author simply forgot, the install reason may be
+ * unset or wrong. Thus, we force the install reason to be enterprise policy.
+ * 3) In all other cases, the install is being performed by a regular app that is neither part
+ * of the system nor a device or profile owner. We have no reason to believe that this app is
+ * acting on behalf of the enterprise admin. Thus, we check whether the install reason was
+ * set to enterprise policy and if so, change it to unknown instead.
+ */
+ private int fixUpInstallReason(String installerPackageName, int installerUid,
+ int installReason) {
+ if (checkUidPermission(android.Manifest.permission.INSTALL_PACKAGES, installerUid)
+ == PERMISSION_GRANTED) {
+ // If the install is being performed by a system app, we trust that app to have set the
+ // install reason correctly.
+ return installReason;
+ }
+
+ final IDevicePolicyManager dpm = IDevicePolicyManager.Stub.asInterface(
+ ServiceManager.getService(Context.DEVICE_POLICY_SERVICE));
+ if (dpm != null) {
+ ComponentName owner = null;
+ try {
+ owner = dpm.getDeviceOwnerComponent(true /* callingUserOnly */);
+ if (owner == null) {
+ owner = dpm.getProfileOwner(UserHandle.getUserId(installerUid));
+ }
+ } catch (RemoteException e) {
+ }
+ if (owner != null && owner.getPackageName().equals(installerPackageName)) {
+ // If the install is being performed by a device or profile owner, the install
+ // reason should be enterprise policy.
+ return PackageManager.INSTALL_REASON_POLICY;
+ }
+ }
+
+ if (installReason == PackageManager.INSTALL_REASON_POLICY) {
+ // If the install is being performed by a regular app (i.e. neither system app nor
+ // device or profile owner), we have no reason to believe that the app is acting on
+ // behalf of an enterprise. If the app set the install reason to enterprise policy,
+ // change it to unknown instead.
+ return PackageManager.INSTALL_REASON_UNKNOWN;
+ }
+
+ // If the install is being performed by a regular app and the install reason was set to any
+ // value but enterprise policy, leave the install reason unchanged.
+ return installReason;
+ }
+
void installStage(String packageName, File stagedDir, String stagedCid,
IPackageInstallObserver2 observer, PackageInstaller.SessionParams sessionParams,
String installerPackageName, int installerUid, UserHandle user,
@@ -12238,10 +12298,12 @@
}
final Message msg = mHandler.obtainMessage(INIT_COPY);
+ final int installReason = fixUpInstallReason(installerPackageName, installerUid,
+ sessionParams.installReason);
final InstallParams params = new InstallParams(origin, null, observer,
sessionParams.installFlags, installerPackageName, sessionParams.volumeUuid,
verificationInfo, user, sessionParams.abiOverride,
- sessionParams.grantedRuntimePermissions, certificates, sessionParams.installReason);
+ sessionParams.grantedRuntimePermissions, certificates, installReason);
params.setTraceMethod("installStage").setTraceCookie(System.identityHashCode(params));
msg.obj = params;
@@ -16564,7 +16626,8 @@
extras, 0, null, null, removedUsers);
if (dataRemoved && !isRemovedPackageSystemUpdate) {
sendPackageBroadcast(Intent.ACTION_PACKAGE_FULLY_REMOVED,
- removedPackage, extras, 0, null, null, removedUsers);
+ removedPackage, extras, Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND,
+ null, null, removedUsers);
}
}
if (removedAppId >= 0) {
@@ -19518,7 +19581,8 @@
count = 0;
for (PackageSetting ps : allPackageSettings) {
final long status = ps.getDomainVerificationStatusForUser(userId);
- if (status >> 32 == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED) {
+ if (status >> 32 == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED
+ && !DEBUG_DOMAIN_VERIFICATION) {
continue;
}
pw.println(prefix + "Package: " + ps.name);
diff --git a/services/core/java/com/android/server/storage/DeviceStorageMonitorService.java b/services/core/java/com/android/server/storage/DeviceStorageMonitorService.java
index 90c711a..a3837b2 100644
--- a/services/core/java/com/android/server/storage/DeviceStorageMonitorService.java
+++ b/services/core/java/com/android/server/storage/DeviceStorageMonitorService.java
@@ -338,9 +338,11 @@
mTotalMemory = (long)mDataFileStats.getBlockCount() *
mDataFileStats.getBlockSize();
mStorageLowIntent = new Intent(Intent.ACTION_DEVICE_STORAGE_LOW);
- mStorageLowIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
+ mStorageLowIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT
+ | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
mStorageOkIntent = new Intent(Intent.ACTION_DEVICE_STORAGE_OK);
- mStorageOkIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
+ mStorageOkIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT
+ | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
mStorageFullIntent = new Intent(Intent.ACTION_DEVICE_STORAGE_FULL);
mStorageFullIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
mStorageNotFullIntent = new Intent(Intent.ACTION_DEVICE_STORAGE_NOT_FULL);
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 7325daab..4b680e5 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -637,6 +637,7 @@
return mFillsParent || !StackId.isTaskResizeAllowed(mStack.mStackId);
}
+ @Override
TaskWindowContainerController getController() {
return (TaskWindowContainerController) super.getController();
}
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
index c3e3141..4a09423 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
@@ -35,7 +35,6 @@
import android.os.Message;
import android.os.RemoteException;
import android.util.Slog;
-import android.view.Display;
import android.view.IWindowSession;
import android.view.Surface;
import android.view.View;
@@ -147,6 +146,7 @@
if (reportNextDraw) {
reportDrawn();
}
+ mSurface.release();
}
private void reportDrawn() {
diff --git a/services/core/java/com/android/server/wm/TaskWindowContainerController.java b/services/core/java/com/android/server/wm/TaskWindowContainerController.java
index 0ca1887..26e36dc 100644
--- a/services/core/java/com/android/server/wm/TaskWindowContainerController.java
+++ b/services/core/java/com/android/server/wm/TaskWindowContainerController.java
@@ -52,7 +52,9 @@
public void handleMessage(Message msg) {
switch (msg.what) {
case REPORT_SNAPSHOT_CHANGED:
- mListener.onSnapshotChanged((TaskSnapshot) msg.obj);
+ if (mListener != null) {
+ mListener.onSnapshotChanged((TaskSnapshot) msg.obj);
+ }
break;
}
}
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index 27efd05..6791da9 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -1462,7 +1462,11 @@
NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
PointerIcon pointerIcon;
- android_view_PointerIcon_getLoadedIcon(env, iconObj, &pointerIcon);
+ status_t result = android_view_PointerIcon_getLoadedIcon(env, iconObj, &pointerIcon);
+ if (result) {
+ jniThrowRuntimeException(env, "Failed to load custom pointer icon.");
+ return;
+ }
SpriteIcon spriteIcon;
pointerIcon.bitmap.copyTo(&spriteIcon.bitmap, kN32_SkColorType);
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 8aebfec..111f37f 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -191,6 +191,7 @@
import java.util.List;
import java.util.Map.Entry;
import java.util.Set;
+import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
/**
@@ -235,7 +236,7 @@
private static final int REQUEST_EXPIRE_PASSWORD = 5571;
- private static final long MS_PER_DAY = 86400 * 1000;
+ private static final long MS_PER_DAY = TimeUnit.DAYS.toMillis(1);
private static final long EXPIRATION_GRACE_PERIOD_MS = 5 * MS_PER_DAY; // 5 days, in ms
@@ -330,7 +331,7 @@
* Minimum timeout in milliseconds after which unlocking with weak auth times out,
* i.e. the user has to use a strong authentication method like password, PIN or pattern.
*/
- private static final long MINIMUM_STRONG_AUTH_TIMEOUT_MS = 1 * 60 * 60 * 1000; // 1h
+ private static final long MINIMUM_STRONG_AUTH_TIMEOUT_MS = TimeUnit.HOURS.toMillis(1);
/**
* Strings logged with {@link
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 08fb591..157ea85 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -955,6 +955,21 @@
}
traceEnd();
+ // Wifi Service must be started first for wifi-related services.
+ traceBeginAndSlog("StartWifi");
+ mSystemServiceManager.startService(WIFI_SERVICE_CLASS);
+ traceEnd();
+ traceBeginAndSlog("StartWifiScanning");
+ mSystemServiceManager.startService(
+ "com.android.server.wifi.scanner.WifiScanningService");
+ traceEnd();
+
+ if (!disableRtt) {
+ traceBeginAndSlog("StartWifiRtt");
+ mSystemServiceManager.startService("com.android.server.wifi.RttService");
+ traceEnd();
+ }
+
if (context.getPackageManager().hasSystemFeature(
PackageManager.FEATURE_WIFI_AWARE)) {
traceBeginAndSlog("StartWifiAware");
@@ -970,19 +985,6 @@
mSystemServiceManager.startService(WIFI_P2P_SERVICE_CLASS);
traceEnd();
}
- traceBeginAndSlog("StartWifi");
- mSystemServiceManager.startService(WIFI_SERVICE_CLASS);
- traceEnd();
- traceBeginAndSlog("StartWifiScanning");
- mSystemServiceManager.startService(
- "com.android.server.wifi.scanner.WifiScanningService");
- traceEnd();
-
- if (!disableRtt) {
- traceBeginAndSlog("StartWifiRtt");
- mSystemServiceManager.startService("com.android.server.wifi.RttService");
- traceEnd();
- }
if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_ETHERNET) ||
mPackageManager.hasSystemFeature(PackageManager.FEATURE_USB_HOST)) {
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 3274df0..8da47c8 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -66,9 +66,11 @@
import java.util.List;
import java.util.Map;
import java.util.Set;
+import java.util.concurrent.TimeUnit;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyInt;
+import static org.mockito.Matchers.anyLong;
import static org.mockito.Matchers.anyObject;
import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.eq;
@@ -2117,8 +2119,37 @@
setupDeviceOwner();
mContext.callerPermissions.add(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS);
- final long MINIMUM_STRONG_AUTH_TIMEOUT_MS = 1 * 60 * 60 * 1000; // 1h
- final long ONE_MINUTE = 60 * 1000;
+ final long MINIMUM_STRONG_AUTH_TIMEOUT_MS = TimeUnit.HOURS.toMillis(1);
+ final long ONE_MINUTE = TimeUnit.MINUTES.toMillis(1);
+ final long MIN_PLUS_ONE_MINUTE = MINIMUM_STRONG_AUTH_TIMEOUT_MS + ONE_MINUTE;
+ final long MAX_MINUS_ONE_MINUTE = DevicePolicyManager.DEFAULT_STRONG_AUTH_TIMEOUT_MS
+ - ONE_MINUTE;
+
+ // verify that the minimum timeout cannot be modified on user builds (system property is
+ // not being read)
+ mContext.buildMock.isDebuggable = false;
+
+ dpm.setRequiredStrongAuthTimeout(admin1, MAX_MINUS_ONE_MINUTE);
+ assertEquals(dpm.getRequiredStrongAuthTimeout(admin1), MAX_MINUS_ONE_MINUTE);
+ assertEquals(dpm.getRequiredStrongAuthTimeout(null), MAX_MINUS_ONE_MINUTE);
+
+ verify(mContext.systemProperties, never()).getLong(anyString(), anyLong());
+
+ // restore to the debuggable build state
+ mContext.buildMock.isDebuggable = true;
+
+ // Always return the default (second arg) when getting system property for long type
+ when(mContext.systemProperties.getLong(anyString(), anyLong())).thenAnswer(
+ new Answer<Long>() {
+ @Override
+ public Long answer(InvocationOnMock invocation) throws Throwable {
+ return (Long) invocation.getArguments()[1];
+ }
+ }
+ );
+
+ // reset to default (0 means the admin is not participating, so default should be returned)
+ dpm.setRequiredStrongAuthTimeout(admin1, 0);
// aggregation should be the default if unset by any admin
assertEquals(dpm.getRequiredStrongAuthTimeout(null),
@@ -2135,7 +2166,7 @@
assertEquals(dpm.getRequiredStrongAuthTimeout(null),
DevicePolicyManager.DEFAULT_STRONG_AUTH_TIMEOUT_MS);
- // 0 means default
+ // 0 means the admin is not participating, so default should be returned
dpm.setRequiredStrongAuthTimeout(admin1, 0);
assertEquals(dpm.getRequiredStrongAuthTimeout(admin1), 0);
assertEquals(dpm.getRequiredStrongAuthTimeout(null),
@@ -2146,12 +2177,14 @@
assertEquals(dpm.getRequiredStrongAuthTimeout(admin1), MINIMUM_STRONG_AUTH_TIMEOUT_MS);
assertEquals(dpm.getRequiredStrongAuthTimeout(null), MINIMUM_STRONG_AUTH_TIMEOUT_MS);
- // value within range
- dpm.setRequiredStrongAuthTimeout(admin1, MINIMUM_STRONG_AUTH_TIMEOUT_MS + ONE_MINUTE);
- assertEquals(dpm.getRequiredStrongAuthTimeout(admin1), MINIMUM_STRONG_AUTH_TIMEOUT_MS
- + ONE_MINUTE);
- assertEquals(dpm.getRequiredStrongAuthTimeout(null), MINIMUM_STRONG_AUTH_TIMEOUT_MS
- + ONE_MINUTE);
+ // values within range
+ dpm.setRequiredStrongAuthTimeout(admin1, MIN_PLUS_ONE_MINUTE);
+ assertEquals(dpm.getRequiredStrongAuthTimeout(admin1), MIN_PLUS_ONE_MINUTE);
+ assertEquals(dpm.getRequiredStrongAuthTimeout(null), MIN_PLUS_ONE_MINUTE);
+
+ dpm.setRequiredStrongAuthTimeout(admin1, MAX_MINUS_ONE_MINUTE);
+ assertEquals(dpm.getRequiredStrongAuthTimeout(admin1), MAX_MINUS_ONE_MINUTE);
+ assertEquals(dpm.getRequiredStrongAuthTimeout(null), MAX_MINUS_ONE_MINUTE);
// reset to default
dpm.setRequiredStrongAuthTimeout(admin1, 0);
diff --git a/services/usb/java/com/android/server/usb/UsbPortManager.java b/services/usb/java/com/android/server/usb/UsbPortManager.java
index 7f182a4..4aff3d54 100644
--- a/services/usb/java/com/android/server/usb/UsbPortManager.java
+++ b/services/usb/java/com/android/server/usb/UsbPortManager.java
@@ -556,7 +556,9 @@
private void sendPortChangedBroadcastLocked(PortInfo portInfo) {
final Intent intent = new Intent(UsbManager.ACTION_USB_PORT_CHANGED);
- intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
+ intent.addFlags(
+ Intent.FLAG_RECEIVER_FOREGROUND |
+ Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
intent.putExtra(UsbManager.EXTRA_PORT, portInfo.mUsbPort);
intent.putExtra(UsbManager.EXTRA_PORT_STATUS, portInfo.mUsbPortStatus);
diff --git a/services/usb/java/com/android/server/usb/UsbProfileGroupSettingsManager.java b/services/usb/java/com/android/server/usb/UsbProfileGroupSettingsManager.java
index e03a14f..2e99b6e 100644
--- a/services/usb/java/com/android/server/usb/UsbProfileGroupSettingsManager.java
+++ b/services/usb/java/com/android/server/usb/UsbProfileGroupSettingsManager.java
@@ -971,7 +971,9 @@
public void accessoryAttached(UsbAccessory accessory) {
Intent intent = new Intent(UsbManager.ACTION_USB_ACCESSORY_ATTACHED);
intent.putExtra(UsbManager.EXTRA_ACCESSORY, accessory);
- intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ intent.addFlags(
+ Intent.FLAG_ACTIVITY_NEW_TASK |
+ Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
ArrayList<ResolveInfo> matches;
String defaultPackage = null;
@@ -1364,7 +1366,9 @@
private static Intent createDeviceAttachedIntent(UsbDevice device) {
Intent intent = new Intent(UsbManager.ACTION_USB_DEVICE_ATTACHED);
intent.putExtra(UsbManager.EXTRA_DEVICE, device);
- intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ intent.addFlags(
+ Intent.FLAG_ACTIVITY_NEW_TASK |
+ Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
return intent;
}
}
diff --git a/services/usb/java/com/android/server/usb/UsbSettingsManager.java b/services/usb/java/com/android/server/usb/UsbSettingsManager.java
index 24d5f09..7a55be4 100644
--- a/services/usb/java/com/android/server/usb/UsbSettingsManager.java
+++ b/services/usb/java/com/android/server/usb/UsbSettingsManager.java
@@ -182,6 +182,7 @@
}
Intent intent = new Intent(UsbManager.ACTION_USB_DEVICE_DETACHED);
+ intent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
intent.putExtra(UsbManager.EXTRA_DEVICE, device);
if (DEBUG) {
@@ -204,6 +205,7 @@
}
Intent intent = new Intent(UsbManager.ACTION_USB_ACCESSORY_DETACHED);
+ intent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
intent.putExtra(UsbManager.EXTRA_ACCESSORY, accessory);
mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
}
diff --git a/test-runner/src/android/test/mock/MockContext.java b/test-runner/src/android/test/mock/MockContext.java
index bcc68b3..b6e701e 100644
--- a/test-runner/src/android/test/mock/MockContext.java
+++ b/test-runner/src/android/test/mock/MockContext.java
@@ -19,6 +19,7 @@
import android.annotation.SystemApi;
import android.app.IApplicationThread;
import android.app.IServiceConnection;
+import android.app.Notification;
import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Context;
@@ -513,6 +514,12 @@
}
@Override
+ public ComponentName startServiceInForeground(Intent service,
+ int id, Notification notification) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
public boolean stopService(Intent service) {
throw new UnsupportedOperationException();
}
@@ -525,6 +532,13 @@
/** @hide */
@Override
+ public ComponentName startServiceInForegroundAsUser(Intent service,
+ int id, Notification notification, UserHandle user) {
+ throw new UnsupportedOperationException();
+ }
+
+ /** @hide */
+ @Override
public boolean stopServiceAsUser(Intent service, UserHandle user) {
throw new UnsupportedOperationException();
}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
index 663e56d..68680d5 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
@@ -40,6 +40,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.app.Notification;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.ContentResolver;
@@ -1820,6 +1821,13 @@
}
@Override
+ public ComponentName startServiceInForeground(Intent service,
+ int id, Notification notification) {
+ // pass
+ return null;
+ }
+
+ @Override
public boolean stopService(Intent arg0) {
// pass
return false;
@@ -1832,6 +1840,13 @@
}
@Override
+ public ComponentName startServiceInForegroundAsUser(Intent service,
+ int id, Notification notification, UserHandle user) {
+ // pass
+ return null;
+ }
+
+ @Override
public boolean stopServiceAsUser(Intent arg0, UserHandle arg1) {
// pass
return false;