Merge "Persist package's test-only flag and always use it" into nyc-mr1-dev
diff --git a/core/java/android/accounts/AccountManagerInternal.java b/core/java/android/accounts/AccountManagerInternal.java
index 0ac7197..68c17c3 100644
--- a/core/java/android/accounts/AccountManagerInternal.java
+++ b/core/java/android/accounts/AccountManagerInternal.java
@@ -73,4 +73,18 @@
*/
public abstract void addOnAppPermissionChangeListener(
@NonNull OnAppPermissionChangeListener listener);
+
+ /**
+ * Backups the account access permissions.
+ * @param userId The user for which to backup.
+ * @return The backup data.
+ */
+ public abstract byte[] backupAccountAccessPermissions(int userId);
+
+ /**
+ * Restores the account access permissions.
+ * @param data The restore data.
+ * @param userId The user for which to restore.
+ */
+ public abstract void restoreAccountAccessPermissions(byte[] data, int userId);
}
diff --git a/core/java/android/content/IntentFilter.java b/core/java/android/content/IntentFilter.java
index 22ab43b..d07b545 100644
--- a/core/java/android/content/IntentFilter.java
+++ b/core/java/android/content/IntentFilter.java
@@ -265,6 +265,7 @@
public static final String SCHEME_HTTPS = "https";
private int mPriority;
+ private int mOrder;
private final ArrayList<String> mActions;
private ArrayList<String> mCategories = null;
private ArrayList<String> mDataSchemes = null;
@@ -425,6 +426,7 @@
*/
public IntentFilter(IntentFilter o) {
mPriority = o.mPriority;
+ mOrder = o.mOrder;
mActions = new ArrayList<String>(o.mActions);
if (o.mCategories != null) {
mCategories = new ArrayList<String>(o.mCategories);
@@ -477,6 +479,16 @@
return mPriority;
}
+ /** @hide */
+ public final void setOrder(int order) {
+ mOrder = order;
+ }
+
+ /** @hide */
+ public final int getOrder() {
+ return mOrder;
+ }
+
/**
* Set whether this filter will needs to be automatically verified against its data URIs or not.
* The default is false.
diff --git a/core/java/android/content/pm/PackageManagerInternal.java b/core/java/android/content/pm/PackageManagerInternal.java
index d208fe7..f5bcf64 100644
--- a/core/java/android/content/pm/PackageManagerInternal.java
+++ b/core/java/android/content/pm/PackageManagerInternal.java
@@ -160,4 +160,12 @@
* Returns {@code true} if a given package can't be wiped. Otherwise, returns {@code false}.
*/
public abstract boolean isPackageDataProtected(int userId, String packageName);
+
+ /**
+ * Gets whether the package was ever launched.
+ * @param packageName The package name.
+ * @param userId The user for which to check.
+ * @return Whether was launched.
+ */
+ public abstract boolean wasPackageEverLaunched(String packageName, int userId);
}
diff --git a/core/java/android/net/NetworkRequest.java b/core/java/android/net/NetworkRequest.java
index 4501f7b..ae72470 100644
--- a/core/java/android/net/NetworkRequest.java
+++ b/core/java/android/net/NetworkRequest.java
@@ -49,7 +49,7 @@
public final int legacyType;
/**
- * A NetworkRequest as used by the system can be one of three types:
+ * A NetworkRequest as used by the system can be one of the following types:
*
* - LISTEN, for which the framework will issue callbacks about any
* and all networks that match the specified NetworkCapabilities,
@@ -64,7 +64,20 @@
* current network (if any) that matches the capabilities of the
* default Internet request (mDefaultRequest), but which cannot cause
* the framework to either create or retain the existence of any
- * specific network.
+ * specific network. Note that from the point of view of the request
+ * matching code, TRACK_DEFAULT is identical to REQUEST: its special
+ * behaviour is not due to different semantics, but to the fact that
+ * the system will only ever create a TRACK_DEFAULT with capabilities
+ * that are identical to the default request's capabilities, thus
+ * causing it to share fate in every way with the default request.
+ *
+ * - BACKGROUND_REQUEST, like REQUEST but does not cause any networks
+ * to retain the NET_CAPABILITY_FOREGROUND capability. A network with
+ * no foreground requests is in the background. A network that has
+ * one or more background requests and loses its last foreground
+ * request to a higher-scoring network will not go into the
+ * background immediately, but will linger and go into the background
+ * after the linger timeout.
*
* - The value NONE is used only by applications. When an application
* creates a NetworkRequest, it does not have a type; the type is set
@@ -77,7 +90,8 @@
NONE,
LISTEN,
TRACK_DEFAULT,
- REQUEST
+ REQUEST,
+ BACKGROUND_REQUEST,
};
/**
@@ -140,7 +154,7 @@
* Add the given capability requirement to this builder. These represent
* the requested network's required capabilities. Note that when searching
* for a network to satisfy a request, all capabilities requested must be
- * satisfied. See {@link NetworkCapabilities} for {@code NET_CAPABILITIY_*}
+ * satisfied. See {@link NetworkCapabilities} for {@code NET_CAPABILITY_*}
* definitions.
*
* @param capability The {@code NetworkCapabilities.NET_CAPABILITY_*} to add.
@@ -284,7 +298,7 @@
};
/**
- * Returns true iff. the contained NetworkRequest is of type LISTEN.
+ * Returns true iff. this NetworkRequest is of type LISTEN.
*
* @hide
*/
@@ -298,8 +312,9 @@
* - should be associated with at most one satisfying network
* at a time;
*
- * - should cause a network to be kept up if it is the best network
- * which can satisfy the NetworkRequest.
+ * - should cause a network to be kept up, but not necessarily in
+ * the foreground, if it is the best network which can satisfy the
+ * NetworkRequest.
*
* For full detail of how isRequest() is used for pairing Networks with
* NetworkRequests read rematchNetworkAndRequests().
@@ -307,9 +322,36 @@
* @hide
*/
public boolean isRequest() {
+ return isForegroundRequest() || isBackgroundRequest();
+ }
+
+ /**
+ * Returns true iff. the contained NetworkRequest is one that:
+ *
+ * - should be associated with at most one satisfying network
+ * at a time;
+ *
+ * - should cause a network to be kept up and in the foreground if
+ * it is the best network which can satisfy the NetworkRequest.
+ *
+ * For full detail of how isRequest() is used for pairing Networks with
+ * NetworkRequests read rematchNetworkAndRequests().
+ *
+ * @hide
+ */
+ public boolean isForegroundRequest() {
return type == Type.TRACK_DEFAULT || type == Type.REQUEST;
}
+ /**
+ * Returns true iff. this NetworkRequest is of type BACKGROUND_REQUEST.
+ *
+ * @hide
+ */
+ public boolean isBackgroundRequest() {
+ return type == Type.BACKGROUND_REQUEST;
+ }
+
public String toString() {
return "NetworkRequest [ " + type + " id=" + requestId +
(legacyType != ConnectivityManager.TYPE_NONE ? ", legacyType=" + legacyType : "") +
diff --git a/core/java/android/util/PackageUtils.java b/core/java/android/util/PackageUtils.java
new file mode 100644
index 0000000..6531aef
--- /dev/null
+++ b/core/java/android/util/PackageUtils.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.util;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.Signature;
+
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+
+/**
+ * Helper functions applicable to packages.
+ * @hide
+ */
+public final class PackageUtils {
+ private final static char[] HEX_ARRAY = "0123456789ABCDEF".toCharArray();
+
+ private PackageUtils() {
+ /* hide constructor */
+ }
+
+ /**
+ * Computes the SHA256 digest of the signing cert for a package.
+ * @param packageManager The package manager.
+ * @param packageName The package for which to generate the digest.
+ * @param userId The user for which to generate the digest.
+ * @return The digest or null if the package does not exist for this user.
+ */
+ public static @Nullable String computePackageCertSha256Digest(
+ @NonNull PackageManager packageManager,
+ @NonNull String packageName, int userId) {
+ final PackageInfo packageInfo;
+ try {
+ packageInfo = packageManager.getPackageInfoAsUser(packageName,
+ PackageManager.GET_SIGNATURES, userId);
+ } catch (PackageManager.NameNotFoundException e) {
+ return null;
+ }
+ return computeCertSha256Digest(packageInfo.signatures[0]);
+ }
+
+ /**
+ * Computes the SHA256 digest of a cert.
+ * @param signature The signature.
+ * @return The digest or null if an error occurs.
+ */
+ public static @Nullable String computeCertSha256Digest(@NonNull Signature signature) {
+ return computeSha256Digest(signature.toByteArray());
+ }
+
+ /**
+ * Computes the SHA256 digest of some data.
+ * @param data The data.
+ * @return The digest or null if an error occurs.
+ */
+ public static @Nullable String computeSha256Digest(@NonNull byte[] data) {
+ MessageDigest messageDigest;
+ try {
+ messageDigest = MessageDigest.getInstance("SHA256");
+ } catch (NoSuchAlgorithmException e) {
+ /* can't happen */
+ return null;
+ }
+
+ messageDigest.update(data);
+
+ final byte[] digest = messageDigest.digest();
+ final int digestLength = digest.length;
+ final int charCount = 2 * digestLength;
+
+ final char[] chars = new char[charCount];
+ for (int i = 0; i < digestLength; i++) {
+ final int byteHex = digest[i] & 0xFF;
+ chars[i * 2] = HEX_ARRAY[byteHex >>> 4];
+ chars[i * 2 + 1] = HEX_ARRAY[byteHex & 0x0F];
+ }
+ return new String(chars);
+ }
+}
diff --git a/core/java/android/view/DragEvent.java b/core/java/android/view/DragEvent.java
index b0f15b5..a394f35 100644
--- a/core/java/android/view/DragEvent.java
+++ b/core/java/android/view/DragEvent.java
@@ -377,6 +377,10 @@
* The object is intended to provide local information about the drag and drop operation. For
* example, it can indicate whether the drag and drop operation is a copy or a move.
* <p>
+ * The local state is available only to views in the activity which has started the drag
+ * operation. In all other activities this method will return null
+ * </p>
+ * <p>
* This method returns valid data for all event actions except for {@link #ACTION_DRAG_ENDED}.
* </p>
* @return The local state object sent to the system by startDrag().
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 908658f..a0afeba 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -20655,8 +20655,10 @@
* @param shadowBuilder A {@link android.view.View.DragShadowBuilder} object for building the
* drag shadow.
* @param myLocalState An {@link java.lang.Object} containing local data about the drag and
- * drop operation. This Object is put into every DragEvent object sent by the system during the
- * current drag.
+ * drop operation. When dispatching drag events to views in the same activity this object
+ * will be available through {@link android.view.DragEvent#getLocalState()}. Views in other
+ * activities will not have access to this data ({@link android.view.DragEvent#getLocalState()}
+ * will return null).
* <p>
* myLocalState is a lightweight mechanism for the sending information from the dragged View
* to the target Views. For example, it can contain flags that differentiate between a
diff --git a/core/java/android/widget/PopupWindow.java b/core/java/android/widget/PopupWindow.java
index b12c03c..9b89491 100644
--- a/core/java/android/widget/PopupWindow.java
+++ b/core/java/android/widget/PopupWindow.java
@@ -1758,11 +1758,22 @@
*/
public int getMaxAvailableHeight(
@NonNull View anchor, int yOffset, boolean ignoreBottomDecorations) {
- final Rect displayFrame = new Rect();
+ Rect displayFrame = null;
+ final Rect visibleDisplayFrame = new Rect();
+
+ anchor.getWindowVisibleDisplayFrame(visibleDisplayFrame);
if (ignoreBottomDecorations) {
+ // In the ignore bottom decorations case we want to
+ // still respect all other decorations so we use the inset visible
+ // frame on the top right and left and take the bottom
+ // value from the full frame.
+ displayFrame = new Rect();
anchor.getWindowDisplayFrame(displayFrame);
+ displayFrame.top = visibleDisplayFrame.top;
+ displayFrame.right = visibleDisplayFrame.right;
+ displayFrame.left = visibleDisplayFrame.left;
} else {
- anchor.getWindowVisibleDisplayFrame(displayFrame);
+ displayFrame = visibleDisplayFrame;
}
final int[] anchorPos = mTmpDrawingLocation;
diff --git a/core/java/android/widget/SearchView.java b/core/java/android/widget/SearchView.java
index 5878cad..9139361 100644
--- a/core/java/android/widget/SearchView.java
+++ b/core/java/android/widget/SearchView.java
@@ -816,9 +816,11 @@
switch (heightMode) {
case MeasureSpec.AT_MOST:
- case MeasureSpec.UNSPECIFIED:
height = Math.min(getPreferredHeight(), height);
break;
+ case MeasureSpec.UNSPECIFIED:
+ height = getPreferredHeight();
+ break;
}
heightMode = MeasureSpec.EXACTLY;
diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java
index 0a4ac0d..1e26c92 100644
--- a/core/java/com/android/internal/app/ResolverActivity.java
+++ b/core/java/com/android/internal/app/ResolverActivity.java
@@ -1469,7 +1469,7 @@
boolean found = false;
// Only loop to the end of into as it was before we started; no dupes in from.
for (int j = 0; j < intoCount; j++) {
- final ResolvedComponentInfo rci = into.get(i);
+ final ResolvedComponentInfo rci = into.get(j);
if (isSameResolvedComponent(newInfo, rci)) {
found = true;
rci.add(intent, newInfo);
diff --git a/core/java/com/android/server/backup/AccountManagerBackupHelper.java b/core/java/com/android/server/backup/AccountManagerBackupHelper.java
new file mode 100644
index 0000000..39b18c0
--- /dev/null
+++ b/core/java/com/android/server/backup/AccountManagerBackupHelper.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.backup;
+
+import android.accounts.AccountManagerInternal;
+import android.app.backup.BlobBackupHelper;
+import android.os.UserHandle;
+import android.util.Slog;
+import com.android.server.LocalServices;
+
+/**
+ * Helper for handling backup of account manager specific state.
+ */
+public class AccountManagerBackupHelper extends BlobBackupHelper {
+ private static final String TAG = "AccountsBackup";
+ private static final boolean DEBUG = false;
+
+ // current schema of the backup state blob
+ private static final int STATE_VERSION = 1;
+
+ // key under which the account access grant state blob is committed to backup
+ private static final String KEY_ACCOUNT_ACCESS_GRANTS = "account_access_grants";
+
+ public AccountManagerBackupHelper() {
+ super(STATE_VERSION, KEY_ACCOUNT_ACCESS_GRANTS);
+ }
+
+ @Override
+ protected byte[] getBackupPayload(String key) {
+ AccountManagerInternal am = LocalServices.getService(AccountManagerInternal.class);
+ if (DEBUG) {
+ Slog.d(TAG, "Handling backup of " + key);
+ }
+ try {
+ switch (key) {
+ case KEY_ACCOUNT_ACCESS_GRANTS: {
+ return am.backupAccountAccessPermissions(UserHandle.USER_SYSTEM);
+ }
+
+ default: {
+ Slog.w(TAG, "Unexpected backup key " + key);
+ }
+ }
+ } catch (Exception e) {
+ Slog.e(TAG, "Unable to store payload " + key);
+ }
+
+ return new byte[0];
+ }
+
+ @Override
+ protected void applyRestoredPayload(String key, byte[] payload) {
+ AccountManagerInternal am = LocalServices.getService(AccountManagerInternal.class);
+ if (DEBUG) {
+ Slog.d(TAG, "Handling restore of " + key);
+ }
+ try {
+ switch (key) {
+ case KEY_ACCOUNT_ACCESS_GRANTS: {
+ am.restoreAccountAccessPermissions(payload, UserHandle.USER_SYSTEM);
+ } break;
+
+ default: {
+ Slog.w(TAG, "Unexpected restore key " + key);
+ }
+ }
+ } catch (Exception e) {
+ Slog.w(TAG, "Unable to restore key " + key);
+ }
+ }
+}
diff --git a/core/java/com/android/server/backup/SystemBackupAgent.java b/core/java/com/android/server/backup/SystemBackupAgent.java
index 9d296fa..5375651 100644
--- a/core/java/com/android/server/backup/SystemBackupAgent.java
+++ b/core/java/com/android/server/backup/SystemBackupAgent.java
@@ -49,6 +49,7 @@
private static final String PERMISSION_HELPER = "permissions";
private static final String USAGE_STATS_HELPER = "usage_stats";
private static final String SHORTCUT_MANAGER_HELPER = "shortcut_manager";
+ private static final String ACCOUNT_MANAGER_HELPER = "account_manager";
// These paths must match what the WallpaperManagerService uses. The leaf *_FILENAME
// are also used in the full-backup file format, so must not change unless steps are
@@ -82,6 +83,7 @@
addHelper(PERMISSION_HELPER, new PermissionBackupHelper());
addHelper(USAGE_STATS_HELPER, new UsageStatsBackupHelper(this));
addHelper(SHORTCUT_MANAGER_HELPER, new ShortcutBackupHelper());
+ addHelper(ACCOUNT_MANAGER_HELPER, new AccountManagerBackupHelper());
super.onBackup(oldState, data, newState);
}
@@ -111,6 +113,7 @@
addHelper(PERMISSION_HELPER, new PermissionBackupHelper());
addHelper(USAGE_STATS_HELPER, new UsageStatsBackupHelper(this));
addHelper(SHORTCUT_MANAGER_HELPER, new ShortcutBackupHelper());
+ addHelper(ACCOUNT_MANAGER_HELPER, new AccountManagerBackupHelper());
try {
super.onRestore(data, appVersionCode, newState);
diff --git a/core/jni/android_hardware_Camera.cpp b/core/jni/android_hardware_Camera.cpp
index 9459257..b926270 100644
--- a/core/jni/android_hardware_Camera.cpp
+++ b/core/jni/android_hardware_Camera.cpp
@@ -350,9 +350,16 @@
postData(msgType, dataPtr, NULL);
}
-void JNICameraContext::postRecordingFrameHandleTimestamp(nsecs_t, native_handle_t*) {
- // This is not needed at app layer. This should not be called because JNICameraContext cannot
- // start video recording.
+void JNICameraContext::postRecordingFrameHandleTimestamp(nsecs_t, native_handle_t* handle) {
+ // Video buffers are not needed at app layer so just return the video buffers here.
+ // This may be called when stagefright just releases camera but there are still outstanding
+ // video buffers.
+ if (mCamera != nullptr) {
+ mCamera->releaseRecordingFrameHandle(handle);
+ } else {
+ native_handle_close(handle);
+ native_handle_delete(handle);
+ }
}
void JNICameraContext::postMetadata(JNIEnv *env, int32_t msgType, camera_frame_metadata_t *metadata)
diff --git a/core/res/res/values-watch/donottranslate.xml b/core/res/res/values-watch/donottranslate.xml
new file mode 100644
index 0000000..d247ff6
--- /dev/null
+++ b/core/res/res/values-watch/donottranslate.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2016 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<resources>
+ <!-- DO NOT TRANSLATE Spans within this text are applied to style composing regions
+ within an EditText widget. The text content is ignored and not used.
+ Note: This is @color/material_deep_teal_200, cannot use @color references here. -->
+ <string name="candidates_style" translatable="false"><font color="#80cbc4">candidates</font></string>
+ </resources>
diff --git a/core/res/res/values-watch/styles_material.xml b/core/res/res/values-watch/styles_material.xml
index f5735e6..a9f6e22 100644
--- a/core/res/res/values-watch/styles_material.xml
+++ b/core/res/res/values-watch/styles_material.xml
@@ -88,9 +88,4 @@
<item name="virtualButtonPressedDrawable">?selectableItemBackground</item>
<item name="descendantFocusability">blocksDescendants</item>
</style>
-
- <!-- DO NOTE TRANSLATE Spans within this text are applied to style composing regions
- within an EditText widget. The text content is ignored and not used.
- Note: This is @color/material_deep_teal_200, cannot use @color references here. -->
- <string name="candidates_style"><font color="#80cbc4">candidates</font></string>
</resources>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 55a87ee..004b31f 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2540,4 +2540,12 @@
<!-- Verizon requires any SMS that starts with //VZWVVM to be treated as a VVM SMS-->
<item>310004,310010,310012,310013,310590,310890,310910,311110,311270,311271,311272,311273,311274,311275,311276,311277,311278,311279,311280,311281,311282,311283,311284,311285,311286,311287,311288,311289,311390,311480,311481,311482,311483,311484,311485,311486,311487,311488,311489;^//VZWVVM.*</item>
</string-array>
+ <!-- This config is holding calling number conversion map - expected to convert to emergency
+ number. Formats for this config as below:
+ <item>[dialstring1],[dialstring2],[dialstring3]:[replacement]</item>
+
+ E.g. for Taiwan Type Approval, 110 and 119 should be converted to 112.
+ <item>110,119:112</item>
+ -->
+ <string-array translatable="false" name="config_convert_to_emergency_number_map" />
</resources>
diff --git a/core/res/res/values/donottranslate.xml b/core/res/res/values/donottranslate.xml
index a139529..3a1679c 100644
--- a/core/res/res/values/donottranslate.xml
+++ b/core/res/res/values/donottranslate.xml
@@ -26,4 +26,7 @@
<string name="icu_abbrev_wday_month_day_no_year">eeeMMMMd</string>
<!-- @hide DO NOT TRANSLATE. date formatting pattern for system ui.-->
<string name="system_ui_date_pattern">@string/icu_abbrev_wday_month_day_no_year</string>
+ <!-- @hide DO NOT TRANSLATE Spans within this text are applied to style composing regions
+ within an EditText widget. The text content is ignored and not used. -->
+ <string name="candidates_style" translatable="false"><u>candidates</u></string>
</resources>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 39f20c2..d0107e1 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -3122,8 +3122,6 @@
<string name="fast_scroll_alphabet">\u0020ABCDEFGHIJKLMNOPQRSTUVWXYZ</string>
<string name="fast_scroll_numeric_alphabet">\u00200123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ</string>
- <string name="candidates_style"><u>candidates</u></string>
-
<!-- External media notification strings -->
<skip />
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index b910531..39127a4 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2699,4 +2699,5 @@
<java-symbol type="drawable" name="ic_restart" />
+ <java-symbol type="array" name="config_convert_to_emergency_number_map" />
</resources>
diff --git a/docs/html/topic/libraries/support-library/features.jd b/docs/html/topic/libraries/support-library/features.jd
index 614392e..b5f189a 100755
--- a/docs/html/topic/libraries/support-library/features.jd
+++ b/docs/html/topic/libraries/support-library/features.jd
@@ -108,7 +108,7 @@
<p>The Gradle build script dependency identifier for this library is as follows:</p>
<pre>
-com.android.support:support-compat:24.2.0
+com.android.support:support-compat:24.2.1
</pre>
<h3 id="v4-core-utils">v4 core-utils library</h3>
@@ -124,7 +124,7 @@
</p>
<pre>
-com.android.support:support-core-utils:24.2.0
+com.android.support:support-core-utils:24.2.1
</pre>
<h3 id="v4-core-ui">v4 core-ui library</h3>
@@ -141,7 +141,7 @@
</p>
<pre>
-com.android.support:support-core-ui:24.2.0
+com.android.support:support-core-ui:24.2.1
</pre>
<h3 id="v4-media-compat">v4 media-compat library</h3>
@@ -158,7 +158,7 @@
</p>
<pre>
-com.android.support:support-media-compat:24.2.0
+com.android.support:support-media-compat:24.2.1
</pre>
<h3 id="v4-fragment">v4 fragment library</h3>
@@ -178,7 +178,7 @@
</p>
<pre>
-com.android.support:support-fragment:24.2.0
+com.android.support:support-fragment:24.2.1
</pre>
<h2 id="multidex">Multidex Support Library</h2>
@@ -245,7 +245,7 @@
<p>The Gradle build script dependency identifier for this library is as follows:</p>
<pre>
-com.android.support:appcompat-v7:24.2.0
+com.android.support:appcompat-v7:24.2.1
</pre>
@@ -260,7 +260,7 @@
<p>The Gradle build script dependency identifier for this library is as follows:</p>
<pre>
-com.android.support:cardview-v7:24.2.0
+com.android.support:cardview-v7:24.2.1
</pre>
@@ -276,7 +276,7 @@
<p>The Gradle build script dependency identifier for this library is as follows:</p>
<pre>
-com.android.support:gridlayout-v7:24.2.0
+com.android.support:gridlayout-v7:24.2.1
</pre>
@@ -299,7 +299,7 @@
<p>The Gradle build script dependency identifier for this library is as follows:</p>
<pre>
-com.android.support:mediarouter-v7:24.2.0
+com.android.support:mediarouter-v7:24.2.1
</pre>
<p class="caution">The v7 mediarouter library APIs introduced in Support Library
@@ -319,7 +319,7 @@
<p>The Gradle build script dependency identifier for this library is as follows:</p>
<pre>
-com.android.support:palette-v7:24.2.0
+com.android.support:palette-v7:24.2.1
</pre>
@@ -335,7 +335,7 @@
<p>The Gradle build script dependency identifier for this library is as follows:</p>
<pre>
-com.android.support:recyclerview-v7:24.2.0
+com.android.support:recyclerview-v7:24.2.1
</pre>
@@ -358,7 +358,7 @@
<p>The Gradle build script dependency identifier for this library is as follows:</p>
<pre>
-com.android.support:preference-v7:24.2.0
+com.android.support:preference-v7:24.2.1
</pre>
<h2 id="v8">v8 Support Library</h2>
@@ -409,7 +409,7 @@
<p>The Gradle build script dependency identifier for this library is as follows:</p>
<pre>
-com.android.support:support-v13:24.2.0
+com.android.support:support-v13:24.2.1
</pre>
@@ -435,7 +435,7 @@
<p>The Gradle build script dependency identifier for this library is as follows:</p>
<pre>
-com.android.support:preference-v14:24.2.0
+com.android.support:preference-v14:24.2.1
</pre>
@@ -458,7 +458,7 @@
<p>The Gradle build script dependency identifier for this library is as follows:</p>
<pre>
-com.android.support:preference-leanback-v17:24.2.0
+com.android.support:preference-leanback-v17:24.2.1
</pre>
@@ -494,7 +494,7 @@
<p>The Gradle build script dependency identifier for this library is as follows:</p>
<pre>
-com.android.support:leanback-v17:24.2.0
+com.android.support:leanback-v17:24.2.1
</pre>
@@ -509,7 +509,7 @@
<p>The Gradle build script dependency identifier for this library is as follows:</p>
<pre>
-com.android.support:support-annotations:24.2.0
+com.android.support:support-annotations:24.2.1
</pre>
@@ -527,7 +527,7 @@
<p>The Gradle build script dependency identifier for this library is as follows:</p>
<pre>
-com.android.support:design:24.2.0
+com.android.support:design:24.2.1
</pre>
@@ -548,7 +548,7 @@
<p>The Gradle build script dependency identifier for this library is as follows:</p>
<pre>
-com.android.support:customtabs:24.2.0
+com.android.support:customtabs:24.2.1
</pre>
@@ -572,7 +572,7 @@
<p>The Gradle build script dependency identifier for this library is as follows:</p>
<pre>
-com.android.support:percent:24.2.0
+com.android.support:percent:24.2.1
</pre>
@@ -595,5 +595,5 @@
<p>The Gradle build script dependency identifier for this library is as follows:</p>
<pre>
-com.android.support:recommendation:24.2.0
+com.android.support:recommendation:24.2.1
</pre>
diff --git a/docs/html/topic/libraries/support-library/revisions.jd b/docs/html/topic/libraries/support-library/revisions.jd
index 4b743d5..9a24d15 100644
--- a/docs/html/topic/libraries/support-library/revisions.jd
+++ b/docs/html/topic/libraries/support-library/revisions.jd
@@ -6,9 +6,71 @@
<p>This page provides details about the Support Library package releases.</p>
<div class="toggle-content opened">
- <p id="rev24-2-0">
+ <p id="rev24-2-1">
<a href="#" onclick="return toggleContent(this)"><img src=
"{@docRoot}assets/images/styles/disclosure_up.png" class=
+ "toggle-content-img" alt="">Android Support Library, revision 24.2.1</a>
+ <em>(September 2016)</em>
+ </p>
+
+ <div class="toggle-content-toggleme">
+
+ <p>Fixed issues:</p>
+
+<ul>
+ <li>{@link android.support.design.widget.FloatingActionButton} can no longer
+ be anchored to indirect children of {@link
+ android.support.design.widget.CoordinatorLayout}. (AOSP issue <a href=
+ "https://code.google.com/p/android/issues/detail?id=220250">220250</a>)
+ </li>
+
+ <li>Image inside {@link
+ android.support.design.widget.CollapsingToolbarLayout} doesn’t scale properly
+ with <code>fitsSystemWindows=true</code>. (AOSP issue <a href=
+ "https://code.google.com/p/android/issues/detail?id=220389">220389</a>)
+ </li>
+
+ <li>{@link android.support.design.widget.CoordinatorLayout} throws {@link
+ java.lang.IndexOutOfBoundsException} when {@link
+ android.support.design.widget.Snackbar} is shown and dismissed. (AOSP issue
+ <a href="https://code.google.com/p/android/issues/detail?id=220762"
+ >220762</a>)
+ </li>
+
+ <li>{@link android.support.design.widget.TextInputLayout} fails to resolve
+ error text color. (AOSP issue <a href=
+ "https://code.google.com/p/android/issues/detail?id=220305">220305</a>)
+ </li>
+
+ <li>{@link android.support.v7.util.SortedList.BatchedCallback#onMoved
+ BatchedCallback.onMoved()} calls {@link
+ android.support.v7.util.SortedList.BatchedCallback#onInserted
+ BatchedCallback.onInserted()}. (AOSP issue <a href=
+ "https://code.google.com/p/android/issues/detail?id=220309">220309</a>)
+ </li>
+
+ <li>{@link android.support.design.widget.TextInputLayout} overrides right
+ compound drawable. (AOSP issue <a href=
+ "https://code.google.com/p/android/issues/detail?id=220728">220728</a>)
+ </li>
+</ul>
+
+<p>
+ A complete list of public bug fixes is available on the <a href=
+ "https://code.google.com/p/android/issues/list?can=1&q=label%3ATarget-Support-24.2.1">
+ AOSP Issue Tracker</a>.
+</p>
+
+
+ </div>
+</div>
+
+<!-- end of collapsible section: 24.2.1 -->
+
+<div class="toggle-content closed">
+ <p id="rev24-2-0">
+ <a href="#" onclick="return toggleContent(this)"><img src=
+ "{@docRoot}assets/images/styles/disclosure_down.png" class=
"toggle-content-img" alt="">Android Support Library, revision 24.2.0</a>
<em>(August 2016)</em>
</p>
@@ -2912,8 +2974,6 @@
<ul>
<li>Added {@link android.support.v7.widget.GridLayout} to provide support for the
{@link android.widget.GridLayout} layout object.</li>
- <li>Added {@link android.support.v7.widget.Space} which can be used to create blank areas
- within a {@link android.support.v7.widget.GridLayout} layout object.</li>
</ul>
</dl>
</div>
diff --git a/libs/input/PointerController.cpp b/libs/input/PointerController.cpp
index 27193b7..abef66f 100644
--- a/libs/input/PointerController.cpp
+++ b/libs/input/PointerController.cpp
@@ -36,6 +36,29 @@
namespace android {
+// --- WeakLooperCallback ---
+
+class WeakLooperCallback: public LooperCallback {
+protected:
+ virtual ~WeakLooperCallback() { }
+
+public:
+ WeakLooperCallback(const wp<LooperCallback>& callback) :
+ mCallback(callback) {
+ }
+
+ virtual int handleEvent(int fd, int events, void* data) {
+ sp<LooperCallback> callback = mCallback.promote();
+ if (callback != NULL) {
+ return callback->handleEvent(fd, events, data);
+ }
+ return 0; // the client is gone, remove the callback
+ }
+
+private:
+ wp<LooperCallback> mCallback;
+};
+
// --- PointerController ---
// Time to wait before starting the fade when the pointer is inactive.
@@ -57,10 +80,11 @@
const sp<Looper>& looper, const sp<SpriteController>& spriteController) :
mPolicy(policy), mLooper(looper), mSpriteController(spriteController) {
mHandler = new WeakMessageHandler(this);
+ mCallback = new WeakLooperCallback(this);
if (mDisplayEventReceiver.initCheck() == NO_ERROR) {
mLooper->addFd(mDisplayEventReceiver.getFd(), Looper::POLL_CALLBACK,
- Looper::EVENT_INPUT, this, nullptr);
+ Looper::EVENT_INPUT, mCallback, nullptr);
} else {
ALOGE("Failed to initialize DisplayEventReceiver.");
}
diff --git a/libs/input/PointerController.h b/libs/input/PointerController.h
index 99292d7..4794f3d 100644
--- a/libs/input/PointerController.h
+++ b/libs/input/PointerController.h
@@ -144,6 +144,7 @@
sp<Looper> mLooper;
sp<SpriteController> mSpriteController;
sp<WeakMessageHandler> mHandler;
+ sp<LooperCallback> mCallback;
DisplayEventReceiver mDisplayEventReceiver;
diff --git a/media/java/android/media/MediaRouter.java b/media/java/android/media/MediaRouter.java
index 101facd..9bf4793 100644
--- a/media/java/android/media/MediaRouter.java
+++ b/media/java/android/media/MediaRouter.java
@@ -219,26 +219,12 @@
}
if (mBluetoothA2dpRoute != null) {
- final boolean a2dpEnabled = isBluetoothA2dpOn();
- if (mainType != AudioRoutesInfo.MAIN_SPEAKER &&
- mSelectedRoute == mBluetoothA2dpRoute && !a2dpEnabled) {
- selectRouteStatic(ROUTE_TYPE_LIVE_AUDIO, mDefaultAudioVideo, false);
- } else if ((mSelectedRoute == mDefaultAudioVideo || mSelectedRoute == null) &&
- a2dpEnabled) {
+ if (mSelectedRoute == mDefaultAudioVideo || mSelectedRoute == null) {
selectRouteStatic(ROUTE_TYPE_LIVE_AUDIO, mBluetoothA2dpRoute, false);
}
}
}
- boolean isBluetoothA2dpOn() {
- try {
- return mAudioService.isBluetoothA2dpOn();
- } catch (RemoteException e) {
- Log.e(TAG, "Error querying Bluetooth A2DP state", e);
- return false;
- }
- }
-
void updateDiscoveryRequest() {
// What are we looking for today?
int routeTypes = 0;
@@ -908,6 +894,11 @@
static void selectRouteStatic(int types, @NonNull RouteInfo route, boolean explicit) {
Log.v(TAG, "Selecting route: " + route);
assert(route != null);
+ if (route == sStatic.mDefaultAudioVideo && sStatic.mBluetoothA2dpRoute != null) {
+ Log.i(TAG, "Change the route to a BT route: " + sStatic.mBluetoothA2dpRoute
+ + "\nDo not select the default route when a BT route is available.");
+ route = sStatic.mBluetoothA2dpRoute;
+ }
final RouteInfo oldRoute = sStatic.mSelectedRoute;
if (oldRoute == route) return;
if (!route.matchesTypes(types)) {
@@ -917,16 +908,6 @@
return;
}
- final RouteInfo btRoute = sStatic.mBluetoothA2dpRoute;
- if (btRoute != null && (types & ROUTE_TYPE_LIVE_AUDIO) != 0 &&
- (route == btRoute || route == sStatic.mDefaultAudioVideo)) {
- try {
- sStatic.mAudioService.setBluetoothA2dpOn(route == btRoute);
- } catch (RemoteException e) {
- Log.e(TAG, "Error changing Bluetooth A2DP state", e);
- }
- }
-
final WifiDisplay activeDisplay =
sStatic.mDisplayService.getWifiDisplayStatus().getActiveDisplay();
final boolean oldRouteHasAddress = oldRoute != null && oldRoute.mDeviceAddress != null;
@@ -966,7 +947,7 @@
static void selectDefaultRouteStatic() {
// TODO: Be smarter about the route types here; this selects for all valid.
if (sStatic.mSelectedRoute != sStatic.mBluetoothA2dpRoute
- && sStatic.mBluetoothA2dpRoute != null && sStatic.isBluetoothA2dpOn()) {
+ && sStatic.mBluetoothA2dpRoute != null) {
selectRouteStatic(ROUTE_TYPE_ANY, sStatic.mBluetoothA2dpRoute, false);
} else {
selectRouteStatic(ROUTE_TYPE_ANY, sStatic.mDefaultAudioVideo, false);
@@ -1298,12 +1279,7 @@
selectedRoute == sStatic.mDefaultAudioVideo) {
dispatchRouteVolumeChanged(selectedRoute);
} else if (sStatic.mBluetoothA2dpRoute != null) {
- try {
- dispatchRouteVolumeChanged(sStatic.mAudioService.isBluetoothA2dpOn() ?
- sStatic.mBluetoothA2dpRoute : sStatic.mDefaultAudioVideo);
- } catch (RemoteException e) {
- Log.e(TAG, "Error checking Bluetooth A2DP state to report volume change", e);
- }
+ dispatchRouteVolumeChanged(sStatic.mBluetoothA2dpRoute);
} else {
dispatchRouteVolumeChanged(sStatic.mDefaultAudioVideo);
}
diff --git a/packages/SettingsProvider/res/values/defaults.xml b/packages/SettingsProvider/res/values/defaults.xml
index f7e9541..cd2d6b3 100644
--- a/packages/SettingsProvider/res/values/defaults.xml
+++ b/packages/SettingsProvider/res/values/defaults.xml
@@ -36,7 +36,7 @@
<fraction name="def_window_transition_scale">100%</fraction>
<bool name="def_haptic_feedback">true</bool>
- <bool name="def_bluetooth_on">false</bool>
+ <bool name="def_bluetooth_on">true</bool>
<bool name="def_wifi_display_on">false</bool>
<bool name="def_install_non_market_apps">false</bool>
<bool name="def_package_verifier_enable">true</bool>
diff --git a/services/core/java/com/android/server/BluetoothManagerService.java b/services/core/java/com/android/server/BluetoothManagerService.java
index 8c5887f..89fdfaf 100644
--- a/services/core/java/com/android/server/BluetoothManagerService.java
+++ b/services/core/java/com/android/server/BluetoothManagerService.java
@@ -293,7 +293,7 @@
*/
private final boolean isBluetoothPersistedStateOn() {
return Settings.Global.getInt(mContentResolver,
- Settings.Global.BLUETOOTH_ON, 0) != BLUETOOTH_OFF;
+ Settings.Global.BLUETOOTH_ON, BLUETOOTH_ON_BLUETOOTH) != BLUETOOTH_OFF;
}
/**
@@ -301,7 +301,7 @@
*/
private final boolean isBluetoothPersistedStateOnBluetooth() {
return Settings.Global.getInt(mContentResolver,
- Settings.Global.BLUETOOTH_ON, 0) == BLUETOOTH_ON_BLUETOOTH;
+ Settings.Global.BLUETOOTH_ON, BLUETOOTH_ON_BLUETOOTH) == BLUETOOTH_ON_BLUETOOTH;
}
/**
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 58431c8..eddc4f4 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -262,6 +262,11 @@
DONT_REAP
};
+ private enum UnneededFor {
+ LINGER, // Determine whether this network is unneeded and should be lingered.
+ TEARDOWN, // Determine whether this network is unneeded and should be torn down.
+ }
+
/**
* used internally to change our mobile data enabled flag
*/
@@ -691,13 +696,13 @@
if (DBG) log("ConnectivityService starting up");
mMetricsLog = logger;
- mDefaultRequest = createInternetRequestForTransport(-1);
+ mDefaultRequest = createInternetRequestForTransport(-1, NetworkRequest.Type.REQUEST);
NetworkRequestInfo defaultNRI = new NetworkRequestInfo(null, mDefaultRequest, new Binder());
mNetworkRequests.put(mDefaultRequest, defaultNRI);
mNetworkRequestInfoLogs.log("REGISTER " + defaultNRI);
mDefaultMobileDataRequest = createInternetRequestForTransport(
- NetworkCapabilities.TRANSPORT_CELLULAR);
+ NetworkCapabilities.TRANSPORT_CELLULAR, NetworkRequest.Type.BACKGROUND_REQUEST);
mHandlerThread = createHandlerThread();
mHandlerThread.start();
@@ -848,15 +853,15 @@
mLingerMonitor = new LingerMonitor(mContext, mNotifier, dailyLimit, rateLimit);
}
- private NetworkRequest createInternetRequestForTransport(int transportType) {
+ private NetworkRequest createInternetRequestForTransport(
+ int transportType, NetworkRequest.Type type) {
NetworkCapabilities netCap = new NetworkCapabilities();
netCap.addCapability(NET_CAPABILITY_INTERNET);
netCap.addCapability(NET_CAPABILITY_NOT_RESTRICTED);
if (transportType > -1) {
netCap.addTransportType(transportType);
}
- return new NetworkRequest(netCap, TYPE_NONE, nextNetworkRequestId(),
- NetworkRequest.Type.REQUEST);
+ return new NetworkRequest(netCap, TYPE_NONE, nextNetworkRequestId(), type);
}
// Used only for testing.
@@ -1970,8 +1975,12 @@
for (NetworkAgentInfo nai : mNetworkAgentInfos.values()) {
pw.println(nai.toString());
pw.increaseIndent();
- pw.println(String.format("Requests: %d request/%d total",
- nai.numRequestNetworkRequests(), nai.numNetworkRequests()));
+ pw.println(String.format(
+ "Requests: REQUEST:%d LISTEN:%d BACKGROUND_REQUEST:%d total:%d",
+ nai.numForegroundNetworkRequests(),
+ nai.numNetworkRequests() - nai.numRequestNetworkRequests(),
+ nai.numBackgroundNetworkRequests(),
+ nai.numNetworkRequests()));
pw.increaseIndent();
for (int i = 0; i < nai.numNetworkRequests(); i++) {
pw.println(nai.requestAt(i).toString());
@@ -2292,15 +2301,13 @@
// 3. If this network is unneeded (which implies it is not lingering), and there is at least
// one lingered request, start lingering.
nai.updateLingerTimer();
- if (nai.isLingering() && nai.numRequestNetworkRequests() > 0) {
+ if (nai.isLingering() && nai.numForegroundNetworkRequests() > 0) {
if (DBG) log("Unlingering " + nai.name());
nai.unlinger();
logNetworkEvent(nai, NetworkEvent.NETWORK_UNLINGER);
- } else if (unneeded(nai) && nai.getLingerExpiry() > 0) { // unneeded() calls isLingering()
+ } else if (unneeded(nai, UnneededFor.LINGER) && nai.getLingerExpiry() > 0) {
int lingerTime = (int) (nai.getLingerExpiry() - now);
- if (DBG) {
- Log.d(TAG, "Lingering " + nai.name() + " for " + lingerTime + "ms");
- }
+ if (DBG) log("Lingering " + nai.name() + " for " + lingerTime + "ms");
nai.linger();
logNetworkEvent(nai, NetworkEvent.NETWORK_LINGER);
notifyNetworkCallbacks(nai, ConnectivityManager.CALLBACK_LOSING, lingerTime);
@@ -2479,15 +2486,37 @@
}
}
- // Is nai unneeded by all NetworkRequests (and should be disconnected)?
- // This is whether it is satisfying any NetworkRequests or were it to become validated,
- // would it have a chance of satisfying any NetworkRequests.
- private boolean unneeded(NetworkAgentInfo nai) {
- if (!nai.everConnected || nai.isVPN() ||
- nai.isLingering() || nai.numRequestNetworkRequests() > 0) {
+ // Determines whether the network is the best (or could become the best, if it validated), for
+ // none of a particular type of NetworkRequests. The type of NetworkRequests considered depends
+ // on the value of reason:
+ //
+ // - UnneededFor.TEARDOWN: non-listen NetworkRequests. If a network is unneeded for this reason,
+ // then it should be torn down.
+ // - UnneededFor.LINGER: foreground NetworkRequests. If a network is unneeded for this reason,
+ // then it should be lingered.
+ private boolean unneeded(NetworkAgentInfo nai, UnneededFor reason) {
+ final int numRequests;
+ switch (reason) {
+ case TEARDOWN:
+ numRequests = nai.numRequestNetworkRequests();
+ break;
+ case LINGER:
+ numRequests = nai.numForegroundNetworkRequests();
+ break;
+ default:
+ Slog.wtf(TAG, "Invalid reason. Cannot happen.");
+ return true;
+ }
+
+ if (!nai.everConnected || nai.isVPN() || nai.isLingering() || numRequests > 0) {
return false;
}
for (NetworkRequestInfo nri : mNetworkRequests.values()) {
+ if (reason == UnneededFor.LINGER && nri.request.isBackgroundRequest()) {
+ // Background requests don't affect lingering.
+ continue;
+ }
+
// If this Network is already the highest scoring Network for a request, or if
// there is hope for it to become one if it validated, then it is needed.
if (nri.request.isRequest() && nai.satisfies(nri.request) &&
@@ -2583,7 +2612,7 @@
// If there are still lingered requests on this network, don't tear it down,
// but resume lingering instead.
updateLingerState(nai, SystemClock.elapsedRealtime());
- if (unneeded(nai)) {
+ if (unneeded(nai, UnneededFor.TEARDOWN)) {
if (DBG) log("no live requests for " + nai.name() + "; disconnecting");
teardownUnneededNetwork(nai);
} else {
@@ -4612,7 +4641,7 @@
// must be no other active linger timers, and we must stop lingering.
oldNetwork.clearLingerState();
- if (unneeded(oldNetwork)) {
+ if (unneeded(oldNetwork, UnneededFor.TEARDOWN)) {
teardownUnneededNetwork(oldNetwork);
}
}
@@ -4631,6 +4660,27 @@
setDefaultDnsSystemProperties(newNetwork.linkProperties.getDnsServers());
}
+ private void processListenRequests(NetworkAgentInfo nai) {
+ // For consistency with previous behaviour, send onLost callbacks before onAvailable.
+ for (NetworkRequestInfo nri : mNetworkRequests.values()) {
+ NetworkRequest nr = nri.request;
+ if (!nr.isListen()) continue;
+ if (nai.isSatisfyingRequest(nr.requestId) && !nai.satisfies(nr)) {
+ nai.removeRequest(nri.request.requestId);
+ callCallbackForRequest(nri, nai, ConnectivityManager.CALLBACK_LOST, 0);
+ }
+ }
+
+ for (NetworkRequestInfo nri : mNetworkRequests.values()) {
+ NetworkRequest nr = nri.request;
+ if (!nr.isListen()) continue;
+ if (nai.satisfies(nr) && !nai.isSatisfyingRequest(nr.requestId)) {
+ nai.addRequest(nr);
+ notifyNetworkCallback(nai, nri);
+ }
+ }
+ }
+
// Handles a network appearing or improving its score.
//
// - Evaluates all current NetworkRequests that can be
@@ -4671,6 +4721,12 @@
ArrayList<NetworkRequestInfo> addedRequests = new ArrayList<NetworkRequestInfo>();
if (VDBG) log(" network has: " + newNetwork.networkCapabilities);
for (NetworkRequestInfo nri : mNetworkRequests.values()) {
+ // Process requests in the first pass and listens in the second pass. This allows us to
+ // change a network's capabilities depending on which requests it has. This is only
+ // correct if the change in capabilities doesn't affect whether the network satisfies
+ // requests or not, and doesn't affect the network's score.
+ if (nri.request.isListen()) continue;
+
final NetworkAgentInfo currentNetwork = mNetworkForRequestId.get(nri.request.requestId);
final boolean satisfies = newNetwork.satisfies(nri.request);
if (newNetwork == currentNetwork && satisfies) {
@@ -4685,13 +4741,6 @@
// check if it satisfies the NetworkCapabilities
if (VDBG) log(" checking if request is satisfied: " + nri.request);
if (satisfies) {
- if (nri.request.isListen()) {
- // This is not a request, it's a callback listener.
- // Add it to newNetwork regardless of score.
- if (newNetwork.addRequest(nri.request)) addedRequests.add(nri);
- continue;
- }
-
// next check if it's better than any current network we're using for
// this request
if (VDBG) {
@@ -4748,16 +4797,14 @@
mNetworkForRequestId.remove(nri.request.requestId);
sendUpdatedScoreToFactories(nri.request, 0);
} else {
- if (nri.request.isRequest()) {
- Slog.wtf(TAG, "BUG: Removing request " + nri.request.requestId + " from " +
- newNetwork.name() +
- " without updating mNetworkForRequestId or factories!");
- }
+ Slog.wtf(TAG, "BUG: Removing request " + nri.request.requestId + " from " +
+ newNetwork.name() +
+ " without updating mNetworkForRequestId or factories!");
}
- // TODO: technically, sending CALLBACK_LOST here is
- // incorrect if nri is a request (not a listen) and there
- // is a replacement network currently connected that can
- // satisfy it. However, the only capability that can both
+ // TODO: Technically, sending CALLBACK_LOST here is
+ // incorrect if there is a replacement network currently
+ // connected that can satisfy nri, which is a request
+ // (not a listen). However, the only capability that can both
// a) be requested and b) change is NET_CAPABILITY_TRUSTED,
// so this code is only incorrect for a network that loses
// the TRUSTED capability, which is a rare case.
@@ -4782,6 +4829,9 @@
}
}
+ // Second pass: process all listens.
+ processListenRequests(newNetwork);
+
// do this after the default net is switched, but
// before LegacyTypeTracker sends legacy broadcasts
for (NetworkRequestInfo nri : addedRequests) notifyNetworkCallback(newNetwork, nri);
@@ -4859,7 +4909,7 @@
}
if (reapUnvalidatedNetworks == ReapUnvalidatedNetworks.REAP) {
for (NetworkAgentInfo nai : mNetworkAgentInfos.values()) {
- if (unneeded(nai)) {
+ if (unneeded(nai, UnneededFor.TEARDOWN)) {
if (nai.getLingerExpiry() > 0) {
// This network has active linger timers and no requests, but is not
// lingering. Linger it.
diff --git a/services/core/java/com/android/server/InputMethodManagerService.java b/services/core/java/com/android/server/InputMethodManagerService.java
index 71ac544..8ae4917 100644
--- a/services/core/java/com/android/server/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/InputMethodManagerService.java
@@ -822,7 +822,7 @@
public void onUnlockUser(final @UserIdInt int userHandle) {
// Called on ActivityManager thread.
mService.mHandler.sendMessage(mService.mHandler.obtainMessage(MSG_SYSTEM_UNLOCK_USER,
- userHandle));
+ userHandle /* arg1 */, 0 /* arg2 */));
}
}
diff --git a/services/core/java/com/android/server/IntentResolver.java b/services/core/java/com/android/server/IntentResolver.java
index 3fdcceb..83d374c 100644
--- a/services/core/java/com/android/server/IntentResolver.java
+++ b/services/core/java/com/android/server/IntentResolver.java
@@ -364,6 +364,7 @@
buildResolveList(intent, categories, debug, defaultOnly,
resolvedType, scheme, listCut.get(i), resultList, userId);
}
+ filterResults(resultList);
sortResults(resultList);
return resultList;
}
@@ -457,6 +458,7 @@
buildResolveList(intent, categories, debug, defaultOnly,
resolvedType, scheme, schemeCut, finalList, userId);
}
+ filterResults(finalList);
sortResults(finalList);
if (debug) {
@@ -521,6 +523,12 @@
Collections.sort(results, mResolvePrioritySorter);
}
+ /**
+ * Apply filtering to the results. This happens before the results are sorted.
+ */
+ protected void filterResults(List<R> results) {
+ }
+
protected void dumpFilter(PrintWriter out, String prefix, F filter) {
out.print(prefix); out.println(filter);
}
diff --git a/services/core/java/com/android/server/accounts/AccountManagerBackupHelper.java b/services/core/java/com/android/server/accounts/AccountManagerBackupHelper.java
new file mode 100644
index 0000000..1361a31
--- /dev/null
+++ b/services/core/java/com/android/server/accounts/AccountManagerBackupHelper.java
@@ -0,0 +1,315 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.accounts;
+
+import android.accounts.Account;
+import android.accounts.AccountManager;
+import android.accounts.AccountManagerInternal;
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteDatabase;
+import android.os.UserHandle;
+import android.text.TextUtils;
+import android.util.Log;
+import android.util.PackageUtils;
+import android.util.Xml;
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.content.PackageMonitor;
+import com.android.internal.util.FastXmlSerializer;
+import com.android.internal.util.XmlUtils;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Helper class for backup and restore of account access grants.
+ */
+public final class AccountManagerBackupHelper {
+ private static final String TAG = "AccountManagerBackupHelper";
+
+ private static final long PENDING_RESTORE_TIMEOUT_MILLIS = 60 * 60 * 1000; // 1 hour
+
+ private static final String TAG_PERMISSIONS = "permissions";
+ private static final String TAG_PERMISSION = "permission";
+ private static final String ATTR_ACCOUNT_SHA_256 = "account-sha-256";
+ private static final String ATTR_PACKAGE = "package";
+ private static final String ATTR_DIGEST = "digest";
+
+ private static final String ACCOUNT_ACCESS_GRANTS = ""
+ + "SELECT " + AccountManagerService.ACCOUNTS_NAME + ", "
+ + AccountManagerService.GRANTS_GRANTEE_UID
+ + " FROM " + AccountManagerService.TABLE_ACCOUNTS
+ + ", " + AccountManagerService.TABLE_GRANTS
+ + " WHERE " + AccountManagerService.GRANTS_ACCOUNTS_ID
+ + "=" + AccountManagerService.ACCOUNTS_ID;
+
+ private final Object mLock = new Object();
+
+ private final AccountManagerService mAccountManagerService;
+ private final AccountManagerInternal mAccountManagerInternal;
+
+ @GuardedBy("mLock")
+ private List<PendingAppPermission> mRestorePendingAppPermissions;
+
+ @GuardedBy("mLock")
+ private RestorePackageMonitor mRestorePackageMonitor;
+
+ @GuardedBy("mLock")
+ private Runnable mRestoreCancelCommand;
+
+ public AccountManagerBackupHelper(AccountManagerService accountManagerService,
+ AccountManagerInternal accountManagerInternal) {
+ mAccountManagerService = accountManagerService;
+ mAccountManagerInternal = accountManagerInternal;
+ }
+
+ private final class PendingAppPermission {
+ private final @NonNull String accountDigest;
+ private final @NonNull String packageName;
+ private final @NonNull String certDigest;
+ private final @IntRange(from = 0) int userId;
+
+ public PendingAppPermission(String accountDigest, String packageName,
+ String certDigest, int userId) {
+ this.accountDigest = accountDigest;
+ this.packageName = packageName;
+ this.certDigest = certDigest;
+ this.userId = userId;
+ }
+
+ public boolean apply(PackageManager packageManager) {
+ Account account = null;
+ AccountManagerService.UserAccounts accounts = mAccountManagerService
+ .getUserAccounts(userId);
+ synchronized (accounts.cacheLock) {
+ for (Account[] accountsPerType : accounts.accountCache.values()) {
+ for (Account accountPerType : accountsPerType) {
+ if (accountDigest.equals(PackageUtils.computeSha256Digest(
+ accountPerType.name.getBytes()))) {
+ account = accountPerType;
+ break;
+ }
+ }
+ if (account != null) {
+ break;
+ }
+ }
+ }
+ if (account == null) {
+ return false;
+ }
+ final PackageInfo packageInfo;
+ try {
+ packageInfo = packageManager.getPackageInfoAsUser(packageName,
+ PackageManager.GET_SIGNATURES, userId);
+ } catch (PackageManager.NameNotFoundException e) {
+ return false;
+ }
+ String currentCertDigest = PackageUtils.computeCertSha256Digest(
+ packageInfo.signatures[0]);
+ if (!certDigest.equals(currentCertDigest)) {
+ return false;
+ }
+ final int uid = packageInfo.applicationInfo.uid;
+ if (!mAccountManagerInternal.hasAccountAccess(account, uid)) {
+ mAccountManagerService.grantAppPermission(account,
+ AccountManager.ACCOUNT_ACCESS_TOKEN_TYPE, uid);
+ }
+ return true;
+ }
+ }
+
+ public byte[] backupAccountAccessPermissions(int userId) {
+ final AccountManagerService.UserAccounts accounts = mAccountManagerService
+ .getUserAccounts(userId);
+ synchronized (accounts.cacheLock) {
+ SQLiteDatabase db = accounts.openHelper.getReadableDatabase();
+ try (
+ Cursor cursor = db.rawQuery(ACCOUNT_ACCESS_GRANTS, null);
+ ) {
+ if (cursor == null || !cursor.moveToFirst()) {
+ return null;
+ }
+
+ final int nameColumnIdx = cursor.getColumnIndex(
+ AccountManagerService.ACCOUNTS_NAME);
+ final int uidColumnIdx = cursor.getColumnIndex(
+ AccountManagerService.GRANTS_GRANTEE_UID);
+
+ ByteArrayOutputStream dataStream = new ByteArrayOutputStream();
+ try {
+ final XmlSerializer serializer = new FastXmlSerializer();
+ serializer.setOutput(dataStream, StandardCharsets.UTF_8.name());
+ serializer.startDocument(null, true);
+ serializer.startTag(null, TAG_PERMISSIONS);
+
+ PackageManager packageManager = mAccountManagerService.mContext
+ .getPackageManager();
+
+ do {
+ final String accountName = cursor.getString(nameColumnIdx);
+ final int uid = cursor.getInt(uidColumnIdx);
+
+ final String[] packageNames = packageManager.getPackagesForUid(uid);
+ if (packageNames == null) {
+ continue;
+ }
+
+ for (String packageName : packageNames) {
+ String digest = PackageUtils.computePackageCertSha256Digest(
+ packageManager, packageName, userId);
+ if (digest != null) {
+ serializer.startTag(null, TAG_PERMISSION);
+ serializer.attribute(null, ATTR_ACCOUNT_SHA_256,
+ PackageUtils.computeSha256Digest(accountName.getBytes()));
+ serializer.attribute(null, ATTR_PACKAGE, packageName);
+ serializer.attribute(null, ATTR_DIGEST, digest);
+ serializer.endTag(null, TAG_PERMISSION);
+ }
+ }
+ } while (cursor.moveToNext());
+
+ serializer.endTag(null, TAG_PERMISSIONS);
+ serializer.endDocument();
+ serializer.flush();
+ } catch (IOException e) {
+ Log.e(TAG, "Error backing up account access grants", e);
+ return null;
+ }
+
+ return dataStream.toByteArray();
+ }
+ }
+ }
+
+ public void restoreAccountAccessPermissions(byte[] data, int userId) {
+ try {
+ ByteArrayInputStream dataStream = new ByteArrayInputStream(data);
+ XmlPullParser parser = Xml.newPullParser();
+ parser.setInput(dataStream, StandardCharsets.UTF_8.name());
+ PackageManager packageManager = mAccountManagerService.mContext.getPackageManager();
+
+ final int permissionsOuterDepth = parser.getDepth();
+ while (XmlUtils.nextElementWithin(parser, permissionsOuterDepth)) {
+ if (!TAG_PERMISSIONS.equals(parser.getName())) {
+ continue;
+ }
+ final int permissionOuterDepth = parser.getDepth();
+ while (XmlUtils.nextElementWithin(parser, permissionOuterDepth)) {
+ if (!TAG_PERMISSION.equals(parser.getName())) {
+ continue;
+ }
+ String accountDigest = parser.getAttributeValue(null, ATTR_ACCOUNT_SHA_256);
+ if (TextUtils.isEmpty(accountDigest)) {
+ XmlUtils.skipCurrentTag(parser);
+ }
+ String packageName = parser.getAttributeValue(null, ATTR_PACKAGE);
+ if (TextUtils.isEmpty(packageName)) {
+ XmlUtils.skipCurrentTag(parser);
+ }
+ String digest = parser.getAttributeValue(null, ATTR_DIGEST);
+ if (TextUtils.isEmpty(digest)) {
+ XmlUtils.skipCurrentTag(parser);
+ }
+
+ PendingAppPermission pendingAppPermission = new PendingAppPermission(
+ accountDigest, packageName, digest, userId);
+
+ if (!pendingAppPermission.apply(packageManager)) {
+ synchronized (mLock) {
+ // Start watching before add pending to avoid a missed signal
+ if (mRestorePackageMonitor == null) {
+ mRestorePackageMonitor = new RestorePackageMonitor();
+ mRestorePackageMonitor.register(mAccountManagerService.mContext,
+ mAccountManagerService.mMessageHandler.getLooper(), true);
+ }
+ if (mRestorePendingAppPermissions == null) {
+ mRestorePendingAppPermissions = new ArrayList<>();
+ }
+ mRestorePendingAppPermissions.add(pendingAppPermission);
+ }
+ }
+ }
+ }
+
+ // Make sure we eventually prune the in-memory pending restores
+ synchronized (mLock) {
+ mRestoreCancelCommand = new CancelRestoreCommand();
+ }
+ mAccountManagerService.mMessageHandler.postDelayed(mRestoreCancelCommand,
+ PENDING_RESTORE_TIMEOUT_MILLIS);
+ } catch (XmlPullParserException | IOException e) {
+ Log.e(TAG, "Error restoring app permissions", e);
+ }
+ }
+
+ private final class RestorePackageMonitor extends PackageMonitor {
+ @Override
+ public void onPackageAdded(String packageName, int uid) {
+ synchronized (mLock) {
+ // Can happen if restore is cancelled and there is a notification in flight
+ if (mRestorePendingAppPermissions == null) {
+ return;
+ }
+ if (UserHandle.getUserId(uid) != UserHandle.USER_SYSTEM) {
+ return;
+ }
+ final int count = mRestorePendingAppPermissions.size();
+ for (int i = count - 1; i >= 0; i--) {
+ PendingAppPermission pendingAppPermission =
+ mRestorePendingAppPermissions.get(i);
+ if (!pendingAppPermission.packageName.equals(packageName)) {
+ continue;
+ }
+ if (pendingAppPermission.apply(
+ mAccountManagerService.mContext.getPackageManager())) {
+ mRestorePendingAppPermissions.remove(i);
+ }
+ }
+ if (mRestorePendingAppPermissions.isEmpty()
+ && mRestoreCancelCommand != null) {
+ mAccountManagerService.mMessageHandler.removeCallbacks(mRestoreCancelCommand);
+ mRestoreCancelCommand.run();
+ mRestoreCancelCommand = null;
+ }
+ }
+ }
+ }
+
+ private final class CancelRestoreCommand implements Runnable {
+ @Override
+ public void run() {
+ synchronized (mLock) {
+ mRestorePendingAppPermissions = null;
+ if (mRestorePackageMonitor != null) {
+ mRestorePackageMonitor.unregister();
+ mRestorePackageMonitor = null;
+ }
+ }
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java
index b86da67..bee7fdb 100644
--- a/services/core/java/com/android/server/accounts/AccountManagerService.java
+++ b/services/core/java/com/android/server/accounts/AccountManagerService.java
@@ -89,12 +89,14 @@
import android.os.storage.StorageManager;
import android.text.TextUtils;
import android.util.Log;
+import android.util.PackageUtils;
import android.util.Pair;
import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseBooleanArray;
import com.android.internal.R;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.content.PackageMonitor;
import com.android.internal.util.ArrayUtils;
@@ -157,13 +159,6 @@
}
@Override
- public void onBootPhase(int phase) {
- if (phase == SystemService.PHASE_ACTIVITY_MANAGER_READY) {
- mService.systemReady();
- }
- }
-
- @Override
public void onUnlockUser(int userHandle) {
mService.onUnlockUser(userHandle);
}
@@ -176,13 +171,13 @@
private static final int MAX_DEBUG_DB_SIZE = 64;
- private final Context mContext;
+ final Context mContext;
private final PackageManager mPackageManager;
private final AppOpsManager mAppOpsManager;
private UserManager mUserManager;
- private final MessageHandler mMessageHandler;
+ final MessageHandler mMessageHandler;
// Messages that can be sent on mHandler
private static final int MESSAGE_TIMED_OUT = 3;
@@ -190,9 +185,9 @@
private final IAccountAuthenticatorCache mAuthenticatorCache;
- private static final String TABLE_ACCOUNTS = "accounts";
- private static final String ACCOUNTS_ID = "_id";
- private static final String ACCOUNTS_NAME = "name";
+ static final String TABLE_ACCOUNTS = "accounts";
+ static final String ACCOUNTS_ID = "_id";
+ static final String ACCOUNTS_NAME = "name";
private static final String ACCOUNTS_TYPE = "type";
private static final String ACCOUNTS_TYPE_COUNT = "count(type)";
private static final String ACCOUNTS_PASSWORD = "password";
@@ -206,10 +201,10 @@
private static final String AUTHTOKENS_TYPE = "type";
private static final String AUTHTOKENS_AUTHTOKEN = "authtoken";
- private static final String TABLE_GRANTS = "grants";
- private static final String GRANTS_ACCOUNTS_ID = "accounts_id";
+ static final String TABLE_GRANTS = "grants";
+ static final String GRANTS_ACCOUNTS_ID = "accounts_id";
private static final String GRANTS_AUTH_TOKEN_TYPE = "auth_token_type";
- private static final String GRANTS_GRANTEE_UID = "uid";
+ static final String GRANTS_GRANTEE_UID = "uid";
private static final String TABLE_EXTRAS = "extras";
private static final String EXTRAS_ID = "_id";
@@ -276,15 +271,15 @@
static class UserAccounts {
private final int userId;
- private final DeDatabaseHelper openHelper;
+ final DeDatabaseHelper openHelper;
private final HashMap<Pair<Pair<Account, String>, Integer>, Integer>
credentialsPermissionNotificationIds =
new HashMap<Pair<Pair<Account, String>, Integer>, Integer>();
private final HashMap<Account, Integer> signinRequiredNotificationIds =
new HashMap<Account, Integer>();
- private final Object cacheLock = new Object();
+ final Object cacheLock = new Object();
/** protected by the {@link #cacheLock} */
- private final HashMap<String, Account[]> accountCache =
+ final HashMap<String, Account[]> accountCache =
new LinkedHashMap<>();
/** protected by the {@link #cacheLock} */
private final HashMap<Account, HashMap<String, String>> userDataCache =
@@ -324,7 +319,7 @@
private final SparseArray<UserAccounts> mUsers = new SparseArray<>();
private final SparseBooleanArray mLocalUnlockedUsers = new SparseBooleanArray();
- private CopyOnWriteArrayList<AccountManagerInternal.OnAppPermissionChangeListener>
+ private final CopyOnWriteArrayList<AccountManagerInternal.OnAppPermissionChangeListener>
mAppPermissionChangeListeners = new CopyOnWriteArrayList<>();
private static AtomicReference<AccountManagerService> sThis = new AtomicReference<>();
@@ -526,9 +521,6 @@
}
}
- public void systemReady() {
- }
-
private UserManager getUserManager() {
if (mUserManager == null) {
mUserManager = UserManager.get(mContext);
@@ -4453,7 +4445,7 @@
}
}
- private class MessageHandler extends Handler {
+ class MessageHandler extends Handler {
MessageHandler(Looper looper) {
super(looper);
}
@@ -5604,7 +5596,7 @@
* which is in the system. This means we don't need to protect it with permissions.
* @hide
*/
- private void grantAppPermission(Account account, String authTokenType, int uid) {
+ void grantAppPermission(Account account, String authTokenType, int uid) {
if (account == null || authTokenType == null) {
Log.e(TAG, "grantAppPermission: called with invalid arguments", new Exception());
return;
@@ -5989,6 +5981,11 @@
}
private final class AccountManagerInternalImpl extends AccountManagerInternal {
+ private final Object mLock = new Object();
+
+ @GuardedBy("mLock")
+ private AccountManagerBackupHelper mBackupHelper;
+
@Override
public void requestAccountAccess(@NonNull Account account, @NonNull String packageName,
@IntRange(from = 0) int userId, @NonNull RemoteCallback callback) {
@@ -6043,5 +6040,27 @@
public boolean hasAccountAccess(@NonNull Account account, @IntRange(from = 0) int uid) {
return AccountManagerService.this.hasAccountAccess(account, null, uid);
}
+
+ @Override
+ public byte[] backupAccountAccessPermissions(int userId) {
+ synchronized (mLock) {
+ if (mBackupHelper == null) {
+ mBackupHelper = new AccountManagerBackupHelper(
+ AccountManagerService.this, this);
+ }
+ return mBackupHelper.backupAccountAccessPermissions(userId);
+ }
+ }
+
+ @Override
+ public void restoreAccountAccessPermissions(byte[] data, int userId) {
+ synchronized (mLock) {
+ if (mBackupHelper == null) {
+ mBackupHelper = new AccountManagerBackupHelper(
+ AccountManagerService.this, this);
+ }
+ mBackupHelper.restoreAccountAccessPermissions(data, userId);
+ }
+ }
}
}
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 275870e..da0c05e 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -206,7 +206,6 @@
private static final int MSG_BT_HEADSET_CNCT_FAILED = 9;
private static final int MSG_SET_ALL_VOLUMES = 10;
private static final int MSG_REPORT_NEW_ROUTES = 12;
- private static final int MSG_SET_FORCE_BT_A2DP_USE = 13;
private static final int MSG_CHECK_MUSIC_ACTIVE = 14;
private static final int MSG_BROADCAST_AUDIO_BECOMING_NOISY = 15;
private static final int MSG_CONFIGURE_SAFE_MEDIA_VOLUME = 16;
@@ -511,6 +510,7 @@
private int mDeviceOrientation = Configuration.ORIENTATION_UNDEFINED;
// Request to override default use of A2DP for media.
+ // FIXME: remove when MediaRouter does not use setBluetoothA2dpOn() anymore
private boolean mBluetoothA2dpEnabled;
private final Object mBluetoothA2dpEnabledLock = new Object();
@@ -840,12 +840,6 @@
RotationHelper.updateOrientation();
}
- synchronized (mBluetoothA2dpEnabledLock) {
- AudioSystem.setForceUse(AudioSystem.FOR_MEDIA,
- mBluetoothA2dpEnabled ?
- AudioSystem.FORCE_NONE : AudioSystem.FORCE_NO_BT_A2DP);
- }
-
synchronized (mSettingsLock) {
AudioSystem.setForceUse(AudioSystem.FOR_DOCK,
mDockAudioMediaEnabled ?
@@ -2706,22 +2700,23 @@
return (mForcedUseForComm == AudioSystem.FORCE_BT_SCO);
}
- /** @see AudioManager#setBluetoothA2dpOn(boolean) */
+ /**
+ * Deprecated.
+ * Keep stub implementation until MediaRouter stops using it.
+ * @deprecated
+ * */
public void setBluetoothA2dpOn(boolean on) {
- synchronized (mBluetoothA2dpEnabledLock) {
- mBluetoothA2dpEnabled = on;
- sendMsg(mAudioHandler, MSG_SET_FORCE_BT_A2DP_USE, SENDMSG_QUEUE,
- AudioSystem.FOR_MEDIA,
- mBluetoothA2dpEnabled ? AudioSystem.FORCE_NONE : AudioSystem.FORCE_NO_BT_A2DP,
- null, 0);
- }
+ mBluetoothA2dpEnabled = on;
+ Log.e(TAG, "setBluetoothA2dpOn() is deprecated, now a no-op",
+ new Exception("Deprecated use of setBluetoothA2dpOn()"));
}
- /** @see AudioManager#isBluetoothA2dpOn() */
+ /** Deprecated.
+ * Keep stub implementation until MediaRouter stops using it
+ * @deprecated
+ * */
public boolean isBluetoothA2dpOn() {
- synchronized (mBluetoothA2dpEnabledLock) {
- return mBluetoothA2dpEnabled;
- }
+ return mBluetoothA2dpEnabled;
}
/** @see AudioManager#startBluetoothSco() */
@@ -4603,7 +4598,6 @@
break;
case MSG_SET_FORCE_USE:
- case MSG_SET_FORCE_BT_A2DP_USE:
setForceUse(msg.arg1, msg.arg2);
break;
@@ -4773,7 +4767,6 @@
VolumeStreamState streamState = mStreamStates[AudioSystem.STREAM_MUSIC];
sendMsg(mAudioHandler, MSG_SET_DEVICE_VOLUME, SENDMSG_QUEUE,
AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, 0, streamState, 0);
- setBluetoothA2dpOnInt(true);
AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP,
AudioSystem.DEVICE_STATE_AVAILABLE, address, name);
// Reset A2DP suspend state each time a new sink is connected
@@ -5106,11 +5099,6 @@
}
synchronized (mConnectedDevices) {
- if ((state == 0) && ((device == AudioSystem.DEVICE_OUT_WIRED_HEADSET) ||
- (device == AudioSystem.DEVICE_OUT_WIRED_HEADPHONE) ||
- (device == AudioSystem.DEVICE_OUT_LINE))) {
- setBluetoothA2dpOnInt(true);
- }
boolean isUsb = ((device & ~AudioSystem.DEVICE_OUT_ALL_USB) == 0) ||
(((device & AudioSystem.DEVICE_BIT_IN) != 0) &&
((device & ~AudioSystem.DEVICE_IN_ALL_USB) == 0));
@@ -5119,11 +5107,6 @@
return;
}
if (state != 0) {
- if ((device == AudioSystem.DEVICE_OUT_WIRED_HEADSET) ||
- (device == AudioSystem.DEVICE_OUT_WIRED_HEADPHONE) ||
- (device == AudioSystem.DEVICE_OUT_LINE)) {
- setBluetoothA2dpOnInt(false);
- }
if ((device & mSafeMediaVolumeDevices) != 0) {
sendMsg(mAudioHandler,
MSG_CHECK_MUSIC_ACTIVE,
@@ -5589,27 +5572,9 @@
}
}
- // Handles request to override default use of A2DP for media.
- // Must be called synchronized on mConnectedDevices
- public void setBluetoothA2dpOnInt(boolean on) {
- synchronized (mBluetoothA2dpEnabledLock) {
- mBluetoothA2dpEnabled = on;
- mAudioHandler.removeMessages(MSG_SET_FORCE_BT_A2DP_USE);
- setForceUseInt_SyncDevices(AudioSystem.FOR_MEDIA,
- mBluetoothA2dpEnabled ? AudioSystem.FORCE_NONE : AudioSystem.FORCE_NO_BT_A2DP);
- }
- }
-
// Must be called synchronized on mConnectedDevices
private void setForceUseInt_SyncDevices(int usage, int config) {
switch (usage) {
- case AudioSystem.FOR_MEDIA:
- if (config == AudioSystem.FORCE_NO_BT_A2DP) {
- mBecomingNoisyIntentDevices &= ~AudioSystem.DEVICE_OUT_ALL_A2DP;
- } else { // config == AudioSystem.FORCE_NONE
- mBecomingNoisyIntentDevices |= AudioSystem.DEVICE_OUT_ALL_A2DP;
- }
- break;
case AudioSystem.FOR_DOCK:
if (config == AudioSystem.FORCE_ANALOG_DOCK) {
mBecomingNoisyIntentDevices |= AudioSystem.DEVICE_OUT_ANLG_DOCK_HEADSET;
diff --git a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
index b0330b9..7044e60 100644
--- a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
+++ b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
@@ -104,14 +104,16 @@
// -----------------------------------------------
// If a network has no chance of satisfying any requests (even if it were to become validated
// and enter state #5), ConnectivityService will disconnect the NetworkAgent's AsyncChannel.
-// If the network ever for any period of time had satisfied a NetworkRequest (i.e. had been
-// the highest scoring that satisfied the NetworkRequest's constraints), but is no longer the
-// highest scoring network for any NetworkRequest, then there will be a 30s pause before
-// ConnectivityService disconnects the NetworkAgent's AsyncChannel. During this pause the
-// network is considered "lingering". This pause exists to allow network communication to be
-// wrapped up rather than abruptly terminated. During this pause if the network begins satisfying
-// a NetworkRequest, ConnectivityService will cancel the future disconnection of the NetworkAgent's
-// AsyncChannel, and the network is no longer considered "lingering".
+//
+// If the network was satisfying a foreground NetworkRequest (i.e. had been the highest scoring that
+// satisfied the NetworkRequest's constraints), but is no longer the highest scoring network for any
+// foreground NetworkRequest, then there will be a 30s pause to allow network communication to be
+// wrapped up rather than abruptly terminated. During this pause the network is said to be
+// "lingering". During this pause if the network begins satisfying a foreground NetworkRequest,
+// ConnectivityService will cancel the future disconnection of the NetworkAgent's AsyncChannel, and
+// the network is no longer considered "lingering". After the linger timer expires, if the network
+// is satisfying one or more background NetworkRequests it is kept up in the background. If it is
+// not, ConnectivityService disconnects the NetworkAgent's AsyncChannel.
public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> {
public NetworkInfo networkInfo;
// This Network object should always be used if possible, so as to encourage reuse of the
@@ -227,11 +229,13 @@
// The list of NetworkRequests being satisfied by this Network.
private final SparseArray<NetworkRequest> mNetworkRequests = new SparseArray<>();
- // The list of NetworkRequests that this Network previously satisfied with the highest
- // score. A non-empty list indicates that if this Network was validated it is lingered.
+
// How many of the satisfied requests are actual requests and not listens.
private int mNumRequestNetworkRequests = 0;
+ // How many of the satisfied requests are of type BACKGROUND_REQUEST.
+ private int mNumBackgroundNetworkRequests = 0;
+
public final Messenger messenger;
public final AsyncChannel asyncChannel;
@@ -265,6 +269,32 @@
//
// These functions must only called on ConnectivityService's main thread.
+ private static final boolean ADD = true;
+ private static final boolean REMOVE = false;
+
+ private void updateRequestCounts(boolean add, NetworkRequest request) {
+ int delta = add ? +1 : -1;
+ switch (request.type) {
+ case REQUEST:
+ case TRACK_DEFAULT:
+ mNumRequestNetworkRequests += delta;
+ break;
+
+ case BACKGROUND_REQUEST:
+ mNumRequestNetworkRequests += delta;
+ mNumBackgroundNetworkRequests += delta;
+ break;
+
+ case LISTEN:
+ break;
+
+ case NONE:
+ default:
+ Log.wtf(TAG, "Unhandled request type " + request.type);
+ break;
+ }
+ }
+
/**
* Add {@code networkRequest} to this network as it's satisfied by this network.
* @return true if {@code networkRequest} was added or false if {@code networkRequest} was
@@ -273,9 +303,15 @@
public boolean addRequest(NetworkRequest networkRequest) {
NetworkRequest existing = mNetworkRequests.get(networkRequest.requestId);
if (existing == networkRequest) return false;
- if (existing != null && existing.isRequest()) mNumRequestNetworkRequests--;
+ if (existing != null) {
+ // Should only happen if the requestId wraps. If that happens lots of other things will
+ // be broken as well.
+ Log.wtf(TAG, String.format("Duplicate requestId for %s and %s on %s",
+ networkRequest, existing, name()));
+ updateRequestCounts(REMOVE, existing);
+ }
mNetworkRequests.put(networkRequest.requestId, networkRequest);
- if (networkRequest.isRequest()) mNumRequestNetworkRequests++;
+ updateRequestCounts(ADD, networkRequest);
return true;
}
@@ -285,9 +321,9 @@
public void removeRequest(int requestId) {
NetworkRequest existing = mNetworkRequests.get(requestId);
if (existing == null) return;
+ updateRequestCounts(REMOVE, existing);
mNetworkRequests.remove(requestId);
if (existing.isRequest()) {
- mNumRequestNetworkRequests--;
unlingerRequest(existing);
}
}
@@ -316,12 +352,37 @@
}
/**
+ * Returns the number of requests currently satisfied by this network of type
+ * {@link android.net.NetworkRequest.Type.BACKGROUND_REQUEST}.
+ */
+ public int numBackgroundNetworkRequests() {
+ return mNumBackgroundNetworkRequests;
+ }
+
+ /**
+ * Returns the number of foreground requests currently satisfied by this network.
+ */
+ public int numForegroundNetworkRequests() {
+ return mNumRequestNetworkRequests - mNumBackgroundNetworkRequests;
+ }
+
+ /**
* Returns the number of requests of any type currently satisfied by this network.
*/
public int numNetworkRequests() {
return mNetworkRequests.size();
}
+ /**
+ * Returns whether the network is a background network. A network is a background network if it
+ * is satisfying no foreground requests and at least one background request. (If it did not have
+ * a background request, it would be a speculative network that is only being kept up because
+ * it might satisfy a request if it validated).
+ */
+ public boolean isBackgroundNetwork() {
+ return numForegroundNetworkRequests() == 0 && mNumBackgroundNetworkRequests > 0;
+ }
+
// Does this network satisfy request?
public boolean satisfies(NetworkRequest request) {
return created &&
diff --git a/services/core/java/com/android/server/content/SyncManager.java b/services/core/java/com/android/server/content/SyncManager.java
index 8c71478..6870c56 100644
--- a/services/core/java/com/android/server/content/SyncManager.java
+++ b/services/core/java/com/android/server/content/SyncManager.java
@@ -48,6 +48,7 @@
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.PackageManagerInternal;
import android.content.pm.ProviderInfo;
import android.content.pm.RegisteredServicesCache;
import android.content.pm.RegisteredServicesCacheListener;
@@ -324,6 +325,8 @@
private final AccountManagerInternal mAccountManagerInternal;
+ private final PackageManagerInternal mPackageManagerInternal;
+
private List<UserInfo> getAllUsers() {
return mUserManager.getUsers();
}
@@ -575,6 +578,7 @@
mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
mAccountManager = (AccountManager) mContext.getSystemService(Context.ACCOUNT_SERVICE);
mAccountManagerInternal = LocalServices.getService(AccountManagerInternal.class);
+ mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class);
mAccountManagerInternal.addOnAppPermissionChangeListener((Account account, int uid) -> {
// If the UID gained access to the account kick-off syncs lacking account access
@@ -891,9 +895,13 @@
+ "isSyncable == SYNCABLE_NO_ACCOUNT_ACCESS");
}
Bundle finalExtras = new Bundle(extras);
+ String packageName = syncAdapterInfo.componentName.getPackageName();
+ // If the app did not run and has no account access, done
+ if (!mPackageManagerInternal.wasPackageEverLaunched(packageName, userId)) {
+ continue;
+ }
mAccountManagerInternal.requestAccountAccess(account.account,
- syncAdapterInfo.componentName.getPackageName(),
- UserHandle.getUserId(owningUid),
+ packageName, userId,
new RemoteCallback((Bundle result) -> {
if (result != null
&& result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT)) {
@@ -2798,9 +2806,14 @@
final int syncOpState = computeSyncOpState(op);
switch (syncOpState) {
case SYNC_OP_STATE_INVALID_NO_ACCOUNT_ACCESS: {
+ String packageName = op.owningPackage;
+ final int userId = UserHandle.getUserId(op.owningUid);
+ // If the app did not run and has no account access, done
+ if (!mPackageManagerInternal.wasPackageEverLaunched(packageName, userId)) {
+ return;
+ }
mAccountManagerInternal.requestAccountAccess(op.target.account,
- op.owningPackage, UserHandle.getUserId(op.owningUid),
- new RemoteCallback((Bundle result) -> {
+ packageName, userId, new RemoteCallback((Bundle result) -> {
if (result != null
&& result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT)) {
updateOrAddPeriodicSync(target, pollFrequency, flex, extras);
diff --git a/services/core/java/com/android/server/pm/EphemeralResolverConnection.java b/services/core/java/com/android/server/pm/EphemeralResolverConnection.java
index 8d926f5..68b465a 100644
--- a/services/core/java/com/android/server/pm/EphemeralResolverConnection.java
+++ b/services/core/java/com/android/server/pm/EphemeralResolverConnection.java
@@ -60,7 +60,7 @@
public EphemeralResolverConnection(Context context, ComponentName componentName) {
mContext = context;
- mIntent = new Intent().setComponent(componentName);
+ mIntent = new Intent(Intent.ACTION_RESOLVE_EPHEMERAL_PACKAGE).setComponent(componentName);
}
public final List<EphemeralResolveInfo> getEphemeralResolveInfoList(
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 419e4ad..7d3658a9 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -212,6 +212,7 @@
import android.util.Log;
import android.util.LogPrinter;
import android.util.MathUtils;
+import android.util.Pair;
import android.util.PrintStreamPrinter;
import android.util.Slog;
import android.util.SparseArray;
@@ -1128,6 +1129,7 @@
final @NonNull String mRequiredInstallerPackage;
final @NonNull String mRequiredUninstallerPackage;
final @Nullable String mSetupWizardPackage;
+ final @Nullable String mStorageManagerPackage;
final @NonNull String mServicesSystemSharedLibraryPackageName;
final @NonNull String mSharedSystemSharedLibraryPackageName;
@@ -2469,6 +2471,9 @@
}
mExpectingBetter.clear();
+ // Resolve the storage manager.
+ mStorageManagerPackage = getStorageManagerPackageName();
+
// Resolve protected action filters. Only the setup wizard is allowed to
// have a high priority filter for these actions.
mSetupWizardPackage = getSetupWizardPackageName();
@@ -11261,6 +11266,19 @@
private static final class EphemeralIntentResolver
extends IntentResolver<EphemeralResolveIntentInfo, EphemeralResolveInfo> {
+ /**
+ * The result that has the highest defined order. Ordering applies on a
+ * per-package basis. Mapping is from package name to Pair of order and
+ * EphemeralResolveInfo.
+ * <p>
+ * NOTE: This is implemented as a field variable for convenience and efficiency.
+ * By having a field variable, we're able to track filter ordering as soon as
+ * a non-zero order is defined. Otherwise, multiple loops across the result set
+ * would be needed to apply ordering. If the intent resolver becomes re-entrant,
+ * this needs to be contained entirely within {@link #filterResults()}.
+ */
+ final ArrayMap<String, Pair<Integer, EphemeralResolveInfo>> mOrderResult = new ArrayMap<>();
+
@Override
protected EphemeralResolveIntentInfo[] newArray(int size) {
return new EphemeralResolveIntentInfo[size];
@@ -11277,7 +11295,51 @@
if (!sUserManager.exists(userId)) {
return null;
}
- return info.getEphemeralResolveInfo();
+ final String packageName = info.getEphemeralResolveInfo().getPackageName();
+ final Integer order = info.getOrder();
+ final Pair<Integer, EphemeralResolveInfo> lastOrderResult =
+ mOrderResult.get(packageName);
+ // ordering is enabled and this item's order isn't high enough
+ if (lastOrderResult != null && lastOrderResult.first >= order) {
+ return null;
+ }
+ final EphemeralResolveInfo res = info.getEphemeralResolveInfo();
+ if (order > 0) {
+ // non-zero order, enable ordering
+ mOrderResult.put(packageName, new Pair<>(order, res));
+ }
+ return res;
+ }
+
+ @Override
+ protected void filterResults(List<EphemeralResolveInfo> results) {
+ // only do work if ordering is enabled [most of the time it won't be]
+ if (mOrderResult.size() == 0) {
+ return;
+ }
+ int resultSize = results.size();
+ for (int i = 0; i < resultSize; i++) {
+ final EphemeralResolveInfo info = results.get(i);
+ final String packageName = info.getPackageName();
+ final Pair<Integer, EphemeralResolveInfo> savedInfo = mOrderResult.get(packageName);
+ if (savedInfo == null) {
+ // package doesn't having ordering
+ continue;
+ }
+ if (savedInfo.second == info) {
+ // circled back to the highest ordered item; remove from order list
+ mOrderResult.remove(savedInfo);
+ if (mOrderResult.size() == 0) {
+ // no more ordered items
+ break;
+ }
+ continue;
+ }
+ // item has a worse order, remove it from the result list
+ results.remove(i);
+ resultSize--;
+ i--;
+ }
}
}
@@ -15513,6 +15575,12 @@
callingUid == getPackageUid(mRequiredUninstallerPackage, 0, callingUserId)) {
return true;
}
+
+ // Allow storage manager to silently uninstall.
+ if (mStorageManagerPackage != null &&
+ callingUid == getPackageUid(mStorageManagerPackage, 0, callingUserId)) {
+ return true;
+ }
return false;
}
@@ -17728,6 +17796,22 @@
}
}
+ private @Nullable String getStorageManagerPackageName() {
+ final Intent intent = new Intent(StorageManager.ACTION_MANAGE_STORAGE);
+
+ final List<ResolveInfo> matches = queryIntentActivitiesInternal(intent, null,
+ MATCH_SYSTEM_ONLY | MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE
+ | MATCH_DISABLED_COMPONENTS,
+ UserHandle.myUserId());
+ if (matches.size() == 1) {
+ return matches.get(0).getComponentInfo().packageName;
+ } else {
+ Slog.e(TAG, "There should probably be exactly one storage manager; found "
+ + matches.size() + ": matches=" + matches);
+ return null;
+ }
+ }
+
@Override
public void setApplicationEnabledSetting(String appPackageName,
int newState, int flags, int userId, String callingPackage) {
@@ -20980,6 +21064,13 @@
public boolean isPackageDataProtected(int userId, String packageName) {
return mProtectedPackages.isPackageDataProtected(userId, packageName);
}
+
+ @Override
+ public boolean wasPackageEverLaunched(String packageName, int userId) {
+ synchronized (mPackages) {
+ return mSettings.wasPackageEverLaunchedLPr(packageName, userId);
+ }
+ }
}
@Override
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index 5126305..b0c536f 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -4154,6 +4154,14 @@
return pkg.getCurrentEnabledStateLPr(classNameStr, userId);
}
+ boolean wasPackageEverLaunchedLPr(String packageName, int userId) {
+ final PackageSetting pkgSetting = mPackages.get(packageName);
+ if (pkgSetting == null) {
+ throw new IllegalArgumentException("Unknown package: " + packageName);
+ }
+ return !pkgSetting.getNotLaunched(userId);
+ }
+
boolean setPackageStoppedStateLPw(PackageManagerService pm, String packageName,
boolean stopped, boolean allowedByPermission, int uid, int userId) {
int appId = UserHandle.getAppId(uid);
diff --git a/services/core/java/com/android/server/wm/WindowAnimator.java b/services/core/java/com/android/server/wm/WindowAnimator.java
index 022848e..1ee5a22 100644
--- a/services/core/java/com/android/server/wm/WindowAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowAnimator.java
@@ -121,6 +121,9 @@
private final AppTokenList mTmpExitingAppTokens = new AppTokenList();
+ /** The window that was previously hiding the Keyguard. */
+ private WindowState mLastShowWinWhenLocked;
+
private String forceHidingToString() {
switch (mForceHiding) {
case KEYGUARD_NOT_SHOWN: return "KEYGUARD_NOT_SHOWN";
@@ -221,27 +224,43 @@
}
}
+ /**
+ * @return The window that is currently hiding the Keyguard, or if it was hiding the Keyguard,
+ * and it's still animating.
+ */
+ private WindowState getWinShowWhenLockedOrAnimating() {
+ final WindowState winShowWhenLocked = (WindowState) mPolicy.getWinShowWhenLockedLw();
+ if (winShowWhenLocked != null) {
+ return winShowWhenLocked;
+ }
+ if (mLastShowWinWhenLocked != null && mLastShowWinWhenLocked.isOnScreen()
+ && mLastShowWinWhenLocked.isAnimatingLw()
+ && (mLastShowWinWhenLocked.mAttrs.flags & FLAG_SHOW_WHEN_LOCKED) != 0) {
+ return mLastShowWinWhenLocked;
+ }
+ return null;
+ }
+
private boolean shouldForceHide(WindowState win) {
final WindowState imeTarget = mService.mInputMethodTarget;
final boolean showImeOverKeyguard = imeTarget != null && imeTarget.isVisibleNow() &&
((imeTarget.getAttrs().flags & FLAG_SHOW_WHEN_LOCKED) != 0
|| !mPolicy.canBeForceHidden(imeTarget, imeTarget.mAttrs));
- final WindowState winShowWhenLocked = (WindowState) mPolicy.getWinShowWhenLockedLw();
+ final WindowState winShowWhenLocked = getWinShowWhenLockedOrAnimating();
final AppWindowToken appShowWhenLocked = winShowWhenLocked == null ?
null : winShowWhenLocked.mAppToken;
boolean allowWhenLocked = false;
// Show IME over the keyguard if the target allows it
allowWhenLocked |= (win.mIsImWindow || imeTarget == win) && showImeOverKeyguard;
- // Show SHOW_WHEN_LOCKED windows
- allowWhenLocked |= (win.mAttrs.flags & FLAG_SHOW_WHEN_LOCKED) != 0;
- // Show windows that are attached to SHOW_WHEN_LOCKED windows
- allowWhenLocked |= win.mAttachedWindow != null
- && (win.mAttachedWindow.mAttrs.flags & FLAG_SHOW_WHEN_LOCKED) != 0;
+ // Show SHOW_WHEN_LOCKED windows that turn on the screen
+ allowWhenLocked |= (win.mAttrs.flags & FLAG_SHOW_WHEN_LOCKED) != 0 && win.mTurnOnScreen;
if (appShowWhenLocked != null) {
allowWhenLocked |= appShowWhenLocked == win.mAppToken
+ // Show all SHOW_WHEN_LOCKED windows if some apps are shown over lockscreen
+ || (win.mAttrs.flags & FLAG_SHOW_WHEN_LOCKED) != 0
// Show error dialogs over apps that are shown on lockscreen
|| (win.mAttrs.privateFlags & PRIVATE_FLAG_SYSTEM_ERROR) != 0;
}
@@ -556,6 +575,11 @@
mPostKeyguardExitAnimation = null;
}
}
+
+ final WindowState winShowWhenLocked = (WindowState) mPolicy.getWinShowWhenLockedLw();
+ if (winShowWhenLocked != null) {
+ mLastShowWinWhenLocked = winShowWhenLocked;
+ }
}
private void updateWallpaperLocked(int displayId) {
diff --git a/services/net/java/android/net/apf/ApfFilter.java b/services/net/java/android/net/apf/ApfFilter.java
index 4bb0902..270d818 100644
--- a/services/net/java/android/net/apf/ApfFilter.java
+++ b/services/net/java/android/net/apf/ApfFilter.java
@@ -171,8 +171,8 @@
private static final int ETH_HEADER_LEN = 14;
private static final int ETH_DEST_ADDR_OFFSET = 0;
private static final int ETH_ETHERTYPE_OFFSET = 12;
- private static final byte[] ETH_BROADCAST_MAC_ADDRESS = new byte[]{
- (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff };
+ private static final byte[] ETH_BROADCAST_MAC_ADDRESS =
+ {(byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff };
// TODO: Make these offsets relative to end of link-layer header; don't include ETH_HEADER_LEN.
private static final int IPV4_FRAGMENT_OFFSET_OFFSET = ETH_HEADER_LEN + 6;
// Endianness is not an issue for this constant because the APF interpreter always operates in
@@ -188,7 +188,7 @@
private static final int IPV6_HEADER_LEN = 40;
// The IPv6 all nodes address ff02::1
private static final byte[] IPV6_ALL_NODES_ADDRESS =
- new byte[]{ (byte) 0xff, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 };
+ { (byte) 0xff, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 };
private static final int ICMP6_TYPE_OFFSET = ETH_HEADER_LEN + IPV6_HEADER_LEN;
private static final int ICMP6_NEIGHBOR_ANNOUNCEMENT = 136;
@@ -206,7 +206,7 @@
private static final int ARP_OPCODE_OFFSET = ARP_HEADER_OFFSET + 6;
private static final short ARP_OPCODE_REQUEST = 1;
private static final short ARP_OPCODE_REPLY = 2;
- private static final byte[] ARP_IPV4_HEADER = new byte[]{
+ private static final byte[] ARP_IPV4_HEADER = {
0, 1, // Hardware type: Ethernet (1)
8, 0, // Protocol type: IP (0x0800)
6, // Hardware size: 6
diff --git a/services/tests/servicestests/src/android/net/apf/ApfTest.java b/services/tests/servicestests/src/android/net/apf/ApfTest.java
index bd76118..26bcf7c 100644
--- a/services/tests/servicestests/src/android/net/apf/ApfTest.java
+++ b/services/tests/servicestests/src/android/net/apf/ApfTest.java
@@ -61,7 +61,7 @@
* Tests for APF program generator and interpreter.
*
* Build, install and run with:
- * runtest frameworks-services -c com.android.server.ApfTest
+ * runtest frameworks-services -c android.net.apf.ApfTest
*/
public class ApfTest extends AndroidTestCase {
private static final int TIMEOUT_MS = 500;
@@ -86,21 +86,45 @@
private final static boolean DROP_MULTICAST = true;
private final static boolean ALLOW_MULTICAST = false;
+ private static String label(int code) {
+ switch (code) {
+ case PASS: return "PASS";
+ case DROP: return "DROP";
+ default: return "UNKNOWN";
+ }
+ }
+
+ private static void assertReturnCodesEqual(int expected, int got) {
+ assertEquals(label(expected), label(got));
+ }
+
private void assertVerdict(int expected, byte[] program, byte[] packet, int filterAge) {
- assertEquals(expected, apfSimulate(program, packet, filterAge));
+ assertReturnCodesEqual(expected, apfSimulate(program, packet, filterAge));
+ }
+
+ private void assertVerdict(int expected, byte[] program, byte[] packet) {
+ assertReturnCodesEqual(expected, apfSimulate(program, packet, 0));
}
private void assertPass(byte[] program, byte[] packet, int filterAge) {
assertVerdict(PASS, program, packet, filterAge);
}
+ private void assertPass(byte[] program, byte[] packet) {
+ assertVerdict(PASS, program, packet);
+ }
+
private void assertDrop(byte[] program, byte[] packet, int filterAge) {
assertVerdict(DROP, program, packet, filterAge);
}
+ private void assertDrop(byte[] program, byte[] packet) {
+ assertVerdict(DROP, program, packet);
+ }
+
private void assertVerdict(int expected, ApfGenerator gen, byte[] packet, int filterAge)
throws IllegalInstructionException {
- assertEquals(expected, apfSimulate(gen.generate(), packet, filterAge));
+ assertReturnCodesEqual(expected, apfSimulate(gen.generate(), packet, filterAge));
}
private void assertPass(ApfGenerator gen, byte[] packet, int filterAge)
@@ -516,7 +540,7 @@
gen = new ApfGenerator();
gen.addLoadImmediate(Register.R0, 1);
gen.addJumpIfBytesNotEqual(Register.R0, new byte[]{123}, gen.DROP_LABEL);
- byte[] packet123 = new byte[]{0,123,0,0,0,0,0,0,0,0,0,0,0,0,0};
+ byte[] packet123 = {0,123,0,0,0,0,0,0,0,0,0,0,0,0,0};
assertPass(gen, packet123, 0);
gen = new ApfGenerator();
gen.addJumpIfBytesNotEqual(Register.R0, new byte[]{123}, gen.DROP_LABEL);
@@ -524,7 +548,7 @@
gen = new ApfGenerator();
gen.addLoadImmediate(Register.R0, 1);
gen.addJumpIfBytesNotEqual(Register.R0, new byte[]{1,2,30,4,5}, gen.DROP_LABEL);
- byte[] packet12345 = new byte[]{0,1,2,3,4,5,0,0,0,0,0,0,0,0,0};
+ byte[] packet12345 = {0,1,2,3,4,5,0,0,0,0,0,0,0,0,0};
assertDrop(gen, packet12345, 0);
gen = new ApfGenerator();
gen.addLoadImmediate(Register.R0, 1);
@@ -575,7 +599,7 @@
}
private static class TestApfFilter extends ApfFilter {
- public final static byte[] MOCK_MAC_ADDR = new byte[]{1,2,3,4,5,6};
+ public final static byte[] MOCK_MAC_ADDR = {1,2,3,4,5,6};
private FileDescriptor mWriteSocket;
public TestApfFilter(IpManager.Callback ipManagerCallback, boolean multicastFilter,
@@ -620,19 +644,21 @@
private static final int ETH_HEADER_LEN = 14;
private static final int ETH_DEST_ADDR_OFFSET = 0;
private static final int ETH_ETHERTYPE_OFFSET = 12;
- private static final byte[] ETH_BROADCAST_MAC_ADDRESS = new byte[]{
- (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff };
+ private static final byte[] ETH_BROADCAST_MAC_ADDRESS =
+ {(byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff };
private static final int IPV4_VERSION_IHL_OFFSET = ETH_HEADER_LEN + 0;
private static final int IPV4_PROTOCOL_OFFSET = ETH_HEADER_LEN + 9;
private static final int IPV4_DEST_ADDR_OFFSET = ETH_HEADER_LEN + 16;
+ private static final byte[] IPV4_BROADCAST_ADDRESS =
+ {(byte) 255, (byte) 255, (byte) 255, (byte) 255};
private static final int IPV6_NEXT_HEADER_OFFSET = ETH_HEADER_LEN + 6;
private static final int IPV6_HEADER_LEN = 40;
private static final int IPV6_DEST_ADDR_OFFSET = ETH_HEADER_LEN + 24;
// The IPv6 all nodes address ff02::1
private static final byte[] IPV6_ALL_NODES_ADDRESS =
- new byte[]{ (byte) 0xff, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 };
+ { (byte) 0xff, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 };
private static final int ICMP6_TYPE_OFFSET = ETH_HEADER_LEN + IPV6_HEADER_LEN;
private static final int ICMP6_ROUTER_ADVERTISEMENT = 134;
@@ -670,14 +696,14 @@
private static final int DHCP_CLIENT_MAC_OFFSET = ETH_HEADER_LEN + UDP_HEADER_LEN + 48;
private static final int ARP_HEADER_OFFSET = ETH_HEADER_LEN;
- private static final byte[] ARP_IPV4_REQUEST_HEADER = new byte[]{
+ private static final byte[] ARP_IPV4_REQUEST_HEADER = {
0, 1, // Hardware type: Ethernet (1)
8, 0, // Protocol type: IP (0x0800)
6, // Hardware size: 6
4, // Protocol size: 4
0, 1 // Opcode: request (1)
};
- private static final byte[] ARP_IPV4_REPLY_HEADER = new byte[]{
+ private static final byte[] ARP_IPV4_REPLY_HEADER = {
0, 1, // Hardware type: Ethernet (1)
8, 0, // Protocol type: IP (0x0800)
6, // Hardware size: 6
@@ -686,9 +712,11 @@
};
private static final int ARP_TARGET_IP_ADDRESS_OFFSET = ETH_HEADER_LEN + 24;
- private static final byte[] MOCK_IPV4_ADDR = new byte[]{10, 0, 0, 1};
- private static final byte[] ANOTHER_IPV4_ADDR = new byte[]{10, 0, 0, 2};
- private static final byte[] IPV4_ANY_HOST_ADDR = new byte[]{0, 0, 0, 0};
+ private static final byte[] MOCK_IPV4_ADDR = {10, 0, 0, 1};
+ private static final byte[] MOCK_BROADCAST_IPV4_ADDR = {10, 0, (byte) 255, (byte) 255};
+ private static final byte[] MOCK_MULTICAST_IPV4_ADDR = {(byte) 224, 0, 0, 1};
+ private static final byte[] ANOTHER_IPV4_ADDR = {10, 0, 0, 2};
+ private static final byte[] IPV4_ANY_HOST_ADDR = {0, 0, 0, 0};
@LargeTest
public void testApfFilterIPv4() throws Exception {
@@ -698,26 +726,43 @@
// Verify empty packet of 100 zero bytes is passed
ByteBuffer packet = ByteBuffer.wrap(new byte[100]);
- assertPass(program, packet.array(), 0);
+ assertPass(program, packet.array());
// Verify unicast IPv4 packet is passed
+ put(packet, ETH_DEST_ADDR_OFFSET, TestApfFilter.MOCK_MAC_ADDR);
packet.putShort(ETH_ETHERTYPE_OFFSET, (short)ETH_P_IP);
- assertPass(program, packet.array(), 0);
+ put(packet, IPV4_DEST_ADDR_OFFSET, MOCK_IPV4_ADDR);
+ assertPass(program, packet.array());
- // Verify broadcast IPv4, not DHCP to us, is dropped
- packet.put(ETH_BROADCAST_MAC_ADDRESS);
- assertDrop(program, packet.array(), 0);
+ // Verify L2 unicast to IPv4 broadcast addresses is passed (b/30231088)
+ put(packet, IPV4_DEST_ADDR_OFFSET, IPV4_BROADCAST_ADDRESS);
+ assertPass(program, packet.array());
+ put(packet, IPV4_DEST_ADDR_OFFSET, MOCK_BROADCAST_IPV4_ADDR);
+ assertPass(program, packet.array());
+
+ // Verify multicast/broadcast IPv4, not DHCP to us, is dropped
+ put(packet, ETH_DEST_ADDR_OFFSET, ETH_BROADCAST_MAC_ADDRESS);
+ assertDrop(program, packet.array());
packet.put(IPV4_VERSION_IHL_OFFSET, (byte)0x45);
- assertDrop(program, packet.array(), 0);
+ assertDrop(program, packet.array());
packet.put(IPV4_PROTOCOL_OFFSET, (byte)IPPROTO_UDP);
- assertDrop(program, packet.array(), 0);
+ assertDrop(program, packet.array());
packet.putShort(UDP_DESTINATION_PORT_OFFSET, (short)DHCP_CLIENT_PORT);
- assertDrop(program, packet.array(), 0);
+ assertDrop(program, packet.array());
+ put(packet, IPV4_DEST_ADDR_OFFSET, MOCK_MULTICAST_IPV4_ADDR);
+ assertDrop(program, packet.array());
+ put(packet, IPV4_DEST_ADDR_OFFSET, MOCK_BROADCAST_IPV4_ADDR);
+ assertDrop(program, packet.array());
+ put(packet, IPV4_DEST_ADDR_OFFSET, IPV4_BROADCAST_ADDRESS);
+ assertDrop(program, packet.array());
// Verify broadcast IPv4 DHCP to us is passed
- packet.position(DHCP_CLIENT_MAC_OFFSET);
- packet.put(TestApfFilter.MOCK_MAC_ADDR);
- assertPass(program, packet.array(), 0);
+ put(packet, DHCP_CLIENT_MAC_OFFSET, TestApfFilter.MOCK_MAC_ADDR);
+ assertPass(program, packet.array());
+
+ // Verify unicast IPv4 DHCP to us is passed
+ put(packet, ETH_DEST_ADDR_OFFSET, TestApfFilter.MOCK_MAC_ADDR);
+ assertPass(program, packet.array());
apfFilter.shutdown();
}
@@ -731,20 +776,19 @@
// Verify empty IPv6 packet is passed
ByteBuffer packet = ByteBuffer.wrap(new byte[100]);
packet.putShort(ETH_ETHERTYPE_OFFSET, (short)ETH_P_IPV6);
- assertPass(program, packet.array(), 0);
+ assertPass(program, packet.array());
// Verify empty ICMPv6 packet is passed
packet.put(IPV6_NEXT_HEADER_OFFSET, (byte)IPPROTO_ICMPV6);
- assertPass(program, packet.array(), 0);
+ assertPass(program, packet.array());
// Verify empty ICMPv6 NA packet is passed
packet.put(ICMP6_TYPE_OFFSET, (byte)ICMP6_NEIGHBOR_ANNOUNCEMENT);
- assertPass(program, packet.array(), 0);
+ assertPass(program, packet.array());
// Verify ICMPv6 NA to ff02::1 is dropped
- packet.position(IPV6_DEST_ADDR_OFFSET);
- packet.put(IPV6_ALL_NODES_ADDRESS);
- assertDrop(program, packet.array(), 0);
+ put(packet, IPV6_DEST_ADDR_OFFSET, IPV6_ALL_NODES_ADDRESS);
+ assertDrop(program, packet.array());
apfFilter.shutdown();
}
@@ -755,58 +799,78 @@
ApfFilter apfFilter = new TestApfFilter(ipManagerCallback, ALLOW_MULTICAST, mLog);
byte[] program = ipManagerCallback.getApfProgram();
+ final byte[] unicastIpv4Addr = {(byte)192,0,2,63};
+ final byte[] broadcastIpv4Addr = {(byte)192,0,(byte)255,(byte)255};
+ final byte[] multicastIpv4Addr = {(byte)224,0,0,1};
+ final byte[] multicastIpv6Addr = {(byte)0xff,2,0,0,0,0,0,0,0,0,0,0,0,0,0,(byte)0xfb};
+
// Construct IPv4 and IPv6 multicast packets.
ByteBuffer mcastv4packet = ByteBuffer.wrap(new byte[100]);
mcastv4packet.putShort(ETH_ETHERTYPE_OFFSET, (short)ETH_P_IP);
- mcastv4packet.position(IPV4_DEST_ADDR_OFFSET);
- mcastv4packet.put(new byte[]{(byte)224,0,0,1});
+ put(mcastv4packet, IPV4_DEST_ADDR_OFFSET, multicastIpv4Addr);
ByteBuffer mcastv6packet = ByteBuffer.wrap(new byte[100]);
mcastv6packet.putShort(ETH_ETHERTYPE_OFFSET, (short)ETH_P_IPV6);
mcastv6packet.put(IPV6_NEXT_HEADER_OFFSET, (byte)IPPROTO_UDP);
- mcastv6packet.position(IPV6_DEST_ADDR_OFFSET);
- mcastv6packet.put(new byte[]{(byte)0xff,2,0,0,0,0,0,0,0,0,0,0,0,0,0,(byte)0xfb});
+ put(mcastv6packet, IPV6_DEST_ADDR_OFFSET, multicastIpv6Addr);
// Construct IPv4 broadcast packet.
- ByteBuffer bcastv4packet = ByteBuffer.wrap(new byte[100]);
- bcastv4packet.put(ETH_BROADCAST_MAC_ADDRESS);
- bcastv4packet.putShort(ETH_ETHERTYPE_OFFSET, (short)ETH_P_IP);
- bcastv4packet.position(IPV4_DEST_ADDR_OFFSET);
- bcastv4packet.put(new byte[]{(byte)192,(byte)0,(byte)2,(byte)63});
+ ByteBuffer bcastv4packet1 = ByteBuffer.wrap(new byte[100]);
+ bcastv4packet1.put(ETH_BROADCAST_MAC_ADDRESS);
+ bcastv4packet1.putShort(ETH_ETHERTYPE_OFFSET, (short)ETH_P_IP);
+ put(bcastv4packet1, IPV4_DEST_ADDR_OFFSET, multicastIpv4Addr);
+
+ ByteBuffer bcastv4packet2 = ByteBuffer.wrap(new byte[100]);
+ bcastv4packet2.put(ETH_BROADCAST_MAC_ADDRESS);
+ bcastv4packet2.putShort(ETH_ETHERTYPE_OFFSET, (short)ETH_P_IP);
+ put(bcastv4packet2, IPV4_DEST_ADDR_OFFSET, IPV4_BROADCAST_ADDRESS);
+
+ // Construct IPv4 broadcast with L2 unicast address packet (b/30231088).
+ ByteBuffer bcastv4unicastl2packet = ByteBuffer.wrap(new byte[100]);
+ bcastv4unicastl2packet.put(TestApfFilter.MOCK_MAC_ADDR);
+ bcastv4unicastl2packet.putShort(ETH_ETHERTYPE_OFFSET, (short)ETH_P_IP);
+ put(bcastv4unicastl2packet, IPV4_DEST_ADDR_OFFSET, broadcastIpv4Addr);
// Verify initially disabled multicast filter is off
- assertPass(program, bcastv4packet.array(), 0);
- assertPass(program, mcastv4packet.array(), 0);
- assertPass(program, mcastv6packet.array(), 0);
+ assertPass(program, mcastv4packet.array());
+ assertPass(program, mcastv6packet.array());
+ assertPass(program, bcastv4packet1.array());
+ assertPass(program, bcastv4packet2.array());
+ assertPass(program, bcastv4unicastl2packet.array());
// Turn on multicast filter and verify it works
ipManagerCallback.resetApfProgramWait();
apfFilter.setMulticastFilter(true);
program = ipManagerCallback.getApfProgram();
- assertDrop(program, bcastv4packet.array(), 0);
- assertDrop(program, mcastv4packet.array(), 0);
- assertDrop(program, mcastv6packet.array(), 0);
+ assertDrop(program, mcastv4packet.array());
+ assertDrop(program, mcastv6packet.array());
+ assertDrop(program, bcastv4packet1.array());
+ assertDrop(program, bcastv4packet2.array());
+ assertPass(program, bcastv4unicastl2packet.array());
// Turn off multicast filter and verify it's off
ipManagerCallback.resetApfProgramWait();
apfFilter.setMulticastFilter(false);
program = ipManagerCallback.getApfProgram();
- assertPass(program, bcastv4packet.array(), 0);
- assertPass(program, mcastv4packet.array(), 0);
- assertPass(program, mcastv6packet.array(), 0);
+ assertPass(program, mcastv4packet.array());
+ assertPass(program, mcastv6packet.array());
+ assertPass(program, bcastv4packet1.array());
+ assertPass(program, bcastv4packet2.array());
+ assertPass(program, bcastv4unicastl2packet.array());
// Verify it can be initialized to on
ipManagerCallback.resetApfProgramWait();
apfFilter.shutdown();
apfFilter = new TestApfFilter(ipManagerCallback, DROP_MULTICAST, mLog);
program = ipManagerCallback.getApfProgram();
- assertDrop(program, bcastv4packet.array(), 0);
- assertDrop(program, mcastv4packet.array(), 0);
- assertDrop(program, mcastv6packet.array(), 0);
+ assertDrop(program, mcastv4packet.array());
+ assertDrop(program, mcastv6packet.array());
+ assertDrop(program, bcastv4packet1.array());
+ assertPass(program, bcastv4unicastl2packet.array());
// Verify that ICMPv6 multicast is not dropped.
mcastv6packet.put(IPV6_NEXT_HEADER_OFFSET, (byte)IPPROTO_ICMPV6);
- assertPass(program, mcastv6packet.array(), 0);
+ assertPass(program, mcastv6packet.array());
apfFilter.shutdown();
}
@@ -819,17 +883,17 @@
private void verifyArpFilter(byte[] program, int filterResult) {
// Verify ARP request packet
- assertPass(program, arpRequestBroadcast(MOCK_IPV4_ADDR), 0);
- assertVerdict(filterResult, program, arpRequestBroadcast(ANOTHER_IPV4_ADDR), 0);
- assertDrop(program, arpRequestBroadcast(IPV4_ANY_HOST_ADDR), 0);
+ assertPass(program, arpRequestBroadcast(MOCK_IPV4_ADDR));
+ assertVerdict(filterResult, program, arpRequestBroadcast(ANOTHER_IPV4_ADDR));
+ assertDrop(program, arpRequestBroadcast(IPV4_ANY_HOST_ADDR));
// Verify unicast ARP reply packet is always accepted.
- assertPass(program, arpReplyUnicast(MOCK_IPV4_ADDR), 0);
- assertPass(program, arpReplyUnicast(ANOTHER_IPV4_ADDR), 0);
- assertPass(program, arpReplyUnicast(IPV4_ANY_HOST_ADDR), 0);
+ assertPass(program, arpReplyUnicast(MOCK_IPV4_ADDR));
+ assertPass(program, arpReplyUnicast(ANOTHER_IPV4_ADDR));
+ assertPass(program, arpReplyUnicast(IPV4_ANY_HOST_ADDR));
// Verify GARP reply packets are always filtered
- assertDrop(program, garpReply(), 0);
+ assertDrop(program, garpReply());
}
@LargeTest
@@ -855,34 +919,26 @@
private static byte[] arpRequestBroadcast(byte[] tip) {
ByteBuffer packet = ByteBuffer.wrap(new byte[100]);
packet.putShort(ETH_ETHERTYPE_OFFSET, (short)ETH_P_ARP);
- packet.position(ETH_DEST_ADDR_OFFSET);
- packet.put(ETH_BROADCAST_MAC_ADDRESS);
- packet.position(ARP_HEADER_OFFSET);
- packet.put(ARP_IPV4_REQUEST_HEADER);
- packet.position(ARP_TARGET_IP_ADDRESS_OFFSET);
- packet.put(tip);
+ put(packet, ETH_DEST_ADDR_OFFSET, ETH_BROADCAST_MAC_ADDRESS);
+ put(packet, ARP_HEADER_OFFSET, ARP_IPV4_REPLY_HEADER);
+ put(packet, ARP_TARGET_IP_ADDRESS_OFFSET, tip);
return packet.array();
}
private static byte[] arpReplyUnicast(byte[] tip) {
ByteBuffer packet = ByteBuffer.wrap(new byte[100]);
packet.putShort(ETH_ETHERTYPE_OFFSET, (short)ETH_P_ARP);
- packet.position(ARP_HEADER_OFFSET);
- packet.put(ARP_IPV4_REPLY_HEADER);
- packet.position(ARP_TARGET_IP_ADDRESS_OFFSET);
- packet.put(tip);
+ put(packet, ARP_HEADER_OFFSET, ARP_IPV4_REPLY_HEADER);
+ put(packet, ARP_TARGET_IP_ADDRESS_OFFSET, tip);
return packet.array();
}
private static byte[] garpReply() {
ByteBuffer packet = ByteBuffer.wrap(new byte[100]);
packet.putShort(ETH_ETHERTYPE_OFFSET, (short)ETH_P_ARP);
- packet.position(ETH_DEST_ADDR_OFFSET);
- packet.put(ETH_BROADCAST_MAC_ADDRESS);
- packet.position(ARP_HEADER_OFFSET);
- packet.put(ARP_IPV4_REPLY_HEADER);
- packet.position(ARP_TARGET_IP_ADDRESS_OFFSET);
- packet.put(IPV4_ANY_HOST_ADDR);
+ put(packet, ETH_DEST_ADDR_OFFSET, ETH_BROADCAST_MAC_ADDRESS);
+ put(packet, ARP_HEADER_OFFSET, ARP_IPV4_REPLY_HEADER);
+ put(packet, ARP_TARGET_IP_ADDRESS_OFFSET, IPV4_ANY_HOST_ADDR);
return packet.array();
}
@@ -893,22 +949,22 @@
byte[] program = ipManagerCallback.getApfProgram();
// Verify new program should drop RA for 1/6th its lifetime
- assertDrop(program, packet.array(), 0);
+ assertDrop(program, packet.array());
assertDrop(program, packet.array(), lifetime/6);
assertPass(program, packet.array(), lifetime/6 + 1);
assertPass(program, packet.array(), lifetime);
// Verify RA checksum is ignored
packet.putShort(ICMP6_RA_CHECKSUM_OFFSET, (short)12345);
- assertDrop(program, packet.array(), 0);
+ assertDrop(program, packet.array());
packet.putShort(ICMP6_RA_CHECKSUM_OFFSET, (short)-12345);
- assertDrop(program, packet.array(), 0);
+ assertDrop(program, packet.array());
// Verify other changes to RA make it not match filter
packet.put(0, (byte)-1);
- assertPass(program, packet.array(), 0);
+ assertPass(program, packet.array());
packet.put(0, (byte)0);
- assertDrop(program, packet.array(), 0);
+ assertDrop(program, packet.array());
}
// Test that when ApfFilter is shown the given packet, it generates a program to filter it
@@ -973,7 +1029,7 @@
basePacket.putShort(ICMP6_RA_ROUTER_LIFETIME_OFFSET, (short)1000);
basePacket.position(IPV6_DEST_ADDR_OFFSET);
basePacket.put(IPV6_ALL_NODES_ADDRESS);
- assertPass(program, basePacket.array(), 0);
+ assertPass(program, basePacket.array());
testRaLifetime(apfFilter, ipManagerCallback, basePacket, 1000);
verifyRaEvent(new RaEvent(1000, -1, -1, -1, -1, -1));
@@ -1069,6 +1125,13 @@
return file.getAbsolutePath();
}
+ private static void put(ByteBuffer buffer, int position, byte[] bytes) {
+ final int original = buffer.position();
+ buffer.position(position);
+ buffer.put(bytes);
+ buffer.position(original);
+ }
+
/**
* Call the APF interpreter the run {@code program} on {@code packet} pretending the
* filter was installed {@code filter_age} seconds ago.
diff --git a/telephony/java/android/telephony/PhoneNumberUtils.java b/telephony/java/android/telephony/PhoneNumberUtils.java
index 03d6d21..7350eec 100644
--- a/telephony/java/android/telephony/PhoneNumberUtils.java
+++ b/telephony/java/android/telephony/PhoneNumberUtils.java
@@ -24,6 +24,7 @@
import android.content.Context;
import android.content.Intent;
+import android.content.res.Resources;
import android.database.Cursor;
import android.location.CountryDetector;
import android.net.Uri;
@@ -3021,4 +3022,79 @@
return SubscriptionManager.getDefaultVoiceSubscriptionId();
}
//==== End of utility methods used only in compareStrictly() =====
+
+
+ /*
+ * The config held calling number conversion map, expected to convert to emergency number.
+ */
+ private static final String[] CONVERT_TO_EMERGENCY_MAP = Resources.getSystem().getStringArray(
+ com.android.internal.R.array.config_convert_to_emergency_number_map);
+ /**
+ * Check whether conversion to emergency number is enabled
+ *
+ * @return {@code true} when conversion to emergency numbers is enabled,
+ * {@code false} otherwise
+ *
+ * @hide
+ */
+ public static boolean isConvertToEmergencyNumberEnabled() {
+ return CONVERT_TO_EMERGENCY_MAP != null && CONVERT_TO_EMERGENCY_MAP.length > 0;
+ }
+
+ /**
+ * Converts to emergency number based on the conversion map.
+ * The conversion map is declared as config_convert_to_emergency_number_map.
+ *
+ * Make sure {@link #isConvertToEmergencyNumberEnabled} is true before calling
+ * this function.
+ *
+ * @return The converted emergency number if the number matches conversion map,
+ * otherwise original number.
+ *
+ * @hide
+ */
+ public static String convertToEmergencyNumber(String number) {
+ if (TextUtils.isEmpty(number)) {
+ return number;
+ }
+
+ String normalizedNumber = normalizeNumber(number);
+
+ // The number is already emergency number. Skip conversion.
+ if (isEmergencyNumber(normalizedNumber)) {
+ return number;
+ }
+
+ for (String convertMap : CONVERT_TO_EMERGENCY_MAP) {
+ if (DBG) log("convertToEmergencyNumber: " + convertMap);
+ String[] entry = null;
+ String[] filterNumbers = null;
+ String convertedNumber = null;
+ if (!TextUtils.isEmpty(convertMap)) {
+ entry = convertMap.split(":");
+ }
+ if (entry != null && entry.length == 2) {
+ convertedNumber = entry[1];
+ if (!TextUtils.isEmpty(entry[0])) {
+ filterNumbers = entry[0].split(",");
+ }
+ }
+ // Skip if the format of entry is invalid
+ if (TextUtils.isEmpty(convertedNumber) || filterNumbers == null
+ || filterNumbers.length == 0) {
+ continue;
+ }
+
+ for (String filterNumber : filterNumbers) {
+ if (DBG) log("convertToEmergencyNumber: filterNumber = " + filterNumber
+ + ", convertedNumber = " + convertedNumber);
+ if (!TextUtils.isEmpty(filterNumber) && filterNumber.equals(normalizedNumber)) {
+ if (DBG) log("convertToEmergencyNumber: Matched. Successfully converted to: "
+ + convertedNumber);
+ return convertedNumber;
+ }
+ }
+ }
+ return number;
+ }
}
diff --git a/wifi/java/android/net/wifi/WifiScanner.java b/wifi/java/android/net/wifi/WifiScanner.java
index 716f1d3..3190ead 100644
--- a/wifi/java/android/net/wifi/WifiScanner.java
+++ b/wifi/java/android/net/wifi/WifiScanner.java
@@ -286,6 +286,12 @@
* {@hide}
*/
private int mBucketsScanned;
+ /**
+ * Indicates that the scan results received are as a result of a scan of all available
+ * channels. This should only be expected to function for single scans.
+ * {@hide}
+ */
+ private boolean mAllChannelsScanned;
/** all scan results discovered in this scan, sorted by timestamp in ascending order */
private ScanResult mResults[];
@@ -298,10 +304,12 @@
}
/** {@hide} */
- public ScanData(int id, int flags, int bucketsScanned, ScanResult[] results) {
+ public ScanData(int id, int flags, int bucketsScanned, boolean allChannelsScanned,
+ ScanResult[] results) {
mId = id;
mFlags = flags;
mBucketsScanned = bucketsScanned;
+ mAllChannelsScanned = allChannelsScanned;
mResults = results;
}
@@ -309,6 +317,7 @@
mId = s.mId;
mFlags = s.mFlags;
mBucketsScanned = s.mBucketsScanned;
+ mAllChannelsScanned = s.mAllChannelsScanned;
mResults = new ScanResult[s.mResults.length];
for (int i = 0; i < s.mResults.length; i++) {
ScanResult result = s.mResults[i];
@@ -330,6 +339,11 @@
return mBucketsScanned;
}
+ /** {@hide} */
+ public boolean isAllChannelsScanned() {
+ return mAllChannelsScanned;
+ }
+
public ScanResult[] getResults() {
return mResults;
}
@@ -345,6 +359,7 @@
dest.writeInt(mId);
dest.writeInt(mFlags);
dest.writeInt(mBucketsScanned);
+ dest.writeInt(mAllChannelsScanned ? 1 : 0);
dest.writeInt(mResults.length);
for (int i = 0; i < mResults.length; i++) {
ScanResult result = mResults[i];
@@ -362,12 +377,13 @@
int id = in.readInt();
int flags = in.readInt();
int bucketsScanned = in.readInt();
+ boolean allChannelsScanned = in.readInt() != 0;
int n = in.readInt();
ScanResult results[] = new ScanResult[n];
for (int i = 0; i < n; i++) {
results[i] = ScanResult.CREATOR.createFromParcel(in);
}
- return new ScanData(id, flags, bucketsScanned, results);
+ return new ScanData(id, flags, bucketsScanned, allChannelsScanned, results);
}
public ScanData[] newArray(int size) {