summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cmds/bootanimation/BootAnimation.cpp9
-rw-r--r--core/java/android/app/Fragment.java7
-rw-r--r--core/java/android/app/FragmentTransaction.java47
-rw-r--r--core/java/android/content/SharedPreferences.java4
-rw-r--r--core/java/android/hardware/fingerprint/FingerprintManager.java21
-rw-r--r--core/java/android/hardware/fingerprint/IFingerprintServiceReceiver.aidl2
-rw-r--r--core/java/android/os/Build.java42
-rw-r--r--core/java/android/view/Choreographer.java3
-rw-r--r--core/java/android/view/View.java4
-rw-r--r--core/java/android/view/animation/AnimationUtils.java34
-rw-r--r--core/java/com/android/internal/policy/BackdropFrameRenderer.java2
-rw-r--r--docs/html/guide/topics/ui/notifiers/toasts.jd31
-rw-r--r--docs/html/topic/libraries/data-binding/index.jd12
-rw-r--r--docs/html/training/articles/assistant.jd306
-rw-r--r--graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java2
-rw-r--r--keystore/java/android/security/keystore/KeyGenParameterSpec.java28
-rw-r--r--libs/hwui/PropertyValuesAnimatorSet.cpp2
-rw-r--r--libs/hwui/RecordingCanvas.cpp71
-rw-r--r--libs/hwui/RecordingCanvas.h2
-rw-r--r--libs/hwui/tests/unit/RecordingCanvasTests.cpp15
-rw-r--r--packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java61
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/views/DropTarget.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java14
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/views/RecentsViewTouchHandler.java20
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/KeyguardAffordanceView.java2
-rw-r--r--packages/WallpaperBackup/AndroidManifest.xml1
-rw-r--r--packages/WallpaperBackup/src/com/android/wallpaperbackup/WallpaperBackupAgent.java2
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java26
-rw-r--r--services/core/java/com/android/server/am/AppErrors.java3
-rw-r--r--services/core/java/com/android/server/am/RecentTasks.java11
-rw-r--r--services/core/java/com/android/server/connectivity/Tethering.java10
-rw-r--r--services/core/java/com/android/server/connectivity/tethering/IPv6TetheringCoordinator.java18
-rw-r--r--services/core/java/com/android/server/connectivity/tethering/IPv6TetheringInterfaceServices.java208
-rw-r--r--services/core/java/com/android/server/fingerprint/AuthenticationClient.java6
-rw-r--r--services/core/java/com/android/server/fingerprint/FingerprintService.java16
-rw-r--r--services/core/java/com/android/server/notification/ConditionProviders.java21
-rw-r--r--services/core/java/com/android/server/notification/ManagedServices.java6
-rw-r--r--services/core/java/com/android/server/notification/NotificationManagerService.java20
-rw-r--r--services/core/java/com/android/server/notification/RankingHelper.java4
-rw-r--r--services/core/java/com/android/server/notification/ZenModeConditions.java1
-rw-r--r--services/core/java/com/android/server/pm/ShortcutPackage.java4
-rw-r--r--services/core/java/com/android/server/pm/ShortcutService.java56
-rw-r--r--services/net/java/android/net/ip/RouterAdvertisementDaemon.java234
-rw-r--r--services/tests/servicestests/assets/shortcut/shortcut_legacy_file.xml2
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java175
47 files changed, 1033 insertions, 550 deletions
diff --git a/cmds/bootanimation/BootAnimation.cpp b/cmds/bootanimation/BootAnimation.cpp
index ebcc9ff0451f..495e56ce64b3 100644
--- a/cmds/bootanimation/BootAnimation.cpp
+++ b/cmds/bootanimation/BootAnimation.cpp
@@ -75,6 +75,7 @@ static const char ACCURATE_TIME_FLAG_FILE_PATH[] = "/data/system/time/time_is_ac
// Java timestamp format. Don't show the clock if the date is before 2000-01-01 00:00:00.
static const long long ACCURATE_TIME_EPOCH = 946684800000;
static const char EXIT_PROP_NAME[] = "service.bootanim.exit";
+static const char PLAY_SOUND_PROP_NAME[] = "persist.sys.bootanim.play_sound";
static const int ANIM_ENTRY_NAME_MAX = 256;
// ---------------------------------------------------------------------------
@@ -778,8 +779,12 @@ bool BootAnimation::playAnimation(const Animation& animation)
// only play audio file the first time we animate the part
if (r == 0 && part.audioData) {
- ALOGD("playing clip for part%d, size=%d", (int) i, part.audioLength);
- audioplay::playClip(part.audioData, part.audioLength);
+ // Read the system property to see if we should play the sound.
+ // If not present, default to playing it.
+ if (property_get_bool(PLAY_SOUND_PROP_NAME, 1)) {
+ ALOGD("playing clip for part%d, size=%d", (int) i, part.audioLength);
+ audioplay::playClip(part.audioData, part.audioLength);
+ }
}
glClearColor(
diff --git a/core/java/android/app/Fragment.java b/core/java/android/app/Fragment.java
index a637ef4f9c54..a5ee68b2e4e5 100644
--- a/core/java/android/app/Fragment.java
+++ b/core/java/android/app/Fragment.java
@@ -1483,9 +1483,10 @@ public class Fragment implements ComponentCallbacks2, OnCreateContextMenuListene
* at this point. If you want to do work once the activity itself is
* created, see {@link #onActivityCreated(Bundle)}.
*
- * <p>If your app's <code>targetSdkVersion</code> is 23 or lower, child fragments
- * being restored from the savedInstanceState are restored after <code>onCreate</code>
- * returns. When targeting N or above and running on an N or newer platform version
+ * <p>If your app's <code>targetSdkVersion</code> is {@link android.os.Build.VERSION_CODES#M}
+ * or lower, child fragments being restored from the savedInstanceState are restored after
+ * <code>onCreate</code> returns. When targeting @link android.os.Build.VERSION_CODES#N} or
+ * above and running on an N or newer platform version
* they are restored by <code>Fragment.onCreate</code>.</p>
*
* @param savedInstanceState If the fragment is being re-created from
diff --git a/core/java/android/app/FragmentTransaction.java b/core/java/android/app/FragmentTransaction.java
index e435580d55fe..633e85b78118 100644
--- a/core/java/android/app/FragmentTransaction.java
+++ b/core/java/android/app/FragmentTransaction.java
@@ -17,7 +17,8 @@ import java.lang.annotation.RetentionPolicy;
* <div class="special reference">
* <h3>Developer Guides</h3>
* <p>For more information about using fragments, read the
- * <a href="{@docRoot}guide/topics/fundamentals/fragments.html">Fragments</a> developer guide.</p>
+ * <a href="{@docRoot}guide/components/fragments.html">Fragments</a> developer
+ * guide.</p>
* </div>
*/
public abstract class FragmentTransaction {
@@ -25,17 +26,17 @@ public abstract class FragmentTransaction {
* Calls {@link #add(int, Fragment, String)} with a 0 containerViewId.
*/
public abstract FragmentTransaction add(Fragment fragment, String tag);
-
+
/**
* Calls {@link #add(int, Fragment, String)} with a null tag.
*/
public abstract FragmentTransaction add(@IdRes int containerViewId, Fragment fragment);
-
+
/**
* Add a fragment to the activity state. This fragment may optionally
* also have its view (if {@link Fragment#onCreateView Fragment.onCreateView}
* returns non-null) inserted into a container view of the activity.
- *
+ *
* @param containerViewId Optional identifier of the container this fragment is
* to be placed in. If 0, it will not be placed in a container.
* @param fragment The fragment to be added. This fragment must not already
@@ -43,64 +44,64 @@ public abstract class FragmentTransaction {
* @param tag Optional tag name for the fragment, to later retrieve the
* fragment with {@link FragmentManager#findFragmentByTag(String)
* FragmentManager.findFragmentByTag(String)}.
- *
+ *
* @return Returns the same FragmentTransaction instance.
*/
public abstract FragmentTransaction add(@IdRes int containerViewId, Fragment fragment,
String tag);
-
+
/**
* Calls {@link #replace(int, Fragment, String)} with a null tag.
*/
public abstract FragmentTransaction replace(@IdRes int containerViewId, Fragment fragment);
-
+
/**
* Replace an existing fragment that was added to a container. This is
* essentially the same as calling {@link #remove(Fragment)} for all
* currently added fragments that were added with the same containerViewId
* and then {@link #add(int, Fragment, String)} with the same arguments
* given here.
- *
+ *
* @param containerViewId Identifier of the container whose fragment(s) are
* to be replaced.
* @param fragment The new fragment to place in the container.
* @param tag Optional tag name for the fragment, to later retrieve the
* fragment with {@link FragmentManager#findFragmentByTag(String)
* FragmentManager.findFragmentByTag(String)}.
- *
+ *
* @return Returns the same FragmentTransaction instance.
*/
public abstract FragmentTransaction replace(@IdRes int containerViewId, Fragment fragment,
String tag);
-
+
/**
* Remove an existing fragment. If it was added to a container, its view
* is also removed from that container.
- *
+ *
* @param fragment The fragment to be removed.
- *
+ *
* @return Returns the same FragmentTransaction instance.
*/
public abstract FragmentTransaction remove(Fragment fragment);
-
+
/**
* Hides an existing fragment. This is only relevant for fragments whose
* views have been added to a container, as this will cause the view to
* be hidden.
- *
+ *
* @param fragment The fragment to be hidden.
- *
+ *
* @return Returns the same FragmentTransaction instance.
*/
public abstract FragmentTransaction hide(Fragment fragment);
-
+
/**
* Shows a previously hidden fragment. This is only relevant for fragments whose
* views have been added to a container, as this will cause the view to
* be shown.
- *
+ *
* @param fragment The fragment to be shown.
- *
+ *
* @return Returns the same FragmentTransaction instance.
*/
public abstract FragmentTransaction show(Fragment fragment);
@@ -135,17 +136,17 @@ public abstract class FragmentTransaction {
* <code>false</code> otherwise.
*/
public abstract boolean isEmpty();
-
+
/**
* Bit mask that is set for all enter transitions.
*/
public static final int TRANSIT_ENTER_MASK = 0x1000;
-
+
/**
* Bit mask that is set for all exit transitions.
*/
public static final int TRANSIT_EXIT_MASK = 0x2000;
-
+
/** Not set up for a transition. */
public static final int TRANSIT_UNSET = -1;
/** No animation for transition. */
@@ -202,7 +203,7 @@ public abstract class FragmentTransaction {
* animations.
*/
public abstract FragmentTransaction setTransitionStyle(@StyleRes int styleRes);
-
+
/**
* Add this transaction to the back stack. This means that the transaction
* will be remembered after it is committed, and will reverse its operation
@@ -269,7 +270,7 @@ public abstract class FragmentTransaction {
* because the state after the commit can be lost if the activity needs to
* be restored from its state. See {@link #commitAllowingStateLoss()} for
* situations where it may be okay to lose the commit.</p>
- *
+ *
* @return Returns the identifier of this transaction's back stack entry,
* if {@link #addToBackStack(String)} had been called. Otherwise, returns
* a negative number.
diff --git a/core/java/android/content/SharedPreferences.java b/core/java/android/content/SharedPreferences.java
index 7f9e17641a19..4b09feda2173 100644
--- a/core/java/android/content/SharedPreferences.java
+++ b/core/java/android/content/SharedPreferences.java
@@ -72,7 +72,9 @@ public interface SharedPreferences {
* {@link #commit} or {@link #apply} are called.
*
* @param key The name of the preference to modify.
- * @param value The new value for the preference.
+ * @param value The new value for the preference. Passing {@code null}
+ * for this argument is equivalent to calling {@link #remove(String)} with
+ * this key.
*
* @return Returns a reference to the same Editor object, so you can
* chain put calls together.
diff --git a/core/java/android/hardware/fingerprint/FingerprintManager.java b/core/java/android/hardware/fingerprint/FingerprintManager.java
index 1ff2e8a11a87..f17fd55bd22a 100644
--- a/core/java/android/hardware/fingerprint/FingerprintManager.java
+++ b/core/java/android/hardware/fingerprint/FingerprintManager.java
@@ -259,6 +259,7 @@ public class FingerprintManager {
public static class AuthenticationResult {
private Fingerprint mFingerprint;
private CryptoObject mCryptoObject;
+ private int mUserId;
/**
* Authentication result
@@ -267,9 +268,10 @@ public class FingerprintManager {
* @param fingerprint the recognized fingerprint data, if allowed.
* @hide
*/
- public AuthenticationResult(CryptoObject crypto, Fingerprint fingerprint) {
+ public AuthenticationResult(CryptoObject crypto, Fingerprint fingerprint, int userId) {
mCryptoObject = crypto;
mFingerprint = fingerprint;
+ mUserId = userId;
}
/**
@@ -286,6 +288,12 @@ public class FingerprintManager {
* @hide
*/
public Fingerprint getFingerprint() { return mFingerprint; }
+
+ /**
+ * Obtain the userId for which this fingerprint was authenticated.
+ * @hide
+ */
+ public int getUserId() { return mUserId; }
};
/**
@@ -792,7 +800,7 @@ public class FingerprintManager {
sendAcquiredResult((Long) msg.obj /* deviceId */, msg.arg1 /* acquire info */);
break;
case MSG_AUTHENTICATION_SUCCEEDED:
- sendAuthenticatedSucceeded((Fingerprint) msg.obj);
+ sendAuthenticatedSucceeded((Fingerprint) msg.obj, msg.arg1 /* userId */);
break;
case MSG_AUTHENTICATION_FAILED:
sendAuthenticatedFailed();
@@ -840,9 +848,10 @@ public class FingerprintManager {
}
}
- private void sendAuthenticatedSucceeded(Fingerprint fp) {
+ private void sendAuthenticatedSucceeded(Fingerprint fp, int userId) {
if (mAuthenticationCallback != null) {
- final AuthenticationResult result = new AuthenticationResult(mCryptoObject, fp);
+ final AuthenticationResult result =
+ new AuthenticationResult(mCryptoObject, fp, userId);
mAuthenticationCallback.onAuthenticationSucceeded(result);
}
}
@@ -981,8 +990,8 @@ public class FingerprintManager {
}
@Override // binder call
- public void onAuthenticationSucceeded(long deviceId, Fingerprint fp) {
- mHandler.obtainMessage(MSG_AUTHENTICATION_SUCCEEDED, fp).sendToTarget();
+ public void onAuthenticationSucceeded(long deviceId, Fingerprint fp, int userId) {
+ mHandler.obtainMessage(MSG_AUTHENTICATION_SUCCEEDED, userId, 0, fp).sendToTarget();
}
@Override // binder call
diff --git a/core/java/android/hardware/fingerprint/IFingerprintServiceReceiver.aidl b/core/java/android/hardware/fingerprint/IFingerprintServiceReceiver.aidl
index 57a429fe5fa7..b024b29fef06 100644
--- a/core/java/android/hardware/fingerprint/IFingerprintServiceReceiver.aidl
+++ b/core/java/android/hardware/fingerprint/IFingerprintServiceReceiver.aidl
@@ -26,7 +26,7 @@ import android.os.UserHandle;
oneway interface IFingerprintServiceReceiver {
void onEnrollResult(long deviceId, int fingerId, int groupId, int remaining);
void onAcquired(long deviceId, int acquiredInfo);
- void onAuthenticationSucceeded(long deviceId, in Fingerprint fp);
+ void onAuthenticationSucceeded(long deviceId, in Fingerprint fp, int userId);
void onAuthenticationFailed(long deviceId);
void onError(long deviceId, int error);
void onRemoved(long deviceId, int fingerId, int groupId);
diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java
index c5e09bdeae6c..f8ae0625c86b 100644
--- a/core/java/android/os/Build.java
+++ b/core/java/android/os/Build.java
@@ -669,7 +669,47 @@ public class Build {
public static final int M = 23;
/**
- * N is for ¯\_(ツ)_/¯.
+ * N is for Nougat.
+ *
+ * <p>Applications targeting this or a later release will get these
+ * new changes in behavior:</p>
+ * <ul>
+ * <li> {@link android.app.DownloadManager.Request#setAllowedNetworkTypes
+ * DownloadManager.Request.setAllowedNetworkTypes}
+ * will disable "allow over metered" when specifying only
+ * {@link android.app.DownloadManager.Request#NETWORK_WIFI}.
+ * <li> {@link android.app.DownloadManager} no longer allows access to raw
+ * file paths.
+ * <li> {@link android.app.Notification.Builder#setShowWhen
+ * Notification.Builder.setShowWhen}
+ * must be called explicitly to have the time shown, and various other changes in
+ * {@link android.app.Notification.Builder Notification.Builder} to how notifications
+ * are shown.</li>
+ * <li>{@link android.content.Context#MODE_WORLD_READABLE} and
+ * {@link android.content.Context#MODE_WORLD_WRITEABLE} are no longer supported.</li>
+ * <li>{@link android.os.FileUriExposedException} will be thrown to applications.</li>
+ * <li>Applications will see global drag and drops as per
+ * {@link android.view.View#DRAG_FLAG_GLOBAL}.</li>
+ * <li>{@link android.webkit.WebView#evaluateJavascript WebView.evaluateJavascript}
+ * will not persist state from an empty WebView.</li>
+ * <li>{@link android.animation.AnimatorSet} will not ignore calls to end() before
+ * start().</li>
+ * <li>{@link android.app.AlarmManager#cancel(android.app.PendingIntent)
+ * AlarmManager.cancel} will throw a NullPointerException if given a null operation.</li>
+ * <li>{@link android.app.FragmentManager} will ensure fragments have been created
+ * before being placed on the back stack.</li>
+ * <li>{@link android.app.FragmentManager} restores fragments in
+ * {@link android.app.Fragment#onCreate Fragment.onCreate} rather than after the
+ * method returns.</li>
+ * <li>{@link android.R.attr#resizeableActivity} defaults to true.</li>
+ * <li>{@link android.graphics.drawable.AnimatedVectorDrawable} throws exceptions when
+ * opening invalid VectorDrawable animations.</li>
+ * <li>{@link android.view.ViewGroup.MarginLayoutParams} will no longer be dropped
+ * when converting between some types of layout params (such as
+ * {@link android.widget.LinearLayout.LayoutParams LinearLayout.LayoutParams} to
+ * {@link android.widget.RelativeLayout.LayoutParams RelativeLayout.LayoutParams}).</li>
+ * <li>Your application processes will not be killed when the device density changes.</li>
+ * </ul>
*/
public static final int N = 24;
diff --git a/core/java/android/view/Choreographer.java b/core/java/android/view/Choreographer.java
index d3db74d1ea24..3316f3aeb60b 100644
--- a/core/java/android/view/Choreographer.java
+++ b/core/java/android/view/Choreographer.java
@@ -25,6 +25,7 @@ import android.os.SystemProperties;
import android.os.Trace;
import android.util.Log;
import android.util.TimeUtils;
+import android.view.animation.AnimationUtils;
import java.io.PrintWriter;
@@ -608,6 +609,7 @@ public final class Choreographer {
try {
Trace.traceBegin(Trace.TRACE_TAG_VIEW, "Choreographer#doFrame");
+ AnimationUtils.lockAnimationClock(frameTimeNanos / TimeUtils.NANOS_PER_MS);
mFrameInfo.markInputHandlingStart();
doCallbacks(Choreographer.CALLBACK_INPUT, frameTimeNanos);
@@ -620,6 +622,7 @@ public final class Choreographer {
doCallbacks(Choreographer.CALLBACK_COMMIT, frameTimeNanos);
} finally {
+ AnimationUtils.unlockAnimationClock();
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index e7553ec943ec..fc250f269271 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -3771,9 +3771,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
* {@link android.os.Build.VERSION_CODES#N API 24} will be able to participate
* in the drag operation and receive the dragged content.
*
- * If this is the only flag set, then the drag recipient will only have access to text data
+ * <p>If this is the only flag set, then the drag recipient will only have access to text data
* and intents contained in the {@link ClipData} object. Access to URIs contained in the
- * {@link ClipData} is determined by other DRAG_FLAG_GLOBAL_* flags.
+ * {@link ClipData} is determined by other DRAG_FLAG_GLOBAL_* flags</p>
*/
public static final int DRAG_FLAG_GLOBAL = 1 << 8; // 256
diff --git a/core/java/android/view/animation/AnimationUtils.java b/core/java/android/view/animation/AnimationUtils.java
index a54d94c5c2e3..351b6dbd6616 100644
--- a/core/java/android/view/animation/AnimationUtils.java
+++ b/core/java/android/view/animation/AnimationUtils.java
@@ -44,6 +44,31 @@ public class AnimationUtils {
private static final int TOGETHER = 0;
private static final int SEQUENTIALLY = 1;
+ private static class AnimationState {
+ boolean animationClockLocked;
+ long currentVsyncTimeMillis;
+ long lastReportedTimeMillis;
+ };
+
+ private static ThreadLocal<AnimationState> sAnimationState
+ = new ThreadLocal<AnimationState>() {
+ @Override
+ protected AnimationState initialValue() {
+ return new AnimationState();
+ }
+ };
+
+ /** @hide */
+ public static void lockAnimationClock(long vsyncMillis) {
+ AnimationState state = sAnimationState.get();
+ state.animationClockLocked = true;
+ state.currentVsyncTimeMillis = vsyncMillis;
+ }
+
+ /** @hide */
+ public static void unlockAnimationClock() {
+ sAnimationState.get().animationClockLocked = false;
+ }
/**
* Returns the current animation time in milliseconds. This time should be used when invoking
@@ -56,7 +81,14 @@ public class AnimationUtils {
* @see android.os.SystemClock
*/
public static long currentAnimationTimeMillis() {
- return SystemClock.uptimeMillis();
+ AnimationState state = sAnimationState.get();
+ if (state.animationClockLocked) {
+ // It's important that time never rewinds
+ return Math.max(state.currentVsyncTimeMillis,
+ state.lastReportedTimeMillis);
+ }
+ state.lastReportedTimeMillis = SystemClock.uptimeMillis();
+ return state.lastReportedTimeMillis;
}
/**
diff --git a/core/java/com/android/internal/policy/BackdropFrameRenderer.java b/core/java/com/android/internal/policy/BackdropFrameRenderer.java
index 0ab3a41f1469..619303f34c32 100644
--- a/core/java/com/android/internal/policy/BackdropFrameRenderer.java
+++ b/core/java/com/android/internal/policy/BackdropFrameRenderer.java
@@ -385,7 +385,7 @@ public class BackdropFrameRenderer extends Thread implements Choreographer.Frame
final int size = DecorView.getNavBarSize(bottomInset, rightInset, leftInset);
if (DecorView.isNavBarToRightEdge(bottomInset, rightInset)) {
mNavigationBarColor.setBounds(width - size, 0, width, height);
- } else if (DecorView.isNavBarToLeftEdge(bottomInset, rightInset)) {
+ } else if (DecorView.isNavBarToLeftEdge(bottomInset, leftInset)) {
mNavigationBarColor.setBounds(0, 0, size, height);
} else {
mNavigationBarColor.setBounds(0, height - size, width, height);
diff --git a/docs/html/guide/topics/ui/notifiers/toasts.jd b/docs/html/guide/topics/ui/notifiers/toasts.jd
index d9627274fa3e..2262a9ab9567 100644
--- a/docs/html/guide/topics/ui/notifiers/toasts.jd
+++ b/docs/html/guide/topics/ui/notifiers/toasts.jd
@@ -76,16 +76,22 @@ To nudge it down, increase the value of the last parameter.
<h2 id="CustomToastView">Creating a Custom Toast View</h2>
-<p>If a simple text message isn't enough, you can create a customized layout for your
-toast notification. To create a custom layout, define a View layout,
-in XML or in your application code, and pass the root {@link android.view.View} object
-to the {@link android.widget.Toast#setView(View)} method.</p>
-
-<p>For example, you can create the layout for the toast visible in the screenshot to the right
-with the following XML (saved as <em>toast_layout.xml</em>):</p>
+<p>
+ If a simple text message isn't enough, you can create a customized layout
+ for your toast notification. To create a custom layout, define a View
+ layout, in XML or in your application code, and pass the root {@link
+ android.view.View} object to the {@link android.widget.Toast#setView(View)}
+ method.
+</p>
+
+<p>
+ For example, you can create the layout for the toast visible in the
+ screenshot to the right with the following XML (saved as
+ <em>layout/custom_toast.xml</em>):
+</p>
<pre>
&lt;LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/toast_layout_root"
+ android:id="@+id/custom_toast_container"
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
@@ -105,13 +111,16 @@ with the following XML (saved as <em>toast_layout.xml</em>):</p>
&lt;/LinearLayout>
</pre>
-<p>Notice that the ID of the LinearLayout element is "toast_layout_root". You must use this
-ID to inflate the layout from the XML, as shown here:</p>
+<p>
+ Notice that the ID of the LinearLayout element is "custom_toast_container".
+ You must use this ID and the ID of the XML layout file "custom_toast" to
+ inflate the layout, as shown here:
+</p>
<pre>
LayoutInflater inflater = getLayoutInflater();
View layout = inflater.inflate(R.layout.custom_toast,
- (ViewGroup) findViewById(R.id.toast_layout_root));
+ (ViewGroup) findViewById(R.id.custom_toast_container));
TextView text = (TextView) layout.findViewById(R.id.text);
text.setText("This is a custom toast");
diff --git a/docs/html/topic/libraries/data-binding/index.jd b/docs/html/topic/libraries/data-binding/index.jd
index 454bb59e7983..ddcc9f2e7ea8 100644
--- a/docs/html/topic/libraries/data-binding/index.jd
+++ b/docs/html/topic/libraries/data-binding/index.jd
@@ -601,7 +601,7 @@ any business logic inside the callback method that you invoked from the listener
&lt;import type="com.example.User"/&gt;
&lt;import type="java.util.List"/&gt;
&lt;variable name="user" type="User"/&gt;
- &lt;variable name="userList" type="List&lt;User&gt;"/&gt;
+ &lt;variable name="userList" type="List&amp;lt;User&amp;gt;"/&gt;
&lt;/data&gt;
</pre>
<p class="caution">
@@ -945,9 +945,9 @@ android:transitionName='&commat;{"image_" + id}'
&lt;import type="android.util.SparseArray"/&gt;
&lt;import type="java.util.Map"/&gt;
&lt;import type="java.util.List"/&gt;
- &lt;variable name="list" type="List&lt;String&gt;"/&gt;
- &lt;variable name="sparse" type="SparseArray&lt;String&gt;"/&gt;
- &lt;variable name="map" type="Map&lt;String, String&gt;"/&gt;
+ &lt;variable name="list" type="List&amp;lt;String&amp;gt;"/&gt;
+ &lt;variable name="sparse" type="SparseArray&amp;lt;String&amp;gt;"/&gt;
+ &lt;variable name="map" type="Map&amp;lt;String, String&amp;gt;"/&gt;
&lt;variable name="index" type="int"/&gt;
&lt;variable name="key" type="String"/&gt;
&lt;/data&gt;
@@ -1247,7 +1247,7 @@ user.put("age", 17);
<pre>
&lt;data&gt;
&lt;import type="android.databinding.ObservableMap"/&gt;
- &lt;variable name="user" type="ObservableMap&lt;String, Object&gt;"/&gt;
+ &lt;variable name="user" type="ObservableMap&amp;lt;String, Object&amp;gt;"/&gt;
&lt;/data&gt;
&lt;TextView
@@ -1277,7 +1277,7 @@ user.add(17);
&lt;data&gt;
&lt;import type="android.databinding.ObservableList"/&gt;
&lt;import type="com.example.my.app.Fields"/&gt;
- &lt;variable name="user" type="ObservableList&lt;Object&gt;"/&gt;
+ &lt;variable name="user" type="ObservableList&amp;lt;Object&amp;gt;"/&gt;
&lt;/data&gt;
&lt;TextView
diff --git a/docs/html/training/articles/assistant.jd b/docs/html/training/articles/assistant.jd
index a1fbd6ba1634..703b3778a275 100644
--- a/docs/html/training/articles/assistant.jd
+++ b/docs/html/training/articles/assistant.jd
@@ -11,110 +11,92 @@ page.article=true
<div id="tb">
<h2>In this document</h2>
<ol>
- <li><a href="#assist_api">Using the Assist API</a>
+ <li><a href="#assist_api">Using the Assistant</a>
<ol>
- <li><a href="#assist_api_lifecycle">Assist API Lifecycle</a></li>
- <li><a href="#source_app">Source App</a></li>
- <li><a href="#destination_app">Destination App</a></li>
+ <li><a href="#source_app">Source app</a></li>
+ <li><a href="#destination_app">Destination app</a></li>
</ol>
</li>
- <li><a href="#implementing_your_own_assistant">Implementing your
- own assistant</a></li>
+ <li><a href="#implementing_your_own_assistant">Implementing Your
+ Own Assistant</a></li>
</ol>
</div>
</div>
<p>
Android 6.0 Marshmallow introduces a new way for users to engage with apps
- through the assistant.
-</p>
+ through the assistant. The assistant is a top-level window that users can view to obtain
+ contextually relevant actions for the current activity. These actions might include deep links
+ to other apps on the device.</p>
<p>
- Users summon the assistant with a long-press on the Home button or by saying
- the {@link android.service.voice.AlwaysOnHotwordDetector keyphrase}. In
- response to the long-press, the system opens a top-level window that displays
- contextually relevant actions for the current activity. These potential
- actions might include deep links to other apps on the device.
+ Users activate the assistant with a long press on the Home button or by saying a
+ <a href="{@docRoot}reference/android/service/voice/AlwaysOnHotwordDetector.html">keyphrase</a>.
+ In response, the system opens a top-level window that displays contextually
+ relevant actions.
</p>
<p>
- This guide explains how Android apps use Android's Assist API to improve the
- assistant user experience.
+ Google App implements the assistant overlay window through a feature called
+ Now on Tap, which works with the Android platform-level functionality. The system allows
+ the user to select an assistant app, which obtains contextual information from your app
+ using Android’s Assist API.
</p>
-
-
-<h2 id="assist_api">Using the Assist API</h2>
-
<p>
- The example below shows how Google Now integrates with the Android assistant
- using a feature called Now on Tap.
+ This guide explains how Android apps use Android's Assist API to improve the assistant
+ user experience.
+<p/>
</p>
+
+<h2 id="assist_api">Using the Assistant</h2>
+
<p>
- The assistant overlay window in our example (2, 3) is implemented by Google
- Now through a feature called Now on Tap, which works in concert with the
- Android platform-level functionality. The system allows the user to select
- the assistant app (Figure 2) that obtains contextual information from the
- <em>source</em> app using the Assist API which is a part of the platform.
+ Figure 1 illustrates a typical user interaction with the assistant. When the user long-presses
+ the Home button, the Assist API callbacks are invoked
+ in the <em>source</em> app (step 1). The assistant renders the overlay window (steps 2 and 3),
+ and then the user selects the action to perform. The assistant executes the selected action,
+ such as firing an intent with a deep link to the (<em>destination</em>) restaurant app (step 4).
</p>
-
<div>
<img src="{@docRoot}images/training/assistant/image01.png">
<p class="img-caption" style="text-align:center;">
Figure 1. Assistant interaction example with the Now on Tap feature of
- Google Now
+ the Google App
</p>
</div>
<p>
- An Android user first configures the assistant and can change system options
- such as using text and view hierarchy as well as the screenshot of the
- current screen (Figure 2).
-</p>
-
-<p>
- From there, the assistant receives the information only when the user
- activates assistance, such as when they tap and hold the Home button ( shown
- in Figure 1, step 1).
+ Users can configure the assistant by selecting <strong>Settings > Apps > Default Apps >
+ Assist &amp; voice input</strong>. Users can change system options such as accessing
+ the screen contents as text and accessing a screenshot, as shown in Figure 2.
</p>
-<div style="float:right;margin:1em;max-width:300px">
+<div id="assist-input-settings" style="float:right;margin:1em;max-width:300px">
<img src="{@docRoot}images/training/assistant/image02.png">
<p class="img-caption" style="text-align:center;">
- Figure 2. Assist &amp; voice input settings (<em>Settings/Apps/Default
- Apps/Assist &amp; voice input</em>)
+ Figure 2. Assist &amp; voice input settings
</p>
</div>
-<h3 id="assist_api_lifecycle">Assist API Lifecycle</h3>
-
-<p>
- Going back to our example from Figure 1, the Assist API callbacks are invoked
- in the <em>source</em> app after step 1 (user long-presses the Home button)
- and before step 2 (the assistant renders the overlay window). Once the user
- selects the action to perform (step 3), the assistant executes it, for
- example by firing an intent with a deep link to the (<em>destination</em>)
- restaurant app (step 4).
-</p>
-
-<h3 id="source_app">Source App</h3>
+<h3 id="source_app">Source app</h3>
<p>
- In most cases, your app does not need to do anything extra to integrate with
- the assistant if you already follow <a href=
+ To ensure that your app works with the assistant as a source of information for the user,
+ you need only follow <a href=
"{@docRoot}guide/topics/ui/accessibility/apps.html">accessibility best
practices</a>. This section describes how to provide additional information
- to help improve the assistant user experience, as well as scenarios, such as
- custom Views, that need special handling.
+ to help improve the assistant user experience as well as scenarios
+ that need special handling, such as custom Views.
</p>
-
-<h4 id="share_additional_information_with_the_assistant">Share Additional Information with the Assistant</h4>
+<h4 id="share_additional_information_with_the_assistant">Share additional information
+ with the assistant</h4>
<p>
In addition to the text and the screenshot, your app can share
- <em>additional</em> information with the assistant. For example, your music
- app can choose to pass current album information, so that the assistant can
+ other information with the assistant. For example, your music
+ app can choose to pass current album information so that the assistant can
suggest smarter actions tailored to the current activity.
</p>
@@ -122,13 +104,13 @@ page.article=true
To provide additional information to the assistant, your app provides
<em>global application context</em> by registering an app listener and
supplies activity-specific information with activity callbacks as shown in
- Figure 3.
+ Figure 3:
</p>
<div>
<img src="{@docRoot}images/training/assistant/image03.png">
<p class="img-caption" style="text-align:center;">
- Figure 3. Assist API lifecycle sequence diagram.
+ Figure 3. Assist API lifecycle sequence diagram
</p>
</div>
@@ -136,43 +118,42 @@ page.article=true
To provide global application context, the app creates an implementation of
{@link android.app.Application.OnProvideAssistDataListener} and registers it
using {@link
- android.app.Application#registerOnProvideAssistDataListener(android.app.Application.OnProvideAssistDataListener)}.
- In order to provide activity-specific contextual information, activity
- overrides {@link android.app.Activity#onProvideAssistData(android.os.Bundle)}
+ android.app.Application#registerOnProvideAssistDataListener(android.app.Application.OnProvideAssistDataListener) registerOnProvideAssistDataListener()}.
+ To provide activity-specific contextual information, the activity
+ overrides {@link android.app.Activity#onProvideAssistData(android.os.Bundle) onProvideAssistData()}
and {@link
- android.app.Activity#onProvideAssistContent(android.app.assist.AssistContent)}.
+ android.app.Activity#onProvideAssistContent(android.app.assist.AssistContent) onProvideAssistContent()}.
The two activity methods are called <em>after</em> the optional global
- callback (registered with {@link
- android.app.Application#registerOnProvideAssistDataListener(android.app.Application.OnProvideAssistDataListener)})
- is invoked. Since the callbacks execute on the main thread, they should
+ callback is invoked. Because the callbacks execute on the main thread, they should
complete <a href="{@docRoot}training/articles/perf-anr.html">promptly</a>.
The callbacks are invoked only when the activity is <a href=
"{@docRoot}reference/android/app/Activity.html#ActivityLifecycle">running</a>.
</p>
-<h5 id="providing_context">Providing Context</h5>
+<h5 id="providing_context">Providing context</h5>
<p>
- {@link android.app.Activity#onProvideAssistData(android.os.Bundle)} is called
- when the user is requesting the assistant to build a full {@link
+ When the user activates the assistant,
+ {@link android.app.Activity#onProvideAssistData(android.os.Bundle) onProvideAssistData()} is called to build a full
+ {@link
android.content.Intent#ACTION_ASSIST} Intent with all of the context of the
current application represented as an instance of the {@link
android.app.assist.AssistStructure}. You can override this method to place
- into the bundle anything you would like to appear in the
- <code>EXTRA_ASSIST_CONTEXT</code> part of the assist Intent.
+ anything you like into the bundle to appear in the
+ {@link android.content.Intent#EXTRA_ASSIST_CONTEXT} part of the assist intent.
</p>
-<h5 id="describing_content">Describing Content</h5>
+<h5 id="describing_content">Describing content</h5>
<p>
Your app can implement {@link
- android.app.Activity#onProvideAssistContent(android.app.assist.AssistContent)}
- to improve assistant user experience by providing references to content
+ android.app.Activity#onProvideAssistContent(android.app.assist.AssistContent) onProvideAssistContent()}
+ to improve the assistant user experience by providing content-related references
related to the current activity. You can describe the app content using the
- common vocabulary defined by <a href="https://schema.org">Schema.org</a>
+ common vocabulary defined by <a href="https://schema.org" class="external-link">Schema.org</a>
through a JSON-LD object. In the example below, a music app provides
- structured data to describe the music album the user is currently
- looking at.
+ structured data to describe the music album that the user is currently
+ viewing:
</p>
<pre class="prettyprint">
@@ -191,127 +172,158 @@ public void onProvideAssistContent(AssistContent <strong>assistContent</strong>)
</pre>
<p>
- Custom implementations of {@link
- android.app.Activity#onProvideAssistContent(android.app.assist.AssistContent)}
- may also adjust the provided {@link
- android.app.assist.AssistContent#setIntent(android.content.Intent) content
- intent} to better reflect the top-level context of the activity, supply
- {@link android.app.assist.AssistContent#setWebUri(android.net.Uri) the URI}
- of the displayed content, and fill in its {@link
- android.app.assist.AssistContent#setClipData(android.content.ClipData)} with
- additional content of interest that the user is currently viewing.
+ You can also improve the user experience with custom implementations of
+ {@link
+ android.app.Activity#onProvideAssistContent(android.app.assist.AssistContent) onProvideAssistContent()},
+ which can provide the following benefits:
+</p>
+<ul>
+ <li><a href="{@docRoot}reference/android/app/assist/AssistContent.html#setIntent(android.content.Intent)">
+ Adjusts the provided content
+ intent</a> to
+ better reflect the top-level context of the activity.</li>
+ <li><a href="{@docRoot}reference/android/app/assist/AssistContent.html#setWebUri(android.net.Uri)">
+ Supplies the URI</a>
+ of the displayed content.</li>
+ <li>Fills in {@link
+ android.app.assist.AssistContent#setClipData(android.content.ClipData) setClipData()} with additional
+ content of interest that the user is currently viewing.</li>
+</ul>
+<p class="note">
+ <strong>Note: </strong>Apps that use a custom text selection implementation likely need
+ to implement {@link
+ android.app.Activity#onProvideAssistContent(android.app.assist.AssistContent) onProvideAssistContent()}
+ and call {@link android.app.assist.AssistContent#setClipData(android.content.ClipData) setClipData()}.
</p>
-<h4 id="default_implementation">Default Implementation</h4>
+<h4 id="default_implementation">Default implementation</h4>
<p>
- If neither {@link
- android.app.Activity#onProvideAssistData(android.os.Bundle)} nor {@link
- android.app.Activity#onProvideAssistContent(android.app.assist.AssistContent)}
- callbacks are implemented, the system will still proceed and pass the
- information collected automatically to the assistant unless the current
+ If neither the {@link
+ android.app.Activity#onProvideAssistData(android.os.Bundle) onProvideAssistData()} nor the {@link
+ android.app.Activity#onProvideAssistContent(android.app.assist.AssistContent) onProvideAssistContent()}
+ callback is implemented, the system still proceeds and passes the
+ automatically collected information to the assistant unless the current
window is flagged as <a href="#excluding_views">secure</a>.
As shown in Figure 3, the system uses the default implementations of {@link
- android.view.View#onProvideStructure(android.view.ViewStructure)} and {@link
- android.view.View#onProvideVirtualStructure(android.view.ViewStructure)} to
+ android.view.View#onProvideStructure(android.view.ViewStructure) onProvideStructure()} and {@link
+ android.view.View#onProvideVirtualStructure(android.view.ViewStructure) onProvideVirtualStructure()} to
collect text and view hierarchy information. If your view implements custom
- text drawing, you should override {@link
- android.view.View#onProvideStructure(android.view.ViewStructure)} to provide
+ text drawing, override {@link
+ android.view.View#onProvideStructure(android.view.ViewStructure) onProvideStructure()} to provide
the assistant with the text shown to the user by calling {@link
- android.view.ViewStructure#setText(java.lang.CharSequence)}.
+ android.view.ViewStructure#setText(java.lang.CharSequence) setText(CharSequence)}.
</p>
<p>
- <strong>In most cases, implementing accessibility support will enable the
- assistant to obtain the information it needs.</strong> This includes
- providing {@link android.R.attr#contentDescription
- android:contentDescription} attributes, populating {@link
- android.view.accessibility.AccessibilityNodeInfo} for custom views, making
- sure custom {@link android.view.ViewGroup ViewGroups} correctly {@link
- android.view.ViewGroup#getChildAt(int) expose} their children, and following
- the best practices described in <a href=
- "{@docRoot}guide/topics/ui/accessibility/apps.html">“Making Applications
- Accessible”</a>.
-</p>
+ <em>In most cases, implementing accessibility support enables the
+ assistant to obtain the information it needs.</em> To implement accessibility support,
+ observe the best practices described in <a href=
+ "{@docRoot}guide/topics/ui/accessibility/apps.html">Making Applications
+ Accessible</a>, including the following:</p>
+
+<ul>
+ <li>Provide {@link android.R.attr#contentDescription
+ android:contentDescription} attributes.</li>
+ <li>Populate {@link
+ android.view.accessibility.AccessibilityNodeInfo} for custom views.</li>
+ <li>Make
+ sure that custom {@link android.view.ViewGroup ViewGroup} objects correctly
+ <a href="{@docRoot}reference/android/view/ViewGroup.html#getChildAt(int)">expose</a>
+ their children.</li>
+</ul>
<h4 id="excluding_views">Excluding views from the assistant</h4>
<p>
- An activity can exclude the current view from the assistant. This is accomplished
+ To handle sensitive information, your app can exclude the current view from the assistant
by setting the {@link android.view.WindowManager.LayoutParams#FLAG_SECURE
- FLAG_SECURE} layout parameter of the WindowManager and must be done
- explicitly for every window created by the activity, including Dialogs. Your
- app can also use {@link android.view.SurfaceView#setSecure(boolean)
- SurfaceView.setSecure} to exclude a surface from the assistant. There is no
+ FLAG_SECURE} layout parameter of the {@link android.view.WindowManager}. You must set {@link
+ android.view.WindowManager.LayoutParams#FLAG_SECURE
+ FLAG_SECURE} explicitly for
+ every window created by the activity, including dialogs. Your app can also use
+ {@link android.view.SurfaceView#setSecure(boolean) setSecure()} to exclude
+ a surface from the assistant. There is no
global (app-level) mechanism to exclude all views from the assistant. Note
- that <code>FLAG_SECURE</code> does not cause the Assist API callbacks to stop
- firing. The activity which uses <code>FLAG_SECURE</code> can still explicitly
+ that {@link android.view.WindowManager.LayoutParams#FLAG_SECURE
+ FLAG_SECURE} does not cause the Assist API callbacks to stop
+ firing. The activity that uses {@link android.view.WindowManager.LayoutParams#FLAG_SECURE
+ FLAG_SECURE} can still explicitly
provide information to the assistant using the callbacks described earlier
this guide.
</p>
-<h4 id="voice_interactions">Voice Interactions</h4>
+<p class="note"><strong>Note: </strong>For enterprise accounts (Android for Work),
+ the administrator can disable
+ the collection of assistant data for the work profile by using the {@link
+ android.app.admin.DevicePolicyManager#setScreenCaptureDisabled(android.content.ComponentName, boolean)
+ setScreenCaptureDisabled()} method of the {@link android.app.admin.DevicePolicyManager} API.</p>
+
+<h4 id="voice_interactions">Voice interactions</h4>
<p>
- Assist API callbacks are also invoked upon {@link
- android.service.voice.AlwaysOnHotwordDetector keyphrase detection}. For more
- information see the <a href="https://developers.google.com/voice-actions/">voice
- actions</a> documentation.
+ Assist API callbacks are also invoked upon
+ <a href="{@docRoot}reference/android/service/voice/AlwaysOnHotwordDetector.html">keyphrase
+ detection</a>. For more information, see the
+ <a href="https://developers.google.com/voice-actions/" class="external-link">Voice
+ Actions</a> documentation.
</p>
<h4 id="z-order_considerations">Z-order considerations</h4>
<p>
The assistant uses a lightweight overlay window displayed on top of the
- current activity. The assistant can be summoned by the user at any time.
- Therefore, apps should not create permanent {@link
- android.Manifest.permission#SYSTEM_ALERT_WINDOW system alert}
- windows interfering with the overlay window shown in Figure 4.
+ current activity. Because the user can activate the assistant at any time,
+ don't create permanent <a
+ href="{@docRoot}reference/android/Manifest.permission.html#SYSTEM_ALERT_WINDOW">
+ system alert</a> windows that interfere with the overlay window, as shown in
+ Figure 4.
</p>
<div style="">
<img src="{@docRoot}images/training/assistant/image04.png">
<p class="img-caption" style="text-align:center;">
- Figure 4. Assist layer Z-order.
+ Figure 4. Assist layer Z-order
</p>
</div>
<p>
- If your app uses {@link
- android.Manifest.permission#SYSTEM_ALERT_WINDOW system alert} windows, it
- must promptly remove them as leaving them on the screen will degrade user
- experience and annoy the users.
+ If your app uses <a
+ href="{@docRoot}reference/android/Manifest.permission.html#SYSTEM_ALERT_WINDOW">
+ system alert</a> windows, remove them promptly because leaving them on the
+ screen degrades the user experience.
</p>
-<h3 id="destination_app">Destination App</h3>
+<h3 id="destination_app">Destination app</h3>
<p>
- The matching between the current user context and potential actions displayed
- in the overlay window (shown in step 3 in Figure 1) is specific to the
- assistant’s implementation. However, consider adding <a href=
- "{@docRoot}training/app-indexing/deep-linking.html">deep linking</a> support
- to your app. The assistant will typically take advantage of deep linking. For
- example, Google Now uses deep linking and <a href=
- "https://developers.google.com/app-indexing/">App Indexing</a> in order to
+ The assistant typically takes advantage of deep linking to find destination apps. To make your
+ app a potential destination app, consider adding <a href=
+ "{@docRoot}training/app-indexing/deep-linking.html">deep linking</a> support. The matching
+ between the current user context and deep links or other potential actions displayed in the
+ overlay window (shown in step 3 in Figure 1) is specific to the assistant’s implementation.
+ For
+ example, the Google App uses deep linking and <a href=
+ "https://developers.google.com/app-indexing/" class="external-link">Firebase App Indexing</a> in order to
drive traffic to destination apps.
</p>
-<h2 id="implementing_your_own_assistant">Implementing your own assistant </h2>
+<h2 id="implementing_your_own_assistant">Implementing Your Own Assistant </h2>
<p>
- Some developers may wish to implement their own assistant. As shown in Figure
- 2, the active assistant app can be selected by the Android user. The
+ You may wish to implement your own assistant. As shown in <a href="#assist-input-settings">Figure
+ 2</a>, the user can select the active assistant app. The
assistant app must provide an implementation of {@link
android.service.voice.VoiceInteractionSessionService} and {@link
android.service.voice.VoiceInteractionSession} as shown in <a href=
- "https://android.googlesource.com/platform/frameworks/base/+/android-5.0.1_r1/tests/VoiceInteraction?autodive=0%2F%2F%2F%2F%2F%2F">
- this</a> example and it requires the {@link
- android.Manifest.permission#BIND_VOICE_INTERACTION} permission. It can then
+ "https://android.googlesource.com/platform/frameworks/base/+/marshmallow-release/tests/VoiceInteraction/" class="external-link">
+ this <code>VoiceInteraction</code> example</a>. It also requires the {@link
+ android.Manifest.permission#BIND_VOICE_INTERACTION} permission. The assistant can then
receive the text and view hierarchy represented as an instance of the {@link
android.app.assist.AssistStructure} in {@link
android.service.voice.VoiceInteractionSession#onHandleAssist(android.os.Bundle,
android.app.assist.AssistStructure,android.app.assist.AssistContent) onHandleAssist()}.
- The assistant receives the screenshot through {@link
+ It receives the screenshot through {@link
android.service.voice.VoiceInteractionSession#onHandleScreenshot(android.graphics.Bitmap)
onHandleScreenshot()}.
</p>
diff --git a/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java b/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java
index 87c472ec6c95..c6a45c1a677f 100644
--- a/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java
+++ b/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java
@@ -210,7 +210,7 @@ public class AnimatedVectorDrawable extends Drawable implements Animatable2 {
/**
* In order to avoid breaking old apps, we only throw exception on invalid VectorDrawable
- * animations * for apps targeting N and later. For older apps, we ignore (i.e. quietly skip)
+ * animations for apps targeting N and later. For older apps, we ignore (i.e. quietly skip)
* these animations.
*
* @return whether invalid animations for vector drawable should be ignored.
diff --git a/keystore/java/android/security/keystore/KeyGenParameterSpec.java b/keystore/java/android/security/keystore/KeyGenParameterSpec.java
index cbef540562e1..ed40b77be4a4 100644
--- a/keystore/java/android/security/keystore/KeyGenParameterSpec.java
+++ b/keystore/java/android/security/keystore/KeyGenParameterSpec.java
@@ -571,13 +571,12 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec {
*
* <p>If this method returns {@code null}, and the spec is used to generate an asymmetric (RSA
* or EC) key pair, the public key will have a self-signed certificate if it has purpose {@link
- * KeyProperties#PURPOSE_SIGN} (see {@link #KeyGenParameterSpec(String, int)). If does not have
- * purpose {@link KeyProperties#PURPOSE_SIGN}, it will have a fake certificate.
+ * KeyProperties#PURPOSE_SIGN}. If does not have purpose {@link KeyProperties#PURPOSE_SIGN}, it
+ * will have a fake certificate.
*
* <p>Symmetric keys, such as AES and HMAC keys, do not have public key certificates. If a
- * {@link KeyGenParameterSpec} with {@link #hasAttestationCertificate()} returning
- * non-{@code null} is used to generate a symmetric (AES or HMAC) key,
- * {@link KeyGenerator#generateKey())} will throw
+ * KeyGenParameterSpec with getAttestationChallenge returning non-null is used to generate a
+ * symmetric (AES or HMAC) key, {@link javax.crypto.KeyGenerator#generateKey()} will throw
* {@link java.security.InvalidAlgorithmParameterException}.
*
* @see Builder#setAttestationChallenge(byte[])
@@ -1050,11 +1049,6 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec {
return this;
}
- /*
- * TODO(swillden): Update this documentation to describe the hardware and software root
- * keys, including information about CRL/OCSP services for discovering revocations, and to
- * link to documentation of the extension format and content.
- */
/**
* Sets whether an attestation certificate will be generated for this key pair, and what
* challenge value will be placed in the certificate. The attestation certificate chain
@@ -1074,17 +1068,15 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec {
*
* <p>If {@code attestationChallenge} is {@code null}, and this spec is used to generate an
* asymmetric (RSA or EC) key pair, the public key certificate will be self-signed if the
- * key has purpose {@link KeyProperties#PURPOSE_SIGN} (see
- * {@link #KeyGenParameterSpec(String, int)). If the key does not have purpose
- * {@link KeyProperties#PURPOSE_SIGN}, it is not possible to use the key to sign a
- * certificate, so the public key certificate will contain a dummy signature.
+ * key has purpose {@link android.security.keystore.KeyProperties#PURPOSE_SIGN}. If the key
+ * does not have purpose {@link android.security.keystore.KeyProperties#PURPOSE_SIGN}, it is
+ * not possible to use the key to sign a certificate, so the public key certificate will
+ * contain a dummy signature.
*
* <p>Symmetric keys, such as AES and HMAC keys, do not have public key certificates. If a
- * {@code getAttestationChallenge} returns non-{@code null} and the spec is used to
- * generate a symmetric (AES or HMAC) key, {@link KeyGenerator#generateKey()} will throw
+ * {@link #getAttestationChallenge()} returns non-null and the spec is used to generate a
+ * symmetric (AES or HMAC) key, {@link javax.crypto.KeyGenerator#generateKey()} will throw
* {@link java.security.InvalidAlgorithmParameterException}.
- *
- * @see Builder#setAttestationChallenge(String attestationChallenge)
*/
@NonNull
public Builder setAttestationChallenge(byte[] attestationChallenge) {
diff --git a/libs/hwui/PropertyValuesAnimatorSet.cpp b/libs/hwui/PropertyValuesAnimatorSet.cpp
index 961132ea42fa..38fb70a92e43 100644
--- a/libs/hwui/PropertyValuesAnimatorSet.cpp
+++ b/libs/hwui/PropertyValuesAnimatorSet.cpp
@@ -156,7 +156,7 @@ void PropertyAnimator::setFraction(float fraction, long iteration) {
// This makes sure we only set the fraction = repeatCount + 1 once. It is needed because there
// might be another animator modifying the same property after this animator finishes, we need
// to make sure we don't set conflicting values on the same property within one frame.
- if ((mLatestFraction == mRepeatCount + 1) && (totalFraction >= mRepeatCount + 1)) {
+ if ((mLatestFraction == mRepeatCount + 1.0) && (totalFraction >= mRepeatCount + 1.0)) {
return;
}
diff --git a/libs/hwui/RecordingCanvas.cpp b/libs/hwui/RecordingCanvas.cpp
index 1802fd41f3d6..0c552bac1d7f 100644
--- a/libs/hwui/RecordingCanvas.cpp
+++ b/libs/hwui/RecordingCanvas.cpp
@@ -149,50 +149,53 @@ int RecordingCanvas::saveLayer(float left, float top, float right, float bottom,
// Map visible bounds back to layer space, and intersect with parameter bounds
Rect layerBounds = visibleBounds;
- Matrix4 inverse;
- inverse.loadInverse(*previous.transform);
- inverse.mapRect(layerBounds);
- layerBounds.doIntersect(unmappedBounds);
+ if (CC_LIKELY(!layerBounds.isEmpty())) {
+ // if non-empty, can safely map by the inverse transform
+ Matrix4 inverse;
+ inverse.loadInverse(*previous.transform);
+ inverse.mapRect(layerBounds);
+ layerBounds.doIntersect(unmappedBounds);
+ }
int saveValue = mState.save((int) flags);
Snapshot& snapshot = *mState.writableSnapshot();
// layerBounds is in original bounds space, but clipped by current recording clip
- if (layerBounds.isEmpty() || unmappedBounds.isEmpty()) {
- // Don't bother recording layer, since it's been rejected
+ if (!layerBounds.isEmpty() && !unmappedBounds.isEmpty()) {
if (CC_LIKELY(clippedLayer)) {
- snapshot.resetClip(0, 0, 0, 0);
+ auto previousClip = getRecordedClip(); // capture before new snapshot clip has changed
+ if (addOp(alloc().create_trivial<BeginLayerOp>(
+ unmappedBounds,
+ *previous.transform, // transform to *draw* with
+ previousClip, // clip to *draw* with
+ refPaint(paint))) >= 0) {
+ snapshot.flags |= Snapshot::kFlagIsLayer | Snapshot::kFlagIsFboLayer;
+ snapshot.initializeViewport(unmappedBounds.getWidth(), unmappedBounds.getHeight());
+ snapshot.transform->loadTranslate(-unmappedBounds.left, -unmappedBounds.top, 0.0f);
+
+ Rect clip = layerBounds;
+ clip.translate(-unmappedBounds.left, -unmappedBounds.top);
+ snapshot.resetClip(clip.left, clip.top, clip.right, clip.bottom);
+ snapshot.roundRectClipState = nullptr;
+ return saveValue;
+ }
+ } else {
+ if (addOp(alloc().create_trivial<BeginUnclippedLayerOp>(
+ unmappedBounds,
+ *mState.currentSnapshot()->transform,
+ getRecordedClip(),
+ refPaint(paint))) >= 0) {
+ snapshot.flags |= Snapshot::kFlagIsLayer;
+ return saveValue;
+ }
}
- return saveValue;
}
+ // Layer not needed, so skip recording it...
if (CC_LIKELY(clippedLayer)) {
- auto previousClip = getRecordedClip(); // note: done before new snapshot's clip has changed
-
- snapshot.flags |= Snapshot::kFlagIsLayer | Snapshot::kFlagIsFboLayer;
- snapshot.initializeViewport(unmappedBounds.getWidth(), unmappedBounds.getHeight());
- snapshot.transform->loadTranslate(-unmappedBounds.left, -unmappedBounds.top, 0.0f);
-
- Rect clip = layerBounds;
- clip.translate(-unmappedBounds.left, -unmappedBounds.top);
- snapshot.resetClip(clip.left, clip.top, clip.right, clip.bottom);
- snapshot.roundRectClipState = nullptr;
-
- addOp(alloc().create_trivial<BeginLayerOp>(
- unmappedBounds,
- *previous.transform, // transform to *draw* with
- previousClip, // clip to *draw* with
- refPaint(paint)));
- } else {
- snapshot.flags |= Snapshot::kFlagIsLayer;
-
- addOp(alloc().create_trivial<BeginUnclippedLayerOp>(
- unmappedBounds,
- *mState.currentSnapshot()->transform,
- getRecordedClip(),
- refPaint(paint)));
+ // ... and set empty clip to reject inner content, if possible
+ snapshot.resetClip(0, 0, 0, 0);
}
-
return saveValue;
}
@@ -619,7 +622,7 @@ void RecordingCanvas::callDrawGLFunction(Functor* functor,
functor));
}
-size_t RecordingCanvas::addOp(RecordedOp* op) {
+int RecordingCanvas::addOp(RecordedOp* op) {
// skip op with empty clip
if (op->localClip && op->localClip->rect.isEmpty()) {
// NOTE: this rejection happens after op construction/content ref-ing, so content ref'd
diff --git a/libs/hwui/RecordingCanvas.h b/libs/hwui/RecordingCanvas.h
index 372be241042a..337e97bf450b 100644
--- a/libs/hwui/RecordingCanvas.h
+++ b/libs/hwui/RecordingCanvas.h
@@ -208,7 +208,7 @@ private:
void drawSimpleRects(const float* rects, int vertexCount, const SkPaint* paint);
- size_t addOp(RecordedOp* op);
+ int addOp(RecordedOp* op);
// ----------------------------------------------------------------------------
// lazy object copy
// ----------------------------------------------------------------------------
diff --git a/libs/hwui/tests/unit/RecordingCanvasTests.cpp b/libs/hwui/tests/unit/RecordingCanvasTests.cpp
index 28b375f1cb69..c072d0b80135 100644
--- a/libs/hwui/tests/unit/RecordingCanvasTests.cpp
+++ b/libs/hwui/tests/unit/RecordingCanvasTests.cpp
@@ -545,6 +545,21 @@ TEST(RecordingCanvas, saveLayer_rotateClipped) {
EXPECT_EQ(3, count);
}
+TEST(RecordingCanvas, saveLayer_rejectBegin) {
+ auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
+ canvas.save(SaveFlags::MatrixClip);
+ canvas.translate(0, -20); // avoid identity case
+ // empty clip rect should force layer + contents to be rejected
+ canvas.clipRect(0, -20, 200, -20, SkRegion::kIntersect_Op);
+ canvas.saveLayerAlpha(0, 0, 200, 200, 128, SaveFlags::ClipToLayer);
+ canvas.drawRect(0, 0, 200, 200, SkPaint());
+ canvas.restore();
+ canvas.restore();
+ });
+
+ ASSERT_EQ(0u, dl->getOps().size()) << "Begin/Rect/End should all be rejected.";
+}
+
TEST(RecordingCanvas, drawRenderNode_rejection) {
auto child = TestUtils::createNode(50, 50, 150, 150,
[](RenderProperties& props, RecordingCanvas& canvas) {
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 1b83ccdefe62..0a862914b14f 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -444,7 +444,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener {
}
}
- private void handleFingerprintAuthenticated() {
+ private void handleFingerprintAuthenticated(int authUserId) {
Trace.beginSection("KeyGuardUpdateMonitor#handlerFingerPrintAuthenticated");
try {
final int userId;
@@ -454,6 +454,10 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener {
Log.e(TAG, "Failed to get current user id: ", e);
return;
}
+ if (userId != authUserId) {
+ Log.d(TAG, "Fingerprint authenticated for wrong user: " + authUserId);
+ return;
+ }
if (isFingerprintDisabled(userId)) {
Log.d(TAG, "Fingerprint disabled by DPM for userId: " + userId);
return;
@@ -745,7 +749,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener {
@Override
public void onAuthenticationSucceeded(AuthenticationResult result) {
Trace.beginSection("KeyguardUpdateMonitor#onAuthenticationSucceeded");
- handleFingerprintAuthenticated();
+ handleFingerprintAuthenticated(result.getUserId());
Trace.endSection();
}
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 b5753ba39a43..745f5a5a773e 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java
@@ -275,10 +275,16 @@ public class TaskStack {
new RectF(0, 0.5f, 1, 1));
@Override
- public boolean acceptsDrop(int x, int y, int width, int height, boolean isCurrentTarget) {
- return isCurrentTarget
- ? areaContainsPoint(expandedTouchDockArea, width, height, x, y)
- : areaContainsPoint(touchArea, width, height, x, y);
+ public boolean acceptsDrop(int x, int y, int width, int height, Rect insets,
+ boolean isCurrentTarget) {
+ if (isCurrentTarget) {
+ getMappedRect(expandedTouchDockArea, width, height, mTmpRect);
+ return mTmpRect.contains(x, y);
+ } else {
+ getMappedRect(touchArea, width, height, mTmpRect);
+ updateBoundsWithSystemInsets(mTmpRect, insets);
+ return mTmpRect.contains(x, y);
+ }
}
// Represents the view state of this dock state
@@ -423,6 +429,7 @@ public class TaskStack {
private final RectF touchArea;
private final RectF dockArea;
private final RectF expandedTouchDockArea;
+ private static final Rect mTmpRect = new Rect();
/**
* @param createMode used to pass to ActivityManager to dock the task
@@ -452,23 +459,11 @@ public class TaskStack {
}
/**
- * Returns whether {@param x} and {@param y} are contained in the area scaled to the
- * given {@param width} and {@param height}.
- */
- public boolean areaContainsPoint(RectF area, int width, int height, float x, float y) {
- int left = (int) (area.left * width);
- int top = (int) (area.top * height);
- int right = (int) (area.right * width);
- int bottom = (int) (area.bottom * height);
- return x >= left && y >= top && x <= right && y <= bottom;
- }
-
- /**
* Returns the docked task bounds with the given {@param width} and {@param height}.
*/
- public Rect getPreDockedBounds(int width, int height) {
- return new Rect((int) (dockArea.left * width), (int) (dockArea.top * height),
- (int) (dockArea.right * width), (int) (dockArea.bottom * height));
+ public Rect getPreDockedBounds(int width, int height, Rect insets) {
+ getMappedRect(dockArea, width, height, mTmpRect);
+ return updateBoundsWithSystemInsets(mTmpRect, insets);
}
/**
@@ -511,10 +506,34 @@ public class TaskStack {
int top = dockArea.bottom < 1f
? 0
: insets.top;
- layoutAlgorithm.getTaskStackBounds(displayRect, windowRectOut, top, insets.left,
- insets.right, taskStackBounds);
+ // For now, ignore the left insets since we always dock on the left and show Recents
+ // on the right
+ layoutAlgorithm.getTaskStackBounds(displayRect, windowRectOut, top, 0, insets.right,
+ taskStackBounds);
return taskStackBounds;
}
+
+ /**
+ * Returns the expanded bounds in certain dock sides such that the bounds account for the
+ * system insets (namely the vertical nav bar). This call modifies and returns the given
+ * {@param bounds}.
+ */
+ private Rect updateBoundsWithSystemInsets(Rect bounds, Rect insets) {
+ if (dockSide == DOCKED_LEFT) {
+ bounds.right += insets.left;
+ } else if (dockSide == DOCKED_RIGHT) {
+ bounds.left -= insets.right;
+ }
+ return bounds;
+ }
+
+ /**
+ * Returns the mapped rect to the given dimensions.
+ */
+ private void getMappedRect(RectF bounds, int width, int height, Rect out) {
+ out.set((int) (bounds.left * width), (int) (bounds.top * height),
+ (int) (bounds.right * width), (int) (bounds.bottom * height));
+ }
}
// A comparator that sorts tasks by their freeform state
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/DropTarget.java b/packages/SystemUI/src/com/android/systemui/recents/views/DropTarget.java
index 3ad368cbf60a..f2a631078d3c 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/DropTarget.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/DropTarget.java
@@ -16,6 +16,8 @@
package com.android.systemui.recents.views;
+import android.graphics.Rect;
+
/**
* Represents a drop target for a drag gesture.
*/
@@ -25,5 +27,5 @@ public interface DropTarget {
* Returns whether this target can accept this drop. The x,y are relative to the top level
* RecentsView, and the width/height are of the RecentsView.
*/
- boolean acceptsDrop(int x, int y, int width, int height, boolean isCurrentTarget);
+ boolean acceptsDrop(int x, int y, int width, int height, Rect insets, boolean isCurrentTarget);
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
index 41622083c792..24ef433bd4d8 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
@@ -104,7 +104,7 @@ public class RecentsView extends FrameLayout {
private boolean mLastTaskLaunchedWasFreeform;
@ViewDebug.ExportedProperty(category="recents")
- private Rect mSystemInsets = new Rect();
+ Rect mSystemInsets = new Rect();
private int mDividerSize;
private Drawable mBackgroundScrim = new ColorDrawable(
@@ -223,13 +223,6 @@ public class RecentsView extends FrameLayout {
}
/**
- * Returns whether the nav bar is on the right.
- */
- public boolean isNavBarOnRight() {
- return mSystemInsets.right > 0;
- }
-
- /**
* Returns whether the last task launched was in the freeform stack or not.
*/
public boolean isLastTaskLaunchedFreeform() {
@@ -746,9 +739,10 @@ public class RecentsView extends FrameLayout {
? overrideHintAlpha
: viewState.hintTextAlpha;
Rect bounds = isDefaultDockState
- ? dockState.getPreDockedBounds(getMeasuredWidth(), getMeasuredHeight())
+ ? dockState.getPreDockedBounds(getMeasuredWidth(), getMeasuredHeight(),
+ mSystemInsets)
: dockState.getDockedBounds(getMeasuredWidth(), getMeasuredHeight(),
- mDividerSize, mSystemInsets, getResources());
+ mDividerSize, mSystemInsets, getResources());
if (viewState.dockAreaOverlay.getCallback() != this) {
viewState.dockAreaOverlay.setCallback(this);
viewState.dockAreaOverlay.setBounds(bounds);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsViewTouchHandler.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsViewTouchHandler.java
index 55d9964f55c1..636d4d6243c6 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsViewTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsViewTouchHandler.java
@@ -45,14 +45,10 @@ import java.util.ArrayList;
* Represents the dock regions for each orientation.
*/
class DockRegion {
- // The phone landscape dock states correspond to the opposite end of the screen that the nav bar
- // appears
- public static TaskStack.DockState[] PHONE_LANDSCAPE_LEFT = {
+ public static TaskStack.DockState[] PHONE_LANDSCAPE = {
+ // We only allow docking to the left in landscape for now on small devices
TaskStack.DockState.LEFT
};
- public static TaskStack.DockState[] PHONE_LANDSCAPE_RIGHT = {
- TaskStack.DockState.RIGHT
- };
public static TaskStack.DockState[] PHONE_PORTRAIT = {
// We only allow docking to the top for now on small devices
TaskStack.DockState.TOP
@@ -120,13 +116,7 @@ public class RecentsViewTouchHandler {
if (config.isLargeScreen) {
return isLandscape ? DockRegion.TABLET_LANDSCAPE : DockRegion.TABLET_PORTRAIT;
} else {
- if (isLandscape) {
- return mRv.isNavBarOnRight()
- ? DockRegion.PHONE_LANDSCAPE_LEFT
- : DockRegion.PHONE_LANDSCAPE_RIGHT;
- } else {
- return DockRegion.PHONE_PORTRAIT;
- }
+ return isLandscape ? DockRegion.PHONE_LANDSCAPE : DockRegion.PHONE_PORTRAIT;
}
}
@@ -237,7 +227,7 @@ public class RecentsViewTouchHandler {
// Give priority to the current drop target to retain the touch handling
if (mLastDropTarget != null) {
if (mLastDropTarget.acceptsDrop((int) evX, (int) evY, width, height,
- true /* isCurrentTarget */)) {
+ mRv.mSystemInsets, true /* isCurrentTarget */)) {
currentDropTarget = mLastDropTarget;
}
}
@@ -246,7 +236,7 @@ public class RecentsViewTouchHandler {
if (currentDropTarget == null) {
for (DropTarget target : mDropTargets) {
if (target.acceptsDrop((int) evX, (int) evY, width, height,
- false /* isCurrentTarget */)) {
+ mRv.mSystemInsets, false /* isCurrentTarget */)) {
currentDropTarget = target;
break;
}
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 21780a6e8864..24e75ac981cf 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
@@ -218,7 +218,8 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
// The drop targets for a task drag
private DropTarget mFreeformWorkspaceDropTarget = new DropTarget() {
@Override
- public boolean acceptsDrop(int x, int y, int width, int height, boolean isCurrentTarget) {
+ public boolean acceptsDrop(int x, int y, int width, int height, Rect insets,
+ boolean isCurrentTarget) {
// This drop target has a fixed bounds and should be checked last, so just fall through
// if it is the current target
if (!isCurrentTarget) {
@@ -230,7 +231,8 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
private DropTarget mStackDropTarget = new DropTarget() {
@Override
- public boolean acceptsDrop(int x, int y, int width, int height, boolean isCurrentTarget) {
+ public boolean acceptsDrop(int x, int y, int width, int height, Rect insets,
+ boolean isCurrentTarget) {
// This drop target has a fixed bounds and should be checked last, so just fall through
// if it is the current target
if (!isCurrentTarget) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardAffordanceView.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardAffordanceView.java
index 0b1984defe07..88f37a320926 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardAffordanceView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardAffordanceView.java
@@ -147,7 +147,7 @@ public class KeyguardAffordanceView extends ImageView {
@Override
protected void onDraw(Canvas canvas) {
- mSupportHardware = false;//canvas.isHardwareAccelerated();
+ mSupportHardware = canvas.isHardwareAccelerated();
drawBackgroundCircle(canvas);
canvas.save();
canvas.scale(mImageScale, mImageScale, getWidth() / 2, getHeight() / 2);
diff --git a/packages/WallpaperBackup/AndroidManifest.xml b/packages/WallpaperBackup/AndroidManifest.xml
index b8cea20afcd4..c548101080b4 100644
--- a/packages/WallpaperBackup/AndroidManifest.xml
+++ b/packages/WallpaperBackup/AndroidManifest.xml
@@ -24,6 +24,7 @@
android:process="system"
android:killAfterRestore="false"
android:allowBackup="true"
+ android:backupInForeground="true"
android:backupAgent=".WallpaperBackupAgent"
android:fullBackupOnly="true" >
</application>
diff --git a/packages/WallpaperBackup/src/com/android/wallpaperbackup/WallpaperBackupAgent.java b/packages/WallpaperBackup/src/com/android/wallpaperbackup/WallpaperBackupAgent.java
index 402d9adf0e7c..82b305087707 100644
--- a/packages/WallpaperBackup/src/com/android/wallpaperbackup/WallpaperBackupAgent.java
+++ b/packages/WallpaperBackup/src/com/android/wallpaperbackup/WallpaperBackupAgent.java
@@ -238,7 +238,7 @@ public class WallpaperBackupAgent extends BackupAgent {
Slog.v(TAG, "Restored crop hint " + cropHint);
}
try (FileInputStream in = new FileInputStream(stage)) {
- mWm.setStream(in, cropHint, true, which);
+ mWm.setStream(in, cropHint.isEmpty() ? null : cropHint, true, which);
} finally {} // auto-closes 'in'
}
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index e336ed5e34ab..ee15cc88dead 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -12566,23 +12566,33 @@ public final class ActivityManagerService extends ActivityManagerNative
synchronized (mPidsSelfLocked) {
final int pid = Binder.getCallingPid();
proc = mPidsSelfLocked.get(pid);
+
if (proc != null && mInVrMode && tid >= 0) {
// ensure the tid belongs to the process
if (!Process.isThreadInProcess(pid, tid)) {
throw new IllegalArgumentException("VR thread does not belong to process");
}
- // reset existing VR thread to CFS
- if (proc.vrThreadTid != 0) {
- Process.setThreadScheduler(proc.vrThreadTid, Process.SCHED_OTHER, 0);
+
+ // reset existing VR thread to CFS if this thread still exists and belongs to
+ // the calling process
+ if (proc.vrThreadTid != 0
+ && Process.isThreadInProcess(pid, proc.vrThreadTid)) {
+ try {
+ Process.setThreadScheduler(proc.vrThreadTid, Process.SCHED_OTHER, 0);
+ } catch (IllegalArgumentException e) {
+ // Ignore this. Only occurs in race condition where previous VR thread
+ // was destroyed during this method call.
+ }
}
- // add check to guarantee that tid belongs to pid?
+
proc.vrThreadTid = tid;
+
// promote to FIFO now if the tid is non-zero
- if (proc.curSchedGroup == ProcessList.SCHED_GROUP_TOP_APP && proc.vrThreadTid > 0) {
- Process.setThreadScheduler(proc.vrThreadTid, Process.SCHED_FIFO | Process.SCHED_RESET_ON_FORK, 1);
+ if (proc.curSchedGroup == ProcessList.SCHED_GROUP_TOP_APP
+ && proc.vrThreadTid > 0) {
+ Process.setThreadScheduler(proc.vrThreadTid,
+ Process.SCHED_FIFO | Process.SCHED_RESET_ON_FORK, 1);
}
- } else {
- //Slog.e("VR_FIFO", "Didn't set thread from setVrThread?");
}
}
}
diff --git a/services/core/java/com/android/server/am/AppErrors.java b/services/core/java/com/android/server/am/AppErrors.java
index 1f156dfd1985..7eff773543c8 100644
--- a/services/core/java/com/android/server/am/AppErrors.java
+++ b/services/core/java/com/android/server/am/AppErrors.java
@@ -638,7 +638,8 @@ class AppErrors {
// Allow restarting for started or bound foreground services that are crashing the
// first time. This includes wallpapers.
- if (sr.crashCount <= 1 && (sr.isForeground || procIsBoundForeground)) {
+ if ((data != null) && (sr.crashCount <= 1)
+ && (sr.isForeground || procIsBoundForeground)) {
data.isRestartableForService = true;
}
}
diff --git a/services/core/java/com/android/server/am/RecentTasks.java b/services/core/java/com/android/server/am/RecentTasks.java
index 5c05ab64e199..beb863b394af 100644
--- a/services/core/java/com/android/server/am/RecentTasks.java
+++ b/services/core/java/com/android/server/am/RecentTasks.java
@@ -653,12 +653,17 @@ class RecentTasks extends ArrayList<TaskRecord> {
&& task.realActivity.equals(tr.realActivity);
// If the document is open in another app or is not the same
// document, we don't need to trim it.
- if (!sameActivity || !sameIntentFilter || multiTasksAllowed) {
+ if (!sameActivity) {
continue;
// Otherwise only trim if we are over our max recents for this task
- } else if (maxRecents > 0 && !doTrim) {
+ } else if (maxRecents > 0) {
--maxRecents;
- continue;
+ if (!doTrim || !sameIntentFilter || multiTasksAllowed) {
+ // We don't want to trim if we are not over the max allowed entries and
+ // the caller doesn't want us to trim, the tasks are not of the same
+ // intent filter, or multiple entries fot the task is allowed.
+ continue;
+ }
}
// Hit the maximum number of documents for this task. Fall through
// and remove this document from recents.
diff --git a/services/core/java/com/android/server/connectivity/Tethering.java b/services/core/java/com/android/server/connectivity/Tethering.java
index b6c8d5d87b4c..e0d8373fc445 100644
--- a/services/core/java/com/android/server/connectivity/Tethering.java
+++ b/services/core/java/com/android/server/connectivity/Tethering.java
@@ -1392,6 +1392,8 @@ public class Tethering extends BaseNetworkObserver implements IControlsTethering
for (Integer netType : mUpstreamIfaceTypes) {
NetworkInfo info = cm.getNetworkInfo(netType.intValue());
+ // TODO: if the network is suspended we should consider
+ // that to be the same as connected here.
if ((info != null) && info.isConnected()) {
upType = netType.intValue();
break;
@@ -1465,6 +1467,10 @@ public class Tethering extends BaseNetworkObserver implements IControlsTethering
// it immediately, because there likely will be no second
// EVENT_ON_AVAILABLE (it was already received).
handleNewUpstreamNetworkState(ns);
+ } else if (mCurrentUpstreamIface == null) {
+ // There are no available upstream networks, or none that
+ // have an IPv4 default route (current metric for success).
+ handleNewUpstreamNetworkState(null);
}
}
@@ -1639,6 +1645,7 @@ public class Tethering extends BaseNetworkObserver implements IControlsTethering
chooseUpstreamType(mTryCell);
mTryCell = !mTryCell;
}
+
@Override
public void exit() {
// TODO: examine if we should check the return value.
@@ -1646,7 +1653,9 @@ public class Tethering extends BaseNetworkObserver implements IControlsTethering
mUpstreamNetworkMonitor.stop();
stopListeningForSimChanges();
notifyTetheredOfNewUpstreamIface(null);
+ handleNewUpstreamNetworkState(null);
}
+
@Override
public boolean processMessage(Message message) {
maybeLogMessage(this, message.what);
@@ -1734,6 +1743,7 @@ public class Tethering extends BaseNetworkObserver implements IControlsTethering
// reevaluation is triggered via received CONNECTIVITY_ACTION
// broadcasts that result in being passed a
// TetherMasterSM.CMD_UPSTREAM_CHANGED.
+ handleNewUpstreamNetworkState(null);
break;
default:
break;
diff --git a/services/core/java/com/android/server/connectivity/tethering/IPv6TetheringCoordinator.java b/services/core/java/com/android/server/connectivity/tethering/IPv6TetheringCoordinator.java
index 825439786360..e94b584ac48a 100644
--- a/services/core/java/com/android/server/connectivity/tethering/IPv6TetheringCoordinator.java
+++ b/services/core/java/com/android/server/connectivity/tethering/IPv6TetheringCoordinator.java
@@ -55,7 +55,7 @@ public class IPv6TetheringCoordinator {
if (VDBG) {
Log.d(TAG, "updateUpstreamNetworkState: " + toDebugString(ns));
}
- if (ns == null || ns.network == null) {
+ if (!canTetherIPv6(ns)) {
stopIPv6TetheringOnAllInterfaces();
setUpstreamNetworkState(null);
return;
@@ -65,8 +65,9 @@ public class IPv6TetheringCoordinator {
!ns.network.equals(mUpstreamNetworkState.network)) {
stopIPv6TetheringOnAllInterfaces();
}
+
setUpstreamNetworkState(ns);
- maybeUpdateIPv6TetheringInterfaces();
+ updateIPv6TetheringInterfaces();
}
private void stopIPv6TetheringOnAllInterfaces() {
@@ -77,9 +78,10 @@ public class IPv6TetheringCoordinator {
}
private void setUpstreamNetworkState(NetworkState ns) {
- if (!canTetherIPv6(ns)) {
+ if (ns == null) {
mUpstreamNetworkState = null;
} else {
+ // Make a deep copy of the parts we need.
mUpstreamNetworkState = new NetworkState(
null,
new LinkProperties(ns.linkProperties),
@@ -94,19 +96,17 @@ public class IPv6TetheringCoordinator {
}
}
- private void maybeUpdateIPv6TetheringInterfaces() {
- if (mUpstreamNetworkState == null) return;
-
+ private void updateIPv6TetheringInterfaces() {
for (TetherInterfaceStateMachine sm : mNotifyList) {
final LinkProperties lp = getInterfaceIPv6LinkProperties(sm.interfaceType());
- if (lp != null) {
- sm.sendMessage(TetherInterfaceStateMachine.CMD_IPV6_TETHER_UPDATE, 0, 0, lp);
- }
+ sm.sendMessage(TetherInterfaceStateMachine.CMD_IPV6_TETHER_UPDATE, 0, 0, lp);
break;
}
}
private LinkProperties getInterfaceIPv6LinkProperties(int interfaceType) {
+ if (mUpstreamNetworkState == null) return null;
+
// NOTE: Here, in future, we would have policies to decide how to divvy
// up the available dedicated prefixes among downstream interfaces.
// At this time we have no such mechanism--we only support tethering
diff --git a/services/core/java/com/android/server/connectivity/tethering/IPv6TetheringInterfaceServices.java b/services/core/java/com/android/server/connectivity/tethering/IPv6TetheringInterfaceServices.java
index b47e079395d8..d087f903560a 100644
--- a/services/core/java/com/android/server/connectivity/tethering/IPv6TetheringInterfaceServices.java
+++ b/services/core/java/com/android/server/connectivity/tethering/IPv6TetheringInterfaceServices.java
@@ -16,6 +16,7 @@
package com.android.server.connectivity.tethering;
+import android.net.INetd;
import android.net.IpPrefix;
import android.net.LinkAddress;
import android.net.LinkProperties;
@@ -27,13 +28,16 @@ import android.net.ip.RouterAdvertisementDaemon.RaParams;
import android.os.INetworkManagementService;
import android.os.RemoteException;
import android.util.Log;
+import android.util.Slog;
import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
+import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.HashSet;
+import java.util.Objects;
/**
@@ -41,13 +45,15 @@ import java.util.HashSet;
*/
class IPv6TetheringInterfaceServices {
private static final String TAG = IPv6TetheringInterfaceServices.class.getSimpleName();
+ private static final IpPrefix LINK_LOCAL_PREFIX = new IpPrefix("fe80::/64");
+ private static final int RFC7421_IP_PREFIX_LENGTH = 64;
private final String mIfName;
private final INetworkManagementService mNMService;
private NetworkInterface mNetworkInterface;
private byte[] mHwAddr;
- private ArrayList<RouteInfo> mLastLocalRoutes;
+ private LinkProperties mLastIPv6LinkProperties;
private RouterAdvertisementDaemon mRaDaemon;
private RaParams mLastRaParams;
@@ -86,8 +92,7 @@ class IPv6TetheringInterfaceServices {
public void stop() {
mNetworkInterface = null;
mHwAddr = null;
- updateLocalRoutes(null);
- updateRaParams(null);
+ setRaParams(null);
if (mRaDaemon != null) {
mRaDaemon.stop();
@@ -104,95 +109,178 @@ class IPv6TetheringInterfaceServices {
public void updateUpstreamIPv6LinkProperties(LinkProperties v6only) {
if (mRaDaemon == null) return;
- if (v6only == null) {
- updateLocalRoutes(null);
- updateRaParams(null);
+ // Avoid unnecessary work on spurious updates.
+ if (Objects.equals(mLastIPv6LinkProperties, v6only)) {
return;
}
- RaParams params = new RaParams();
- params.mtu = v6only.getMtu();
- params.hasDefaultRoute = v6only.hasIPv6DefaultRoute();
+ RaParams params = null;
- ArrayList<RouteInfo> localRoutes = new ArrayList<RouteInfo>();
- for (LinkAddress linkAddr : v6only.getLinkAddresses()) {
- final IpPrefix prefix = new IpPrefix(linkAddr.getAddress(),
- linkAddr.getPrefixLength());
+ if (v6only != null) {
+ params = new RaParams();
+ params.mtu = v6only.getMtu();
+ params.hasDefaultRoute = v6only.hasIPv6DefaultRoute();
- // Accumulate routes representing "prefixes to be assigned to the
- // local interface", for subsequent addition to the local network
- // in the routing rules.
- localRoutes.add(new RouteInfo(prefix, null, mIfName));
+ for (LinkAddress linkAddr : v6only.getLinkAddresses()) {
+ if (linkAddr.getPrefixLength() != RFC7421_IP_PREFIX_LENGTH) continue;
- params.prefixes.add(prefix);
+ final IpPrefix prefix = new IpPrefix(
+ linkAddr.getAddress(), linkAddr.getPrefixLength());
+ params.prefixes.add(prefix);
+
+ final Inet6Address dnsServer = getLocalDnsIpFor(prefix);
+ if (dnsServer != null) {
+ params.dnses.add(dnsServer);
+ }
+ }
}
+ // If v6only is null, we pass in null to setRaParams(), which handles
+ // deprecation of any existing RA data.
+
+ setRaParams(params);
+ mLastIPv6LinkProperties = v6only;
+ }
- // We need to be able to send unicast RAs, and clients might like to
- // ping the default router's link-local address, so add that as well.
- localRoutes.add(new RouteInfo(new IpPrefix("fe80::/64"), null, mIfName));
- // TODO: Add a local interface address, update dnsmasq to listen on the
- // new address, and use only that address as a DNS server.
- for (InetAddress dnsServer : v6only.getDnsServers()) {
- if (dnsServer instanceof Inet6Address) {
- params.dnses.add((Inet6Address) dnsServer);
+ private void configureLocalRoutes(
+ HashSet<IpPrefix> deprecatedPrefixes, HashSet<IpPrefix> newPrefixes) {
+ // [1] Remove the routes that are deprecated.
+ if (!deprecatedPrefixes.isEmpty()) {
+ final ArrayList<RouteInfo> toBeRemoved = getLocalRoutesFor(deprecatedPrefixes);
+ try {
+ final int removalFailures = mNMService.removeRoutesFromLocalNetwork(toBeRemoved);
+ if (removalFailures > 0) {
+ Log.e(TAG, String.format("Failed to remove %d IPv6 routes from local table.",
+ removalFailures));
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to remove IPv6 routes from local table: ", e);
}
}
- updateLocalRoutes(localRoutes);
- updateRaParams(params);
- }
+ // [2] Add only the routes that have not previously been added.
+ if (newPrefixes != null && !newPrefixes.isEmpty()) {
+ HashSet<IpPrefix> addedPrefixes = (HashSet) newPrefixes.clone();
+ if (mLastRaParams != null) {
+ addedPrefixes.removeAll(mLastRaParams.prefixes);
+ }
- private void updateLocalRoutes(ArrayList<RouteInfo> localRoutes) {
- if (localRoutes != null) {
- // TODO: Compare with mLastLocalRoutes and take appropriate
- // appropriate action on the difference between the two.
+ if (mLastRaParams == null || mLastRaParams.prefixes.isEmpty()) {
+ // We need to be able to send unicast RAs, and clients might
+ // like to ping the default router's link-local address. Note
+ // that we never remove the link-local route from the network
+ // until Tethering disables tethering on the interface.
+ addedPrefixes.add(LINK_LOCAL_PREFIX);
+ }
- if (!localRoutes.isEmpty()) {
+ if (!addedPrefixes.isEmpty()) {
+ final ArrayList<RouteInfo> toBeAdded = getLocalRoutesFor(addedPrefixes);
try {
- mNMService.addInterfaceToLocalNetwork(mIfName, localRoutes);
+ // It's safe to call addInterfaceToLocalNetwork() even if
+ // the interface is already in the local_network.
+ mNMService.addInterfaceToLocalNetwork(mIfName, toBeAdded);
} catch (RemoteException e) {
Log.e(TAG, "Failed to add IPv6 routes to local table: ", e);
}
}
- } else {
- if (mLastLocalRoutes != null && !mLastLocalRoutes.isEmpty()) {
+ }
+ }
+
+ private void configureLocalDns(
+ HashSet<Inet6Address> deprecatedDnses, HashSet<Inet6Address> newDnses) {
+ INetd netd = getNetdServiceOrNull();
+ if (netd == null) {
+ if (newDnses != null) newDnses.clear();
+ Log.e(TAG, "No netd service instance available; not setting local IPv6 addresses");
+ return;
+ }
+
+ // [1] Remove deprecated local DNS IP addresses.
+ if (!deprecatedDnses.isEmpty()) {
+ for (Inet6Address dns : deprecatedDnses) {
+ final String dnsString = dns.getHostAddress();
try {
- final int removalFailures =
- mNMService.removeRoutesFromLocalNetwork(mLastLocalRoutes);
- if (removalFailures > 0) {
- Log.e(TAG,
- String.format("Failed to remove %d IPv6 routes from local table.",
- removalFailures));
- }
+ netd.interfaceDelAddress(mIfName, dnsString, RFC7421_IP_PREFIX_LENGTH);
} catch (RemoteException e) {
- Log.e(TAG, "Failed to remove IPv6 routes from local table: ", e);
+ Log.e(TAG, "Failed to remove local dns IP: " + dnsString, e);
}
}
}
- mLastLocalRoutes = localRoutes;
+ // [2] Add only the local DNS IP addresses that have not previously been added.
+ if (newDnses != null && !newDnses.isEmpty()) {
+ final HashSet<Inet6Address> addedDnses = (HashSet) newDnses.clone();
+ if (mLastRaParams != null) {
+ addedDnses.removeAll(mLastRaParams.dnses);
+ }
+
+ for (Inet6Address dns : addedDnses) {
+ final String dnsString = dns.getHostAddress();
+ try {
+ netd.interfaceAddAddress(mIfName, dnsString, RFC7421_IP_PREFIX_LENGTH);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to add local dns IP: " + dnsString, e);
+ newDnses.remove(dns);
+ }
+ }
+ }
+
+ try {
+ netd.tetherApplyDnsInterfaces();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to update local DNS caching server");
+ if (newDnses != null) newDnses.clear();
+ }
}
- private void updateRaParams(RaParams params) {
+ private void setRaParams(RaParams newParams) {
if (mRaDaemon != null) {
- HashSet<IpPrefix> deprecated = null;
+ final RaParams deprecatedParams =
+ RaParams.getDeprecatedRaParams(mLastRaParams, newParams);
- if (mLastRaParams != null) {
- deprecated = new HashSet<>();
+ configureLocalRoutes(deprecatedParams.prefixes,
+ (newParams != null) ? newParams.prefixes : null);
- for (IpPrefix ipp : mLastRaParams.prefixes) {
- if (params == null || !params.prefixes.contains(ipp)) {
- deprecated.add(ipp);
- }
- }
- }
+ configureLocalDns(deprecatedParams.dnses,
+ (newParams != null) ? newParams.dnses : null);
+
+ mRaDaemon.buildNewRa(deprecatedParams, newParams);
+ }
+
+ mLastRaParams = newParams;
+ }
+
+ // Accumulate routes representing "prefixes to be assigned to the local
+ // interface", for subsequent modification of local_network routing.
+ private ArrayList<RouteInfo> getLocalRoutesFor(HashSet<IpPrefix> prefixes) {
+ final ArrayList<RouteInfo> localRoutes = new ArrayList<RouteInfo>();
+ for (IpPrefix ipp : prefixes) {
+ localRoutes.add(new RouteInfo(ipp, null, mIfName));
+ }
+ return localRoutes;
+ }
- // Currently, we send spurious RAs (5) whenever there's any update.
- // TODO: Compare params with mLastParams to avoid spurious updates.
- mRaDaemon.buildNewRa(params, deprecated);
+ private INetd getNetdServiceOrNull() {
+ if (mNMService != null) {
+ try {
+ return mNMService.getNetdService();
+ } catch (RemoteException ignored) {
+ // This blocks until netd can be reached, but it can return
+ // null during a netd crash.
+ }
}
+ return null;
+ }
- mLastRaParams = params;
+ // Given a prefix like 2001:db8::/64 return 2001:db8::1.
+ private static Inet6Address getLocalDnsIpFor(IpPrefix localPrefix) {
+ final byte[] dnsBytes = localPrefix.getRawAddress();
+ dnsBytes[dnsBytes.length - 1] = 0x1;
+ try {
+ return Inet6Address.getByAddress(null, dnsBytes, 0);
+ } catch (UnknownHostException e) {
+ Slog.wtf(TAG, "Failed to construct Inet6Address from: " + localPrefix);
+ return null;
+ }
}
}
diff --git a/services/core/java/com/android/server/fingerprint/AuthenticationClient.java b/services/core/java/com/android/server/fingerprint/AuthenticationClient.java
index c04b9a1bed0f..87da866603cc 100644
--- a/services/core/java/com/android/server/fingerprint/AuthenticationClient.java
+++ b/services/core/java/com/android/server/fingerprint/AuthenticationClient.java
@@ -39,9 +39,9 @@ public abstract class AuthenticationClient extends ClientMonitor {
public abstract void resetFailedAttempts();
public AuthenticationClient(Context context, long halDeviceId, IBinder token,
- IFingerprintServiceReceiver receiver, int callingUserId, int groupId, long opId,
+ IFingerprintServiceReceiver receiver, int targetUserId, int groupId, long opId,
boolean restricted, String owner) {
- super(context, halDeviceId, token, receiver, callingUserId, groupId, restricted, owner);
+ super(context, halDeviceId, token, receiver, targetUserId, groupId, restricted, owner);
mOpId = opId;
}
@@ -65,7 +65,7 @@ public abstract class AuthenticationClient extends ClientMonitor {
Fingerprint fp = !getIsRestricted()
? new Fingerprint("" /* TODO */, groupId, fingerId, getHalDeviceId())
: null;
- receiver.onAuthenticationSucceeded(getHalDeviceId(), fp);
+ receiver.onAuthenticationSucceeded(getHalDeviceId(), fp, getTargetUserId());
}
} catch (RemoteException e) {
Slog.w(TAG, "Failed to notify Authenticated:", e);
diff --git a/services/core/java/com/android/server/fingerprint/FingerprintService.java b/services/core/java/com/android/server/fingerprint/FingerprintService.java
index 1066434f10f1..73c8469ca709 100644
--- a/services/core/java/com/android/server/fingerprint/FingerprintService.java
+++ b/services/core/java/com/android/server/fingerprint/FingerprintService.java
@@ -470,10 +470,10 @@ public class FingerprintService extends SystemService implements IBinder.DeathRe
/**
* @param opPackageName name of package for caller
- * @param foregroundOnly only allow this call while app is in the foreground
+ * @param requireForeground only allow this call while app is in the foreground
* @return true if caller can use fingerprint API
*/
- private boolean canUseFingerprint(String opPackageName, boolean foregroundOnly, int uid,
+ private boolean canUseFingerprint(String opPackageName, boolean requireForeground, int uid,
int pid) {
checkPermission(USE_FINGERPRINT);
if (isKeyguard(opPackageName)) {
@@ -488,7 +488,7 @@ public class FingerprintService extends SystemService implements IBinder.DeathRe
Slog.w(TAG, "Rejecting " + opPackageName + " ; permission denied");
return false;
}
- if (foregroundOnly && !isForegroundActivity(uid, pid)) {
+ if (requireForeground && !(isForegroundActivity(uid, pid) || currentClient(opPackageName))){
Slog.w(TAG, "Rejecting " + opPackageName + " ; not in foreground");
return false;
}
@@ -496,6 +496,14 @@ public class FingerprintService extends SystemService implements IBinder.DeathRe
}
/**
+ * @param opPackageName package of the caller
+ * @return true if this is the same client currently using fingerprint
+ */
+ private boolean currentClient(String opPackageName) {
+ return mCurrentClient != null && mCurrentClient.getOwnerString().equals(opPackageName);
+ }
+
+ /**
* @param clientPackage
* @return true if this is keyguard package
*/
@@ -528,7 +536,7 @@ public class FingerprintService extends SystemService implements IBinder.DeathRe
if (DEBUG) Slog.v(TAG, "startAuthentication(" + opPackageName + ")");
AuthenticationClient client = new AuthenticationClient(getContext(), mHalDeviceId, token,
- receiver, callingUserId, groupId, opId, restricted, opPackageName) {
+ receiver, mCurrentUserId, groupId, opId, restricted, opPackageName) {
@Override
public boolean handleFailedAttempt() {
mFailedAttempts++;
diff --git a/services/core/java/com/android/server/notification/ConditionProviders.java b/services/core/java/com/android/server/notification/ConditionProviders.java
index 62fe70cf176f..2fab2887c28e 100644
--- a/services/core/java/com/android/server/notification/ConditionProviders.java
+++ b/services/core/java/com/android/server/notification/ConditionProviders.java
@@ -17,6 +17,8 @@
package com.android.server.notification;
import android.annotation.NonNull;
+import android.app.INotificationManager;
+import android.app.NotificationManager;
import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Context;
@@ -161,6 +163,25 @@ public class ConditionProviders extends ManagedServices {
}
}
+ @Override
+ public void onPackagesChanged(boolean removingPackage, String[] pkgList) {
+ if (removingPackage) {
+ INotificationManager inm = NotificationManager.getService();
+
+ if (pkgList != null && (pkgList.length > 0)) {
+ for (String pkgName : pkgList) {
+ try {
+ inm.removeAutomaticZenRules(pkgName);
+ inm.setNotificationPolicyAccessGranted(pkgName, false);
+ } catch (Exception e) {
+ Slog.e(TAG, "Failed to clean up rules for " + pkgName, e);
+ }
+ }
+ }
+ }
+ super.onPackagesChanged(removingPackage, pkgList);
+ }
+
public ManagedServiceInfo checkServiceToken(IConditionProvider provider) {
synchronized(mMutex) {
return checkServiceTokenLocked(provider);
diff --git a/services/core/java/com/android/server/notification/ManagedServices.java b/services/core/java/com/android/server/notification/ManagedServices.java
index dc85dd7ef40e..14e2ba3896d8 100644
--- a/services/core/java/com/android/server/notification/ManagedServices.java
+++ b/services/core/java/com/android/server/notification/ManagedServices.java
@@ -217,8 +217,8 @@ abstract public class ManagedServices {
return mEnabledServicesPackageNames.contains(pkg);
}
- public void onPackagesChanged(boolean queryReplace, String[] pkgList) {
- if (DEBUG) Slog.d(TAG, "onPackagesChanged queryReplace=" + queryReplace
+ public void onPackagesChanged(boolean removingPackage, String[] pkgList) {
+ if (DEBUG) Slog.d(TAG, "onPackagesChanged removingPackage=" + removingPackage
+ " pkgList=" + (pkgList == null ? null : Arrays.asList(pkgList))
+ " mEnabledServicesPackageNames=" + mEnabledServicesPackageNames);
boolean anyServicesInvolved = false;
@@ -234,7 +234,7 @@ abstract public class ManagedServices {
if (anyServicesInvolved) {
// if we're not replacing a package, clean up orphaned bits
- if (!queryReplace) {
+ if (removingPackage) {
updateSettingsAccordingToInstalledServices();
rebuildRestoredPackages();
}
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 2dbbc88e0599..bb55240fc859 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -696,9 +696,9 @@ public class NotificationManagerService extends SystemService {
int changeUserId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE,
UserHandle.USER_ALL);
String pkgList[] = null;
- boolean queryReplace = queryRemove &&
- intent.getBooleanExtra(Intent.EXTRA_REPLACING, false);
- if (DBG) Slog.i(TAG, "action=" + action + " queryReplace=" + queryReplace);
+ boolean removingPackage = queryRemove &&
+ !intent.getBooleanExtra(Intent.EXTRA_REPLACING, false);
+ if (DBG) Slog.i(TAG, "action=" + action + " removing=" + removingPackage);
if (action.equals(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE)) {
pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
} else if (action.equals(Intent.ACTION_PACKAGES_SUSPENDED)) {
@@ -747,10 +747,10 @@ public class NotificationManagerService extends SystemService {
}
}
}
- mListeners.onPackagesChanged(queryReplace, pkgList);
- mRankerServices.onPackagesChanged(queryReplace, pkgList);
- mConditionProviders.onPackagesChanged(queryReplace, pkgList);
- mRankingHelper.onPackagesChanged(queryReplace, pkgList);
+ mListeners.onPackagesChanged(removingPackage, pkgList);
+ mRankerServices.onPackagesChanged(removingPackage, pkgList);
+ mConditionProviders.onPackagesChanged(removingPackage, pkgList);
+ mRankingHelper.onPackagesChanged(removingPackage, pkgList);
}
}
};
@@ -3894,14 +3894,14 @@ public class NotificationManagerService extends SystemService {
}
@Override
- public void onPackagesChanged(boolean queryReplace, String[] pkgList) {
- if (DEBUG) Slog.d(TAG, "onPackagesChanged queryReplace=" + queryReplace
+ public void onPackagesChanged(boolean removingPackage, String[] pkgList) {
+ if (DEBUG) Slog.d(TAG, "onPackagesChanged removingPackage=" + removingPackage
+ " pkgList=" + (pkgList == null ? null : Arrays.asList(pkgList)));
if (mRankerServicePackageName == null) {
return;
}
- if (pkgList != null && (pkgList.length > 0)) {
+ if (pkgList != null && (pkgList.length > 0) && !removingPackage) {
for (String pkgName : pkgList) {
if (mRankerServicePackageName.equals(pkgName)) {
registerRanker();
diff --git a/services/core/java/com/android/server/notification/RankingHelper.java b/services/core/java/com/android/server/notification/RankingHelper.java
index 78b3f4134dd3..90484027446c 100644
--- a/services/core/java/com/android/server/notification/RankingHelper.java
+++ b/services/core/java/com/android/server/notification/RankingHelper.java
@@ -504,8 +504,8 @@ public class RankingHelper implements RankingConfig {
return packageBans;
}
- public void onPackagesChanged(boolean queryReplace, String[] pkgList) {
- if (queryReplace || pkgList == null || pkgList.length == 0
+ public void onPackagesChanged(boolean removingPackage, String[] pkgList) {
+ if (!removingPackage || pkgList == null || pkgList.length == 0
|| mRestoredWithoutUids.isEmpty()) {
return; // nothing to do
}
diff --git a/services/core/java/com/android/server/notification/ZenModeConditions.java b/services/core/java/com/android/server/notification/ZenModeConditions.java
index 86ca97d48531..1c12a961ad04 100644
--- a/services/core/java/com/android/server/notification/ZenModeConditions.java
+++ b/services/core/java/com/android/server/notification/ZenModeConditions.java
@@ -147,6 +147,7 @@ public class ZenModeConditions implements ConditionProviders.Callback {
if (mConditionProviders.subscribeIfNecessary(rule.component, rule.conditionId)) {
mSubscriptions.put(rule.conditionId, rule.component);
} else {
+ rule.condition = null;
if (DEBUG) Log.d(TAG, "zmc failed to subscribe");
}
}
diff --git a/services/core/java/com/android/server/pm/ShortcutPackage.java b/services/core/java/com/android/server/pm/ShortcutPackage.java
index 51c9619b77ca..827b88a5b61a 100644
--- a/services/core/java/com/android/server/pm/ShortcutPackage.java
+++ b/services/core/java/com/android/server/pm/ShortcutPackage.java
@@ -667,8 +667,8 @@ class ShortcutPackage extends ShortcutPackageItem {
// - version code hasn't change
// - lastUpdateTime hasn't change
// - all target activities are still enabled.
- if ((getPackageInfo().getVersionCode() >= pi.versionCode)
- && (getPackageInfo().getLastUpdateTime() >= pi.lastUpdateTime)
+ if ((getPackageInfo().getVersionCode() == pi.versionCode)
+ && (getPackageInfo().getLastUpdateTime() == pi.lastUpdateTime)
&& areAllActivitiesStillEnabled()) {
return false;
}
diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java
index 558467b2ef85..d5767b4cad8a 100644
--- a/services/core/java/com/android/server/pm/ShortcutService.java
+++ b/services/core/java/com/android/server/pm/ShortcutService.java
@@ -2601,17 +2601,9 @@ public class ShortcutService extends IShortcutService.Stub {
/* appStillExists = */ false);
}
}
- final long now = injectCurrentTimeMillis();
- // Then for each installed app, publish manifest shortcuts when needed.
- forUpdatedPackages(ownerUserId, user.getLastAppScanTime(), ai -> {
- user.rescanPackageIfNeeded(ai.packageName, /* forceRescan=*/ false);
- });
-
- // Write the time just before the scan, because there may be apps that have just
- // been updated, and we want to catch them in the next time.
- user.setLastAppScanTime(now);
- scheduleSaveUser(ownerUserId);
+ rescanUpdatedPackagesLocked(ownerUserId, user.getLastAppScanTime(),
+ /* forceRescan=*/ false);
}
} finally {
logDurationStat(Stats.CHECK_PACKAGE_CHANGES, start);
@@ -2619,6 +2611,24 @@ public class ShortcutService extends IShortcutService.Stub {
verifyStates();
}
+ private void rescanUpdatedPackagesLocked(@UserIdInt int userId, long lastScanTime,
+ boolean forceRescan) {
+ final ShortcutUser user = getUserShortcutsLocked(userId);
+
+ final long now = injectCurrentTimeMillis();
+
+ // Then for each installed app, publish manifest shortcuts when needed.
+ forUpdatedPackages(userId, lastScanTime, ai -> {
+ user.attemptToRestoreIfNeededAndSave(this, ai.packageName, userId);
+ user.rescanPackageIfNeeded(ai.packageName, forceRescan);
+ });
+
+ // Write the time just before the scan, because there may be apps that have just
+ // been updated, and we want to catch them in the next time.
+ user.setLastAppScanTime(now);
+ scheduleSaveUser(userId);
+ }
+
private void handlePackageAdded(String packageName, @UserIdInt int userId) {
if (DEBUG) {
Slog.d(TAG, String.format("handlePackageAdded: %s user=%d", packageName, userId));
@@ -2626,7 +2636,7 @@ public class ShortcutService extends IShortcutService.Stub {
synchronized (mLock) {
final ShortcutUser user = getUserShortcutsLocked(userId);
user.attemptToRestoreIfNeededAndSave(this, packageName, userId);
- user.rescanPackageIfNeeded(packageName, /* forceRescan=*/ false);
+ user.rescanPackageIfNeeded(packageName, /* forceRescan=*/ true);
}
verifyStates();
}
@@ -2641,7 +2651,7 @@ public class ShortcutService extends IShortcutService.Stub {
user.attemptToRestoreIfNeededAndSave(this, packageName, userId);
if (isPackageInstalled(packageName, userId)) {
- user.rescanPackageIfNeeded(packageName, /* forceRescan=*/ false);
+ user.rescanPackageIfNeeded(packageName, /* forceRescan=*/ true);
}
}
verifyStates();
@@ -2863,7 +2873,10 @@ public class ShortcutService extends IShortcutService.Stub {
for (int i = list.size() - 1; i >= 0; i--) {
final PackageInfo pi = list.get(i);
- if (pi.lastUpdateTime >= lastScanTime) {
+ // If the package has been updated since the last scan time, then scan it.
+ // Also if it's a system app with no update, lastUpdateTime is not reliable, so
+ // just scan it.
+ if (pi.lastUpdateTime >= lastScanTime || isPureSystemApp(pi.applicationInfo)) {
if (DEBUG) {
Slog.d(TAG, "Found updated package " + pi.packageName);
}
@@ -2872,6 +2885,13 @@ public class ShortcutService extends IShortcutService.Stub {
}
}
+ /**
+ * @return true if it's a system app with no updates.
+ */
+ private boolean isPureSystemApp(ApplicationInfo ai) {
+ return ai.isSystemApp() && !ai.isUpdatedSystemApp();
+ }
+
private boolean isApplicationFlagSet(@NonNull String packageName, int userId, int flags) {
final ApplicationInfo ai = injectApplicationInfoWithUninstalled(packageName, userId);
return (ai != null) && ((ai.flags & flags) == flags);
@@ -3109,12 +3129,10 @@ public class ShortcutService extends IShortcutService.Stub {
}
mUsers.put(userId, user);
- // Then purge all the save images.
- final File bitmapPath = getUserBitmapFilePath(userId);
- final boolean success = FileUtils.deleteContents(bitmapPath);
- if (!success) {
- Slog.w(TAG, "Failed to delete " + bitmapPath);
- }
+ // Rescan all packages to re-publish manifest shortcuts and do other checks.
+ rescanUpdatedPackagesLocked(userId,
+ 0, // lastScanTime = 0; rescan all packages.
+ /* forceRescan= */ true);
saveUserLocked(userId);
}
diff --git a/services/net/java/android/net/ip/RouterAdvertisementDaemon.java b/services/net/java/android/net/ip/RouterAdvertisementDaemon.java
index 1a9d2f23935e..a52cdcc98b9c 100644
--- a/services/net/java/android/net/ip/RouterAdvertisementDaemon.java
+++ b/services/net/java/android/net/ip/RouterAdvertisementDaemon.java
@@ -47,6 +47,7 @@ import java.nio.ByteOrder;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
+import java.util.Iterator;
import java.util.Map;
import java.util.Random;
import java.util.Set;
@@ -61,10 +62,6 @@ import java.util.concurrent.atomic.AtomicInteger;
* - Rewrite using Handler (and friends) so that AlarmManager can deliver
* "kick" messages when it's time to send a multicast RA.
*
- * - Support transmitting MAX_URGENT_RTR_ADVERTISEMENTS number of empty
- * RAs with zero default router lifetime when transitioning from an
- * advertising state to a non-advertising state.
- *
* @hide
*/
public class RouterAdvertisementDaemon {
@@ -112,8 +109,7 @@ public class RouterAdvertisementDaemon {
@GuardedBy("mLock")
private int mRaLength;
@GuardedBy("mLock")
- private final HashMap<IpPrefix, Integer> mDeprecatedPrefixes;
-
+ private final DeprecatedInfoTracker mDeprecatedInfoTracker;
@GuardedBy("mLock")
private RaParams mRaParams;
@@ -140,6 +136,88 @@ public class RouterAdvertisementDaemon {
prefixes = (HashSet) other.prefixes.clone();
dnses = (HashSet) other.dnses.clone();
}
+
+ // Returns the subset of RA parameters that become deprecated when
+ // moving from announcing oldRa to announcing newRa.
+ //
+ // Currently only tracks differences in |prefixes| and |dnses|.
+ public static RaParams getDeprecatedRaParams(RaParams oldRa, RaParams newRa) {
+ RaParams newlyDeprecated = new RaParams();
+
+ if (oldRa != null) {
+ for (IpPrefix ipp : oldRa.prefixes) {
+ if (newRa == null || !newRa.prefixes.contains(ipp)) {
+ newlyDeprecated.prefixes.add(ipp);
+ }
+ }
+
+ for (Inet6Address dns : oldRa.dnses) {
+ if (newRa == null || !newRa.dnses.contains(dns)) {
+ newlyDeprecated.dnses.add(dns);
+ }
+ }
+ }
+
+ return newlyDeprecated;
+ }
+ }
+
+ private static class DeprecatedInfoTracker {
+ private final HashMap<IpPrefix, Integer> mPrefixes = new HashMap<>();
+ private final HashMap<Inet6Address, Integer> mDnses = new HashMap<>();
+
+ Set<IpPrefix> getPrefixes() { return mPrefixes.keySet(); }
+
+ void putPrefixes(Set<IpPrefix> prefixes) {
+ for (IpPrefix ipp : prefixes) {
+ mPrefixes.put(ipp, MAX_URGENT_RTR_ADVERTISEMENTS);
+ }
+ }
+
+ void removePrefixes(Set<IpPrefix> prefixes) {
+ for (IpPrefix ipp : prefixes) {
+ mPrefixes.remove(ipp);
+ }
+ }
+
+ Set<Inet6Address> getDnses() { return mDnses.keySet(); }
+
+ void putDnses(Set<Inet6Address> dnses) {
+ for (Inet6Address dns : dnses) {
+ mDnses.put(dns, MAX_URGENT_RTR_ADVERTISEMENTS);
+ }
+ }
+
+ void removeDnses(Set<Inet6Address> dnses) {
+ for (Inet6Address dns : dnses) {
+ mDnses.remove(dns);
+ }
+ }
+
+ boolean isEmpty() { return mPrefixes.isEmpty() && mDnses.isEmpty(); }
+
+ private boolean decrementCounters() {
+ boolean removed = decrementCounter(mPrefixes);
+ removed |= decrementCounter(mDnses);
+ return removed;
+ }
+
+ private <T> boolean decrementCounter(HashMap<T, Integer> map) {
+ boolean removed = false;
+
+ for (Iterator<Map.Entry<T, Integer>> it = map.entrySet().iterator();
+ it.hasNext();) {
+ Map.Entry<T, Integer> kv = it.next();
+ if (kv.getValue() == 0) {
+ it.remove();
+ removed = true;
+ } else {
+ kv.setValue(kv.getValue() - 1);
+ }
+ }
+
+ return removed;
+ }
}
@@ -148,29 +226,24 @@ public class RouterAdvertisementDaemon {
mIfIndex = ifindex;
mHwAddr = hwaddr;
mAllNodes = new InetSocketAddress(getAllNodesForScopeId(mIfIndex), 0);
- mDeprecatedPrefixes = new HashMap<>();
+ mDeprecatedInfoTracker = new DeprecatedInfoTracker();
}
- public void buildNewRa(RaParams params, HashSet<IpPrefix> newlyDeprecated) {
- if (newlyDeprecated != null) {
- synchronized (mLock) {
- for (IpPrefix ipp : newlyDeprecated) {
- mDeprecatedPrefixes.put(ipp, MAX_URGENT_RTR_ADVERTISEMENTS);
- }
+ public void buildNewRa(RaParams deprecatedParams, RaParams newParams) {
+ synchronized (mLock) {
+ if (deprecatedParams != null) {
+ mDeprecatedInfoTracker.putPrefixes(deprecatedParams.prefixes);
+ mDeprecatedInfoTracker.putDnses(deprecatedParams.dnses);
}
- }
- // TODO: Send MAX_URGENT_RTR_ADVERTISEMENTS zero router lifetime RAs,
- // iff. we have already sent an RA.
- if (params == null || params.prefixes.isEmpty()) {
- // No RA to be served at this time.
- clearRa();
- return;
- }
+ if (newParams != null) {
+ // Process information that is no longer deprecated.
+ mDeprecatedInfoTracker.removePrefixes(newParams.prefixes);
+ mDeprecatedInfoTracker.removeDnses(newParams.dnses);
+ }
- synchronized (mLock) {
- mRaParams = params;
- assembleRa();
+ mRaParams = newParams;
+ assembleRaLocked();
}
maybeNotifyMulticastTransmitter();
@@ -196,73 +269,64 @@ public class RouterAdvertisementDaemon {
mUnicastResponder = null;
}
- private void clearRa() {
- boolean notifySocket;
- synchronized (mLock) {
- notifySocket = (mRaLength != 0);
- mRaLength = 0;
- }
- if (notifySocket) {
- maybeNotifyMulticastTransmitter();
- }
- }
-
- private void assembleRa() {
+ private void assembleRaLocked() {
final ByteBuffer ra = ByteBuffer.wrap(mRA);
ra.order(ByteOrder.BIG_ENDIAN);
- synchronized (mLock) {
- try {
- putHeader(ra, mRaParams.hasDefaultRoute);
-
- putSlla(ra, mHwAddr);
-
- // https://tools.ietf.org/html/rfc5175#section-4 says:
- //
- // "MUST NOT be added to a Router Advertisement message
- // if no flags in the option are set."
- //
- // putExpandedFlagsOption(ra);
+ boolean shouldSendRA = false;
+ try {
+ putHeader(ra, mRaParams != null && mRaParams.hasDefaultRoute);
+ putSlla(ra, mHwAddr);
+ mRaLength = ra.position();
+
+ // https://tools.ietf.org/html/rfc5175#section-4 says:
+ //
+ // "MUST NOT be added to a Router Advertisement message
+ // if no flags in the option are set."
+ //
+ // putExpandedFlagsOption(ra);
+
+ if (mRaParams != null) {
putMtu(ra, mRaParams.mtu);
+ mRaLength = ra.position();
for (IpPrefix ipp : mRaParams.prefixes) {
putPio(ra, ipp, DEFAULT_LIFETIME, DEFAULT_LIFETIME);
- mDeprecatedPrefixes.remove(ipp);
- }
-
- for (IpPrefix ipp : mDeprecatedPrefixes.keySet()) {
- putPio(ra, ipp, 0, 0);
+ mRaLength = ra.position();
+ shouldSendRA = true;
}
if (mRaParams.dnses.size() > 0) {
- putRdnss(ra, mRaParams.dnses);
+ putRdnss(ra, mRaParams.dnses, DEFAULT_LIFETIME);
+ mRaLength = ra.position();
+ shouldSendRA = true;
}
+ }
+ for (IpPrefix ipp : mDeprecatedInfoTracker.getPrefixes()) {
+ putPio(ra, ipp, 0, 0);
mRaLength = ra.position();
- } catch (BufferOverflowException e) {
- Log.e(TAG, "Could not construct new RA: " + e);
- mRaLength = 0;
- return;
+ shouldSendRA = true;
}
- }
- }
- private int decrementDeprecatedPrefixes() {
- int removed = 0;
-
- synchronized (mLock) {
- for (Map.Entry<IpPrefix, Integer> kv : mDeprecatedPrefixes.entrySet()) {
- if (kv.getValue() == 0) {
- mDeprecatedPrefixes.remove(kv.getKey());
- removed++;
- } else {
- kv.setValue(kv.getValue() - 1);
- }
+ final Set<Inet6Address> deprecatedDnses = mDeprecatedInfoTracker.getDnses();
+ if (!deprecatedDnses.isEmpty()) {
+ putRdnss(ra, deprecatedDnses, 0);
+ mRaLength = ra.position();
+ shouldSendRA = true;
}
+ } catch (BufferOverflowException e) {
+ // The packet up to mRaLength is valid, since it has been updated
+ // progressively as the RA was built. Log an error, and continue
+ // on as best as possible.
+ Log.e(TAG, "Could not construct new RA: " + e);
}
- return removed;
+ // We have nothing worth announcing; indicate as much to maybeSendRA().
+ if (!shouldSendRA) {
+ mRaLength = 0;
+ }
}
private void maybeNotifyMulticastTransmitter() {
@@ -461,7 +525,7 @@ public class RouterAdvertisementDaemon {
}
}
- private static void putRdnss(ByteBuffer ra, Set<Inet6Address> dnses) {
+ private static void putRdnss(ByteBuffer ra, Set<Inet6Address> dnses, int lifetime) {
/**
Recursive DNS Server (RDNSS) Option
@@ -483,9 +547,15 @@ public class RouterAdvertisementDaemon {
ra.put(ND_OPTION_RDNSS)
.put(RDNSS_NUM_8OCTETS)
.putShort(asShort(0))
- .putInt(DEFAULT_LIFETIME);
+ .putInt(lifetime);
for (Inet6Address dns : dnses) {
+ // NOTE: If the full of list DNS servers doesn't fit in the packet,
+ // this code will cause a buffer overflow and the RA won't include
+ // include this instance of the option at all.
+ //
+ // TODO: Consider looking at ra.remaining() to determine how many
+ // DNS servers will fit, and adding only those.
ra.put(dns.getAddress());
}
}
@@ -601,10 +671,12 @@ public class RouterAdvertisementDaemon {
}
maybeSendRA(mAllNodes);
- if (decrementDeprecatedPrefixes() > 0) {
- // At least one deprecated PIO has been removed;
- // reassemble the RA.
- assembleRa();
+ synchronized (mLock) {
+ if (mDeprecatedInfoTracker.decrementCounters()) {
+ // At least one deprecated PIO has been removed;
+ // reassemble the RA.
+ assembleRaLocked();
+ }
}
}
}
@@ -619,17 +691,17 @@ public class RouterAdvertisementDaemon {
}
private int getNextMulticastTransmitDelaySec() {
- int countDeprecatedPrefixes = 0;
+ boolean deprecationInProgress = false;
synchronized (mLock) {
if (mRaLength < MIN_RA_HEADER_SIZE) {
// No actual RA to send; just sleep for 1 day.
return DAY_IN_SECONDS;
}
- countDeprecatedPrefixes = mDeprecatedPrefixes.size();
+ deprecationInProgress = !mDeprecatedInfoTracker.isEmpty();
}
final int urgentPending = mUrgentAnnouncements.getAndDecrement();
- if (urgentPending > 0 || countDeprecatedPrefixes > 0) {
+ if ((urgentPending > 0) || deprecationInProgress) {
return MIN_DELAY_BETWEEN_RAS_SEC;
}
diff --git a/services/tests/servicestests/assets/shortcut/shortcut_legacy_file.xml b/services/tests/servicestests/assets/shortcut/shortcut_legacy_file.xml
index 872dc3a26773..f7eee9155da4 100644
--- a/services/tests/servicestests/assets/shortcut/shortcut_legacy_file.xml
+++ b/services/tests/servicestests/assets/shortcut/shortcut_legacy_file.xml
@@ -16,7 +16,7 @@
<user locales="en-US" last-app-scan-time="3113976673">
<package name="com.android.test.1" call-count="0" last-reset="1468976368772">
<package-info version="25" last_udpate_time="1230796800000" />
- <shortcut id="manifest-shortcut-storage" activity="com.android.test.1/com.android.test.1.Settings" title="Storage" titleid="2131625197" titlename="storage_settings" textid="0" dmessageid="0" intent="#Intent;action=android.settings.INTERNAL_STORAGE_SETTINGS;end" timestamp="1469050672334" rank="4" flags="420" icon-res="2130837747" icon-resname="drawable/ic_shortcut_storage" >
+ <shortcut id="manifest-shortcut-storage" activity="com.android.test.1/com.android.test.1.Settings" title="Storage" intent="#Intent;action=android.settings.INTERNAL_STORAGE_SETTINGS;end" timestamp="1469050672334" flags="1" >
<intent-extras>
<int name="key" value="12345" />
</intent-extras>
diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java
index b1c0ed44ba69..75a34272d82b 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java
@@ -57,13 +57,11 @@ import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.eq;
-import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
import android.Manifest.permission;
import android.app.ActivityManager;
@@ -82,6 +80,7 @@ import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
+import android.os.Process;
import android.os.UserHandle;
import android.test.suitebuilder.annotation.SmallTest;
import android.util.Log;
@@ -4149,28 +4148,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
ArgumentCaptor<List> shortcuts;
- // First, call the event without updating the versions.
- reset(c0);
- reset(c10);
-
- mService.mPackageMonitor.onReceive(getTestContext(),
- genPackageUpdateIntent(CALLING_PACKAGE_1, USER_0));
- mService.mPackageMonitor.onReceive(getTestContext(),
- genPackageUpdateIntent(CALLING_PACKAGE_1, USER_10));
-
- waitOnMainThread();
-
- // Version not changed, so no callback.
- verify(c0, times(0)).onShortcutsChanged(
- eq(CALLING_PACKAGE_1),
- any(List.class),
- any(UserHandle.class));
- verify(c10, times(0)).onShortcutsChanged(
- eq(CALLING_PACKAGE_1),
- any(List.class),
- any(UserHandle.class));
-
- // Next, update the version info for package 1.
+ // Update the version info for package 1.
reset(c0);
reset(c10);
updatePackageVersion(CALLING_PACKAGE_1, 1);
@@ -5174,7 +5152,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
}
- public void testBackupAndRestore_manifestNotRestored() {
+ public void testBackupAndRestore_manifestRePublished() {
// Publish two manifest shortcuts.
addManifestShortcutResource(
new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
@@ -5183,9 +5161,15 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
mService.mPackageMonitor.onReceive(mServiceContext,
genPackageAddIntent(CALLING_PACKAGE_1, USER_0));
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ assertTrue(mManager.setDynamicShortcuts(list(
+ makeShortcut("s1"), makeShortcut("s2"), makeShortcut("s3"))));
+ });
+
// Pin from launcher 1.
runWithCaller(LAUNCHER_1, USER_0, () -> {
- mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, list("ms1", "ms2"), HANDLE_USER_0);
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_1,
+ list("ms1", "ms2", "s1", "s2"), HANDLE_USER_0);
});
// Update and now ms2 is gone -> disabled.
@@ -5199,9 +5183,18 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
// Make sure the manifest shortcuts have been published.
runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
assertWith(getCallerShortcuts())
- .areAllPinned()
- .haveIds("ms1", "ms2")
+ .selectManifest()
+ .haveIds("ms1")
+
+ .revertToOriginalList()
+ .selectDynamic()
+ .haveIds("s1", "s2", "s3")
+ .revertToOriginalList()
+ .selectPinned()
+ .haveIds("ms1", "ms2", "s1", "s2")
+
+ .revertToOriginalList()
.selectByIds("ms1")
.areAllManifest()
.areAllEnabled()
@@ -5212,10 +5205,130 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest {
.areAllDisabled();
});
- // Now do the regular backup & restore test.
- // The existence of the manifest shortcuts shouldn't affect the result.
- prepareCrossProfileDataSet();
backupAndRestore();
+
+ // When re-installing the app, the manifest shortcut should be re-published.
+ mService.mPackageMonitor.onReceive(mServiceContext,
+ genPackageAddIntent(CALLING_PACKAGE_1, USER_0));
+ mService.mPackageMonitor.onReceive(mServiceContext,
+ genPackageAddIntent(LAUNCHER_1, USER_0));
+
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ assertWith(getCallerVisibleShortcuts())
+ .selectPinned()
+ // ms2 was disabled, so not restored.
+ .haveIds("ms1", "s1", "s2")
+ .areAllEnabled()
+
+ .revertToOriginalList()
+ .selectByIds("ms1")
+ .areAllManifest()
+
+ .revertToOriginalList()
+ .selectByIds("s1", "s2")
+ .areAllNotDynamic()
+ ;
+ });
+ }
+
+ /**
+ * It's the case with preintalled apps -- when applyRestore() is called, the system
+ * apps are already installed, so manifest shortcuts need to be re-published.
+ */
+ public void testBackupAndRestore_appAlreadyInstalledWhenRestored() {
+ // Pre-backup. Same as testBackupAndRestore_manifestRePublished().
+
+ // Publish two manifest shortcuts.
+ addManifestShortcutResource(
+ new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
+ R.xml.shortcut_2);
+ updatePackageVersion(CALLING_PACKAGE_1, 1);
+ mService.mPackageMonitor.onReceive(mServiceContext,
+ genPackageAddIntent(CALLING_PACKAGE_1, USER_0));
+
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ assertTrue(mManager.setDynamicShortcuts(list(
+ makeShortcut("s1"), makeShortcut("s2"), makeShortcut("s3"))));
+ });
+
+ // Pin from launcher 1.
+ runWithCaller(LAUNCHER_1, USER_0, () -> {
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_1,
+ list("ms1", "ms2", "s1", "s2"), HANDLE_USER_0);
+ });
+
+ // Update and now ms2 is gone -> disabled.
+ addManifestShortcutResource(
+ new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
+ R.xml.shortcut_1);
+ updatePackageVersion(CALLING_PACKAGE_1, 1);
+ mService.mPackageMonitor.onReceive(mServiceContext,
+ genPackageAddIntent(CALLING_PACKAGE_1, USER_0));
+
+ // Make sure the manifest shortcuts have been published.
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ assertWith(getCallerShortcuts())
+ .selectManifest()
+ .haveIds("ms1")
+
+ .revertToOriginalList()
+ .selectDynamic()
+ .haveIds("s1", "s2", "s3")
+
+ .revertToOriginalList()
+ .selectPinned()
+ .haveIds("ms1", "ms2", "s1", "s2")
+
+ .revertToOriginalList()
+ .selectByIds("ms1")
+ .areAllManifest()
+ .areAllEnabled()
+
+ .revertToOriginalList()
+ .selectByIds("ms2")
+ .areAllNotManifest()
+ .areAllDisabled();
+ });
+
+ // Backup and *without restarting the service, just call applyRestore()*.
+ {
+ int prevUid = mInjectedCallingUid;
+ mInjectedCallingUid = Process.SYSTEM_UID; // Only system can call it.
+
+ dumpsysOnLogcat("Before backup");
+
+ final byte[] payload = mService.getBackupPayload(USER_0);
+ if (ENABLE_DUMP) {
+ final String xml = new String(payload);
+ Log.v(TAG, "Backup payload:");
+ for (String line : xml.split("\n")) {
+ Log.v(TAG, line);
+ }
+ }
+ mService.applyRestore(payload, USER_0);
+
+ dumpsysOnLogcat("After restore");
+
+ mInjectedCallingUid = prevUid;
+ }
+
+ // The check is also the same as testBackupAndRestore_manifestRePublished().
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ assertWith(getCallerVisibleShortcuts())
+ .selectPinned()
+ // ms2 was disabled, so not restored.
+ .haveIds("ms1", "s1", "s2")
+ .areAllEnabled()
+
+ .revertToOriginalList()
+ .selectByIds("ms1")
+ .areAllManifest()
+
+ .revertToOriginalList()
+ .selectByIds("s1", "s2")
+ .areAllNotDynamic()
+ ;
+ });
}
public void testSaveAndLoad_crossProfile() {