summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Android.mk1
-rw-r--r--CleanSpec.mk1
-rw-r--r--api/current.txt31
-rw-r--r--core/java/android/accounts/AccountManager.java5
-rw-r--r--core/java/android/app/Application.java8
-rw-r--r--core/java/android/app/INotificationManager.aidl2
-rw-r--r--core/java/android/content/Context.java10
-rw-r--r--core/java/android/content/Intent.java15
-rw-r--r--core/java/android/content/RestrictionEntry.aidl20
-rw-r--r--core/java/android/content/RestrictionEntry.java275
-rw-r--r--core/java/android/content/pm/PackageInfo.java7
-rw-r--r--core/java/android/content/pm/PackageParser.java9
-rw-r--r--core/java/android/inputmethodservice/IInputMethodWrapper.java10
-rw-r--r--core/java/android/os/IUserManager.aidl4
-rw-r--r--core/java/android/os/UserManager.java41
-rw-r--r--core/java/android/provider/Settings.java16
-rw-r--r--core/java/android/security/IKeystoreService.java16
-rw-r--r--core/java/android/view/ViewRootImpl.java11
-rw-r--r--core/java/android/view/inputmethod/InputMethodManager.java5
-rw-r--r--core/java/android/webkit/AccessibilityInjectorFallback.java1
-rw-r--r--core/java/android/widget/ExpandableListView.java18
-rw-r--r--core/java/android/widget/ImageView.java2
-rw-r--r--core/java/android/widget/ListView.java44
-rw-r--r--core/java/android/widget/TextView.java3
-rw-r--r--core/java/com/android/internal/view/IInputMethod.aidl7
-rw-r--r--core/java/com/android/internal/view/IInputMethodCallback.aidl8
-rw-r--r--core/java/com/android/internal/view/IInputSessionCallback.aidl29
-rw-r--r--core/res/AndroidManifest.xml4
-rw-r--r--core/res/res/layout/keyguard_emergency_carrier_area.xml5
-rw-r--r--core/res/res/layout/simple_expandable_list_item_1.xml1
-rw-r--r--core/res/res/layout/simple_expandable_list_item_2.xml2
-rw-r--r--core/res/res/values/attrs_manifest.xml7
-rw-r--r--core/res/res/values/config.xml2
-rw-r--r--core/res/res/values/public.xml11
-rw-r--r--core/res/res/values/strings.xml16
-rw-r--r--core/res/res/values/symbols.xml4
-rw-r--r--keystore/java/android/security/KeyStore.java4
-rw-r--r--keystore/tests/src/android/security/KeyStoreTest.java36
-rw-r--r--libs/hwui/Caches.cpp1
-rw-r--r--libs/hwui/DisplayList.cpp6
-rw-r--r--libs/hwui/OpenGLRenderer.cpp12
-rw-r--r--libs/hwui/OpenGLRenderer.h2
-rw-r--r--libs/hwui/PathCache.cpp7
-rw-r--r--libs/hwui/PathCache.h16
-rw-r--r--libs/hwui/Snapshot.cpp11
-rw-r--r--libs/hwui/Snapshot.h2
-rw-r--r--libs/hwui/thread/TaskManager.cpp6
-rw-r--r--libs/hwui/thread/TaskManager.h6
-rw-r--r--libs/hwui/utils/Pair.h58
-rw-r--r--policy/src/com/android/internal/policy/impl/keyguard/EmergencyCarrierArea.java62
-rw-r--r--services/input/InputDispatcher.cpp4
-rw-r--r--services/java/com/android/server/InputMethodManagerService.java10
-rw-r--r--services/java/com/android/server/NotificationManagerService.java129
-rw-r--r--services/java/com/android/server/Watchdog.java9
-rw-r--r--services/java/com/android/server/accounts/AccountManagerService.java94
-rw-r--r--services/java/com/android/server/am/ServiceRecord.java41
-rw-r--r--services/java/com/android/server/content/ContentService.java8
-rw-r--r--services/java/com/android/server/pm/UserManagerService.java154
-rw-r--r--services/java/com/android/server/wifi/WifiService.java12
-rw-r--r--wifi/java/android/net/wifi/WifiManager.java9
60 files changed, 1214 insertions, 136 deletions
diff --git a/Android.mk b/Android.mk
index 104293cf8a74..8090448620f0 100644
--- a/Android.mk
+++ b/Android.mk
@@ -199,6 +199,7 @@ LOCAL_SRC_FILES += \
core/java/com/android/internal/view/IInputMethodClient.aidl \
core/java/com/android/internal/view/IInputMethodManager.aidl \
core/java/com/android/internal/view/IInputMethodSession.aidl \
+ core/java/com/android/internal/view/IInputSessionCallback.aidl \
core/java/com/android/internal/widget/ILockSettings.aidl \
core/java/com/android/internal/widget/IRemoteViewsFactory.aidl \
core/java/com/android/internal/widget/IRemoteViewsAdapterConnection.aidl \
diff --git a/CleanSpec.mk b/CleanSpec.mk
index b87fa48bae3f..e715a9fee55a 100644
--- a/CleanSpec.mk
+++ b/CleanSpec.mk
@@ -156,6 +156,7 @@ $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/lib/librtp_jni.so)
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/symbols/system/lib/librtp_jni.so)
$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/telephony/java/com/android/internal/telephony/SmsRawData.*)
$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates)
+$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/core/java/com/android/internal/view/IInputMethodCallback.*)
# ************************************************
# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
# ************************************************
diff --git a/api/current.txt b/api/current.txt
index 12dc6b4e38e9..f4b4d5320e85 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -846,6 +846,7 @@ package android {
field public static final int reqNavigation = 16843306; // 0x101022a
field public static final int reqTouchScreen = 16843303; // 0x1010227
field public static final int required = 16843406; // 0x101028e
+ field public static final int requiredForAllUsers = 16843728; // 0x10103d0
field public static final int requiresFadingEdge = 16843685; // 0x10103a5
field public static final int requiresSmallestWidthDp = 16843620; // 0x1010364
field public static final int resizeMode = 16843619; // 0x1010363
@@ -5405,6 +5406,7 @@ package android.content {
method public abstract java.lang.String[] fileList();
method public abstract android.content.Context getApplicationContext();
method public abstract android.content.pm.ApplicationInfo getApplicationInfo();
+ method public java.util.List<android.content.RestrictionEntry> getApplicationRestrictions();
method public abstract android.content.res.AssetManager getAssets();
method public abstract java.io.File getCacheDir();
method public abstract java.lang.ClassLoader getClassLoader();
@@ -5848,6 +5850,7 @@ package android.content {
field public static final java.lang.String ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE = "android.intent.action.EXTERNAL_APPLICATIONS_UNAVAILABLE";
field public static final java.lang.String ACTION_FACTORY_TEST = "android.intent.action.FACTORY_TEST";
field public static final java.lang.String ACTION_GET_CONTENT = "android.intent.action.GET_CONTENT";
+ field public static final java.lang.String ACTION_GET_RESTRICTION_ENTRIES = "android.intent.action.GET_RESTRICTION_ENTRIES";
field public static final java.lang.String ACTION_GTALK_SERVICE_CONNECTED = "android.intent.action.GTALK_CONNECTED";
field public static final java.lang.String ACTION_GTALK_SERVICE_DISCONNECTED = "android.intent.action.GTALK_DISCONNECTED";
field public static final java.lang.String ACTION_HEADSET_PLUG = "android.intent.action.HEADSET_PLUG";
@@ -5989,6 +5992,7 @@ package android.content {
field public static final java.lang.String EXTRA_REFERRER = "android.intent.extra.REFERRER";
field public static final java.lang.String EXTRA_REMOTE_INTENT_TOKEN = "android.intent.extra.remote_intent_token";
field public static final java.lang.String EXTRA_REPLACING = "android.intent.extra.REPLACING";
+ field public static final java.lang.String EXTRA_RESTRICTIONS = "android.intent.extra.restrictions";
field public static final java.lang.String EXTRA_RETURN_RESULT = "android.intent.extra.RETURN_RESULT";
field public static final java.lang.String EXTRA_SHORTCUT_ICON = "android.intent.extra.shortcut.ICON";
field public static final java.lang.String EXTRA_SHORTCUT_ICON_RESOURCE = "android.intent.extra.shortcut.ICON_RESOURCE";
@@ -6231,6 +6235,33 @@ package android.content {
ctor public ReceiverCallNotAllowedException(java.lang.String);
}
+ public class RestrictionEntry implements android.os.Parcelable {
+ ctor public RestrictionEntry(java.lang.String, java.lang.String);
+ ctor public RestrictionEntry(java.lang.String, boolean);
+ ctor public RestrictionEntry(java.lang.String, java.lang.String[]);
+ ctor public RestrictionEntry(android.os.Parcel);
+ method public int describeContents();
+ method public boolean getBooleanValue();
+ method public java.lang.String[] getMultipleValues();
+ method public java.lang.String getStringValue();
+ method public void setMultipleValues(java.lang.String[]);
+ method public void setValue(java.lang.String);
+ method public void setValue(boolean);
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator CREATOR;
+ field public static final int TYPE_BOOLEAN = 1; // 0x1
+ field public static final int TYPE_CHOICE = 2; // 0x2
+ field public static final int TYPE_CHOICE_LEVEL = 3; // 0x3
+ field public static final int TYPE_MULTI_SELECT = 4; // 0x4
+ field public static final int TYPE_NULL = 0; // 0x0
+ field public java.lang.String[] choices;
+ field public java.lang.String description;
+ field public java.lang.String key;
+ field public java.lang.String title;
+ field public int type;
+ field public java.lang.String[] values;
+ }
+
public class SearchRecentSuggestionsProvider extends android.content.ContentProvider {
ctor public SearchRecentSuggestionsProvider();
method public int delete(android.net.Uri, java.lang.String, java.lang.String[]);
diff --git a/core/java/android/accounts/AccountManager.java b/core/java/android/accounts/AccountManager.java
index f8b7a0c949be..313260f65f9f 100644
--- a/core/java/android/accounts/AccountManager.java
+++ b/core/java/android/accounts/AccountManager.java
@@ -152,6 +152,9 @@ public class AccountManager {
public static final int ERROR_CODE_BAD_ARGUMENTS = 7;
public static final int ERROR_CODE_BAD_REQUEST = 8;
+ /** @hide */
+ public static final int ERROR_CODE_USER_RESTRICTED = 100;
+
/**
* Bundle key used for the {@link String} account name in results
* from methods which return information about a particular account.
@@ -1526,7 +1529,7 @@ public class AccountManager {
}
public void onError(int code, String message) {
- if (code == ERROR_CODE_CANCELED) {
+ if (code == ERROR_CODE_CANCELED || code == ERROR_CODE_USER_RESTRICTED) {
// the authenticator indicated that this request was canceled, do so now
cancel(true /* mayInterruptIfRunning */);
return;
diff --git a/core/java/android/app/Application.java b/core/java/android/app/Application.java
index 132388ef3e24..7b074382b447 100644
--- a/core/java/android/app/Application.java
+++ b/core/java/android/app/Application.java
@@ -17,14 +17,17 @@
package android.app;
import java.util.ArrayList;
+import java.util.List;
import android.content.ComponentCallbacks;
import android.content.ComponentCallbacks2;
import android.content.Context;
import android.content.ContextWrapper;
import android.content.Intent;
+import android.content.RestrictionEntry;
import android.content.res.Configuration;
import android.os.Bundle;
+import android.os.UserManager;
/**
* Base class for those who need to maintain global application state. You can
@@ -131,6 +134,11 @@ public class Application extends ContextWrapper implements ComponentCallbacks2 {
}
}
+ public List<RestrictionEntry> getApplicationRestrictions() {
+ return ((UserManager) getSystemService(USER_SERVICE))
+ .getApplicationRestrictions(getPackageName(), android.os.Process.myUserHandle());
+ }
+
public void registerComponentCallbacks(ComponentCallbacks callback) {
synchronized (mComponentCallbacks) {
mComponentCallbacks.add(callback);
diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl
index 14bcc0d7fba4..3d9b2ae83518 100644
--- a/core/java/android/app/INotificationManager.aidl
+++ b/core/java/android/app/INotificationManager.aidl
@@ -41,7 +41,7 @@ interface INotificationManager
StatusBarNotification[] getActiveNotifications(String callingPkg);
StatusBarNotification[] getHistoricalNotifications(String callingPkg, int count);
- void registerListener(in INotificationListener listener, int userid);
+ void registerListener(in INotificationListener listener, String pkg, int userid);
void unregisterListener(in INotificationListener listener, int userid);
}
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 8a9eed27d4f4..7dd76cd676f0 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -45,6 +45,7 @@ import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
+import java.util.List;
/**
* Interface to global information about an application environment. This is
@@ -286,6 +287,15 @@ public abstract class Context {
public abstract Context getApplicationContext();
/**
+ * Returns the list of restrictions for the application, or null if there are no
+ * restrictions.
+ * @return
+ */
+ public List<RestrictionEntry> getApplicationRestrictions() {
+ return getApplicationContext().getApplicationRestrictions();
+ }
+
+ /**
* Add a new {@link ComponentCallbacks} to the base application of the
* Context, which will be called at the same times as the ComponentCallbacks
* methods of activities and other components are called. Note that you
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 53c47d2abe9d..e1461e3dde7e 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -2415,6 +2415,15 @@ public class Intent implements Parcelable, Cloneable {
"android.intent.action.PRE_BOOT_COMPLETED";
/**
+ * Broadcast to a specific application to query any supported restrictions to impose
+ * on restricted users. The response should contain an extra {@link #EXTRA_RESTRICTIONS}
+ * which is of type <code>ArrayList&lt;RestrictionEntry&gt;</code>.
+ * @see RestrictionEntry
+ */
+ public static final String ACTION_GET_RESTRICTION_ENTRIES =
+ "android.intent.action.GET_RESTRICTION_ENTRIES";
+
+ /**
* Sent the first time a user is starting, to allow system apps to
* perform one time initialization. (This will not be seen by third
* party applications because a newly initialized user does not have any
@@ -3146,6 +3155,12 @@ public class Intent implements Parcelable, Cloneable {
public static final String EXTRA_USER_HANDLE =
"android.intent.extra.user_handle";
+ /**
+ * Extra used in the response from a BroadcastReceiver that handles
+ * {@link #ACTION_GET_RESTRICTION_ENTRIES}.
+ */
+ public static final String EXTRA_RESTRICTIONS = "android.intent.extra.restrictions";
+
// ---------------------------------------------------------------------
// ---------------------------------------------------------------------
// Intent flags (see mFlags variable).
diff --git a/core/java/android/content/RestrictionEntry.aidl b/core/java/android/content/RestrictionEntry.aidl
new file mode 100644
index 000000000000..b93eee3a5ff7
--- /dev/null
+++ b/core/java/android/content/RestrictionEntry.aidl
@@ -0,0 +1,20 @@
+/*
+**
+** Copyright 2013, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+package android.content;
+
+parcelable RestrictionEntry;
diff --git a/core/java/android/content/RestrictionEntry.java b/core/java/android/content/RestrictionEntry.java
new file mode 100644
index 000000000000..97c4cb8e3c3d
--- /dev/null
+++ b/core/java/android/content/RestrictionEntry.java
@@ -0,0 +1,275 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.lang.annotation.Inherited;
+
+/**
+ * Applications can expose restrictions for a restricted user on a
+ * multiuser device. The administrator can configure these restrictions that will then be
+ * applied to the restricted user. Each RestrictionsEntry is one configurable restriction.
+ * <p/>
+ * Any application that chooses to expose such restrictions does so by implementing a
+ * receiver that handles the {@link Intent#ACTION_GET_RESTRICTION_ENTRIES} action.
+ * The receiver then returns a result bundle that contains an entry called "restrictions", whose
+ * value is an ArrayList<RestrictionsEntry>.
+ */
+public class RestrictionEntry implements Parcelable {
+
+ /**
+ * A type of restriction. Use this one for information that needs to be transferred across
+ * but shouldn't be presented to the user in the UI.
+ */
+ public static final int TYPE_NULL = 0;
+ /**
+ * A type of restriction. Use this for storing true/false values, typically presented as
+ * a checkbox in the UI.
+ */
+ public static final int TYPE_BOOLEAN = 1;
+ /**
+ * A type of restriction. Use this for storing a string value, typically presented as
+ * a single-select list. The {@link #values} and {@link #choices} need to have the list of
+ * possible values and the corresponding localized strings, respectively, to present in the UI.
+ */
+ public static final int TYPE_CHOICE = 2;
+ /**
+ * A type of restriction. Use this for storing a string value, typically presented as
+ * a single-select list. The {@link #values} and {@link #choices} need to have the list of
+ * possible values and the corresponding localized strings, respectively, to present in the UI.
+ * The presentation could imply that values in lower array indices are included when a
+ * particular value is chosen.
+ */
+ public static final int TYPE_CHOICE_LEVEL = 3;
+ /**
+ * A type of restriction. Use this for presenting a multi-select list where more than one
+ * entry can be selected, such as for choosing specific titles to white-list.
+ * The {@link #values} and {@link #choices} need to have the list of
+ * possible values and the corresponding localized strings, respectively, to present in the UI.
+ * Use {@link #getMultipleValues()} and {@link #setMultipleValues(String[])} to manipulate
+ * the selections.
+ */
+ public static final int TYPE_MULTI_SELECT = 4;
+
+ /** The type of restriction. */
+ public int type;
+
+ /** The unique key that identifies the restriction. */
+ public String key;
+
+ /** The user-visible title of the restriction. */
+ public String title;
+
+ /** The user-visible secondary description of the restriction. */
+ public String description;
+
+ /** The user-visible set of choices used for single-select and multi-select lists. */
+ public String [] choices;
+
+ /** The values corresponding to the user-visible choices. The value(s) of this entry will
+ * one or more of these, returned by {@link #getMultipleValues()} and
+ * {@link #getStringValue()}.
+ */
+ public String [] values;
+
+ /* The chosen value, whose content depends on the type of the restriction. */
+ private String currentValue;
+ /* List of selected choices in the multi-select case. */
+ private String[] currentValues;
+
+ /**
+ * Constructor for {@link #TYPE_CHOICE} and {@link #TYPE_CHOICE_LEVEL} types.
+ * @param key the unique key for this restriction
+ * @param value the current value
+ */
+ public RestrictionEntry(String key, String value) {
+ this.key = key;
+ this.currentValue = value;
+ }
+
+ /**
+ * Constructor for {@link #TYPE_BOOLEAN} type.
+ * @param key the unique key for this restriction
+ * @param value the current value
+ */
+ public RestrictionEntry(String key, boolean value) {
+ this.key = key;
+ setValue(value);
+ }
+
+ /**
+ * Constructor for {@link #TYPE_MULTI_SELECT} type.
+ * @param key the unique key for this restriction
+ * @param multipleValues the list of values that are currently selected
+ */
+ public RestrictionEntry(String key, String[] multipleValues) {
+ this.key = key;
+ this.currentValues = multipleValues;
+ }
+
+ /**
+ * Returns the current value. Null for {@link #TYPE_MULTI_SELECT} type.
+ * @return the current value
+ */
+ public String getStringValue() {
+ return currentValue;
+ }
+
+ /**
+ * Returns the list of current selections. Null if the type is not {@link #TYPE_MULTI_SELECT}.
+ * @return the list of current selections.
+ */
+ public String[] getMultipleValues() {
+ return currentValues;
+ }
+
+ /**
+ * Returns the current boolean value for entries of type {@link #TYPE_BOOLEAN}.
+ * @return the current value
+ */
+ public boolean getBooleanValue() {
+ return Boolean.parseBoolean(currentValue);
+ }
+
+ /**
+ * Set the current string value.
+ * @param s the current value
+ */
+ public void setValue(String s) {
+ currentValue = s;
+ }
+
+ /**
+ * Sets the current boolean value.
+ * @param b the current value
+ */
+ public void setValue(boolean b) {
+ currentValue = Boolean.toString(b);
+ }
+
+ /**
+ * Sets the current list of selected values.
+ * @param values the current list of selected values
+ */
+ public void setMultipleValues(String[] values) {
+ currentValues = values;
+ }
+
+ private boolean equalArrays(String[] one, String[] other) {
+ if (one.length != other.length) return false;
+ for (int i = 0; i < one.length; i++) {
+ if (!one[i].equals(other[i])) return false;
+ }
+ return true;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o == this) return true;
+ if (!(o instanceof RestrictionEntry)) return false;
+ final RestrictionEntry other = (RestrictionEntry) o;
+ // Make sure that either currentValue matches or currentValues matches.
+ return type == other.type && key.equals(other.key)
+ &&
+ ((currentValues == null && other.currentValues == null
+ && currentValue != null && currentValue.equals(other.currentValue))
+ ||
+ (currentValue == null && other.currentValue == null
+ && currentValues != null && equalArrays(currentValues, other.currentValues)));
+ }
+
+ @Override
+ public int hashCode() {
+ int result = 17;
+ result = 31 * result + key.hashCode();
+ if (currentValue != null) {
+ result = 31 * result + currentValue.hashCode();
+ } else if (currentValues != null) {
+ for (String value : currentValues) {
+ if (value != null) {
+ result = 31 * result + value.hashCode();
+ }
+ }
+ }
+ return result;
+ }
+
+ private String[] readArray(Parcel in) {
+ int count = in.readInt();
+ String[] values = new String[count];
+ for (int i = 0; i < count; i++) {
+ values[i] = in.readString();
+ }
+ return values;
+ }
+
+ public RestrictionEntry(Parcel in) {
+ type = in.readInt();
+ key = in.readString();
+ title = in.readString();
+ description = in.readString();
+ choices = readArray(in);
+ values = readArray(in);
+ currentValue = in.readString();
+ currentValues = readArray(in);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ private void writeArray(Parcel dest, String[] values) {
+ if (values == null) {
+ dest.writeInt(0);
+ } else {
+ dest.writeInt(values.length);
+ for (int i = 0; i < values.length; i++) {
+ dest.writeString(values[i]);
+ }
+ }
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(type);
+ dest.writeString(key);
+ dest.writeString(title);
+ dest.writeString(description);
+ writeArray(dest, choices);
+ writeArray(dest, values);
+ dest.writeString(currentValue);
+ writeArray(dest, currentValues);
+ }
+
+ public static final Creator<RestrictionEntry> CREATOR = new Creator<RestrictionEntry>() {
+ public RestrictionEntry createFromParcel(Parcel source) {
+ return new RestrictionEntry(source);
+ }
+
+ public RestrictionEntry[] newArray(int size) {
+ return new RestrictionEntry[size];
+ }
+ };
+
+ @Override
+ public String toString() {
+ return "RestrictionsEntry {type=" + type + ", key=" + key + ", value=" + currentValue + "}";
+ }
+}
diff --git a/core/java/android/content/pm/PackageInfo.java b/core/java/android/content/pm/PackageInfo.java
index 85f7aa5113a2..77ca7f68e75e 100644
--- a/core/java/android/content/pm/PackageInfo.java
+++ b/core/java/android/content/pm/PackageInfo.java
@@ -217,7 +217,10 @@ public class PackageInfo implements Parcelable {
* @hide
*/
public int installLocation = INSTALL_LOCATION_INTERNAL_ONLY;
-
+
+ /** @hide */
+ public boolean requiredForAllUsers;
+
public PackageInfo() {
}
@@ -258,6 +261,7 @@ public class PackageInfo implements Parcelable {
dest.writeTypedArray(configPreferences, parcelableFlags);
dest.writeTypedArray(reqFeatures, parcelableFlags);
dest.writeInt(installLocation);
+ dest.writeInt(requiredForAllUsers ? 1 : 0);
}
public static final Parcelable.Creator<PackageInfo> CREATOR
@@ -296,5 +300,6 @@ public class PackageInfo implements Parcelable {
configPreferences = source.createTypedArray(ConfigurationInfo.CREATOR);
reqFeatures = source.createTypedArray(FeatureInfo.CREATOR);
installLocation = source.readInt();
+ requiredForAllUsers = source.readInt() != 0;
}
}
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 5eac90373d17..149b8e5d2a95 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -289,6 +289,7 @@ public class PackageParser {
pi.sharedUserLabel = p.mSharedUserLabel;
pi.applicationInfo = generateApplicationInfo(p, flags, state, userId);
pi.installLocation = p.installLocation;
+ pi.requiredForAllUsers = p.mRequiredForAllUsers;
pi.firstInstallTime = firstInstallTime;
pi.lastUpdateTime = lastUpdateTime;
if ((flags&PackageManager.GET_GIDS) != 0) {
@@ -1760,6 +1761,11 @@ public class PackageParser {
false)) {
ai.flags |= ApplicationInfo.FLAG_PERSISTENT;
}
+ if (sa.getBoolean(
+ com.android.internal.R.styleable.AndroidManifestApplication_requiredForAllUsers,
+ false)) {
+ owner.mRequiredForAllUsers = true;
+ }
}
if (sa.getBoolean(
@@ -3271,6 +3277,9 @@ public class PackageParser {
public int installLocation;
+ /* An app that's required for all users and cannot be uninstalled for a user */
+ public boolean mRequiredForAllUsers;
+
/**
* Digest suitable for comparing whether this package's manifest is the
* same as another.
diff --git a/core/java/android/inputmethodservice/IInputMethodWrapper.java b/core/java/android/inputmethodservice/IInputMethodWrapper.java
index 11282305dd41..0d981be7eb8f 100644
--- a/core/java/android/inputmethodservice/IInputMethodWrapper.java
+++ b/core/java/android/inputmethodservice/IInputMethodWrapper.java
@@ -20,8 +20,8 @@ import com.android.internal.os.HandlerCaller;
import com.android.internal.os.SomeArgs;
import com.android.internal.view.IInputContext;
import com.android.internal.view.IInputMethod;
-import com.android.internal.view.IInputMethodCallback;
import com.android.internal.view.IInputMethodSession;
+import com.android.internal.view.IInputSessionCallback;
import com.android.internal.view.InputConnectionWrapper;
import android.content.Context;
@@ -80,8 +80,8 @@ class IInputMethodWrapper extends IInputMethod.Stub
// NOTE: we should have a cache of these.
static class InputMethodSessionCallbackWrapper implements InputMethod.SessionCallback {
final Context mContext;
- final IInputMethodCallback mCb;
- InputMethodSessionCallbackWrapper(Context context, IInputMethodCallback cb) {
+ final IInputSessionCallback mCb;
+ InputMethodSessionCallbackWrapper(Context context, IInputSessionCallback cb) {
mContext = context;
mCb = cb;
}
@@ -175,7 +175,7 @@ class IInputMethodWrapper extends IInputMethod.Stub
}
case DO_CREATE_SESSION: {
inputMethod.createSession(new InputMethodSessionCallbackWrapper(
- mCaller.mContext, (IInputMethodCallback)msg.obj));
+ mCaller.mContext, (IInputSessionCallback)msg.obj));
return;
}
case DO_SET_SESSION_ENABLED:
@@ -249,7 +249,7 @@ class IInputMethodWrapper extends IInputMethod.Stub
inputContext, attribute));
}
- public void createSession(IInputMethodCallback callback) {
+ public void createSession(IInputSessionCallback callback) {
mCaller.executeOrSendMessage(mCaller.obtainMessageO(DO_CREATE_SESSION, callback));
}
diff --git a/core/java/android/os/IUserManager.aidl b/core/java/android/os/IUserManager.aidl
index 34c9740557a4..4c2d7a643220 100644
--- a/core/java/android/os/IUserManager.aidl
+++ b/core/java/android/os/IUserManager.aidl
@@ -20,6 +20,7 @@ package android.os;
import android.os.Bundle;
import android.os.ParcelFileDescriptor;
import android.content.pm.UserInfo;
+import android.content.RestrictionEntry;
import android.graphics.Bitmap;
/**
@@ -40,4 +41,7 @@ interface IUserManager {
int getUserHandle(int userSerialNumber);
Bundle getUserRestrictions(int userHandle);
void setUserRestrictions(in Bundle restrictions, int userHandle);
+ void setApplicationRestrictions(in String packageName, in List<RestrictionEntry> entries,
+ int userHandle);
+ List<RestrictionEntry> getApplicationRestrictions(in String packageName, int userHandle);
}
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 51e3e7c93733..7c055283775b 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -17,6 +17,7 @@ package android.os;
import android.app.ActivityManagerNative;
import android.content.Context;
+import android.content.RestrictionEntry;
import android.content.pm.UserInfo;
import android.content.res.Resources;
import android.graphics.Bitmap;
@@ -126,7 +127,19 @@ public class UserManager {
public boolean isUserAGoat() {
return false;
}
-
+
+ /**
+ * @hide
+ */
+ public boolean isUserRestricted() {
+ try {
+ return mService.getUserInfo(getUserHandle()).isRestricted();
+ } catch (RemoteException re) {
+ Log.w(TAG, "Could not check if user restricted ", re);
+ return false;
+ }
+ }
+
/**
* Return whether the given user is actively running. This means that
* the user is in the "started" state, not "stopped" -- it is currently
@@ -450,10 +463,34 @@ public class UserManager {
}
/**
- * Returns whether the current user is allow to toggle location sharing settings.
+ * Returns whether the current user is allowed to toggle location sharing settings.
* @hide
*/
public boolean isLocationSharingToggleAllowed() {
return getUserRestrictions().getBoolean(ALLOW_CONFIG_LOCATION_ACCESS);
}
+
+ /**
+ * @hide
+ */
+ public List<RestrictionEntry> getApplicationRestrictions(String packageName, UserHandle user) {
+ try {
+ return mService.getApplicationRestrictions(packageName, user.getIdentifier());
+ } catch (RemoteException re) {
+ Log.w(TAG, "Could not get application restrictions for user " + user.getIdentifier());
+ }
+ return null;
+ }
+
+ /**
+ * @hide
+ */
+ public void setApplicationRestrictions(String packageName, List<RestrictionEntry> entries,
+ UserHandle user) {
+ try {
+ mService.setApplicationRestrictions(packageName, entries, user.getIdentifier());
+ } catch (RemoteException re) {
+ Log.w(TAG, "Could not set application restrictions for user " + user.getIdentifier());
+ }
+ }
}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index d251ca25e068..19283be3897f 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -4039,6 +4039,14 @@ public final class Settings {
public static final String SCREENSAVER_DEFAULT_COMPONENT = "screensaver_default_component";
/**
+ * Name of a package that the current user has explicitly allowed to see all of that
+ * user's notifications.
+ *
+ * @hide
+ */
+ public static final String ENABLED_NOTIFICATION_LISTENERS = "enabled_notification_listeners";
+
+ /**
* This are the settings to be backed up.
*
* NOTE: Settings are backed up and restored in the order they appear
@@ -4791,6 +4799,14 @@ public final class Settings {
"wifi_scan_always_enabled";
/**
+ * Setting to indicate whether the user should be notified that scans are still
+ * available when Wi-Fi is turned off
+ * @hide
+ */
+ public static final String WIFI_NOTIFY_SCAN_ALWAYS_AVAILABLE =
+ "wifi_notify_scan_always_enabled";
+
+ /**
* Used to save the Wifi_ON state prior to tethering.
* This state will be checked to restore Wifi after
* the user turns off tethering.
diff --git a/core/java/android/security/IKeystoreService.java b/core/java/android/security/IKeystoreService.java
index 2ae3c6415d20..a890d9bdcd84 100644
--- a/core/java/android/security/IKeystoreService.java
+++ b/core/java/android/security/IKeystoreService.java
@@ -407,15 +407,18 @@ public interface IKeystoreService extends IInterface {
}
@Override
- public int migrate(String name, int targetUid) throws RemoteException {
+ public int duplicate(String srcKey, int srcUid, String destKey, int destUid)
+ throws RemoteException {
Parcel _data = Parcel.obtain();
Parcel _reply = Parcel.obtain();
int _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
- _data.writeString(name);
- _data.writeInt(targetUid);
- mRemote.transact(Stub.TRANSACTION_migrate, _data, _reply, 0);
+ _data.writeString(srcKey);
+ _data.writeInt(srcUid);
+ _data.writeString(destKey);
+ _data.writeInt(destUid);
+ mRemote.transact(Stub.TRANSACTION_duplicate, _data, _reply, 0);
_reply.readException();
_result = _reply.readInt();
} finally {
@@ -448,7 +451,7 @@ public interface IKeystoreService extends IInterface {
static final int TRANSACTION_grant = IBinder.FIRST_CALL_TRANSACTION + 17;
static final int TRANSACTION_ungrant = IBinder.FIRST_CALL_TRANSACTION + 18;
static final int TRANSACTION_getmtime = IBinder.FIRST_CALL_TRANSACTION + 19;
- static final int TRANSACTION_migrate = IBinder.FIRST_CALL_TRANSACTION + 20;
+ static final int TRANSACTION_duplicate = IBinder.FIRST_CALL_TRANSACTION + 20;
/**
* Cast an IBinder object into an IKeystoreService interface, generating
@@ -534,5 +537,6 @@ public interface IKeystoreService extends IInterface {
public long getmtime(String name) throws RemoteException;
- public int migrate(String name, int targetUid) throws RemoteException;
+ public int duplicate(String srcKey, int srcUid, String destKey, int destUid)
+ throws RemoteException;
}
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 8808af04e21f..f06387ceab04 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -245,8 +245,10 @@ public final class ViewRootImpl implements ViewParent,
// Input event queue.
QueuedInputEvent mFirstPendingInputEvent;
+ int mPendingInputEventCount;
QueuedInputEvent mCurrentInputEvent;
boolean mProcessInputEventsScheduled;
+ String mPendingInputEventQueueLengthCounterName = "pq";
boolean mWindowAttributesChanged = false;
int mWindowAttributesChangesFlag = 0;
@@ -642,6 +644,8 @@ public final class ViewRootImpl implements ViewParent,
if (view.getImportantForAccessibility() == View.IMPORTANT_FOR_ACCESSIBILITY_AUTO) {
view.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES);
}
+
+ mPendingInputEventQueueLengthCounterName = "pq:" + attrs.getTitle();
}
}
}
@@ -4427,6 +4431,9 @@ public final class ViewRootImpl implements ViewParent,
}
last.mNext = q;
}
+ mPendingInputEventCount += 1;
+ Trace.traceCounter(Trace.TRACE_TAG_INPUT, mPendingInputEventQueueLengthCounterName,
+ mPendingInputEventCount);
if (processImmediately) {
doProcessInputEvents();
@@ -4451,6 +4458,10 @@ public final class ViewRootImpl implements ViewParent,
q.mNext = null;
mCurrentInputEvent = q;
+ mPendingInputEventCount -= 1;
+ Trace.traceCounter(Trace.TRACE_TAG_INPUT, mPendingInputEventQueueLengthCounterName,
+ mPendingInputEventCount);
+
final int result = deliverInputEvent(q);
if (result != EVENT_IN_PROGRESS) {
finishCurrentInputEvent(result == EVENT_HANDLED);
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index d258f4d5059a..dba235452e86 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -525,11 +525,6 @@ public final class InputMethodManager {
public void finishedEvent(int seq, boolean handled) {
InputMethodManager.this.finishedEvent(seq, handled);
}
-
- @Override
- public void sessionCreated(IInputMethodSession session) {
- // Stub -- not for use in the client.
- }
};
InputMethodManager(IInputMethodManager service, Looper looper) {
diff --git a/core/java/android/webkit/AccessibilityInjectorFallback.java b/core/java/android/webkit/AccessibilityInjectorFallback.java
index 6417527b9630..40cc4e9fa505 100644
--- a/core/java/android/webkit/AccessibilityInjectorFallback.java
+++ b/core/java/android/webkit/AccessibilityInjectorFallback.java
@@ -438,7 +438,6 @@ class AccessibilityInjectorFallback {
event.setFromIndex(0);
event.setToIndex(selection.length());
sendAccessibilityEvent(event);
- event.recycle();
}
}
diff --git a/core/java/android/widget/ExpandableListView.java b/core/java/android/widget/ExpandableListView.java
index a746370a4270..0dd567b344bb 100644
--- a/core/java/android/widget/ExpandableListView.java
+++ b/core/java/android/widget/ExpandableListView.java
@@ -288,6 +288,9 @@ public class ExpandableListView extends ListView {
// Get more expandable list-related info for this item
pos = mConnector.getUnflattenedPos(childFlPos);
+ final boolean isLayoutRtl = isLayoutRtl();
+ final int width = getWidth();
+
// If this item type and the previous item type are different, then we need to change
// the left & right bounds
if (pos.position.type != lastItemType) {
@@ -300,9 +303,18 @@ public class ExpandableListView extends ListView {
indicatorRect.left = mIndicatorLeft;
indicatorRect.right = mIndicatorRight;
}
-
- indicatorRect.left += mPaddingLeft;
- indicatorRect.right += mPaddingLeft;
+
+ if (isLayoutRtl) {
+ final int temp = indicatorRect.left;
+ indicatorRect.left = width - indicatorRect.right;
+ indicatorRect.right = width - temp;
+
+ indicatorRect.left -= mPaddingRight;
+ indicatorRect.right -= mPaddingRight;
+ } else {
+ indicatorRect.left += mPaddingLeft;
+ indicatorRect.right += mPaddingLeft;
+ }
lastItemType = pos.position.type;
}
diff --git a/core/java/android/widget/ImageView.java b/core/java/android/widget/ImageView.java
index 1bbf4ebcc4d7..cde6ceb00cfc 100644
--- a/core/java/android/widget/ImageView.java
+++ b/core/java/android/widget/ImageView.java
@@ -204,7 +204,7 @@ public class ImageView extends View {
@Override
public boolean hasOverlappingRendering() {
- return (getBackground() != null);
+ return (getBackground() != null && getBackground().getCurrent() != null);
}
@Override
diff --git a/core/java/android/widget/ListView.java b/core/java/android/widget/ListView.java
index 7c40a6494442..4b62c2d2450f 100644
--- a/core/java/android/widget/ListView.java
+++ b/core/java/android/widget/ListView.java
@@ -2417,6 +2417,34 @@ public class ListView extends AbsListView {
}
/**
+ * Used by {@link #arrowScrollImpl(int)} to help determine the next selected position
+ * to move to. This can return a position currently not represented by a view on screen
+ * but only in the direction given.
+ *
+ * @param selectedPos Current selected position to move from
+ * @param direction Direction to move in
+ * @return Desired selected position after moving in the given direction
+ */
+ private final int nextSelectedPositionForDirection(int selectedPos, int direction) {
+ int nextSelected;
+ if (direction == View.FOCUS_DOWN) {
+ nextSelected = selectedPos != INVALID_POSITION && selectedPos >= mFirstPosition ?
+ selectedPos + 1 :
+ mFirstPosition;
+ } else {
+ final int lastPos = mFirstPosition + getChildCount() - 1;
+ nextSelected = selectedPos != INVALID_POSITION && selectedPos < lastPos?
+ selectedPos - 1 :
+ lastPos;
+ }
+
+ if (nextSelected < 0 || nextSelected >= mAdapter.getCount()) {
+ return INVALID_POSITION;
+ }
+ return lookForSelectablePosition(nextSelected, direction == View.FOCUS_DOWN);
+ }
+
+ /**
* Handle an arrow scroll going up or down. Take into account whether items are selectable,
* whether there are focusable items etc.
*
@@ -2431,7 +2459,7 @@ public class ListView extends AbsListView {
View selectedView = getSelectedView();
int selectedPos = mSelectedPosition;
- int nextSelectedPosition = lookForSelectablePositionOnScreen(direction);
+ int nextSelectedPosition = nextSelectedPositionForDirection(selectedPos, direction);
int amountToScroll = amountToScroll(direction, nextSelectedPosition);
// if we are moving focus, we may OVERRIDE the default behavior
@@ -2643,14 +2671,18 @@ public class ListView extends AbsListView {
final int listBottom = getHeight() - mListPadding.bottom;
final int listTop = mListPadding.top;
- final int numChildren = getChildCount();
+ int numChildren = getChildCount();
if (direction == View.FOCUS_DOWN) {
int indexToMakeVisible = numChildren - 1;
if (nextSelectedPosition != INVALID_POSITION) {
indexToMakeVisible = nextSelectedPosition - mFirstPosition;
}
-
+ while (numChildren <= indexToMakeVisible) {
+ // Child to view is not attached yet.
+ addViewBelow(getChildAt(numChildren - 1), mFirstPosition + numChildren - 1);
+ numChildren++;
+ }
final int positionToMakeVisible = mFirstPosition + indexToMakeVisible;
final View viewToMakeVisible = getChildAt(indexToMakeVisible);
@@ -2684,6 +2716,12 @@ public class ListView extends AbsListView {
if (nextSelectedPosition != INVALID_POSITION) {
indexToMakeVisible = nextSelectedPosition - mFirstPosition;
}
+ while (indexToMakeVisible < 0) {
+ // Child to view is not attached yet.
+ addViewAbove(getChildAt(0), mFirstPosition);
+ mFirstPosition--;
+ indexToMakeVisible = nextSelectedPosition - mFirstPosition;
+ }
final int positionToMakeVisible = mFirstPosition + indexToMakeVisible;
final View viewToMakeVisible = getChildAt(indexToMakeVisible);
int goalTop = listTop;
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 5fbb642826f6..52b7a81f395d 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -4794,7 +4794,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
@Override
public boolean hasOverlappingRendering() {
- return (getBackground() != null || mText instanceof Spannable || hasSelection());
+ return ((getBackground() != null && getBackground().getCurrent() != null)
+ || mText instanceof Spannable || hasSelection());
}
/**
diff --git a/core/java/com/android/internal/view/IInputMethod.aidl b/core/java/com/android/internal/view/IInputMethod.aidl
index 5db860b1950a..bd947e94c536 100644
--- a/core/java/com/android/internal/view/IInputMethod.aidl
+++ b/core/java/com/android/internal/view/IInputMethod.aidl
@@ -16,17 +16,14 @@
package com.android.internal.view;
-import android.graphics.Rect;
import android.os.IBinder;
import android.os.ResultReceiver;
-import android.view.KeyEvent;
-import android.view.MotionEvent;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputBinding;
import android.view.inputmethod.InputMethodSubtype;
import com.android.internal.view.IInputContext;
-import com.android.internal.view.IInputMethodCallback;
import com.android.internal.view.IInputMethodSession;
+import com.android.internal.view.IInputSessionCallback;
/**
* Top-level interface to an input method component (implemented in a
@@ -44,7 +41,7 @@ oneway interface IInputMethod {
void restartInput(in IInputContext inputContext, in EditorInfo attribute);
- void createSession(IInputMethodCallback callback);
+ void createSession(IInputSessionCallback callback);
void setSessionEnabled(IInputMethodSession session, boolean enabled);
diff --git a/core/java/com/android/internal/view/IInputMethodCallback.aidl b/core/java/com/android/internal/view/IInputMethodCallback.aidl
index 480cc0e5cd59..717a82d7c6f6 100644
--- a/core/java/com/android/internal/view/IInputMethodCallback.aidl
+++ b/core/java/com/android/internal/view/IInputMethodCallback.aidl
@@ -16,13 +16,6 @@
package com.android.internal.view;
-import android.graphics.Rect;
-import android.view.KeyEvent;
-import android.view.MotionEvent;
-import com.android.internal.view.IInputContext;
-import com.android.internal.view.IInputMethodSession;
-import android.os.IBinder;
-
/**
* Helper interface for IInputMethod to allow the input method to call back
* to its client with results from incoming calls.
@@ -30,5 +23,4 @@ import android.os.IBinder;
*/
oneway interface IInputMethodCallback {
void finishedEvent(int seq, boolean handled);
- void sessionCreated(IInputMethodSession session);
}
diff --git a/core/java/com/android/internal/view/IInputSessionCallback.aidl b/core/java/com/android/internal/view/IInputSessionCallback.aidl
new file mode 100644
index 000000000000..2b48f33cbdc0
--- /dev/null
+++ b/core/java/com/android/internal/view/IInputSessionCallback.aidl
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.view;
+
+import com.android.internal.view.IInputMethodSession;
+
+/**
+ * Helper interface for IInputMethod to allow the input method to notify the client when a new
+ * session has been created.
+ * {@hide}
+ */
+
+oneway interface IInputSessionCallback {
+ void sessionCreated(IInputMethodSession session);
+}
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 5a1c0f89fd5d..be8f5f4b191f 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -1073,10 +1073,10 @@
<!-- Group of permissions that are related to the screenlock. -->
<permission-group android:name="android.permission-group.SCREENLOCK"
- android:label="@string/permgrouplab_storage"
+ android:label="@string/permgrouplab_screenlock"
android:icon="@drawable/perm_group_screenlock"
android:permissionGroupFlags="personalInfo"
- android:description="@string/permgroupdesc_storage"
+ android:description="@string/permgroupdesc_screenlock"
android:priority="230" />
<!-- Allows applications to disable the keyguard -->
diff --git a/core/res/res/layout/keyguard_emergency_carrier_area.xml b/core/res/res/layout/keyguard_emergency_carrier_area.xml
index 52adc0407223..b8a7654c8dce 100644
--- a/core/res/res/layout/keyguard_emergency_carrier_area.xml
+++ b/core/res/res/layout/keyguard_emergency_carrier_area.xml
@@ -18,7 +18,7 @@
-->
<!-- This contains emergency call button and carrier as shared by pin/pattern/password screens -->
-<LinearLayout
+<com.android.internal.policy.impl.keyguard.EmergencyCarrierArea
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
@@ -29,6 +29,7 @@
android:clickable="true">
<com.android.internal.policy.impl.keyguard.CarrierText
+ android:id="@+id/carrier_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:singleLine="true"
@@ -72,4 +73,4 @@
android:visibility="gone"/>
</LinearLayout>
-</LinearLayout>
+</com.android.internal.policy.impl.keyguard.EmergencyCarrierArea>
diff --git a/core/res/res/layout/simple_expandable_list_item_1.xml b/core/res/res/layout/simple_expandable_list_item_1.xml
index 9810a6098d17..7cce12a13d45 100644
--- a/core/res/res/layout/simple_expandable_list_item_1.xml
+++ b/core/res/res/layout/simple_expandable_list_item_1.xml
@@ -21,4 +21,5 @@
android:paddingStart="?android:attr/expandableListPreferredItemPaddingLeft"
android:textAppearance="?android:attr/textAppearanceListItem"
android:gravity="center_vertical"
+ android:textAlignment="viewStart"
/>
diff --git a/core/res/res/layout/simple_expandable_list_item_2.xml b/core/res/res/layout/simple_expandable_list_item_2.xml
index ed845f8d35b8..da60d7a5e03f 100644
--- a/core/res/res/layout/simple_expandable_list_item_2.xml
+++ b/core/res/res/layout/simple_expandable_list_item_2.xml
@@ -28,6 +28,7 @@
android:layout_height="wrap_content"
android:layout_marginTop="6dip"
android:textAppearance="?android:attr/textAppearanceListItem"
+ android:textAlignment="viewStart"
/>
<TextView android:id="@android:id/text2"
@@ -36,6 +37,7 @@
android:layout_below="@android:id/text1"
android:layout_alignStart="@android:id/text1"
android:textAppearance="?android:attr/textAppearanceSmall"
+ android:textAlignment="viewStart"
/>
</TwoLineListItem>
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index f1d8c03c5e58..6f59817bf671 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -254,7 +254,11 @@
not normally be used by applications; it requires that the system keep
your application running at all times. -->
<attr name="persistent" format="boolean" />
-
+
+ <!-- Flag to specify if this application needs to be present for all users. Only pre-installed
+ applications can request this feature. Default value is false. -->
+ <attr name="requiredForAllUsers" format="boolean" />
+
<!-- Flag indicating whether the application can be debugged, even when
running on a device that is running in user mode. -->
<attr name="debuggable" format="boolean" />
@@ -844,6 +848,7 @@
for normal behavior. -->
<attr name="hasCode" format="boolean" />
<attr name="persistent" />
+ <attr name="requiredForAllUsers" />
<!-- Specify whether the components in this application are enabled or not (that is, can be
instantiated by the system).
If "false", it overrides any component specific values (a value of "true" will not
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 6a8407f9d43b..7a6a1e988f88 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -1041,4 +1041,6 @@
<string name="config_chooseTypeAndAccountActivity"
>android/android.accounts.ChooseTypeAndAccountActivity</string>
+ <!-- Apps that are authorized to access shared accounts, overridden by product overlays -->
+ <string name="config_appsAuthorizedForSharedAccounts"></string>
</resources>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 42e5cf1ba4ba..9f810af3ecc8 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2033,15 +2033,10 @@
=============================================================== -->
<eat-comment />
- <public type="attr" name="mipMap" id="0x010103cd" />
- <public type="attr" name="mirrorForRtl" id="0x010103ce" />
-
- <!-- ===============================================================
- Resources added in version 19 of the platform
- =============================================================== -->
- <eat-comment />
-
+ <public type="attr" name="mipMap" />
+ <public type="attr" name="mirrorForRtl" />
<public type="attr" name="windowOverscan" />
+ <public type="attr" name="requiredForAllUsers" />
<public type="style" name="Theme.NoTitleBar.Overscan" />
<public type="style" name="Theme.Light.NoTitleBar.Overscan" />
<public type="style" name="Theme.Black.NoTitleBar.Overscan" />
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 447d94c78101..a2d45708e919 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -474,6 +474,11 @@
<string name="permgroupdesc_camera">Direct access to camera for image or video capture.</string>
<!-- Title of a category of application permissions, listed so the user can choose whether they want to allow the application to do this. -->
+ <string name="permgrouplab_screenlock">Lock screen</string>
+ <!-- Description of a category of application permissions, listed so the user can choose whether they want to allow the application to do this. -->
+ <string name="permgroupdesc_screenlock">Ability to affect behavior of the lock screen on your device.</string>
+
+ <!-- Title of a category of application permissions, listed so the user can choose whether they want to allow the application to do this. -->
<string name="permgrouplab_appInfo">Your applications information</string>
<!-- Description of a category of application permissions, listed so the user can choose whether they want to allow the application to do this. -->
<string name="permgroupdesc_appInfo">Ability to affect behavior of other applications on your device.</string>
@@ -2984,6 +2989,15 @@
<!-- If the device is getting low on internal storage, a notification is shown to the user. This is the message of that notification. -->
<string name="low_internal_storage_view_text">Some system functions may not work</string>
+ <!-- [CHAR LIMIT=NONE] Stub notification title for an app running a service that has provided
+ a bad bad notification for itself. -->
+ <string name="app_running_notification_title"><xliff:g id="app_name">%1$s</xliff:g>
+ running</string>
+ <!-- [CHAR LIMIT=NONE] Stub notification text for an app running a service that has provided
+ a bad bad notification for itself. -->
+ <string name="app_running_notification_text"><xliff:g id="app_name">%1$s</xliff:g>
+ is currently running</string>
+
<!-- Preference framework strings. -->
<string name="ok">OK</string>
<!-- Preference framework strings. -->
@@ -4035,7 +4049,7 @@
<!-- Message shown in dialog when user is attempting to set the music volume above the
recommended maximum level for headphones -->
<string name="safe_media_volume_warning" product="default">
- "Raise volume above safe level?\nListening at high volume for long periods may damage your hearing."
+ "Raise volume above recommended level?\nListening at high volume for long periods may damage your hearing."
</string>
<!-- Text spoken when the user is performing a gesture that will enable accessibility. [CHAR LIMIT=none] -->
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 614ac07165e9..80e77dd6e187 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -330,6 +330,8 @@
<java-symbol type="string" name="addToDictionary" />
<java-symbol type="string" name="action_bar_home_description" />
<java-symbol type="string" name="action_bar_up_description" />
+ <java-symbol type="string" name="app_running_notification_title" />
+ <java-symbol type="string" name="app_running_notification_text" />
<java-symbol type="string" name="delete" />
<java-symbol type="string" name="deleteText" />
<java-symbol type="string" name="ellipsis_two_dots" />
@@ -865,6 +867,7 @@
<java-symbol type="string" name="owner_name" />
<java-symbol type="string" name="config_chooseAccountActivity" />
<java-symbol type="string" name="config_chooseTypeAndAccountActivity" />
+ <java-symbol type="string" name="config_appsAuthorizedForSharedAccounts" />
<java-symbol type="plurals" name="abbrev_in_num_days" />
@@ -1323,6 +1326,7 @@
<java-symbol type="id" name="keyguard_bouncer_frame" />
<java-symbol type="id" name="app_widget_container" />
<java-symbol type="id" name="view_flipper" />
+ <java-symbol type="id" name="carrier_text" />
<java-symbol type="id" name="emergency_call_button" />
<java-symbol type="id" name="keyguard_host_view" />
<java-symbol type="id" name="delete_button" />
diff --git a/keystore/java/android/security/KeyStore.java b/keystore/java/android/security/KeyStore.java
index 4dc0beb6cf9e..12c0ed8b217f 100644
--- a/keystore/java/android/security/KeyStore.java
+++ b/keystore/java/android/security/KeyStore.java
@@ -287,9 +287,9 @@ public class KeyStore {
}
}
- public boolean migrate(String key, int uid) {
+ public boolean duplicate(String srcKey, int srcUid, String destKey, int destUid) {
try {
- return mBinder.migrate(key, uid) == NO_ERROR;
+ return mBinder.duplicate(srcKey, srcUid, destKey, destUid) == NO_ERROR;
} catch (RemoteException e) {
Log.w(TAG, "Cannot connect to keystore", e);
return false;
diff --git a/keystore/tests/src/android/security/KeyStoreTest.java b/keystore/tests/src/android/security/KeyStoreTest.java
index 8f8ee9215764..1de1eaf5bd50 100644
--- a/keystore/tests/src/android/security/KeyStoreTest.java
+++ b/keystore/tests/src/android/security/KeyStoreTest.java
@@ -553,7 +553,7 @@ public class KeyStoreTest extends ActivityUnitTestCase<Activity> {
mKeyStore.ungrant(TEST_KEYNAME, 0));
}
- public void testMigrate_grantedUid_Wifi_Success() throws Exception {
+ public void testDuplicate_grantedUid_Wifi_Success() throws Exception {
assertTrue(mKeyStore.password(TEST_PASSWD));
assertFalse(mKeyStore.contains(TEST_KEYNAME));
@@ -563,13 +563,35 @@ public class KeyStoreTest extends ActivityUnitTestCase<Activity> {
assertTrue(mKeyStore.contains(TEST_KEYNAME));
assertFalse(mKeyStore.contains(TEST_KEYNAME, Process.WIFI_UID));
- assertTrue(mKeyStore.migrate(TEST_KEYNAME, Process.WIFI_UID));
+ // source doesn't exist
+ assertFalse(mKeyStore.duplicate(TEST_KEYNAME1, -1, TEST_KEYNAME1, Process.WIFI_UID));
+ assertFalse(mKeyStore.contains(TEST_KEYNAME1, Process.WIFI_UID));
- assertFalse(mKeyStore.contains(TEST_KEYNAME));
- assertTrue(mKeyStore.contains(TEST_KEYNAME, Process.WIFI_UID));
+ // Copy from current UID to granted UID
+ assertTrue(mKeyStore.duplicate(TEST_KEYNAME, -1, TEST_KEYNAME1, Process.WIFI_UID));
+ assertTrue(mKeyStore.contains(TEST_KEYNAME));
+ assertFalse(mKeyStore.contains(TEST_KEYNAME1));
+ assertFalse(mKeyStore.contains(TEST_KEYNAME, Process.WIFI_UID));
+ assertTrue(mKeyStore.contains(TEST_KEYNAME1, Process.WIFI_UID));
+ assertFalse(mKeyStore.duplicate(TEST_KEYNAME, -1, TEST_KEYNAME1, Process.WIFI_UID));
+
+ // Copy from granted UID to same granted UID
+ assertTrue(mKeyStore.duplicate(TEST_KEYNAME1, Process.WIFI_UID, TEST_KEYNAME2,
+ Process.WIFI_UID));
+ assertFalse(mKeyStore.contains(TEST_KEYNAME, Process.WIFI_UID));
+ assertTrue(mKeyStore.contains(TEST_KEYNAME1, Process.WIFI_UID));
+ assertTrue(mKeyStore.contains(TEST_KEYNAME2, Process.WIFI_UID));
+ assertFalse(mKeyStore.duplicate(TEST_KEYNAME1, Process.WIFI_UID, TEST_KEYNAME2,
+ Process.WIFI_UID));
+
+ assertTrue(mKeyStore.duplicate(TEST_KEYNAME, -1, TEST_KEYNAME2, -1));
+ assertTrue(mKeyStore.contains(TEST_KEYNAME));
+ assertFalse(mKeyStore.contains(TEST_KEYNAME1));
+ assertTrue(mKeyStore.contains(TEST_KEYNAME2));
+ assertFalse(mKeyStore.duplicate(TEST_KEYNAME, -1, TEST_KEYNAME2, -1));
}
- public void testMigrate_ungrantedUid_Bluetooth_Failure() throws Exception {
+ public void testDuplicate_ungrantedUid_Bluetooth_Failure() throws Exception {
assertTrue(mKeyStore.password(TEST_PASSWD));
assertFalse(mKeyStore.contains(TEST_KEYNAME));
@@ -579,7 +601,9 @@ public class KeyStoreTest extends ActivityUnitTestCase<Activity> {
assertTrue(mKeyStore.contains(TEST_KEYNAME));
assertFalse(mKeyStore.contains(TEST_KEYNAME, Process.BLUETOOTH_UID));
- assertFalse(mKeyStore.migrate(TEST_KEYNAME, Process.BLUETOOTH_UID));
+ assertFalse(mKeyStore.duplicate(TEST_KEYNAME, -1, TEST_KEYNAME2, Process.BLUETOOTH_UID));
+ assertFalse(mKeyStore.duplicate(TEST_KEYNAME, Process.BLUETOOTH_UID, TEST_KEYNAME2,
+ Process.BLUETOOTH_UID));
assertTrue(mKeyStore.contains(TEST_KEYNAME));
assertFalse(mKeyStore.contains(TEST_KEYNAME, Process.BLUETOOTH_UID));
diff --git a/libs/hwui/Caches.cpp b/libs/hwui/Caches.cpp
index dc3a4e2dc8cb..57d1a4f27a55 100644
--- a/libs/hwui/Caches.cpp
+++ b/libs/hwui/Caches.cpp
@@ -310,6 +310,7 @@ void Caches::flush(FlushMode mode) {
fontRenderer->flush();
textureCache.flush();
pathCache.clear();
+ tasks.stop();
// fall through
case kFlushMode_Layers:
layerCache.clear();
diff --git a/libs/hwui/DisplayList.cpp b/libs/hwui/DisplayList.cpp
index 55ddd17f5cb6..d985ad00e849 100644
--- a/libs/hwui/DisplayList.cpp
+++ b/libs/hwui/DisplayList.cpp
@@ -351,9 +351,9 @@ void DisplayList::outputViewProperties(const int level) {
level * 2, "", mTransformMatrix, MATRIX_ARGS(mTransformMatrix));
}
}
- if (mAlpha < 1 && !mCaching) {
- if (!mHasOverlappingRendering) {
- ALOGD("%*sSetAlpha %.2f", level * 2, "", mAlpha);
+ if (mAlpha < 1) {
+ if (mCaching || !mHasOverlappingRendering) {
+ ALOGD("%*sScaleAlpha %.2f", level * 2, "", mAlpha);
} else {
int flags = SkCanvas::kHasAlphaLayer_SaveFlag;
if (mClipChildren) {
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index bc28d655aec4..2cf7183360d1 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -193,6 +193,7 @@ status_t OpenGLRenderer::prepareDirty(float left, float top,
mSaveCount = 1;
mSnapshot->setClip(left, top, right, bottom);
+ mTilingClip.set(left, top, right, bottom);
mDirtyClip = true;
updateLayers();
@@ -206,8 +207,7 @@ status_t OpenGLRenderer::prepareDirty(float left, float top,
// invoked during the frame
mSuppressTiling = mCaches.hasRegisteredFunctors();
- mTilingSnapshot = mSnapshot;
- startTiling(mTilingSnapshot, true);
+ startTiling(mSnapshot, true);
debugOverdraw(true, true);
@@ -252,9 +252,9 @@ void OpenGLRenderer::syncState() {
void OpenGLRenderer::startTiling(const sp<Snapshot>& s, bool opaque) {
if (!mSuppressTiling) {
- Rect* clip = mTilingSnapshot->clipRect;
+ Rect* clip = &mTilingClip;
if (s->flags & Snapshot::kFlagFboTarget) {
- clip = &s->layer->clipRect;
+ clip = &(s->layer->clipRect);
}
startTiling(*clip, s->height, opaque);
@@ -480,10 +480,10 @@ void OpenGLRenderer::debugOverdraw(bool enable, bool clear) {
void OpenGLRenderer::renderOverdraw() {
if (mCaches.debugOverdraw && getTargetFbo() == 0) {
- const Rect* clip = mTilingSnapshot->clipRect;
+ const Rect* clip = &mTilingClip;
mCaches.enableScissor();
- mCaches.setScissor(clip->left, mTilingSnapshot->height - clip->bottom,
+ mCaches.setScissor(clip->left, mFirstSnapshot->height - clip->bottom,
clip->right - clip->left, clip->bottom - clip->top);
mCaches.stencil.enableDebugTest(2);
diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h
index 71bd6bb124f0..7bb93957bc03 100644
--- a/libs/hwui/OpenGLRenderer.h
+++ b/libs/hwui/OpenGLRenderer.h
@@ -947,7 +947,7 @@ private:
// Current state
sp<Snapshot> mSnapshot;
// State used to define the clipping region
- sp<Snapshot> mTilingSnapshot;
+ Rect mTilingClip;
// Used to draw textured quads
TextureVertex mMeshVertices[4];
diff --git a/libs/hwui/PathCache.cpp b/libs/hwui/PathCache.cpp
index 490c22a00553..07c420721fa5 100644
--- a/libs/hwui/PathCache.cpp
+++ b/libs/hwui/PathCache.cpp
@@ -347,14 +347,15 @@ void PathCache::PathProcessor::onProcess(const sp<Task<SkBitmap*> >& task) {
// Paths
///////////////////////////////////////////////////////////////////////////////
-void PathCache::remove(SkPath* path) {
+void PathCache::remove(const path_pair_t& pair) {
Vector<PathDescription> pathsToRemove;
LruCache<PathDescription, PathTexture*>::Iterator i(mCache);
while (i.next()) {
const PathDescription& key = i.key();
if (key.type == kShapePath &&
- (key.shape.path.mPath == path || key.shape.path.mPath == path->getSourcePath())) {
+ (key.shape.path.mPath == pair.getFirst() ||
+ key.shape.path.mPath == pair.getSecond())) {
pathsToRemove.push(key);
}
}
@@ -366,7 +367,7 @@ void PathCache::remove(SkPath* path) {
void PathCache::removeDeferred(SkPath* path) {
Mutex::Autolock l(mLock);
- mGarbage.push(path);
+ mGarbage.push(path_pair_t(path, const_cast<SkPath*>(path->getSourcePath())));
}
void PathCache::clearGarbage() {
diff --git a/libs/hwui/PathCache.h b/libs/hwui/PathCache.h
index e6d92df0b06c..146723162fa5 100644
--- a/libs/hwui/PathCache.h
+++ b/libs/hwui/PathCache.h
@@ -26,6 +26,7 @@
#include "Debug.h"
#include "Properties.h"
#include "Texture.h"
+#include "utils/Pair.h"
class SkBitmap;
class SkCanvas;
@@ -215,10 +216,6 @@ public:
PathTexture* get(SkPath* path, SkPaint* paint);
/**
- * Removes an entry.
- */
- void remove(SkPath* path);
- /**
* Removes the specified path. This is meant to be called from threads
* that are not the EGL context thread.
*/
@@ -251,6 +248,8 @@ public:
float& left, float& top, float& offset, uint32_t& width, uint32_t& height);
private:
+ typedef Pair<SkPath*, SkPath*> path_pair_t;
+
PathTexture* addTexture(const PathDescription& entry,
const SkPath *path, const SkPaint* paint);
PathTexture* addTexture(const PathDescription& entry, SkBitmap* bitmap);
@@ -261,6 +260,12 @@ private:
}
/**
+ * Removes an entry.
+ * The pair must define first=path, second=sourcePath
+ */
+ void remove(const path_pair_t& pair);
+
+ /**
* Ensures there is enough space in the cache for a texture of the specified
* dimensions.
*/
@@ -318,7 +323,8 @@ private:
bool mDebugEnabled;
sp<PathProcessor> mProcessor;
- Vector<SkPath*> mGarbage;
+
+ Vector<path_pair_t> mGarbage;
mutable Mutex mLock;
}; // class PathCache
diff --git a/libs/hwui/Snapshot.cpp b/libs/hwui/Snapshot.cpp
index 923913e864a9..d26ee3884433 100644
--- a/libs/hwui/Snapshot.cpp
+++ b/libs/hwui/Snapshot.cpp
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+#define LOG_TAG "OpenGLRenderer"
+
#include "Snapshot.h"
#include <SkCanvas.h>
@@ -199,5 +201,14 @@ bool Snapshot::isIgnored() const {
return invisible || empty;
}
+void Snapshot::dump() const {
+ ALOGD("Snapshot %p, flags %x, prev %p, height %d, ignored %d, hasComplexClip %d",
+ this, flags, previous.get(), height, isIgnored(), clipRegion && !clipRegion->isEmpty());
+ ALOGD(" ClipRect (at %p) %.1f %.1f %.1f %.1f",
+ clipRect, clipRect->left, clipRect->top, clipRect->right, clipRect->bottom);
+ ALOGD(" Transform (at %p):", transform);
+ transform->dump();
+}
+
}; // namespace uirenderer
}; // namespace android
diff --git a/libs/hwui/Snapshot.h b/libs/hwui/Snapshot.h
index ffd47295b618..cc6d0cda003e 100644
--- a/libs/hwui/Snapshot.h
+++ b/libs/hwui/Snapshot.h
@@ -228,6 +228,8 @@ public:
*/
float alpha;
+ void dump() const;
+
private:
void ensureClipRegion();
void copyClipRectFromRegion();
diff --git a/libs/hwui/thread/TaskManager.cpp b/libs/hwui/thread/TaskManager.cpp
index ce6c8c04483d..c8bfd9c88165 100644
--- a/libs/hwui/thread/TaskManager.cpp
+++ b/libs/hwui/thread/TaskManager.cpp
@@ -48,6 +48,12 @@ bool TaskManager::canRunTasks() const {
return mThreads.size() > 0;
}
+void TaskManager::stop() {
+ for (size_t i = 0; i < mThreads.size(); i++) {
+ mThreads[i]->exit();
+ }
+}
+
bool TaskManager::addTaskBase(const sp<TaskBase>& task, const sp<TaskProcessorBase>& processor) {
if (mThreads.size() > 0) {
TaskWrapper wrapper(task, processor);
diff --git a/libs/hwui/thread/TaskManager.h b/libs/hwui/thread/TaskManager.h
index bc86062819cc..477314bbbb00 100644
--- a/libs/hwui/thread/TaskManager.h
+++ b/libs/hwui/thread/TaskManager.h
@@ -47,6 +47,12 @@ public:
*/
bool canRunTasks() const;
+ /**
+ * Stops all allocated threads. Adding tasks will start
+ * the threads again as necessary.
+ */
+ void stop();
+
private:
template <typename T>
friend class TaskProcessor;
diff --git a/libs/hwui/utils/Pair.h b/libs/hwui/utils/Pair.h
new file mode 100644
index 000000000000..172606a4d586
--- /dev/null
+++ b/libs/hwui/utils/Pair.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HWUI_PAIR_H
+#define ANDROID_HWUI_PAIR_H
+
+namespace android {
+namespace uirenderer {
+
+template <typename F, typename S>
+struct Pair {
+ F first;
+ S second;
+
+ Pair() { }
+ Pair(const Pair& o) : first(o.first), second(o.second) { }
+ Pair(const F& f, const S& s) : first(f), second(s) { }
+
+ inline const F& getFirst() const {
+ return first;
+ }
+
+ inline const S& getSecond() const {
+ return second;
+ }
+};
+
+}; // namespace uirenderer
+
+template <typename F, typename S>
+struct trait_trivial_ctor< uirenderer::Pair<F, S> >
+{ enum { value = aggregate_traits<F, S>::has_trivial_ctor }; };
+template <typename F, typename S>
+struct trait_trivial_dtor< uirenderer::Pair<F, S> >
+{ enum { value = aggregate_traits<F, S>::has_trivial_dtor }; };
+template <typename F, typename S>
+struct trait_trivial_copy< uirenderer::Pair<F, S> >
+{ enum { value = aggregate_traits<F, S>::has_trivial_copy }; };
+template <typename F, typename S>
+struct trait_trivial_move< uirenderer::Pair<F, S> >
+{ enum { value = aggregate_traits<F, S>::has_trivial_move }; };
+
+}; // namespace android
+
+#endif // ANDROID_HWUI_PAIR_H
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/EmergencyCarrierArea.java b/policy/src/com/android/internal/policy/impl/keyguard/EmergencyCarrierArea.java
new file mode 100644
index 000000000000..cfe1ef4be98e
--- /dev/null
+++ b/policy/src/com/android/internal/policy/impl/keyguard/EmergencyCarrierArea.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.policy.impl.keyguard;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.MotionEvent;
+import android.view.View;
+import android.widget.LinearLayout;
+
+import com.android.internal.R;
+
+public class EmergencyCarrierArea extends LinearLayout {
+
+ private CarrierText mCarrierText;
+ private EmergencyButton mEmergencyButton;
+
+ public EmergencyCarrierArea(Context context) {
+ super(context);
+ }
+
+ public EmergencyCarrierArea(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+ mCarrierText = (CarrierText) findViewById(R.id.carrier_text);
+ mEmergencyButton = (EmergencyButton) findViewById(R.id.emergency_call_button);
+
+ // The emergency button overlaps the carrier text, only noticeable when highlighted.
+ // So temporarily hide the carrier text while the emergency button is pressed.
+ mEmergencyButton.setOnTouchListener(new OnTouchListener(){
+ @Override
+ public boolean onTouch(View v, MotionEvent event) {
+ switch(event.getAction()) {
+ case MotionEvent.ACTION_DOWN:
+ mCarrierText.animate().alpha(0);
+ break;
+ case MotionEvent.ACTION_UP:
+ mCarrierText.animate().alpha(1);
+ break;
+ }
+ return false;
+ }});
+ }
+}
diff --git a/services/input/InputDispatcher.cpp b/services/input/InputDispatcher.cpp
index 0465215a8e8c..23a846b7ce95 100644
--- a/services/input/InputDispatcher.cpp
+++ b/services/input/InputDispatcher.cpp
@@ -538,7 +538,9 @@ void InputDispatcher::dropInboundEventLocked(EventEntry* entry, DropReason dropR
}
bool InputDispatcher::isAppSwitchKeyCode(int32_t keyCode) {
- return keyCode == AKEYCODE_HOME || keyCode == AKEYCODE_ENDCALL;
+ return keyCode == AKEYCODE_HOME
+ || keyCode == AKEYCODE_ENDCALL
+ || keyCode == AKEYCODE_APP_SWITCH;
}
bool InputDispatcher::isAppSwitchKeyEventLocked(KeyEntry* keyEntry) {
diff --git a/services/java/com/android/server/InputMethodManagerService.java b/services/java/com/android/server/InputMethodManagerService.java
index f6c9d8273513..0863bd6e9128 100644
--- a/services/java/com/android/server/InputMethodManagerService.java
+++ b/services/java/com/android/server/InputMethodManagerService.java
@@ -23,7 +23,7 @@ import com.android.internal.os.SomeArgs;
import com.android.internal.util.FastXmlSerializer;
import com.android.internal.view.IInputContext;
import com.android.internal.view.IInputMethod;
-import com.android.internal.view.IInputMethodCallback;
+import com.android.internal.view.IInputSessionCallback;
import com.android.internal.view.IInputMethodClient;
import com.android.internal.view.IInputMethodManager;
import com.android.internal.view.IInputMethodSession;
@@ -554,7 +554,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
}
}
- private static class MethodCallback extends IInputMethodCallback.Stub {
+ private static class MethodCallback extends IInputSessionCallback.Stub {
private final IInputMethod mMethod;
private final InputMethodManagerService mParentIMMS;
@@ -564,10 +564,6 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
}
@Override
- public void finishedEvent(int seq, boolean handled) throws RemoteException {
- }
-
- @Override
public void sessionCreated(IInputMethodSession session) throws RemoteException {
mParentIMMS.onSessionCreated(mMethod, session);
}
@@ -2330,7 +2326,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
args = (SomeArgs)msg.obj;
try {
((IInputMethod)args.arg1).createSession(
- (IInputMethodCallback)args.arg2);
+ (IInputSessionCallback)args.arg2);
} catch (RemoteException e) {
}
args.recycle();
diff --git a/services/java/com/android/server/NotificationManagerService.java b/services/java/com/android/server/NotificationManagerService.java
index 9e036d1124ec..5cf1c285f2db 100644
--- a/services/java/com/android/server/NotificationManagerService.java
+++ b/services/java/com/android/server/NotificationManagerService.java
@@ -50,12 +50,11 @@ import android.os.Binder;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
-import android.os.Parcel;
-import android.os.Parcelable;
import android.os.Process;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.UserHandle;
+import android.os.UserManager;
import android.os.Vibrator;
import android.provider.Settings;
import android.telephony.TelephonyManager;
@@ -124,6 +123,7 @@ public class NotificationManagerService extends INotificationManager.Stub
final Context mContext;
final IActivityManager mAm;
+ final UserManager mUserManager;
final IBinder mForegroundToken = new Binder();
private WorkerHandler mHandler;
@@ -164,6 +164,7 @@ public class NotificationManagerService extends INotificationManager.Stub
private final AppOpsManager mAppOps;
private ArrayList<NotificationListenerInfo> mListeners = new ArrayList<NotificationListenerInfo>();
+ private ArrayList<String> mEnabledListenersForCurrentUser = new ArrayList<String>();
// Notification control database. For now just contains disabled packages.
private AtomicFile mPolicyFile;
@@ -180,20 +181,27 @@ public class NotificationManagerService extends INotificationManager.Stub
private class NotificationListenerInfo implements DeathRecipient {
INotificationListener listener;
+ String pkg;
int userid;
- public NotificationListenerInfo(INotificationListener listener, int userid) {
+ boolean isSystem;
+
+ public NotificationListenerInfo(INotificationListener listener, String pkg, int userid,
+ boolean isSystem) {
this.listener = listener;
+ this.pkg = pkg;
this.userid = userid;
+ this.isSystem = isSystem;
}
- boolean userMatches(StatusBarNotification sbn) {
+ boolean enabledAndUserMatches(StatusBarNotification sbn) {
+ final int nid = sbn.getUserId();
+ if (!(isSystem || isEnabledForUser(nid))) return false;
if (this.userid == UserHandle.USER_ALL) return true;
- int nid = sbn.getUserId();
return (nid == UserHandle.USER_ALL || nid == this.userid);
}
public void notifyPostedIfUserMatch(StatusBarNotification sbn) {
- if (!userMatches(sbn)) return;
+ if (!enabledAndUserMatches(sbn)) return;
try {
listener.onNotificationPosted(sbn);
} catch (RemoteException ex) {
@@ -202,7 +210,7 @@ public class NotificationManagerService extends INotificationManager.Stub
}
public void notifyRemovedIfUserMatch(StatusBarNotification sbn) {
- if (!userMatches(sbn)) return;
+ if (!enabledAndUserMatches(sbn)) return;
try {
listener.onNotificationRemoved(sbn);
} catch (RemoteException ex) {
@@ -214,6 +222,14 @@ public class NotificationManagerService extends INotificationManager.Stub
public void binderDied() {
unregisterListener(this.listener, this.userid);
}
+
+ /** convenience method for looking in mEnabledListenersForCurrentUser */
+ public boolean isEnabledForUser(int userid) {
+ for (int i=0; i<mEnabledListenersForCurrentUser.size(); i++) {
+ if (this.pkg.equals(mEnabledListenersForCurrentUser.get(i))) return true;
+ }
+ return false;
+ }
}
private static class Archive {
@@ -413,12 +429,14 @@ public class NotificationManagerService extends INotificationManager.Stub
}
public StatusBarNotification[] getActiveNotifications(String callingPkg) {
+ // enforce() will ensure the calling uid has the correct permission
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.ACCESS_NOTIFICATIONS,
"NotificationManagerService.getActiveNotifications");
StatusBarNotification[] tmp = null;
int uid = Binder.getCallingUid();
+ // noteOp will check to make sure the callingPkg matches the uid
if (mAppOps.noteOpNoThrow(AppOpsManager.OP_ACCESS_NOTIFICATIONS, uid, callingPkg)
== AppOpsManager.MODE_ALLOWED) {
synchronized (mNotificationList) {
@@ -433,12 +451,14 @@ public class NotificationManagerService extends INotificationManager.Stub
}
public StatusBarNotification[] getHistoricalNotifications(String callingPkg, int count) {
+ // enforce() will ensure the calling uid has the correct permission
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.ACCESS_NOTIFICATIONS,
"NotificationManagerService.getHistoricalNotifications");
StatusBarNotification[] tmp = null;
int uid = Binder.getCallingUid();
+ // noteOp will check to make sure the callingPkg matches the uid
if (mAppOps.noteOpNoThrow(AppOpsManager.OP_ACCESS_NOTIFICATIONS, uid, callingPkg)
== AppOpsManager.MODE_ALLOWED) {
synchronized (mArchive) {
@@ -448,12 +468,27 @@ public class NotificationManagerService extends INotificationManager.Stub
return tmp;
}
+ boolean packageCanTapNotificationsForUser(final int uid, final String pkg) {
+ // Make sure the package and uid match, and that the package is allowed access
+ return (AppOpsManager.MODE_ALLOWED
+ == mAppOps.checkOpNoThrow(AppOpsManager.OP_ACCESS_NOTIFICATIONS, uid, pkg));
+ }
+
@Override
- public void registerListener(final INotificationListener listener, final int userid) {
- checkCallerIsSystem();
+ public void registerListener(final INotificationListener listener,
+ final String pkg, final int userid) {
+ // ensure system or allowed pkg
+ int uid = Binder.getCallingUid();
+ boolean isSystem = (UserHandle.getAppId(uid) == Process.SYSTEM_UID || uid == 0);
+ if (!(isSystem || packageCanTapNotificationsForUser(uid, pkg))) {
+ throw new SecurityException("Package " + pkg
+ + " may not listen for notifications");
+ }
+
synchronized (mNotificationList) {
try {
- NotificationListenerInfo info = new NotificationListenerInfo(listener, userid);
+ NotificationListenerInfo info
+ = new NotificationListenerInfo(listener, pkg, userid, isSystem);
listener.asBinder().linkToDeath(info, 0);
mListeners.add(info);
} catch (RemoteException e) {
@@ -464,7 +499,9 @@ public class NotificationManagerService extends INotificationManager.Stub
@Override
public void unregisterListener(INotificationListener listener, int userid) {
- checkCallerIsSystem();
+ // no need to check permissions; if your listener binder is in the list,
+ // that's proof that you had permission to add it in the first place
+
synchronized (mNotificationList) {
final int N = mListeners.size();
for (int i=N-1; i>=0; i--) {
@@ -740,37 +777,67 @@ public class NotificationManagerService extends INotificationManager.Stub
} else if (action.equals(Intent.ACTION_USER_PRESENT)) {
// turn off LED when user passes through lock screen
mNotificationLight.turnOff();
+ } else if (action.equals(Intent.ACTION_USER_SWITCHED)) {
+ // reload per-user settings
+ mSettingsObserver.update(null);
}
}
};
class SettingsObserver extends ContentObserver {
+ private final Uri NOTIFICATION_LIGHT_PULSE_URI
+ = Settings.System.getUriFor(Settings.System.NOTIFICATION_LIGHT_PULSE);
+
+ private final Uri ENABLED_NOTIFICATION_LISTENERS_URI
+ = Settings.System.getUriFor(Settings.Secure.ENABLED_NOTIFICATION_LISTENERS);
+
SettingsObserver(Handler handler) {
super(handler);
}
void observe() {
ContentResolver resolver = mContext.getContentResolver();
- resolver.registerContentObserver(Settings.System.getUriFor(
- Settings.System.NOTIFICATION_LIGHT_PULSE), false, this);
- update();
+ resolver.registerContentObserver(NOTIFICATION_LIGHT_PULSE_URI,
+ false, this);
+ resolver.registerContentObserver(ENABLED_NOTIFICATION_LISTENERS_URI,
+ false, this);
+ update(null);
}
- @Override public void onChange(boolean selfChange) {
- update();
+ @Override public void onChange(boolean selfChange, Uri uri) {
+ update(uri);
}
- public void update() {
+ public void update(Uri uri) {
ContentResolver resolver = mContext.getContentResolver();
- boolean pulseEnabled = Settings.System.getInt(resolver,
- Settings.System.NOTIFICATION_LIGHT_PULSE, 0) != 0;
- if (mNotificationPulseEnabled != pulseEnabled) {
- mNotificationPulseEnabled = pulseEnabled;
- updateNotificationPulse();
+ if (uri == null || NOTIFICATION_LIGHT_PULSE_URI.equals(uri)) {
+ boolean pulseEnabled = Settings.System.getInt(resolver,
+ Settings.System.NOTIFICATION_LIGHT_PULSE, 0) != 0;
+ if (mNotificationPulseEnabled != pulseEnabled) {
+ mNotificationPulseEnabled = pulseEnabled;
+ updateNotificationPulse();
+ }
+ }
+ if (uri == null || ENABLED_NOTIFICATION_LISTENERS_URI.equals(uri)) {
+ String pkglist = Settings.Secure.getString(
+ mContext.getContentResolver(),
+ Settings.Secure.ENABLED_NOTIFICATION_LISTENERS);
+ mEnabledListenersForCurrentUser.clear();
+ if (pkglist != null) {
+ String[] pkgs = pkglist.split(";");
+ for (int i=0; i<pkgs.length; i++) {
+ final String pkg = pkgs[i];
+ if (pkg != null && ! "".equals(pkg)) {
+ mEnabledListenersForCurrentUser.add(pkgs[i]);
+ }
+ }
+ }
}
}
}
+ private SettingsObserver mSettingsObserver;
+
static long[] getLongArray(Resources r, int resid, int maxlen, long[] def) {
int[] ar = r.getIntArray(resid);
if (ar == null) {
@@ -791,6 +858,7 @@ public class NotificationManagerService extends INotificationManager.Stub
mContext = context;
mVibrator = (Vibrator)context.getSystemService(Context.VIBRATOR_SERVICE);
mAm = ActivityManagerNative.getDefault();
+ mUserManager = (UserManager)context.getSystemService(Context.USER_SERVICE);
mToastQueue = new ArrayList<ToastRecord>();
mHandler = new WorkerHandler();
@@ -838,6 +906,7 @@ public class NotificationManagerService extends INotificationManager.Stub
filter.addAction(TelephonyManager.ACTION_PHONE_STATE_CHANGED);
filter.addAction(Intent.ACTION_USER_PRESENT);
filter.addAction(Intent.ACTION_USER_STOPPED);
+ filter.addAction(Intent.ACTION_USER_SWITCHED);
mContext.registerReceiver(mIntentReceiver, filter);
IntentFilter pkgFilter = new IntentFilter();
pkgFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
@@ -849,8 +918,8 @@ public class NotificationManagerService extends INotificationManager.Stub
IntentFilter sdFilter = new IntentFilter(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
mContext.registerReceiver(mIntentReceiver, sdFilter);
- SettingsObserver observer = new SettingsObserver(mHandler);
- observer.observe();
+ mSettingsObserver = new SettingsObserver(mHandler);
+ mSettingsObserver.observe();
}
/**
@@ -1706,6 +1775,18 @@ public class NotificationManagerService extends INotificationManager.Stub
pw.println("Current Notification Manager state:");
+ pw.print(" Enabled listeners: [");
+ for (String pkg : mEnabledListenersForCurrentUser) {
+ pw.print(" " + pkg);
+ }
+ pw.println(" ]");
+
+ pw.println(" Live listeners:");
+ for (NotificationListenerInfo info : mListeners) {
+ pw.println(" " + info.pkg + " (user " + info.userid + "): " + info.listener
+ + (info.isSystem?" SYSTEM":""));
+ }
+
int N;
synchronized (mToastQueue) {
diff --git a/services/java/com/android/server/Watchdog.java b/services/java/com/android/server/Watchdog.java
index b2a8ad834dc4..1663106964dc 100644
--- a/services/java/com/android/server/Watchdog.java
+++ b/services/java/com/android/server/Watchdog.java
@@ -29,6 +29,7 @@ import android.content.IntentFilter;
import android.os.BatteryManager;
import android.os.Debug;
import android.os.Handler;
+import android.os.Looper;
import android.os.Message;
import android.os.Process;
import android.os.ServiceManager;
@@ -114,6 +115,10 @@ public class Watchdog extends Thread {
* Used for scheduling monitor callbacks and checking memory usage.
*/
final class HeartbeatHandler extends Handler {
+ HeartbeatHandler(Looper looper) {
+ super(looper);
+ }
+
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
@@ -183,7 +188,9 @@ public class Watchdog extends Thread {
private Watchdog() {
super("watchdog");
- mHandler = new HeartbeatHandler();
+ // Explicitly bind the HeartbeatHandler to run on the ServerThread, so
+ // that it can't get accidentally bound to another thread.
+ mHandler = new HeartbeatHandler(Looper.getMainLooper());
}
public void init(Context context, BatteryService battery,
diff --git a/services/java/com/android/server/accounts/AccountManagerService.java b/services/java/com/android/server/accounts/AccountManagerService.java
index 49295f56b3e2..09daf569349b 100644
--- a/services/java/com/android/server/accounts/AccountManagerService.java
+++ b/services/java/com/android/server/accounts/AccountManagerService.java
@@ -455,7 +455,6 @@ public class AccountManagerService
@Override
public void onServiceChanged(AuthenticatorDescription desc, int userId, boolean removed) {
- Slog.d(TAG, "onServiceChanged() for userId " + userId);
validateAccountsInternal(getUserAccounts(userId), false /* invalidateAuthenticatorCache */);
}
@@ -588,16 +587,12 @@ public class AccountManagerService
if (result != null) {
if (result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT, false)) {
// Create a Session for the target user and pass in the bundle
- Slog.i(TAG, "getAccountCredentialsForCloning returned success, "
- + "sending result to target user");
completeCloningAccount(result, account, toAccounts);
} else {
- Slog.e(TAG, "getAccountCredentialsForCloning returned failure");
clonePassword(fromAccounts, toAccounts, account);
}
return;
} else {
- Slog.e(TAG, "getAccountCredentialsForCloning returned null");
clonePassword(fromAccounts, toAccounts, account);
super.onResult(result);
}
@@ -645,15 +640,12 @@ public class AccountManagerService
if (result != null) {
if (result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT, false)) {
// TODO: Anything?
- Slog.i(TAG, "addAccount returned success");
} else {
// TODO: Show error notification
// TODO: Should we remove the shadow account to avoid retries?
- Slog.e(TAG, "addAccountFromCredentials returned failure");
}
return;
} else {
- Slog.e(TAG, "addAccountFromCredentials returned null");
super.onResult(result);
}
}
@@ -1433,6 +1425,17 @@ public class AccountManagerService
if (accountType == null) throw new IllegalArgumentException("accountType is null");
checkManageAccountsPermission();
+ // Is user allowed to modify accounts?
+ if (!getUserManager().getUserRestrictions(Binder.getCallingUserHandle())
+ .getBoolean(UserManager.ALLOW_MODIFY_ACCOUNTS)) {
+ try {
+ response.onError(AccountManager.ERROR_CODE_USER_RESTRICTED,
+ "User is not allowed to add an account!");
+ } catch (RemoteException re) {
+ }
+ return;
+ }
+
UserAccounts accounts = getUserAccountsForCaller();
final int pid = Binder.getCallingPid();
final int uid = Binder.getCallingUid();
@@ -1573,17 +1576,19 @@ public class AccountManagerService
private volatile Account[] mAccountsOfType = null;
private volatile ArrayList<Account> mAccountsWithFeatures = null;
private volatile int mCurrentAccount = 0;
+ private int mCallingUid;
public GetAccountsByTypeAndFeatureSession(UserAccounts accounts,
- IAccountManagerResponse response, String type, String[] features) {
+ IAccountManagerResponse response, String type, String[] features, int callingUid) {
super(accounts, response, type, false /* expectActivityLaunch */,
true /* stripAuthTokenFromResult */);
+ mCallingUid = callingUid;
mFeatures = features;
}
public void run() throws RemoteException {
synchronized (mAccounts.cacheLock) {
- mAccountsOfType = getAccountsFromCacheLocked(mAccounts, mAccountType);
+ mAccountsOfType = getAccountsFromCacheLocked(mAccounts, mAccountType, mCallingUid);
}
// check whether each account matches the requested features
mAccountsWithFeatures = new ArrayList<Account>(mAccountsOfType.length);
@@ -1668,10 +1673,11 @@ public class AccountManagerService
public Account[] getAccounts(int userId) {
checkReadAccountsPermission();
UserAccounts accounts = getUserAccounts(userId);
+ int callingUid = Binder.getCallingUid();
long identityToken = clearCallingIdentity();
try {
synchronized (accounts.cacheLock) {
- return getAccountsFromCacheLocked(accounts, null);
+ return getAccountsFromCacheLocked(accounts, null, callingUid);
}
} finally {
restoreCallingIdentity(identityToken);
@@ -1711,7 +1717,8 @@ public class AccountManagerService
UserAccounts userAccounts = getUserAccounts(userId);
if (userAccounts == null) continue;
synchronized (userAccounts.cacheLock) {
- Account[] accounts = getAccountsFromCacheLocked(userAccounts, null);
+ Account[] accounts = getAccountsFromCacheLocked(userAccounts, null,
+ Binder.getCallingUid());
for (int a = 0; a < accounts.length; a++) {
runningAccounts.add(new AccountAndUser(accounts[a], userId));
}
@@ -1725,9 +1732,10 @@ public class AccountManagerService
@Override
public Account[] getAccountsAsUser(String type, int userId) {
+ final int callingUid = Binder.getCallingUid();
// Only allow the system process to read accounts of other users
if (userId != UserHandle.getCallingUserId()
- && Binder.getCallingUid() != android.os.Process.myUid()) {
+ && callingUid != android.os.Process.myUid()) {
throw new SecurityException("User " + UserHandle.getCallingUserId()
+ " trying to get account for " + userId);
}
@@ -1742,7 +1750,7 @@ public class AccountManagerService
long identityToken = clearCallingIdentity();
try {
synchronized (accounts.cacheLock) {
- return getAccountsFromCacheLocked(accounts, type);
+ return getAccountsFromCacheLocked(accounts, type, callingUid);
}
} finally {
restoreCallingIdentity(identityToken);
@@ -1826,19 +1834,21 @@ public class AccountManagerService
if (type == null) throw new IllegalArgumentException("accountType is null");
checkReadAccountsPermission();
UserAccounts userAccounts = getUserAccountsForCaller();
+ int callingUid = Binder.getCallingUid();
long identityToken = clearCallingIdentity();
try {
if (features == null || features.length == 0) {
Account[] accounts;
synchronized (userAccounts.cacheLock) {
- accounts = getAccountsFromCacheLocked(userAccounts, type);
+ accounts = getAccountsFromCacheLocked(userAccounts, type, callingUid);
}
Bundle result = new Bundle();
result.putParcelableArray(AccountManager.KEY_ACCOUNTS, accounts);
onResult(response, result);
return;
}
- new GetAccountsByTypeAndFeatureSession(userAccounts, response, type, features).bind();
+ new GetAccountsByTypeAndFeatureSession(userAccounts, response, type, features,
+ callingUid).bind();
} finally {
restoreCallingIdentity(identityToken);
}
@@ -2352,7 +2362,8 @@ public class AccountManagerService
}
}
} else {
- Account[] accounts = getAccountsFromCacheLocked(userAccounts, null /* type */);
+ Account[] accounts = getAccountsFromCacheLocked(userAccounts, null /* type */,
+ android.os.Process.myUid());
fout.println("Accounts: " + accounts.length);
for (Account account : accounts) {
fout.println(" " + account);
@@ -2691,13 +2702,56 @@ public class AccountManagerService
accounts.accountCache.put(account.type, newAccountsForType);
}
- protected Account[] getAccountsFromCacheLocked(UserAccounts userAccounts, String accountType) {
+ private Account[] filterSharedAccounts(UserAccounts userAccounts, Account[] unfiltered,
+ int callingUid) {
+ if (getUserManager() == null || userAccounts == null || userAccounts.userId < 0
+ || callingUid == android.os.Process.myUid()) {
+ return unfiltered;
+ }
+ if (mUserManager.getUserInfo(userAccounts.userId).isRestricted()) {
+ String[] packages = mPackageManager.getPackagesForUid(callingUid);
+ // If any of the packages includes a white listed package, return the full set,
+ // otherwise return non-shared accounts only.
+ // This might be a temporary way to specify a whitelist
+ String whiteList = mContext.getResources().getString(
+ com.android.internal.R.string.config_appsAuthorizedForSharedAccounts);
+ for (String packageName : packages) {
+ if (whiteList.contains(";" + packageName + ";")) {
+ return unfiltered;
+ }
+ }
+ ArrayList<Account> allowed = new ArrayList<Account>();
+ Account[] sharedAccounts = getSharedAccountsAsUser(userAccounts.userId);
+ if (sharedAccounts == null || sharedAccounts.length == 0) return unfiltered;
+ for (Account account : unfiltered) {
+ boolean found = false;
+ for (Account shared : sharedAccounts) {
+ if (shared.equals(account)) {
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ allowed.add(account);
+ }
+ }
+ Account[] filtered = new Account[allowed.size()];
+ allowed.toArray(filtered);
+ return filtered;
+ } else {
+ return unfiltered;
+ }
+ }
+
+ protected Account[] getAccountsFromCacheLocked(UserAccounts userAccounts, String accountType,
+ int callingUid) {
if (accountType != null) {
final Account[] accounts = userAccounts.accountCache.get(accountType);
if (accounts == null) {
return EMPTY_ACCOUNT_ARRAY;
} else {
- return Arrays.copyOf(accounts, accounts.length);
+ return filterSharedAccounts(userAccounts, Arrays.copyOf(accounts, accounts.length),
+ callingUid);
}
} else {
int totalLength = 0;
@@ -2714,7 +2768,7 @@ public class AccountManagerService
accountsOfType.length);
totalLength += accountsOfType.length;
}
- return accounts;
+ return filterSharedAccounts(userAccounts, accounts, callingUid);
}
}
diff --git a/services/java/com/android/server/am/ServiceRecord.java b/services/java/com/android/server/am/ServiceRecord.java
index 1ac6bdfb850f..8ff1c7d5fba7 100644
--- a/services/java/com/android/server/am/ServiceRecord.java
+++ b/services/java/com/android/server/am/ServiceRecord.java
@@ -16,6 +16,9 @@
package com.android.server.am;
+import android.app.PendingIntent;
+import android.net.Uri;
+import android.provider.Settings;
import com.android.internal.os.BatteryStatsImpl;
import com.android.server.NotificationManagerService;
@@ -369,6 +372,44 @@ class ServiceRecord extends Binder {
}
try {
if (foregroundNoti.icon == 0) {
+ // It is not correct for the caller to supply a notification
+ // icon, but this used to be able to slip through, so for
+ // those dirty apps give it the app's icon.
+ foregroundNoti.icon = appInfo.icon;
+ if (foregroundNoti.contentView == null) {
+ // In this case the app may not have specified a
+ // content view... so we'll give them something to show.
+ CharSequence appName = appInfo.loadLabel(
+ ams.mContext.getPackageManager());
+ if (appName == null) {
+ appName = appInfo.packageName;
+ }
+ Context ctx = null;
+ try {
+ ctx = ams.mContext.createPackageContext(
+ appInfo.packageName, 0);
+ Intent runningIntent = new Intent(
+ Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
+ runningIntent.setData(Uri.fromParts("package",
+ appInfo.packageName, null));
+ PendingIntent pi = PendingIntent.getActivity(ams.mContext, 0,
+ runningIntent, PendingIntent.FLAG_UPDATE_CURRENT);
+ foregroundNoti.setLatestEventInfo(ctx,
+ ams.mContext.getString(
+ com.android.internal.R.string
+ .app_running_notification_title,
+ appName),
+ ams.mContext.getString(
+ com.android.internal.R.string
+ .app_running_notification_text,
+ appName),
+ pi);
+ } catch (PackageManager.NameNotFoundException e) {
+ foregroundNoti.icon = 0;
+ }
+ }
+ }
+ if (foregroundNoti.icon == 0) {
// Notifications whose icon is 0 are defined to not show
// a notification, silently ignoring it. We don't want to
// just ignore it, we want to prevent the service from
diff --git a/services/java/com/android/server/content/ContentService.java b/services/java/com/android/server/content/ContentService.java
index 3b92338c1752..545ec935c648 100644
--- a/services/java/com/android/server/content/ContentService.java
+++ b/services/java/com/android/server/content/ContentService.java
@@ -37,7 +37,9 @@ import android.os.Parcel;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.UserHandle;
+import android.text.format.DateUtils;
import android.util.Log;
+import android.util.Slog;
import android.util.SparseIntArray;
import java.io.FileDescriptor;
@@ -406,6 +408,12 @@ public final class ContentService extends IContentService.Stub {
"no permission to write the sync settings");
int userId = UserHandle.getCallingUserId();
+ if (pollFrequency <= DateUtils.MINUTE_IN_MILLIS) {
+ Slog.w(TAG, "Requested poll frequency of " + pollFrequency
+ + "ms being rounded up to 60 seconds.");
+ pollFrequency = DateUtils.MINUTE_IN_MILLIS;
+ }
+
long identityToken = clearCallingIdentity();
try {
getSyncManager().getSyncStorageEngine().addPeriodicSync(
diff --git a/services/java/com/android/server/pm/UserManagerService.java b/services/java/com/android/server/pm/UserManagerService.java
index 1414cbd8c14f..636b0e59913a 100644
--- a/services/java/com/android/server/pm/UserManagerService.java
+++ b/services/java/com/android/server/pm/UserManagerService.java
@@ -25,7 +25,10 @@ import android.app.IStopUserCallback;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
+import android.content.RestrictionEntry;
+import android.content.SharedPreferences;
import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.UserInfo;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
@@ -83,11 +86,17 @@ public class UserManagerService extends IUserManager.Stub {
private static final String TAG_USERS = "users";
private static final String TAG_USER = "user";
private static final String TAG_RESTRICTIONS = "restrictions";
+ private static final String TAG_ENTRY = "entry";
+ private static final String TAG_VALUE = "value";
+ private static final String ATTR_KEY = "key";
+ private static final String ATTR_MULTIPLE = "m";
private static final String USER_INFO_DIR = "system" + File.separator + "users";
private static final String USER_LIST_FILENAME = "userlist.xml";
private static final String USER_PHOTO_FILENAME = "photo.png";
+ private static final String RESTRICTIONS_FILE_PREFIX = "res_";
+
private static final int MIN_USER_ID = 10;
private static final int USER_VERSION = 2;
@@ -947,6 +956,151 @@ public class UserManagerService extends IUserManager.Stub {
}
@Override
+ public List<RestrictionEntry> getApplicationRestrictions(String packageName, int userId) {
+ if (UserHandle.getCallingUserId() != userId
+ || Binder.getCallingUid() != getUidForPackage(packageName)) {
+ checkManageUsersPermission("Only system can get restrictions for other users/apps");
+ }
+ synchronized (mPackagesLock) {
+ // Read the restrictions from XML
+ return readApplicationRestrictionsLocked(packageName, userId);
+ }
+ }
+
+ @Override
+ public void setApplicationRestrictions(String packageName, List<RestrictionEntry> entries,
+ int userId) {
+ if (UserHandle.getCallingUserId() != userId
+ || Binder.getCallingUid() != getUidForPackage(packageName)) {
+ checkManageUsersPermission("Only system can set restrictions for other users/apps");
+ }
+ synchronized (mPackagesLock) {
+ // Write the restrictions to XML
+ writeApplicationRestrictionsLocked(packageName, entries, userId);
+ }
+ }
+
+ private int getUidForPackage(String packageName) {
+ try {
+ return mContext.getPackageManager().getApplicationInfo(packageName,
+ PackageManager.GET_UNINSTALLED_PACKAGES).uid;
+ } catch (NameNotFoundException nnfe) {
+ return -1;
+ }
+ }
+
+ private List<RestrictionEntry> readApplicationRestrictionsLocked(String packageName,
+ int userId) {
+ final ArrayList<RestrictionEntry> entries = new ArrayList<RestrictionEntry>();
+ final ArrayList<String> values = new ArrayList<String>();
+
+ FileInputStream fis = null;
+ try {
+ AtomicFile restrictionsFile =
+ new AtomicFile(new File(Environment.getUserSystemDirectory(userId),
+ RESTRICTIONS_FILE_PREFIX + packageName + ".xml"));
+ fis = restrictionsFile.openRead();
+ XmlPullParser parser = Xml.newPullParser();
+ parser.setInput(fis, null);
+ int type;
+ while ((type = parser.next()) != XmlPullParser.START_TAG
+ && type != XmlPullParser.END_DOCUMENT) {
+ ;
+ }
+
+ if (type != XmlPullParser.START_TAG) {
+ Slog.e(LOG_TAG, "Unable to read restrictions file "
+ + restrictionsFile.getBaseFile());
+ return entries;
+ }
+
+ while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
+ if (type == XmlPullParser.START_TAG && parser.getName().equals(TAG_ENTRY)) {
+ String key = parser.getAttributeValue(null, ATTR_KEY);
+ String multiple = parser.getAttributeValue(null, ATTR_MULTIPLE);
+ if (multiple != null) {
+ int count = Integer.parseInt(multiple);
+ while (count > 0 && (type = parser.next()) != XmlPullParser.END_DOCUMENT) {
+ if (type == XmlPullParser.START_TAG
+ && parser.getName().equals(TAG_VALUE)) {
+ values.add(parser.nextText().trim());
+ count--;
+ }
+ }
+ String [] valueStrings = new String[values.size()];
+ values.toArray(valueStrings);
+ Slog.d(LOG_TAG, "Got RestrictionEntry " + key + "," + valueStrings);
+ RestrictionEntry entry = new RestrictionEntry(key, valueStrings);
+ entries.add(entry);
+ } else {
+ String value = parser.nextText().trim();
+ Slog.d(LOG_TAG, "Got RestrictionEntry " + key + "," + value);
+ RestrictionEntry entry = new RestrictionEntry(key, value);
+ entries.add(entry);
+ }
+ }
+ }
+
+ } catch (IOException ioe) {
+ } catch (XmlPullParserException pe) {
+ } finally {
+ if (fis != null) {
+ try {
+ fis.close();
+ } catch (IOException e) {
+ }
+ }
+ }
+ return entries;
+ }
+
+ private void writeApplicationRestrictionsLocked(String packageName,
+ List<RestrictionEntry> entries, int userId) {
+ FileOutputStream fos = null;
+ AtomicFile restrictionsFile = new AtomicFile(
+ new File(Environment.getUserSystemDirectory(userId),
+ RESTRICTIONS_FILE_PREFIX + packageName + ".xml"));
+ try {
+ fos = restrictionsFile.startWrite();
+ final BufferedOutputStream bos = new BufferedOutputStream(fos);
+
+ // XmlSerializer serializer = XmlUtils.serializerInstance();
+ final XmlSerializer serializer = new FastXmlSerializer();
+ serializer.setOutput(bos, "utf-8");
+ serializer.startDocument(null, true);
+ serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
+
+ serializer.startTag(null, TAG_RESTRICTIONS);
+
+ for (RestrictionEntry entry : entries) {
+ serializer.startTag(null, TAG_ENTRY);
+ serializer.attribute(null, ATTR_KEY, entry.key);
+ if (entry.getStringValue() != null || entry.getMultipleValues() == null) {
+ String value = entry.getStringValue();
+ serializer.text(value != null ? value : "");
+ } else {
+ String[] values = entry.getMultipleValues();
+ serializer.attribute(null, ATTR_MULTIPLE, Integer.toString(values.length));
+ for (String value : values) {
+ serializer.startTag(null, TAG_VALUE);
+ serializer.text(value != null ? value : "");
+ serializer.endTag(null, TAG_VALUE);
+ }
+ }
+ serializer.endTag(null, TAG_ENTRY);
+ }
+
+ serializer.endTag(null, TAG_RESTRICTIONS);
+
+ serializer.endDocument();
+ restrictionsFile.finishWrite(fos);
+ } catch (Exception e) {
+ restrictionsFile.failWrite(fos);
+ Slog.e(LOG_TAG, "Error writing application restrictions list");
+ }
+ }
+
+ @Override
public int getUserSerialNumber(int userHandle) {
synchronized (mPackagesLock) {
if (!exists(userHandle)) return -1;
diff --git a/services/java/com/android/server/wifi/WifiService.java b/services/java/com/android/server/wifi/WifiService.java
index c46f2174593e..9dde58f75975 100644
--- a/services/java/com/android/server/wifi/WifiService.java
+++ b/services/java/com/android/server/wifi/WifiService.java
@@ -396,6 +396,18 @@ public final class WifiService extends IWifiManager.Stub {
long ident = Binder.clearCallingIdentity();
try {
+
+ /* Turning off Wi-Fi when scans are still available */
+ if (!enable && isScanningAlwaysAvailable()) {
+ /* This can be changed by user in the app to not be notified again */
+ boolean notifyUser = (Settings.Global.getInt(mContext.getContentResolver(),
+ Settings.Global.WIFI_NOTIFY_SCAN_ALWAYS_AVAILABLE, 1) == 1);
+ if (notifyUser) {
+ Intent intent = new Intent(WifiManager.ACTION_NOTIFY_SCAN_ALWAYS_AVAILABLE);
+ mContext.startActivityAsUser(intent, null, UserHandle.CURRENT);
+ }
+ }
+
if (! mSettingsStore.handleWifiToggled(enable)) {
// Nothing to do if wifi cannot be toggled
return true;
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index f6543104ddd7..0c0a144ae234 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -408,6 +408,15 @@ public class WifiManager {
"android.net.wifi.action.REQUEST_SCAN_ALWAYS_AVAILABLE";
/**
+ * Activity Action: Show a system activity that notifies the user that
+ * scanning is still available when Wi-Fi is turned off
+ * @hide
+ */
+ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+ public static final String ACTION_NOTIFY_SCAN_ALWAYS_AVAILABLE =
+ "android.net.wifi.action.NOTIFY_SCAN_ALWAYS_AVAILABLE";
+
+ /**
* Activity Action: Pick a Wi-Fi network to connect to.
* <p>Input: Nothing.
* <p>Output: Nothing.