Merge "Add BIND_CONNECTION_SERVICE permission." into lmp-dev
diff --git a/CleanSpec.mk b/CleanSpec.mk
index ae2f3ad..c7cf940 100644
--- a/CleanSpec.mk
+++ b/CleanSpec.mk
@@ -218,6 +218,9 @@
$(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/services.core_intermediates)
$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/services_intermediates)
+$(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/services.core_intermediates)
+$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/services_intermediates)
# ******************************************************************
# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST ABOVE THIS BANNER
diff --git a/api/current.txt b/api/current.txt
index b3d4e60..5778c7e 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -464,7 +464,6 @@
field public static final int contentInsetLeft = 16843861; // 0x1010455
field public static final int contentInsetRight = 16843862; // 0x1010456
field public static final int contentInsetStart = 16843859; // 0x1010453
- field public static final int contentRatingSystemXml = 16843955; // 0x10104b3
field public static final int controlX1 = 16843798; // 0x1010416
field public static final int controlX2 = 16843800; // 0x1010418
field public static final int controlY1 = 16843799; // 0x1010417
@@ -1355,6 +1354,7 @@
field public static final int trimPathEnd = 16843811; // 0x1010423
field public static final int trimPathOffset = 16843812; // 0x1010424
field public static final int trimPathStart = 16843810; // 0x1010422
+ field public static final int tvContentRatingDescription = 16843955; // 0x10104b3
field public static final int type = 16843169; // 0x10101a1
field public static final int typeface = 16842902; // 0x1010096
field public static final int uiOptions = 16843672; // 0x1010398
@@ -4490,6 +4490,7 @@
public class KeyguardManager {
method public deprecated void exitKeyguardSecurely(android.app.KeyguardManager.OnKeyguardExitResult);
+ method public android.content.Intent getConfirmDeviceCredentialIntent(java.lang.CharSequence, java.lang.CharSequence);
method public boolean inKeyguardRestrictedInputMode();
method public boolean isKeyguardLocked();
method public boolean isKeyguardSecure();
@@ -7680,7 +7681,6 @@
field public static final java.lang.String ACTION_CHOOSER = "android.intent.action.CHOOSER";
field public static final java.lang.String ACTION_CLOSE_SYSTEM_DIALOGS = "android.intent.action.CLOSE_SYSTEM_DIALOGS";
field public static final java.lang.String ACTION_CONFIGURATION_CHANGED = "android.intent.action.CONFIGURATION_CHANGED";
- field public static final java.lang.String ACTION_CONFIRM_DEVICE_CREDENTIAL = "android.intent.action.CONFIRM_DEVICE_CREDENTIAL";
field public static final java.lang.String ACTION_CREATE_DOCUMENT = "android.intent.action.CREATE_DOCUMENT";
field public static final java.lang.String ACTION_CREATE_SHORTCUT = "android.intent.action.CREATE_SHORTCUT";
field public static final java.lang.String ACTION_DATE_CHANGED = "android.intent.action.DATE_CHANGED";
@@ -7824,7 +7824,6 @@
field public static final java.lang.String EXTRA_CHANGED_PACKAGE_LIST = "android.intent.extra.changed_package_list";
field public static final java.lang.String EXTRA_CHANGED_UID_LIST = "android.intent.extra.changed_uid_list";
field public static final java.lang.String EXTRA_DATA_REMOVED = "android.intent.extra.DATA_REMOVED";
- field public static final java.lang.String EXTRA_DETAILS = "android.intent.extra.DETAILS";
field public static final java.lang.String EXTRA_DOCK_STATE = "android.intent.extra.DOCK_STATE";
field public static final int EXTRA_DOCK_STATE_CAR = 2; // 0x2
field public static final int EXTRA_DOCK_STATE_DESK = 1; // 0x1
@@ -8527,6 +8526,15 @@
field public int reqTouchScreen;
}
+ public final class FeatureGroupInfo implements android.os.Parcelable {
+ ctor public FeatureGroupInfo();
+ ctor public FeatureGroupInfo(android.content.pm.FeatureGroupInfo);
+ method public int describeContents();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator CREATOR;
+ field public android.content.pm.FeatureInfo[] features;
+ }
+
public class FeatureInfo implements android.os.Parcelable {
ctor public FeatureInfo();
ctor public FeatureInfo(android.content.pm.FeatureInfo);
@@ -8541,36 +8549,6 @@
field public int reqGlEsVersion;
}
- public class InstallSessionInfo implements android.os.Parcelable {
- method public int describeContents();
- method public android.graphics.Bitmap getAppIcon();
- method public java.lang.CharSequence getAppLabel();
- method public java.lang.String getAppPackageName();
- method public android.content.Intent getDetailsIntent();
- method public java.lang.String getInstallerPackageName();
- method public float getProgress();
- method public int getSessionId();
- method public boolean isOpen();
- method public void writeToParcel(android.os.Parcel, int);
- field public static final android.os.Parcelable.Creator CREATOR;
- }
-
- public class InstallSessionParams implements android.os.Parcelable {
- ctor public InstallSessionParams(int);
- method public int describeContents();
- method public void setAppIcon(android.graphics.Bitmap);
- method public void setAppLabel(java.lang.CharSequence);
- method public void setAppPackageName(java.lang.String);
- method public void setInstallLocation(int);
- method public void setOriginatingUri(android.net.Uri);
- method public void setReferrerUri(android.net.Uri);
- method public void setSize(long);
- method public void writeToParcel(android.os.Parcel, int);
- field public static final android.os.Parcelable.Creator CREATOR;
- field public static final int MODE_FULL_INSTALL = 1; // 0x1
- field public static final int MODE_INHERIT_EXISTING = 2; // 0x2
- }
-
public class InstrumentationInfo extends android.content.pm.PackageItemInfo implements android.os.Parcelable {
ctor public InstrumentationInfo();
ctor public InstrumentationInfo(android.content.pm.InstrumentationInfo);
@@ -8648,6 +8626,7 @@
field public android.content.pm.ActivityInfo[] activities;
field public android.content.pm.ApplicationInfo applicationInfo;
field public android.content.pm.ConfigurationInfo[] configPreferences;
+ field public android.content.pm.FeatureGroupInfo[] featureGroups;
field public long firstInstallTime;
field public int[] gids;
field public int installLocation;
@@ -8672,37 +8651,35 @@
public class PackageInstaller {
method public void addSessionCallback(android.content.pm.PackageInstaller.SessionCallback);
method public void addSessionCallback(android.content.pm.PackageInstaller.SessionCallback, android.os.Handler);
- method public int createSession(android.content.pm.InstallSessionParams) throws java.io.IOException;
- method public java.util.List<android.content.pm.InstallSessionInfo> getAllSessions();
- method public java.util.List<android.content.pm.InstallSessionInfo> getMySessions();
- method public android.content.pm.InstallSessionInfo getSessionInfo(int);
+ method public int createSession(android.content.pm.PackageInstaller.SessionParams) throws java.io.IOException;
+ method public java.util.List<android.content.pm.PackageInstaller.SessionInfo> getAllSessions();
+ method public java.util.List<android.content.pm.PackageInstaller.SessionInfo> getMySessions();
+ method public android.content.pm.PackageInstaller.SessionInfo getSessionInfo(int);
method public android.content.pm.PackageInstaller.Session openSession(int);
method public void removeSessionCallback(android.content.pm.PackageInstaller.SessionCallback);
- method public void uninstall(java.lang.String, android.content.pm.PackageInstaller.UninstallCallback);
+ method public void uninstall(java.lang.String, android.content.IntentSender);
field public static final java.lang.String ACTION_SESSION_DETAILS = "android.content.pm.action.SESSION_DETAILS";
+ field public static final java.lang.String EXTRA_PACKAGE_NAMES = "android.content.pm.extra.PACKAGE_NAMES";
field public static final java.lang.String EXTRA_SESSION_ID = "android.content.pm.extra.SESSION_ID";
- }
-
- public static abstract class PackageInstaller.CommitCallback {
- ctor public PackageInstaller.CommitCallback();
- method public abstract void onFailure(int, java.lang.String, android.os.Bundle);
- method public abstract void onSuccess();
- method public abstract void onUserActionRequired(android.content.Intent);
- field public static final java.lang.String EXTRA_PACKAGE_NAME = "android.content.pm.extra.PACKAGE_NAME";
- field public static final int FAILURE_ABORTED = 5; // 0x5
- field public static final int FAILURE_CONFLICT = 2; // 0x2
- field public static final int FAILURE_INCOMPATIBLE = 4; // 0x4
- field public static final int FAILURE_INVALID = 1; // 0x1
- field public static final int FAILURE_STORAGE = 3; // 0x3
- field public static final int FAILURE_UNKNOWN = 0; // 0x0
+ field public static final java.lang.String EXTRA_STATUS = "android.content.pm.extra.STATUS";
+ field public static final java.lang.String EXTRA_STATUS_MESSAGE = "android.content.pm.extra.STATUS_MESSAGE";
+ field public static final int STATUS_FAILURE = 1; // 0x1
+ field public static final int STATUS_FAILURE_ABORTED = 2; // 0x2
+ field public static final int STATUS_FAILURE_BLOCKED = 1; // 0x1
+ field public static final int STATUS_FAILURE_CONFLICT = 4; // 0x4
+ field public static final int STATUS_FAILURE_INCOMPATIBLE = 6; // 0x6
+ field public static final int STATUS_FAILURE_INVALID = 3; // 0x3
+ field public static final int STATUS_FAILURE_STORAGE = 5; // 0x5
+ field public static final int STATUS_SUCCESS = 0; // 0x0
+ field public static final int STATUS_USER_ACTION_REQUIRED = -1; // 0xffffffff
}
public static class PackageInstaller.Session implements java.io.Closeable {
method public void abandon();
method public void close();
- method public void commit(android.content.pm.PackageInstaller.CommitCallback);
+ method public void commit(android.content.IntentSender);
method public void fsync(java.io.OutputStream) throws java.io.IOException;
- method public java.lang.String[] list();
+ method public java.lang.String[] getNames();
method public java.io.InputStream openRead(java.lang.String) throws java.io.IOException;
method public java.io.OutputStream openWrite(java.lang.String, long, long) throws java.io.IOException;
method public void setProgress(float);
@@ -8717,14 +8694,34 @@
method public abstract void onProgressChanged(int, float);
}
- public static abstract class PackageInstaller.UninstallCallback {
- ctor public PackageInstaller.UninstallCallback();
- method public abstract void onFailure(int, java.lang.String, android.os.Bundle);
- method public abstract void onSuccess();
- method public abstract void onUserActionRequired(android.content.Intent);
- field public static final int FAILURE_ABORTED = 2; // 0x2
- field public static final int FAILURE_BLOCKED = 1; // 0x1
- field public static final int FAILURE_UNKNOWN = 0; // 0x0
+ public static class PackageInstaller.SessionInfo implements android.os.Parcelable {
+ method public int describeContents();
+ method public android.graphics.Bitmap getAppIcon();
+ method public java.lang.CharSequence getAppLabel();
+ method public java.lang.String getAppPackageName();
+ method public android.content.Intent getDetailsIntent();
+ method public java.lang.String getInstallerPackageName();
+ method public float getProgress();
+ method public int getSessionId();
+ method public boolean isOpen();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator CREATOR;
+ }
+
+ public static class PackageInstaller.SessionParams implements android.os.Parcelable {
+ ctor public PackageInstaller.SessionParams(int);
+ method public int describeContents();
+ method public void setAppIcon(android.graphics.Bitmap);
+ method public void setAppLabel(java.lang.CharSequence);
+ method public void setAppPackageName(java.lang.String);
+ method public void setInstallLocation(int);
+ method public void setOriginatingUri(android.net.Uri);
+ method public void setReferrerUri(android.net.Uri);
+ method public void setSize(long);
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator CREATOR;
+ field public static final int MODE_FULL_INSTALL = 1; // 0x1
+ field public static final int MODE_INHERIT_EXISTING = 2; // 0x2
}
public class PackageItemInfo {
@@ -11655,7 +11652,7 @@
public class AnimatedStateListDrawable extends android.graphics.drawable.StateListDrawable {
ctor public AnimatedStateListDrawable();
method public void addState(int[], android.graphics.drawable.Drawable, int);
- method public void addTransition(int, int, android.graphics.drawable.Drawable, boolean);
+ method public void addTransition(int, int, T, boolean);
}
public class AnimatedVectorDrawable extends android.graphics.drawable.Drawable implements android.graphics.drawable.Animatable {
@@ -13586,7 +13583,7 @@
public class InputMethodService extends android.inputmethodservice.AbstractInputMethodService {
ctor public InputMethodService();
- method public boolean enableHardwareAcceleration();
+ method public deprecated boolean enableHardwareAcceleration();
method public int getBackDisposition();
method public int getCandidatesHiddenVisibility();
method public android.view.inputmethod.InputBinding getCurrentInputBinding();
@@ -16717,6 +16714,7 @@
method public void onPlaybackStateChanged(android.media.session.PlaybackState);
method public void onQueueChanged(java.util.List<android.media.session.MediaSession.Track>);
method public void onQueueTitleChanged(java.lang.CharSequence);
+ method public void onSessionDestroyed();
method public void onSessionEvent(java.lang.String, android.os.Bundle);
method public void onVolumeInfoChanged(android.media.session.MediaController.VolumeInfo);
}
@@ -23026,9 +23024,9 @@
method public boolean isUserRunning(android.os.UserHandle);
method public boolean isUserRunningOrStopping(android.os.UserHandle);
method public boolean setRestrictionsChallenge(java.lang.String);
- method public void setUserRestriction(java.lang.String, boolean);
- method public void setUserRestrictions(android.os.Bundle);
- method public void setUserRestrictions(android.os.Bundle, android.os.UserHandle);
+ method public deprecated void setUserRestriction(java.lang.String, boolean);
+ method public deprecated void setUserRestrictions(android.os.Bundle);
+ method public deprecated void setUserRestrictions(android.os.Bundle, android.os.UserHandle);
field public static final java.lang.String DISALLOW_ADD_USER = "no_add_user";
field public static final java.lang.String DISALLOW_ADJUST_VOLUME = "no_adjust_volume";
field public static final java.lang.String DISALLOW_APPS_CONTROL = "no_control_apps";
diff --git a/cmds/media/src/com/android/commands/media/Media.java b/cmds/media/src/com/android/commands/media/Media.java
index d9faf4c..b37f896 100644
--- a/cmds/media/src/com/android/commands/media/Media.java
+++ b/cmds/media/src/com/android/commands/media/Media.java
@@ -178,6 +178,13 @@
public ControllerMonitor(ISessionController controller) {
mController = controller;
}
+
+ @Override
+ public void onSessionDestroyed() {
+ System.out.println("onSessionDestroyed. Enter q to quit.");
+
+ }
+
@Override
public void onEvent(String event, Bundle extras) {
System.out.println("onSessionEvent event=" + event + ", extras=" + extras);
@@ -218,7 +225,7 @@
void printUsageMessage() {
try {
System.out.println("V2Monitoring session " + mController.getTag()
- + "... available commands:");
+ + "... available commands: play, pause, next, previous");
} catch (RemoteException e) {
System.out.println("Error trying to monitor session!");
}
@@ -250,6 +257,14 @@
addNewline = false;
} else if ("q".equals(line) || "quit".equals(line)) {
break;
+ } else if ("play".equals(line)) {
+ mController.play();
+ } else if ("pause".equals(line)) {
+ mController.pause();
+ } else if ("next".equals(line)) {
+ mController.next();
+ } else if ("previous".equals(line)) {
+ mController.previous();
} else {
System.out.println("Invalid command: " + line);
}
diff --git a/cmds/pm/src/com/android/commands/pm/Pm.java b/cmds/pm/src/com/android/commands/pm/Pm.java
index 15152e5..46d8ade 100644
--- a/cmds/pm/src/com/android/commands/pm/Pm.java
+++ b/cmds/pm/src/com/android/commands/pm/Pm.java
@@ -19,21 +19,22 @@
import android.app.ActivityManager;
import android.app.ActivityManagerNative;
import android.app.IActivityManager;
-import android.app.PackageDeleteObserver;
import android.app.PackageInstallObserver;
import android.content.ComponentName;
+import android.content.IIntentReceiver;
+import android.content.IIntentSender;
import android.content.Intent;
+import android.content.IntentSender;
import android.content.pm.ApplicationInfo;
import android.content.pm.FeatureInfo;
import android.content.pm.IPackageDataObserver;
import android.content.pm.IPackageInstaller;
import android.content.pm.IPackageManager;
-import android.content.pm.InstallSessionInfo;
-import android.content.pm.InstallSessionParams;
import android.content.pm.InstrumentationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageInstaller;
-import android.content.pm.PackageInstaller.CommitCallback;
+import android.content.pm.PackageInstaller.SessionInfo;
+import android.content.pm.PackageInstaller.SessionParams;
import android.content.pm.PackageItemInfo;
import android.content.pm.PackageManager;
import android.content.pm.ParceledListSlice;
@@ -74,6 +75,8 @@
import java.util.Comparator;
import java.util.List;
import java.util.WeakHashMap;
+import java.util.concurrent.SynchronousQueue;
+import java.util.concurrent.TimeUnit;
public final class Pm {
private static final String TAG = "Pm";
@@ -776,36 +779,6 @@
}
}
- class LocalCommitCallback extends CommitCallback {
- boolean finished;
- boolean success;
- String msg;
-
- private void setResult(boolean success, String msg) {
- synchronized (this) {
- this.finished = true;
- this.success = success;
- this.msg = msg;
- notifyAll();
- }
- }
-
- @Override
- public void onUserActionRequired(Intent intent) {
- setResult(false, "Unexepected user action required!");
- }
-
- @Override
- public void onSuccess() {
- setResult(true, null);
- }
-
- @Override
- public void onFailure(int failureReason, String msg, Bundle extras) {
- setResult(false, msg);
- }
- }
-
/**
* Converts a failure code into a string by using reflection to find a matching constant
* in PackageManager.
@@ -1007,8 +980,7 @@
private void runInstallCreate() throws RemoteException {
String installerPackageName = null;
- final InstallSessionParams params = new InstallSessionParams(
- InstallSessionParams.MODE_FULL_INSTALL);
+ final SessionParams params = new SessionParams(SessionParams.MODE_FULL_INSTALL);
params.installFlags = PackageManager.INSTALL_ALL_USERS;
String opt;
@@ -1031,7 +1003,7 @@
} else if (opt.equals("-d")) {
params.installFlags |= PackageManager.INSTALL_ALLOW_DOWNGRADE;
} else if (opt.equals("-p")) {
- params.mode = InstallSessionParams.MODE_INHERIT_EXISTING;
+ params.mode = SessionParams.MODE_INHERIT_EXISTING;
} else if (opt.equals("-S")) {
params.setSize(Long.parseLong(nextOptionData()));
} else if (opt.equals("--abi")) {
@@ -1073,7 +1045,7 @@
}
}
- final InstallSessionInfo info = mInstaller.getSessionInfo(sessionId);
+ final SessionInfo info = mInstaller.getSessionInfo(sessionId);
PackageInstaller.Session session = null;
InputStream in = null;
@@ -1117,22 +1089,20 @@
try {
session = new PackageInstaller.Session(mInstaller.openSession(sessionId));
- final LocalCommitCallback callback = new LocalCommitCallback();
- session.commit(callback);
+ final LocalIntentReceiver receiver = new LocalIntentReceiver();
+ session.commit(receiver.getIntentSender());
- synchronized (callback) {
- while (!callback.finished) {
- try {
- callback.wait();
- } catch (InterruptedException e) {
- }
- }
- if (!callback.success) {
- throw new IllegalStateException("Failure [" + callback.msg + "]");
- }
+ final Intent result = receiver.getResult();
+ final int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS,
+ PackageInstaller.STATUS_FAILURE);
+ if (status == PackageInstaller.STATUS_SUCCESS) {
+ System.out.println("Success");
+ } else {
+ Log.e(TAG, "Failure details: " + result.getExtras());
+ System.out.println("Failure ["
+ + result.getStringExtra(PackageInstaller.EXTRA_STATUS_MESSAGE) + "]");
+ return;
}
-
- System.out.println("Success");
} finally {
IoUtils.closeQuietly(session);
}
@@ -1274,28 +1244,14 @@
}
}
- class LocalPackageDeleteObserver extends PackageDeleteObserver {
- boolean finished;
- boolean result;
-
- @Override
- public void onPackageDeleted(String name, int returnCode, String msg) {
- synchronized (this) {
- finished = true;
- result = returnCode == PackageManager.DELETE_SUCCEEDED;
- notifyAll();
- }
- }
- }
-
- private void runUninstall() {
- int unInstallFlags = 0;
+ private void runUninstall() throws RemoteException {
+ int flags = 0;
int userId = UserHandle.USER_ALL;
String opt;
while ((opt=nextOption()) != null) {
if (opt.equals("-k")) {
- unInstallFlags |= PackageManager.DELETE_KEEP_DATA;
+ flags |= PackageManager.DELETE_KEEP_DATA;
} else if (opt.equals("--user")) {
String param = nextArg();
if (isNumber(param)) {
@@ -1320,7 +1276,7 @@
if (userId == UserHandle.USER_ALL) {
userId = UserHandle.USER_OWNER;
- unInstallFlags |= PackageManager.DELETE_ALL_USERS;
+ flags |= PackageManager.DELETE_ALL_USERS;
} else {
PackageInfo info;
try {
@@ -1340,38 +1296,25 @@
// user set flag so it disables rather than reverting to system
// version of the app.
if (isSystem) {
- unInstallFlags |= PackageManager.DELETE_SYSTEM_APP;
+ flags |= PackageManager.DELETE_SYSTEM_APP;
}
}
- boolean result = deletePackage(pkg, unInstallFlags, userId);
- if (result) {
+ final LocalIntentReceiver receiver = new LocalIntentReceiver();
+ mInstaller.uninstall(pkg, flags, receiver.getIntentSender(), userId);
+
+ final Intent result = receiver.getResult();
+ final int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS,
+ PackageInstaller.STATUS_FAILURE);
+ if (status == PackageInstaller.STATUS_SUCCESS) {
System.out.println("Success");
} else {
- System.out.println("Failure");
+ Log.e(TAG, "Failure details: " + result.getExtras());
+ System.out.println("Failure ["
+ + result.getStringExtra(PackageInstaller.EXTRA_STATUS_MESSAGE) + "]");
}
}
- private boolean deletePackage(String packageName, int flags, int userId) {
- LocalPackageDeleteObserver obs = new LocalPackageDeleteObserver();
- try {
- mInstaller.uninstall(packageName, flags, obs.getBinder(), userId);
-
- synchronized (obs) {
- while (!obs.finished) {
- try {
- obs.wait();
- } catch (InterruptedException e) {
- }
- }
- }
- } catch (RemoteException e) {
- System.err.println(e.toString());
- System.err.println(PM_NOT_RUNNING_ERR);
- }
- return obs.result;
- }
-
static class ClearDataObserver extends IPackageDataObserver.Stub {
boolean finished;
boolean result;
@@ -1384,7 +1327,6 @@
notifyAll();
}
}
-
}
private void runClear() {
@@ -1717,6 +1659,35 @@
throw new IllegalArgumentException("ABI " + abi + " not supported on this device");
}
+ private static class LocalIntentReceiver {
+ private final SynchronousQueue<Intent> mResult = new SynchronousQueue<>();
+
+ private IIntentSender.Stub mLocalSender = new IIntentSender.Stub() {
+ @Override
+ public int send(int code, Intent intent, String resolvedType,
+ IIntentReceiver finishedReceiver, String requiredPermission) {
+ try {
+ mResult.offer(intent, 5, TimeUnit.SECONDS);
+ } catch (InterruptedException e) {
+ throw new RuntimeException(e);
+ }
+ return 0;
+ }
+ };
+
+ public IntentSender getIntentSender() {
+ return new IntentSender((IIntentSender) mLocalSender);
+ }
+
+ public Intent getResult() {
+ try {
+ return mResult.poll(30, TimeUnit.SECONDS);
+ } catch (InterruptedException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ }
+
private String nextOption() {
if (mNextArg >= mArgs.length) {
return null;
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 9bbd544..21c8b75 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -6119,6 +6119,7 @@
*
* @hide
*/
+ @SystemApi
public interface TranslucentConversionListener {
/**
* Callback made following {@link Activity#convertToTranslucent} once all visible Activities
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 1b82d8e..d9ea671 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -1807,9 +1807,13 @@
}
}
- public void installSystemApplicationInfo(ApplicationInfo info) {
+ public void installSystemApplicationInfo(ApplicationInfo info, ClassLoader classLoader) {
synchronized (this) {
- getSystemContext().installSystemApplicationInfo(info);
+ getSystemContext().installSystemApplicationInfo(info, classLoader);
+
+ // The code package for "android" in the system server needs
+ // to be the system context's package.
+ mPackages.put("android", new WeakReference<LoadedApk>(getSystemContext().mPackageInfo));
// give ourselves a default profiler
mProfiler = new Profiler();
diff --git a/core/java/android/app/ActivityTransitionCoordinator.java b/core/java/android/app/ActivityTransitionCoordinator.java
index f7c0d23..7d2f677 100644
--- a/core/java/android/app/ActivityTransitionCoordinator.java
+++ b/core/java/android/app/ActivityTransitionCoordinator.java
@@ -239,7 +239,7 @@
return mWindow;
}
- protected ViewGroup getDecor() {
+ public ViewGroup getDecor() {
return (mWindow == null) ? null : (ViewGroup) mWindow.getDecorView();
}
diff --git a/core/java/android/app/ActivityTransitionState.java b/core/java/android/app/ActivityTransitionState.java
index 4f3b02c..613e248 100644
--- a/core/java/android/app/ActivityTransitionState.java
+++ b/core/java/android/app/ActivityTransitionState.java
@@ -17,9 +17,11 @@
import android.os.Bundle;
import android.os.ResultReceiver;
+import android.transition.Transition;
import android.util.ArrayMap;
import android.util.SparseArray;
import android.view.View;
+import android.view.ViewGroup;
import android.view.Window;
import java.lang.ref.WeakReference;
@@ -245,13 +247,23 @@
} else {
if (!mHasExited) {
mHasExited = true;
+ Transition enterViewsTransition = null;
+ ViewGroup decor = null;
if (mEnterTransitionCoordinator != null) {
+ enterViewsTransition = mEnterTransitionCoordinator.getEnterViewsTransition();
+ decor = mEnterTransitionCoordinator.getDecor();
mEnterTransitionCoordinator.cancelEnter();
mEnterTransitionCoordinator = null;
+ if (enterViewsTransition != null && decor != null) {
+ enterViewsTransition.pause(decor);
+ }
}
ExitTransitionCoordinator exitCoordinator =
new ExitTransitionCoordinator(activity, mEnteringNames, null, null, true);
+ if (enterViewsTransition != null && decor != null) {
+ enterViewsTransition.resume(decor);
+ }
exitCoordinator.startExit(activity.mResultCode, activity.mResultData);
}
return true;
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index a935dc0..b2812e3 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -1568,7 +1568,7 @@
synchronized (mLock) {
if (mInstaller == null) {
try {
- mInstaller = new PackageInstaller(this, mPM.getPackageInstaller(),
+ mInstaller = new PackageInstaller(mContext, this, mPM.getPackageInstaller(),
mContext.getPackageName(), mContext.getUserId());
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 4cf8cb4..da343ac 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -2301,8 +2301,8 @@
}
}
- void installSystemApplicationInfo(ApplicationInfo info) {
- mPackageInfo.installSystemApplicationInfo(info);
+ void installSystemApplicationInfo(ApplicationInfo info, ClassLoader classLoader) {
+ mPackageInfo.installSystemApplicationInfo(info, classLoader);
}
final void scheduleFinalCleanup(String who, String what) {
diff --git a/core/java/android/app/EnterTransitionCoordinator.java b/core/java/android/app/EnterTransitionCoordinator.java
index 54c5bc4..75ecbd9 100644
--- a/core/java/android/app/EnterTransitionCoordinator.java
+++ b/core/java/android/app/EnterTransitionCoordinator.java
@@ -59,6 +59,7 @@
private boolean mIsViewsTransitionComplete;
private boolean mIsSharedElementTransitionComplete;
private ArrayList<Matrix> mSharedElementParentMatrices;
+ private Transition mEnterViewsTransition;
public EnterTransitionCoordinator(Activity activity, ResultReceiver resultReceiver,
ArrayList<String> sharedElementNames, boolean isReturning) {
@@ -104,6 +105,10 @@
triggerViewsReady(mapNamedElements(accepted, localNames));
}
+ public Transition getEnterViewsTransition() {
+ return mEnterViewsTransition;
+ }
+
@Override
protected void viewsReady(ArrayMap<String, View> sharedElements) {
super.viewsReady(sharedElements);
@@ -399,10 +404,17 @@
viewTransitionComplete();
} else {
viewsTransition.forceVisibility(View.INVISIBLE, true);
- setTransitionAlpha(mTransitioningViews, 1);
viewsTransition.addListener(new ContinueTransitionListener() {
@Override
+ public void onTransitionStart(Transition transition) {
+ mEnterViewsTransition = transition;
+ setTransitionAlpha(mTransitioningViews, 1);
+ super.onTransitionStart(transition);
+ }
+
+ @Override
public void onTransitionEnd(Transition transition) {
+ mEnterViewsTransition = null;
transition.removeListener(this);
viewTransitionComplete();
super.onTransitionEnd(transition);
diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl
index 4b65934..e8f6818 100644
--- a/core/java/android/app/INotificationManager.aidl
+++ b/core/java/android/app/INotificationManager.aidl
@@ -66,7 +66,7 @@
boolean setZenModeConfig(in ZenModeConfig config);
oneway void notifyConditions(String pkg, in IConditionProvider provider, in Condition[] conditions);
oneway void requestZenModeConditions(in IConditionListener callback, int relevance);
- oneway void setZenModeCondition(in Uri conditionId);
+ oneway void setZenModeCondition(in Condition condition);
oneway void setAutomaticZenModeConditions(in Uri[] conditionIds);
Condition[] getAutomaticZenModeConditions();
}
\ No newline at end of file
diff --git a/core/java/android/app/KeyguardManager.java b/core/java/android/app/KeyguardManager.java
index db91742a..50e3a10 100644
--- a/core/java/android/app/KeyguardManager.java
+++ b/core/java/android/app/KeyguardManager.java
@@ -16,6 +16,7 @@
package android.app;
+import android.content.Intent;
import android.os.Binder;
import android.os.RemoteException;
import android.os.IBinder;
@@ -24,7 +25,7 @@
import android.view.WindowManagerGlobal;
/**
- * Class that can be used to lock and unlock the keyboard. Get an instance of this
+ * Class that can be used to lock and unlock the keyboard. Get an instance of this
* class by calling {@link android.content.Context#getSystemService(java.lang.String)}
* with argument {@link android.content.Context#KEYGUARD_SERVICE}. The
* actual class to control the keyboard locking is
@@ -34,6 +35,45 @@
private IWindowManager mWM;
/**
+ * Intent used to prompt user for device credentials.
+ * @hide
+ */
+ public static final String ACTION_CONFIRM_DEVICE_CREDENTIAL =
+ "android.app.action.CONFIRM_DEVICE_CREDENTIAL";
+
+ /**
+ * A CharSequence dialog title to show to the user when used with a
+ * {@link #ACTION_CONFIRM_DEVICE_CREDENTIAL}.
+ * @hide
+ */
+ public static final String EXTRA_TITLE = "android.app.extra.TITLE";
+
+ /**
+ * A CharSequence description to show to the user when used with
+ * {@link #ACTION_CONFIRM_DEVICE_CREDENTIAL}.
+ * @hide
+ */
+ public static final String EXTRA_DESCRIPTION = "android.app.extra.DESCRIPTION";
+
+ /**
+ * Get an intent to prompt the user to confirm credentials (pin, pattern or password)
+ * for the current user of the device. The caller is expected to launch this activity using
+ * {@link android.app.Activity#startActivityForResult(Intent, int)} and check for
+ * {@link android.app.Activity#RESULT_OK} if the user successfully completes the challenge.
+ *
+ * @return the intent for launching the activity or null if no password is required.
+ **/
+ public Intent getConfirmDeviceCredentialIntent(CharSequence title, CharSequence description) {
+ if (!isKeyguardSecure()) return null;
+ Intent intent = new Intent(ACTION_CONFIRM_DEVICE_CREDENTIAL);
+ intent.putExtra(EXTRA_TITLE, title);
+ intent.putExtra(EXTRA_DESCRIPTION, description);
+ // For security reasons, only allow this to come from system settings.
+ intent.setPackage("com.android.settings");
+ return intent;
+ }
+
+ /**
* @deprecated Use {@link android.view.WindowManager.LayoutParams#FLAG_DISMISS_KEYGUARD}
* and/or {@link android.view.WindowManager.LayoutParams#FLAG_SHOW_WHEN_LOCKED}
* instead; this allows you to seamlessly hide the keyguard as your application
@@ -58,7 +98,7 @@
*
* A good place to call this is from {@link android.app.Activity#onResume()}
*
- * Note: This call has no effect while any {@link android.app.admin.DevicePolicyManager}
+ * Note: This call has no effect while any {@link android.app.admin.DevicePolicyManager}
* is enabled that requires a password.
*
* <p>This method requires the caller to hold the permission
@@ -121,7 +161,7 @@
* permissions be requested.
*
* Enables you to lock or unlock the keyboard. Get an instance of this class by
- * calling {@link android.content.Context#getSystemService(java.lang.String) Context.getSystemService()}.
+ * calling {@link android.content.Context#getSystemService(java.lang.String) Context.getSystemService()}.
* This class is wrapped by {@link android.app.KeyguardManager KeyguardManager}.
* @param tag A tag that informally identifies who you are (for debugging who
* is disabling he keyguard).
diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java
index 24c2835..e0c7816 100644
--- a/core/java/android/app/LoadedApk.java
+++ b/core/java/android/app/LoadedApk.java
@@ -40,6 +40,7 @@
import android.os.Trace;
import android.os.UserHandle;
import android.util.AndroidRuntimeException;
+import android.util.Log;
import android.util.Slog;
import android.util.SparseArray;
import android.view.DisplayAdjustments;
@@ -199,9 +200,10 @@
/**
* Sets application info about the system package.
*/
- void installSystemApplicationInfo(ApplicationInfo info) {
+ void installSystemApplicationInfo(ApplicationInfo info, ClassLoader classLoader) {
assert info.packageName.equals("android");
mApplicationInfo = info;
+ mClassLoader = classLoader;
}
public String getPackageName() {
@@ -262,10 +264,6 @@
if (!Objects.equals(mPackageName, ActivityThread.currentPackageName())) {
final String isa = VMRuntime.getRuntime().vmInstructionSet();
try {
- // TODO: We can probably do away with the isa argument since
- // the AM and PM have enough information to figure this out
- // themselves. If we do need it, we should match it against the
- // list of devices ISAs before sending it down to installd.
ActivityThread.getPackageManager().performDexOptIfNeeded(mPackageName, isa);
} catch (RemoteException re) {
// Ignored.
@@ -646,8 +644,17 @@
}
private void rewriteRValues(ClassLoader cl, String packageName, int id) {
+ final Class<?> rClazz;
try {
- final Class<?> rClazz = cl.loadClass(packageName + ".R");
+ rClazz = cl.loadClass(packageName + ".R");
+ } catch (ClassNotFoundException e) {
+ // This is not necessarily an error, as some packages do not ship with resources
+ // (or they do not need rewriting).
+ Log.i(TAG, "Could not find R class for package '" + packageName + "'");
+ return;
+ }
+
+ try {
Class<?>[] declaredClasses = rClazz.getDeclaredClasses();
for (Class<?> clazz : declaredClasses) {
try {
diff --git a/core/java/android/app/backup/BackupManager.java b/core/java/android/app/backup/BackupManager.java
index e9297b9..1bb4eba 100644
--- a/core/java/android/app/backup/BackupManager.java
+++ b/core/java/android/app/backup/BackupManager.java
@@ -229,6 +229,28 @@
}
/**
+ * Enable/disable data restore at application install time. When enabled, app
+ * installation will include an attempt to fetch the app's historical data from
+ * the archival restore dataset (if any). When disabled, no such attempt will
+ * be made.
+ *
+ * <p>Callers must hold the android.permission.BACKUP permission to use this method.
+ *
+ * @hide
+ */
+ @SystemApi
+ public void setAutoRestore(boolean isEnabled) {
+ checkServiceBinder();
+ if (sService != null) {
+ try {
+ sService.setAutoRestore(isEnabled);
+ } catch (RemoteException e) {
+ Log.e(TAG, "setAutoRestore() couldn't connect");
+ }
+ }
+ }
+
+ /**
* Identify the currently selected transport. Callers must hold the
* android.permission.BACKUP permission to use this method.
* @return The name of the currently active backup transport. In case of
diff --git a/core/java/android/appwidget/AppWidgetProviderInfo.java b/core/java/android/appwidget/AppWidgetProviderInfo.java
index a767246..0b40e35 100644
--- a/core/java/android/appwidget/AppWidgetProviderInfo.java
+++ b/core/java/android/appwidget/AppWidgetProviderInfo.java
@@ -271,13 +271,13 @@
* @return The provider icon.
*/
public final Drawable loadIcon(@NonNull Context context, int density) {
- return loadDrawable(context, density, providerInfo.getIconResource());
+ return loadDrawable(context, density, providerInfo.getIconResource(), true);
}
/**
* Loads a preview of what the AppWidget will look like after it's configured.
- * If not supplied, the AppWidget's icon will be used. A client can optionally
- * provide a desired deinsity such as {@link android.util.DisplayMetrics#DENSITY_LOW}
+ * A client can optionally provide a desired density such as
+ * {@link android.util.DisplayMetrics#DENSITY_LOW}
* {@link android.util.DisplayMetrics#DENSITY_MEDIUM}, etc. If no density is
* provided, the density of the current display will be used.
* <p>
@@ -288,10 +288,10 @@
* @param context Context for accessing resources.
* @param density The optional desired density as per
* {@link android.util.DisplayMetrics#densityDpi}.
- * @return The widget preview image.
+ * @return The widget preview image or {@null} if preview image is not available.
*/
public final Drawable loadPreviewImage(@NonNull Context context, int density) {
- return loadDrawable(context, density, previewImage);
+ return loadDrawable(context, density, previewImage, false);
}
/**
@@ -361,7 +361,8 @@
return 0;
}
- private Drawable loadDrawable(Context context, int density, int resourceId) {
+ private Drawable loadDrawable(Context context, int density, int resourceId,
+ boolean loadDefaultIcon) {
try {
Resources resources = context.getPackageManager().getResourcesForApplication(
providerInfo.applicationInfo);
@@ -374,7 +375,7 @@
} catch (PackageManager.NameNotFoundException | Resources.NotFoundException e) {
/* ignore */
}
- return providerInfo.loadIcon(context.getPackageManager());
+ return loadDefaultIcon ? providerInfo.loadIcon(context.getPackageManager()) : null;
}
/**
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index abe4f0a..61e105b 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -1419,21 +1419,6 @@
public static final String ACTION_UNINSTALL_PACKAGE = "android.intent.action.UNINSTALL_PACKAGE";
/**
- * Activity Action: Prompt the user to confirm credentials (pin, pattern or password)
- * for the current user of the device. Launch this activity using
- * {@link android.app.Activity#startActivityForResult(Intent, int)} and check if the
- * result is {@link android.app.Activity#RESULT_OK} for a successful response to the
- * challenge.<p/>
- * This intent is handled by the system at a high priority and applications cannot intercept
- * it.<p/>
- * You can use {@link android.app.KeyguardManager#isKeyguardSecure()} to determine if the user will be
- * prompted.
- */
- @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
- public static final String ACTION_CONFIRM_DEVICE_CREDENTIAL = "android.intent.action.CONFIRM_DEVICE_CREDENTIAL";
-
-
- /**
* Specify whether the package should be uninstalled for all users.
* @hide because these should not be part of normal application flow.
*/
@@ -3192,17 +3177,11 @@
/**
* A CharSequence dialog title to provide to the user when used with a
- * {@link #ACTION_CHOOSER} or {@link #ACTION_CONFIRM_DEVICE_CREDENTIAL}.
+ * {@link #ACTION_CHOOSER}.
*/
public static final String EXTRA_TITLE = "android.intent.extra.TITLE";
/**
- * A CharSequence description to provide to the user when used with
- * {@link #ACTION_CONFIRM_DEVICE_CREDENTIAL}.
- */
- public static final String EXTRA_DETAILS = "android.intent.extra.DETAILS";
-
- /**
* A Parcelable[] of {@link Intent} or
* {@link android.content.pm.LabeledIntent} objects as set with
* {@link #putExtra(String, Parcelable[])} of additional activities to place
diff --git a/core/java/android/content/pm/FeatureGroupInfo.java b/core/java/android/content/pm/FeatureGroupInfo.java
new file mode 100644
index 0000000..79a6eea
--- /dev/null
+++ b/core/java/android/content/pm/FeatureGroupInfo.java
@@ -0,0 +1,65 @@
+/**
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy
+ * of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+package android.content.pm;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * A set of features that can be requested by an application. This corresponds
+ * to information collected from the
+ * AndroidManifest.xml's {@code <feature-group>} tag.
+ */
+public final class FeatureGroupInfo implements Parcelable {
+
+ /**
+ * The list of features that are required by this group.
+ *
+ * @see FeatureInfo#FLAG_REQUIRED
+ */
+ public FeatureInfo[] features;
+
+ public FeatureGroupInfo() {
+ }
+
+ public FeatureGroupInfo(FeatureGroupInfo other) {
+ features = other.features;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeTypedArray(features, flags);
+ }
+
+ public static final Creator<FeatureGroupInfo> CREATOR = new Creator<FeatureGroupInfo>() {
+ @Override
+ public FeatureGroupInfo createFromParcel(Parcel source) {
+ FeatureGroupInfo group = new FeatureGroupInfo();
+ group.features = source.createTypedArray(FeatureInfo.CREATOR);
+ return group;
+ }
+
+ @Override
+ public FeatureGroupInfo[] newArray(int size) {
+ return new FeatureGroupInfo[size];
+ }
+ };
+}
diff --git a/core/java/android/content/pm/FeatureInfo.java b/core/java/android/content/pm/FeatureInfo.java
index d919fc3..79fa327 100644
--- a/core/java/android/content/pm/FeatureInfo.java
+++ b/core/java/android/content/pm/FeatureInfo.java
@@ -22,7 +22,7 @@
/**
* A single feature that can be requested by an application. This corresponds
* to information collected from the
- * AndroidManifest.xml's <uses-feature> tag.
+ * AndroidManifest.xml's {@code <uses-feature>} tag.
*/
public class FeatureInfo implements Parcelable {
/**
diff --git a/core/java/android/content/pm/IPackageInstaller.aidl b/core/java/android/content/pm/IPackageInstaller.aidl
index 5223476..97be8f0 100644
--- a/core/java/android/content/pm/IPackageInstaller.aidl
+++ b/core/java/android/content/pm/IPackageInstaller.aidl
@@ -19,23 +19,22 @@
import android.content.pm.IPackageDeleteObserver2;
import android.content.pm.IPackageInstallerCallback;
import android.content.pm.IPackageInstallerSession;
-import android.content.pm.InstallSessionInfo;
-import android.content.pm.InstallSessionParams;
+import android.content.pm.PackageInstaller;
+import android.content.IntentSender;
/** {@hide} */
interface IPackageInstaller {
- int createSession(in InstallSessionParams params, String installerPackageName, int userId);
+ int createSession(in PackageInstaller.SessionParams params, String installerPackageName, int userId);
IPackageInstallerSession openSession(int sessionId);
- InstallSessionInfo getSessionInfo(int sessionId);
- List<InstallSessionInfo> getAllSessions(int userId);
- List<InstallSessionInfo> getMySessions(String installerPackageName, int userId);
+ PackageInstaller.SessionInfo getSessionInfo(int sessionId);
+ List<PackageInstaller.SessionInfo> getAllSessions(int userId);
+ List<PackageInstaller.SessionInfo> getMySessions(String installerPackageName, int userId);
void registerCallback(IPackageInstallerCallback callback, int userId);
void unregisterCallback(IPackageInstallerCallback callback);
- void uninstall(String packageName, int flags, in IPackageDeleteObserver2 observer, int userId);
- void uninstallSplit(String packageName, String splitName, int flags, in IPackageDeleteObserver2 observer, int userId);
+ void uninstall(String packageName, int flags, in IntentSender statusReceiver, int userId);
void setPermissionsResult(int sessionId, boolean accepted);
}
diff --git a/core/java/android/content/pm/IPackageInstallerSession.aidl b/core/java/android/content/pm/IPackageInstallerSession.aidl
index af0323f..aee3ba7 100644
--- a/core/java/android/content/pm/IPackageInstallerSession.aidl
+++ b/core/java/android/content/pm/IPackageInstallerSession.aidl
@@ -17,6 +17,7 @@
package android.content.pm;
import android.content.pm.IPackageInstallObserver2;
+import android.content.IntentSender;
import android.os.ParcelFileDescriptor;
/** {@hide} */
@@ -24,11 +25,11 @@
void setClientProgress(float progress);
void addClientProgress(float progress);
- String[] list();
+ String[] getNames();
ParcelFileDescriptor openWrite(String name, long offsetBytes, long lengthBytes);
ParcelFileDescriptor openRead(String name);
void close();
- void commit(in IPackageInstallObserver2 observer);
+ void commit(in IntentSender statusReceiver);
void abandon();
}
diff --git a/core/java/android/content/pm/InstallSessionInfo.aidl b/core/java/android/content/pm/InstallSessionInfo.aidl
deleted file mode 100644
index 3d21bbd..0000000
--- a/core/java/android/content/pm/InstallSessionInfo.aidl
+++ /dev/null
@@ -1,19 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.content.pm;
-
-parcelable InstallSessionInfo;
diff --git a/core/java/android/content/pm/InstallSessionInfo.java b/core/java/android/content/pm/InstallSessionInfo.java
deleted file mode 100644
index 161bcde..0000000
--- a/core/java/android/content/pm/InstallSessionInfo.java
+++ /dev/null
@@ -1,181 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.content.pm;
-
-import android.annotation.Nullable;
-import android.content.Intent;
-import android.graphics.Bitmap;
-import android.os.Parcel;
-import android.os.Parcelable;
-
-/**
- * Details for an active install session.
- */
-public class InstallSessionInfo implements Parcelable {
-
- /** {@hide} */
- public int sessionId;
- /** {@hide} */
- public String installerPackageName;
- /** {@hide} */
- public String resolvedBaseCodePath;
- /** {@hide} */
- public float progress;
- /** {@hide} */
- public boolean sealed;
- /** {@hide} */
- public boolean open;
-
- /** {@hide} */
- public int mode;
- /** {@hide} */
- public long sizeBytes;
- /** {@hide} */
- public String appPackageName;
- /** {@hide} */
- public Bitmap appIcon;
- /** {@hide} */
- public CharSequence appLabel;
-
- /** {@hide} */
- public InstallSessionInfo() {
- }
-
- /** {@hide} */
- public InstallSessionInfo(Parcel source) {
- sessionId = source.readInt();
- installerPackageName = source.readString();
- resolvedBaseCodePath = source.readString();
- progress = source.readFloat();
- sealed = source.readInt() != 0;
- open = source.readInt() != 0;
-
- mode = source.readInt();
- sizeBytes = source.readLong();
- appPackageName = source.readString();
- appIcon = source.readParcelable(null);
- appLabel = source.readString();
- }
-
- /**
- * Return the ID for this session.
- */
- public int getSessionId() {
- return sessionId;
- }
-
- /**
- * Return the package name of the app that owns this session.
- */
- public @Nullable String getInstallerPackageName() {
- return installerPackageName;
- }
-
- /**
- * Return current overall progress of this session, between 0 and 1.
- * <p>
- * Note that this progress may not directly correspond to the value reported
- * by {@link PackageInstaller.Session#setProgress(float)}, as the system may
- * carve out a portion of the overall progress to represent its own internal
- * installation work.
- */
- public float getProgress() {
- return progress;
- }
-
- /**
- * Return if this session is currently open.
- */
- public boolean isOpen() {
- return open;
- }
-
- /**
- * Return the package name this session is working with. May be {@code null}
- * if unknown.
- */
- public @Nullable String getAppPackageName() {
- return appPackageName;
- }
-
- /**
- * Return an icon representing the app being installed. May be {@code null}
- * if unavailable.
- */
- public @Nullable Bitmap getAppIcon() {
- return appIcon;
- }
-
- /**
- * Return a label representing the app being installed. May be {@code null}
- * if unavailable.
- */
- public @Nullable CharSequence getAppLabel() {
- return appLabel;
- }
-
- /**
- * Return an Intent that can be started to view details about this install
- * session. This may surface actions such as pause, resume, or cancel.
- * <p>
- * In some cases, a matching Activity may not exist, so ensure you safeguard
- * against this.
- *
- * @see PackageInstaller#ACTION_SESSION_DETAILS
- */
- public @Nullable Intent getDetailsIntent() {
- final Intent intent = new Intent(PackageInstaller.ACTION_SESSION_DETAILS);
- intent.putExtra(PackageInstaller.EXTRA_SESSION_ID, sessionId);
- intent.setPackage(installerPackageName);
- intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- return intent;
- }
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void writeToParcel(Parcel dest, int flags) {
- dest.writeInt(sessionId);
- dest.writeString(installerPackageName);
- dest.writeString(resolvedBaseCodePath);
- dest.writeFloat(progress);
- dest.writeInt(sealed ? 1 : 0);
- dest.writeInt(open ? 1 : 0);
-
- dest.writeInt(mode);
- dest.writeLong(sizeBytes);
- dest.writeString(appPackageName);
- dest.writeParcelable(appIcon, flags);
- dest.writeString(appLabel != null ? appLabel.toString() : null);
- }
-
- public static final Parcelable.Creator<InstallSessionInfo>
- CREATOR = new Parcelable.Creator<InstallSessionInfo>() {
- @Override
- public InstallSessionInfo createFromParcel(Parcel p) {
- return new InstallSessionInfo(p);
- }
-
- @Override
- public InstallSessionInfo[] newArray(int size) {
- return new InstallSessionInfo[size];
- }
- };
-}
diff --git a/core/java/android/content/pm/InstallSessionParams.aidl b/core/java/android/content/pm/InstallSessionParams.aidl
deleted file mode 100644
index 81b7574..0000000
--- a/core/java/android/content/pm/InstallSessionParams.aidl
+++ /dev/null
@@ -1,19 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.content.pm;
-
-parcelable InstallSessionParams;
diff --git a/core/java/android/content/pm/InstallSessionParams.java b/core/java/android/content/pm/InstallSessionParams.java
deleted file mode 100644
index 1716e39..0000000
--- a/core/java/android/content/pm/InstallSessionParams.java
+++ /dev/null
@@ -1,227 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.content.pm;
-
-import android.annotation.Nullable;
-import android.app.ActivityManager;
-import android.content.Intent;
-import android.graphics.Bitmap;
-import android.net.Uri;
-import android.os.Parcel;
-import android.os.Parcelable;
-
-import com.android.internal.util.IndentingPrintWriter;
-
-/**
- * Parameters for creating a new {@link PackageInstaller.Session}.
- */
-public class InstallSessionParams implements Parcelable {
-
- /** {@hide} */
- public static final int MODE_INVALID = -1;
-
- /**
- * Mode for an install session whose staged APKs should fully replace any
- * existing APKs for the target app.
- */
- public static final int MODE_FULL_INSTALL = 1;
-
- /**
- * Mode for an install session that should inherit any existing APKs for the
- * target app, unless they have been explicitly overridden (based on split
- * name) by the session. For example, this can be used to add one or more
- * split APKs to an existing installation.
- * <p>
- * If there are no existing APKs for the target app, this behaves like
- * {@link #MODE_FULL_INSTALL}.
- */
- public static final int MODE_INHERIT_EXISTING = 2;
-
- /** {@hide} */
- public int mode = MODE_INVALID;
- /** {@hide} */
- public int installFlags;
- /** {@hide} */
- public int installLocation = PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY;
- /** {@hide} */
- public long sizeBytes = -1;
- /** {@hide} */
- public String appPackageName;
- /** {@hide} */
- public Bitmap appIcon;
- /** {@hide} */
- public String appLabel;
- /** {@hide} */
- public Uri originatingUri;
- /** {@hide} */
- public Uri referrerUri;
- /** {@hide} */
- public String abiOverride;
-
- /**
- * Construct parameters for a new package install session.
- *
- * @param mode one of {@link #MODE_FULL_INSTALL} or
- * {@link #MODE_INHERIT_EXISTING} describing how the session
- * should interact with an existing app.
- */
- public InstallSessionParams(int mode) {
- this.mode = mode;
- }
-
- /** {@hide} */
- public InstallSessionParams(Parcel source) {
- mode = source.readInt();
- installFlags = source.readInt();
- installLocation = source.readInt();
- sizeBytes = source.readLong();
- appPackageName = source.readString();
- appIcon = source.readParcelable(null);
- appLabel = source.readString();
- originatingUri = source.readParcelable(null);
- referrerUri = source.readParcelable(null);
- abiOverride = source.readString();
- }
-
- /**
- * Provide value of {@link PackageInfo#installLocation}, which may be used
- * to determine where the app will be staged. Defaults to
- * {@link PackageInfo#INSTALL_LOCATION_INTERNAL_ONLY}.
- */
- public void setInstallLocation(int installLocation) {
- this.installLocation = installLocation;
- }
-
- /**
- * @deprecated use {@link PackageInstaller.Session#openRead(String)} to
- * calculate message digest instead.
- * @hide
- */
- @Deprecated
- public void setSignatures(@Nullable Signature[] signatures) {
- throw new UnsupportedOperationException();
- }
-
- /**
- * Optionally indicate the total size (in bytes) of all APKs that will be
- * delivered in this session. The system may use this to ensure enough disk
- * space exists before proceeding, or to estimate container size for
- * installations living on external storage.
- *
- * @see PackageInfo#INSTALL_LOCATION_AUTO
- * @see PackageInfo#INSTALL_LOCATION_PREFER_EXTERNAL
- */
- public void setSize(long sizeBytes) {
- this.sizeBytes = sizeBytes;
- }
-
- /**
- * Optionally set the package name of the app being installed. It's strongly
- * recommended that you provide this value when known, so that observers can
- * communicate installing apps to users.
- * <p>
- * If the APKs staged in the session aren't consistent with this package
- * name, the install will fail. Regardless of this value, all APKs in the
- * app must have the same package name.
- */
- public void setAppPackageName(@Nullable String appPackageName) {
- this.appPackageName = appPackageName;
- }
-
- /**
- * Optionally set an icon representing the app being installed. This should
- * be roughly {@link ActivityManager#getLauncherLargeIconSize()} in both
- * dimensions.
- */
- public void setAppIcon(@Nullable Bitmap appIcon) {
- this.appIcon = appIcon;
- }
-
- /**
- * Optionally set a label representing the app being installed.
- */
- public void setAppLabel(@Nullable CharSequence appLabel) {
- this.appLabel = (appLabel != null) ? appLabel.toString() : null;
- }
-
- /**
- * Optionally set the URI where this package was downloaded from. Used for
- * verification purposes.
- *
- * @see Intent#EXTRA_ORIGINATING_URI
- */
- public void setOriginatingUri(@Nullable Uri originatingUri) {
- this.originatingUri = originatingUri;
- }
-
- /**
- * Optionally set the URI that referred you to install this package. Used
- * for verification purposes.
- *
- * @see Intent#EXTRA_REFERRER
- */
- public void setReferrerUri(@Nullable Uri referrerUri) {
- this.referrerUri = referrerUri;
- }
-
- /** {@hide} */
- public void dump(IndentingPrintWriter pw) {
- pw.printPair("mode", mode);
- pw.printHexPair("installFlags", installFlags);
- pw.printPair("installLocation", installLocation);
- pw.printPair("sizeBytes", sizeBytes);
- pw.printPair("appPackageName", appPackageName);
- pw.printPair("appIcon", (appIcon != null));
- pw.printPair("appLabel", appLabel);
- pw.printPair("originatingUri", originatingUri);
- pw.printPair("referrerUri", referrerUri);
- pw.printPair("abiOverride", abiOverride);
- pw.println();
- }
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void writeToParcel(Parcel dest, int flags) {
- dest.writeInt(mode);
- dest.writeInt(installFlags);
- dest.writeInt(installLocation);
- dest.writeLong(sizeBytes);
- dest.writeString(appPackageName);
- dest.writeParcelable(appIcon, flags);
- dest.writeString(appLabel);
- dest.writeParcelable(originatingUri, flags);
- dest.writeParcelable(referrerUri, flags);
- dest.writeString(abiOverride);
- }
-
- public static final Parcelable.Creator<InstallSessionParams>
- CREATOR = new Parcelable.Creator<InstallSessionParams>() {
- @Override
- public InstallSessionParams createFromParcel(Parcel p) {
- return new InstallSessionParams(p);
- }
-
- @Override
- public InstallSessionParams[] newArray(int size) {
- return new InstallSessionParams[size];
- }
- };
-}
diff --git a/core/java/android/content/pm/PackageInfo.java b/core/java/android/content/pm/PackageInfo.java
index 49ffef2..a0e3c4a 100644
--- a/core/java/android/content/pm/PackageInfo.java
+++ b/core/java/android/content/pm/PackageInfo.java
@@ -180,7 +180,7 @@
* {@link android.R.styleable#AndroidManifestUsesConfiguration
* <uses-configuration>} tags included under <manifest>,
* or null if there were none. This is only filled in if the flag
- * {@link PackageManager#GET_CONFIGURATIONS} was set.
+ * {@link PackageManager#GET_CONFIGURATIONS} was set.
*/
public ConfigurationInfo[] configPreferences;
@@ -192,6 +192,16 @@
public FeatureInfo[] reqFeatures;
/**
+ * Groups of features that this application has requested.
+ * Each group contains a set of features that are required.
+ * A device must match the features listed in {@link #reqFeatures} and one
+ * or more FeatureGroups in order to have satisfied the feature requirement.
+ *
+ * @see FeatureInfo#FLAG_REQUIRED
+ */
+ public FeatureGroupInfo[] featureGroups;
+
+ /**
* Constant corresponding to <code>auto</code> in
* the {@link android.R.attr#installLocation} attribute.
* @hide
@@ -300,6 +310,7 @@
dest.writeTypedArray(signatures, parcelableFlags);
dest.writeTypedArray(configPreferences, parcelableFlags);
dest.writeTypedArray(reqFeatures, parcelableFlags);
+ dest.writeTypedArray(featureGroups, parcelableFlags);
dest.writeInt(installLocation);
dest.writeInt(requiredForAllUsers ? 1 : 0);
dest.writeInt(requiredForProfile);
@@ -344,6 +355,7 @@
signatures = source.createTypedArray(Signature.CREATOR);
configPreferences = source.createTypedArray(ConfigurationInfo.CREATOR);
reqFeatures = source.createTypedArray(FeatureInfo.CREATOR);
+ featureGroups = source.createTypedArray(FeatureGroupInfo.CREATOR);
installLocation = source.readInt();
requiredForAllUsers = source.readInt() != 0;
requiredForProfile = source.readInt();
diff --git a/core/java/android/content/pm/PackageInstallerParams.aidl b/core/java/android/content/pm/PackageInstaller.aidl
similarity index 88%
rename from core/java/android/content/pm/PackageInstallerParams.aidl
rename to core/java/android/content/pm/PackageInstaller.aidl
index b3dde21..270f870 100644
--- a/core/java/android/content/pm/PackageInstallerParams.aidl
+++ b/core/java/android/content/pm/PackageInstaller.aidl
@@ -16,4 +16,5 @@
package android.content.pm;
-parcelable PackageInstallerParams;
+parcelable PackageInstaller.SessionParams;
+parcelable PackageInstaller.SessionInfo;
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index d70e22c..aa4ea45 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -20,17 +20,24 @@
import android.annotation.Nullable;
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
-import android.app.PackageDeleteObserver;
-import android.app.PackageInstallObserver;
+import android.app.ActivityManager;
+import android.content.Context;
import android.content.Intent;
-import android.os.Bundle;
+import android.content.IntentSender;
+import android.graphics.Bitmap;
+import android.net.Uri;
import android.os.FileBridge;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
+import android.os.Parcel;
import android.os.ParcelFileDescriptor;
+import android.os.Parcelable;
import android.os.RemoteException;
import android.util.ExceptionUtils;
+import android.util.Log;
+
+import com.android.internal.util.IndentingPrintWriter;
import java.io.Closeable;
import java.io.IOException;
@@ -67,13 +74,15 @@
* </ul>
*/
public class PackageInstaller {
+ private static final String TAG = "PackageInstaller";
+
/**
* Activity Action: Show details about a particular install session. This
* may surface actions such as pause, resume, or cancel.
* <p>
* This should always be scoped to the installer package that owns the
- * session. Clients should use {@link InstallSessionInfo#getDetailsIntent()}
- * to build this intent correctly.
+ * session. Clients should use {@link SessionInfo#getDetailsIntent()} to
+ * build this intent correctly.
* <p>
* In some cases, a matching Activity may not exist, so ensure you safeguard
* against this.
@@ -92,9 +101,110 @@
*/
public static final String EXTRA_SESSION_ID = "android.content.pm.extra.SESSION_ID";
+ public static final String EXTRA_STATUS = "android.content.pm.extra.STATUS";
+ public static final String EXTRA_STATUS_MESSAGE = "android.content.pm.extra.STATUS_MESSAGE";
+
+ /**
+ * List of package names that are relevant to a status.
+ *
+ * @see Intent#getStringArrayExtra(String)
+ */
+ public static final String EXTRA_PACKAGE_NAMES = "android.content.pm.extra.PACKAGE_NAMES";
+
+ /** {@hide} */
+ public static final String EXTRA_LEGACY_STATUS = "android.content.pm.extra.LEGACY_STATUS";
+ /** {@hide} */
+ public static final String EXTRA_LEGACY_BUNDLE = "android.content.pm.extra.LEGACY_BUNDLE";
/** {@hide} */
public static final String EXTRA_CALLBACK = "android.content.pm.extra.CALLBACK";
+ /**
+ * User action is currently required to proceed. You can launch the intent
+ * activity described by {@link Intent#EXTRA_INTENT} to involve the user and
+ * continue.
+ * <p>
+ * You may choose to immediately launch the intent if the user is actively
+ * using your app. Otherwise, you should use a notification to guide the
+ * user back into your app before launching.
+ *
+ * @see Intent#getParcelableExtra(String)
+ */
+ public static final int STATUS_USER_ACTION_REQUIRED = -1;
+
+ /**
+ * The operation succeeded.
+ */
+ public static final int STATUS_SUCCESS = 0;
+
+ /**
+ * The operation failed in a generic way. The system will always try to
+ * provide a more specific failure reason, but in some rare cases this may
+ * be delivered.
+ *
+ * @see #EXTRA_STATUS_MESSAGE
+ */
+ public static final int STATUS_FAILURE = 1;
+
+ /**
+ * The operation failed because it was blocked. For example, a device policy
+ * may be blocking the operation, a package verifier may have blocked the
+ * operation, or the app may be required for core system operation.
+ *
+ * @see #EXTRA_STATUS_MESSAGE
+ */
+ public static final int STATUS_FAILURE_BLOCKED = 1;
+
+ /**
+ * The operation failed because it was actively aborted. For example, the
+ * user actively declined requested permissions, or the session was
+ * abandoned.
+ *
+ * @see #EXTRA_STATUS_MESSAGE
+ */
+ public static final int STATUS_FAILURE_ABORTED = 2;
+
+ /**
+ * The operation failed because one or more of the APKs was invalid. For
+ * example, they might be malformed, corrupt, incorrectly signed,
+ * mismatched, etc.
+ *
+ * @see #EXTRA_STATUS_MESSAGE
+ */
+ public static final int STATUS_FAILURE_INVALID = 3;
+
+ /**
+ * The operation failed because it conflicts (or is inconsistent with) with
+ * another package already installed on the device. For example, an existing
+ * permission, incompatible certificates, etc. The user may be able to
+ * uninstall another app to fix the issue.
+ * <p>
+ * The result may also contain {@link #EXTRA_PACKAGE_NAMES} with the
+ * specific packages identified as the cause of the conflict.
+ *
+ * @see #EXTRA_STATUS_MESSAGE
+ */
+ public static final int STATUS_FAILURE_CONFLICT = 4;
+
+ /**
+ * The operation failed because of storage issues. For example, the device
+ * may be running low on space, or external media may be unavailable. The
+ * user may be able to help free space or insert different external media.
+ *
+ * @see #EXTRA_STATUS_MESSAGE
+ */
+ public static final int STATUS_FAILURE_STORAGE = 5;
+
+ /**
+ * The operation failed because it is fundamentally incompatible with this
+ * device. For example, the app may require a hardware feature that doesn't
+ * exist, it may be missing native code for the ABIs supported by the
+ * device, or it requires a newer SDK version, etc.
+ *
+ * @see #EXTRA_STATUS_MESSAGE
+ */
+ public static final int STATUS_FAILURE_INCOMPATIBLE = 6;
+
+ private final Context mContext;
private final PackageManager mPm;
private final IPackageInstaller mInstaller;
private final int mUserId;
@@ -103,8 +213,9 @@
private final ArrayList<SessionCallbackDelegate> mDelegates = new ArrayList<>();
/** {@hide} */
- public PackageInstaller(PackageManager pm, IPackageInstaller installer,
+ public PackageInstaller(Context context, PackageManager pm, IPackageInstaller installer,
String installerPackageName, int userId) {
+ mContext = context;
mPm = pm;
mInstaller = installer;
mInstallerPackageName = installerPackageName;
@@ -126,7 +237,7 @@
* This ID remains consistent across device reboots until the
* session is finalized. IDs are not reused during a given boot.
*/
- public int createSession(@NonNull InstallSessionParams params) throws IOException {
+ public int createSession(@NonNull SessionParams params) throws IOException {
try {
return mInstaller.createSession(params, mInstallerPackageName, mUserId);
} catch (RuntimeException e) {
@@ -153,7 +264,7 @@
* Return details for a specific session. To succeed, the caller must either
* own this session, or be the current home app.
*/
- public @Nullable InstallSessionInfo getSessionInfo(int sessionId) {
+ public @Nullable SessionInfo getSessionInfo(int sessionId) {
try {
return mInstaller.getSessionInfo(sessionId);
} catch (RemoteException e) {
@@ -165,7 +276,7 @@
* Return list of all active install sessions, regardless of the installer.
* To succeed, the caller must be the current home app.
*/
- public @NonNull List<InstallSessionInfo> getAllSessions() {
+ public @NonNull List<SessionInfo> getAllSessions() {
try {
return mInstaller.getAllSessions(mUserId);
} catch (RemoteException e) {
@@ -176,7 +287,7 @@
/**
* Return list of all install sessions owned by the calling app.
*/
- public @NonNull List<InstallSessionInfo> getMySessions() {
+ public @NonNull List<SessionInfo> getMySessions() {
try {
return mInstaller.getMySessions(mInstallerPackageName, mUserId);
} catch (RemoteException e) {
@@ -189,25 +300,9 @@
* method is only available to the current "installer of record" for the
* package.
*/
- public void uninstall(@NonNull String packageName, @NonNull UninstallCallback callback) {
+ public void uninstall(@NonNull String packageName, @NonNull IntentSender statusReceiver) {
try {
- mInstaller.uninstall(packageName, 0,
- new UninstallCallbackDelegate(callback).getBinder(), mUserId);
- } catch (RemoteException e) {
- throw e.rethrowAsRuntimeException();
- }
- }
-
- /**
- * Uninstall only a specific split from the given package.
- *
- * @hide
- */
- public void uninstall(@NonNull String packageName, @NonNull String splitName,
- @NonNull UninstallCallback callback) {
- try {
- mInstaller.uninstallSplit(packageName, splitName, 0,
- new UninstallCallbackDelegate(callback).getBinder(), mUserId);
+ mInstaller.uninstall(packageName, 0, statusReceiver, mUserId);
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
}
@@ -353,6 +448,14 @@
* calling thread.
*/
public void addSessionCallback(@NonNull SessionCallback callback, @NonNull Handler handler) {
+ // TODO: remove this temporary guard once we have new prebuilts
+ final ApplicationInfo info = mContext.getApplicationInfo();
+ if ("com.google.android.googlequicksearchbox".equals(info.packageName)
+ && info.versionCode <= 300400070) {
+ Log.d(TAG, "Ignoring callback request from old prebuilt");
+ return;
+ }
+
synchronized (mDelegates) {
final SessionCallbackDelegate delegate = new SessionCallbackDelegate(callback,
handler.getLooper());
@@ -436,7 +539,7 @@
* You can write data into the returned stream, optionally call
* {@link #fsync(OutputStream)} as needed to ensure bytes have been
* persisted to disk, and then close when finished. All streams must be
- * closed before calling {@link #commit(CommitCallback)}.
+ * closed before calling {@link #commit(IntentSender)}.
*
* @param name arbitrary, unique name of your choosing to identify the
* APK being written. You can open a file again for
@@ -476,14 +579,14 @@
}
/**
- * List all APK names contained in this session.
+ * Return all APK names contained in this session.
* <p>
* This returns all names which have been previously written through
* {@link #openWrite(String, long, long)} as part of this session.
*/
- public @NonNull String[] list() {
+ public @NonNull String[] getNames() {
try {
- return mSession.list();
+ return mSession.getNames();
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
}
@@ -518,9 +621,9 @@
* on the session. If the device reboots before the session has been
* finalized, you may commit the session again.
*/
- public void commit(@NonNull CommitCallback callback) {
+ public void commit(@NonNull IntentSender statusReceiver) {
try {
- mSession.commit(new CommitCallbackDelegate(callback).getBinder());
+ mSession.commit(statusReceiver);
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
}
@@ -553,169 +656,350 @@
}
/**
- * Events for a specific uninstall request.
+ * Parameters for creating a new {@link PackageInstaller.Session}.
*/
- public static abstract class UninstallCallback {
- /**
- * Generic unknown failure. The system will always try to provide a more
- * specific failure reason, but in some rare cases this may be
- * delivered.
- */
- public static final int FAILURE_UNKNOWN = 0;
+ public static class SessionParams implements Parcelable {
+
+ /** {@hide} */
+ public static final int MODE_INVALID = -1;
/**
- * This uninstall was blocked. The package may be required for core
- * system operation, or the user may be restricted. Attempting to
- * uninstall again will have the same result.
+ * Mode for an install session whose staged APKs should fully replace any
+ * existing APKs for the target app.
*/
- public static final int FAILURE_BLOCKED = 1;
+ public static final int MODE_FULL_INSTALL = 1;
/**
- * This uninstall was actively aborted. For example, the user declined
- * to uninstall. You may try to uninstall again.
- */
- public static final int FAILURE_ABORTED = 2;
-
- /**
- * User action is required to proceed. You can start the given intent
- * activity to involve the user and continue.
+ * Mode for an install session that should inherit any existing APKs for the
+ * target app, unless they have been explicitly overridden (based on split
+ * name) by the session. For example, this can be used to add one or more
+ * split APKs to an existing installation.
* <p>
- * You may choose to immediately launch the intent if the user is
- * actively using your app. However, you should use a notification to
- * guide the user back into your app if not currently active.
+ * If there are no existing APKs for the target app, this behaves like
+ * {@link #MODE_FULL_INSTALL}.
*/
- public abstract void onUserActionRequired(Intent intent);
+ public static final int MODE_INHERIT_EXISTING = 2;
- public abstract void onSuccess();
- public abstract void onFailure(int failureReason, String msg, Bundle extras);
- }
+ /** {@hide} */
+ public int mode = MODE_INVALID;
+ /** {@hide} */
+ public int installFlags;
+ /** {@hide} */
+ public int installLocation = PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY;
+ /** {@hide} */
+ public long sizeBytes = -1;
+ /** {@hide} */
+ public String appPackageName;
+ /** {@hide} */
+ public Bitmap appIcon;
+ /** {@hide} */
+ public String appLabel;
+ /** {@hide} */
+ public Uri originatingUri;
+ /** {@hide} */
+ public Uri referrerUri;
+ /** {@hide} */
+ public String abiOverride;
- /** {@hide} */
- private static class UninstallCallbackDelegate extends PackageDeleteObserver {
- private final UninstallCallback target;
+ /**
+ * Construct parameters for a new package install session.
+ *
+ * @param mode one of {@link #MODE_FULL_INSTALL} or
+ * {@link #MODE_INHERIT_EXISTING} describing how the session
+ * should interact with an existing app.
+ */
+ public SessionParams(int mode) {
+ this.mode = mode;
+ }
- public UninstallCallbackDelegate(UninstallCallback target) {
- this.target = target;
+ /** {@hide} */
+ public SessionParams(Parcel source) {
+ mode = source.readInt();
+ installFlags = source.readInt();
+ installLocation = source.readInt();
+ sizeBytes = source.readLong();
+ appPackageName = source.readString();
+ appIcon = source.readParcelable(null);
+ appLabel = source.readString();
+ originatingUri = source.readParcelable(null);
+ referrerUri = source.readParcelable(null);
+ abiOverride = source.readString();
+ }
+
+ /**
+ * Provide value of {@link PackageInfo#installLocation}, which may be used
+ * to determine where the app will be staged. Defaults to
+ * {@link PackageInfo#INSTALL_LOCATION_INTERNAL_ONLY}.
+ */
+ public void setInstallLocation(int installLocation) {
+ this.installLocation = installLocation;
+ }
+
+ /**
+ * Optionally indicate the total size (in bytes) of all APKs that will be
+ * delivered in this session. The system may use this to ensure enough disk
+ * space exists before proceeding, or to estimate container size for
+ * installations living on external storage.
+ *
+ * @see PackageInfo#INSTALL_LOCATION_AUTO
+ * @see PackageInfo#INSTALL_LOCATION_PREFER_EXTERNAL
+ */
+ public void setSize(long sizeBytes) {
+ this.sizeBytes = sizeBytes;
+ }
+
+ /**
+ * Optionally set the package name of the app being installed. It's strongly
+ * recommended that you provide this value when known, so that observers can
+ * communicate installing apps to users.
+ * <p>
+ * If the APKs staged in the session aren't consistent with this package
+ * name, the install will fail. Regardless of this value, all APKs in the
+ * app must have the same package name.
+ */
+ public void setAppPackageName(@Nullable String appPackageName) {
+ this.appPackageName = appPackageName;
+ }
+
+ /**
+ * Optionally set an icon representing the app being installed. This should
+ * be roughly {@link ActivityManager#getLauncherLargeIconSize()} in both
+ * dimensions.
+ */
+ public void setAppIcon(@Nullable Bitmap appIcon) {
+ this.appIcon = appIcon;
+ }
+
+ /**
+ * Optionally set a label representing the app being installed.
+ */
+ public void setAppLabel(@Nullable CharSequence appLabel) {
+ this.appLabel = (appLabel != null) ? appLabel.toString() : null;
+ }
+
+ /**
+ * Optionally set the URI where this package was downloaded from. Used for
+ * verification purposes.
+ *
+ * @see Intent#EXTRA_ORIGINATING_URI
+ */
+ public void setOriginatingUri(@Nullable Uri originatingUri) {
+ this.originatingUri = originatingUri;
+ }
+
+ /**
+ * Optionally set the URI that referred you to install this package. Used
+ * for verification purposes.
+ *
+ * @see Intent#EXTRA_REFERRER
+ */
+ public void setReferrerUri(@Nullable Uri referrerUri) {
+ this.referrerUri = referrerUri;
+ }
+
+ /** {@hide} */
+ public void dump(IndentingPrintWriter pw) {
+ pw.printPair("mode", mode);
+ pw.printHexPair("installFlags", installFlags);
+ pw.printPair("installLocation", installLocation);
+ pw.printPair("sizeBytes", sizeBytes);
+ pw.printPair("appPackageName", appPackageName);
+ pw.printPair("appIcon", (appIcon != null));
+ pw.printPair("appLabel", appLabel);
+ pw.printPair("originatingUri", originatingUri);
+ pw.printPair("referrerUri", referrerUri);
+ pw.printPair("abiOverride", abiOverride);
+ pw.println();
}
@Override
- public void onUserActionRequired(Intent intent) {
- target.onUserActionRequired(intent);
+ public int describeContents() {
+ return 0;
}
@Override
- public void onPackageDeleted(String basePackageName, int returnCode, String msg) {
- if (returnCode == PackageManager.DELETE_SUCCEEDED) {
- target.onSuccess();
- } else {
- final int failureReason = PackageManager.deleteStatusToFailureReason(returnCode);
- msg = PackageManager.deleteStatusToString(returnCode) + ": " + msg;
- target.onFailure(failureReason, msg, null);
- }
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(mode);
+ dest.writeInt(installFlags);
+ dest.writeInt(installLocation);
+ dest.writeLong(sizeBytes);
+ dest.writeString(appPackageName);
+ dest.writeParcelable(appIcon, flags);
+ dest.writeString(appLabel);
+ dest.writeParcelable(originatingUri, flags);
+ dest.writeParcelable(referrerUri, flags);
+ dest.writeString(abiOverride);
}
+
+ public static final Parcelable.Creator<SessionParams>
+ CREATOR = new Parcelable.Creator<SessionParams>() {
+ @Override
+ public SessionParams createFromParcel(Parcel p) {
+ return new SessionParams(p);
+ }
+
+ @Override
+ public SessionParams[] newArray(int size) {
+ return new SessionParams[size];
+ }
+ };
}
/**
- * Final result of a session commit request.
+ * Details for an active install session.
*/
- public static abstract class CommitCallback {
- /**
- * Generic unknown failure. The system will always try to provide a more
- * specific failure reason, but in some rare cases this may be
- * delivered.
- */
- public static final int FAILURE_UNKNOWN = 0;
+ public static class SessionInfo implements Parcelable {
+
+ /** {@hide} */
+ public int sessionId;
+ /** {@hide} */
+ public String installerPackageName;
+ /** {@hide} */
+ public String resolvedBaseCodePath;
+ /** {@hide} */
+ public float progress;
+ /** {@hide} */
+ public boolean sealed;
+ /** {@hide} */
+ public boolean open;
+
+ /** {@hide} */
+ public int mode;
+ /** {@hide} */
+ public long sizeBytes;
+ /** {@hide} */
+ public String appPackageName;
+ /** {@hide} */
+ public Bitmap appIcon;
+ /** {@hide} */
+ public CharSequence appLabel;
+
+ /** {@hide} */
+ public SessionInfo() {
+ }
+
+ /** {@hide} */
+ public SessionInfo(Parcel source) {
+ sessionId = source.readInt();
+ installerPackageName = source.readString();
+ resolvedBaseCodePath = source.readString();
+ progress = source.readFloat();
+ sealed = source.readInt() != 0;
+ open = source.readInt() != 0;
+
+ mode = source.readInt();
+ sizeBytes = source.readLong();
+ appPackageName = source.readString();
+ appIcon = source.readParcelable(null);
+ appLabel = source.readString();
+ }
/**
- * One or more of the APKs included in the session was invalid. For
- * example, they might be malformed, corrupt, incorrectly signed,
- * mismatched, etc. The installer may want to try downloading and
- * installing again.
+ * Return the ID for this session.
*/
- public static final int FAILURE_INVALID = 1;
+ public int getSessionId() {
+ return sessionId;
+ }
/**
- * This install session conflicts (or is inconsistent with) with another
- * package already installed on the device. For example, an existing
- * permission, incompatible certificates, etc. The user may be able to
- * uninstall another app to fix the issue.
+ * Return the package name of the app that owns this session.
+ */
+ public @Nullable String getInstallerPackageName() {
+ return installerPackageName;
+ }
+
+ /**
+ * Return current overall progress of this session, between 0 and 1.
* <p>
- * The extras bundle may contain {@link #EXTRA_PACKAGE_NAME} with the
- * specific packages identified as the cause of the conflict.
+ * Note that this progress may not directly correspond to the value reported
+ * by {@link PackageInstaller.Session#setProgress(float)}, as the system may
+ * carve out a portion of the overall progress to represent its own internal
+ * installation work.
*/
- public static final int FAILURE_CONFLICT = 2;
+ public float getProgress() {
+ return progress;
+ }
/**
- * This install session failed due to storage issues. For example,
- * the device may be running low on space, or the required external
- * media may be unavailable. The user may be able to help free space
- * or insert the correct media.
+ * Return if this session is currently open.
*/
- public static final int FAILURE_STORAGE = 3;
+ public boolean isOpen() {
+ return open;
+ }
/**
- * This install session is fundamentally incompatible with this
- * device. For example, the package may require a hardware feature
- * that doesn't exist, it may be missing native code for the device
- * ABI, or it requires a newer SDK version, etc. This install would
- * never succeed.
+ * Return the package name this session is working with. May be {@code null}
+ * if unknown.
*/
- public static final int FAILURE_INCOMPATIBLE = 4;
+ public @Nullable String getAppPackageName() {
+ return appPackageName;
+ }
/**
- * This install session failed because it was actively aborted. For
- * example, the user declined requested permissions, or a verifier
- * rejected the session.
+ * Return an icon representing the app being installed. May be {@code null}
+ * if unavailable.
+ */
+ public @Nullable Bitmap getAppIcon() {
+ return appIcon;
+ }
+
+ /**
+ * Return a label representing the app being installed. May be {@code null}
+ * if unavailable.
+ */
+ public @Nullable CharSequence getAppLabel() {
+ return appLabel;
+ }
+
+ /**
+ * Return an Intent that can be started to view details about this install
+ * session. This may surface actions such as pause, resume, or cancel.
+ * <p>
+ * In some cases, a matching Activity may not exist, so ensure you safeguard
+ * against this.
*
- * @see PackageManager#VERIFICATION_REJECT
+ * @see PackageInstaller#ACTION_SESSION_DETAILS
*/
- public static final int FAILURE_ABORTED = 5;
-
- public static final String EXTRA_PACKAGE_NAME = "android.content.pm.extra.PACKAGE_NAME";
-
- /**
- * User action is required to proceed. You can start the given intent
- * activity to involve the user and continue.
- * <p>
- * You may choose to immediately launch the intent if the user is
- * actively using your app. However, you should use a notification to
- * guide the user back into your app if not currently active.
- */
- public abstract void onUserActionRequired(Intent intent);
-
- public abstract void onSuccess();
- public abstract void onFailure(int failureReason, String msg, Bundle extras);
- }
-
- /** {@hide} */
- private static class CommitCallbackDelegate extends PackageInstallObserver {
- private final CommitCallback target;
-
- public CommitCallbackDelegate(CommitCallback target) {
- this.target = target;
+ public @Nullable Intent getDetailsIntent() {
+ final Intent intent = new Intent(PackageInstaller.ACTION_SESSION_DETAILS);
+ intent.putExtra(PackageInstaller.EXTRA_SESSION_ID, sessionId);
+ intent.setPackage(installerPackageName);
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ return intent;
}
@Override
- public void onUserActionRequired(Intent intent) {
- target.onUserActionRequired(intent);
+ public int describeContents() {
+ return 0;
}
@Override
- public void onPackageInstalled(String basePackageName, int returnCode, String msg,
- Bundle extras) {
- if (returnCode == PackageManager.INSTALL_SUCCEEDED) {
- target.onSuccess();
- } else {
- final int failureReason = PackageManager.installStatusToFailureReason(returnCode);
- msg = PackageManager.installStatusToString(returnCode) + ": " + msg;
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(sessionId);
+ dest.writeString(installerPackageName);
+ dest.writeString(resolvedBaseCodePath);
+ dest.writeFloat(progress);
+ dest.writeInt(sealed ? 1 : 0);
+ dest.writeInt(open ? 1 : 0);
- if (extras != null) {
- extras.putString(CommitCallback.EXTRA_PACKAGE_NAME,
- extras.getString(PackageManager.EXTRA_FAILURE_EXISTING_PACKAGE));
- }
-
- target.onFailure(failureReason, msg, extras);
- }
+ dest.writeInt(mode);
+ dest.writeLong(sizeBytes);
+ dest.writeString(appPackageName);
+ dest.writeParcelable(appIcon, flags);
+ dest.writeString(appLabel != null ? appLabel.toString() : null);
}
+
+ public static final Parcelable.Creator<SessionInfo>
+ CREATOR = new Parcelable.Creator<SessionInfo>() {
+ @Override
+ public SessionInfo createFromParcel(Parcel p) {
+ return new SessionInfo(p);
+ }
+
+ @Override
+ public SessionInfo[] newArray(int size) {
+ return new SessionInfo[size];
+ }
+ };
}
}
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index b957a15..56b7164 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -28,8 +28,6 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.content.IntentSender;
-import android.content.pm.PackageInstaller.CommitCallback;
-import android.content.pm.PackageInstaller.UninstallCallback;
import android.content.pm.PackageParser.PackageParserException;
import android.content.res.Resources;
import android.content.res.XmlResourceParser;
@@ -179,9 +177,9 @@
/**
* {@link PackageInfo} flag: return information about
* hardware preferences in
- * {@link PackageInfo#configPreferences PackageInfo.configPreferences} and
- * requested features in {@link PackageInfo#reqFeatures
- * PackageInfo.reqFeatures}.
+ * {@link PackageInfo#configPreferences PackageInfo.configPreferences},
+ * and requested features in {@link PackageInfo#reqFeatures} and
+ * {@link PackageInfo#featureGroups}.
*/
public static final int GET_CONFIGURATIONS = 0x00004000;
@@ -3797,6 +3795,16 @@
public abstract boolean isPackageAvailable(String packageName);
/** {@hide} */
+ public static String installStatusToString(int status, String msg) {
+ final String str = installStatusToString(status);
+ if (msg != null) {
+ return str + ": " + msg;
+ } else {
+ return str;
+ }
+ }
+
+ /** {@hide} */
public static String installStatusToString(int status) {
switch (status) {
case INSTALL_SUCCEEDED: return "INSTALL_SUCCEEDED";
@@ -3845,49 +3853,60 @@
}
/** {@hide} */
- public static int installStatusToFailureReason(int status) {
+ public static int installStatusToPublicStatus(int status) {
switch (status) {
- case INSTALL_FAILED_ALREADY_EXISTS: return CommitCallback.FAILURE_CONFLICT;
- case INSTALL_FAILED_INVALID_APK: return CommitCallback.FAILURE_INVALID;
- case INSTALL_FAILED_INVALID_URI: return CommitCallback.FAILURE_INVALID;
- case INSTALL_FAILED_INSUFFICIENT_STORAGE: return CommitCallback.FAILURE_STORAGE;
- case INSTALL_FAILED_DUPLICATE_PACKAGE: return CommitCallback.FAILURE_CONFLICT;
- case INSTALL_FAILED_NO_SHARED_USER: return CommitCallback.FAILURE_CONFLICT;
- case INSTALL_FAILED_UPDATE_INCOMPATIBLE: return CommitCallback.FAILURE_CONFLICT;
- case INSTALL_FAILED_SHARED_USER_INCOMPATIBLE: return CommitCallback.FAILURE_CONFLICT;
- case INSTALL_FAILED_MISSING_SHARED_LIBRARY: return CommitCallback.FAILURE_INCOMPATIBLE;
- case INSTALL_FAILED_REPLACE_COULDNT_DELETE: return CommitCallback.FAILURE_CONFLICT;
- case INSTALL_FAILED_DEXOPT: return CommitCallback.FAILURE_INVALID;
- case INSTALL_FAILED_OLDER_SDK: return CommitCallback.FAILURE_INCOMPATIBLE;
- case INSTALL_FAILED_CONFLICTING_PROVIDER: return CommitCallback.FAILURE_CONFLICT;
- case INSTALL_FAILED_NEWER_SDK: return CommitCallback.FAILURE_INCOMPATIBLE;
- case INSTALL_FAILED_TEST_ONLY: return CommitCallback.FAILURE_INVALID;
- case INSTALL_FAILED_CPU_ABI_INCOMPATIBLE: return CommitCallback.FAILURE_INCOMPATIBLE;
- case INSTALL_FAILED_MISSING_FEATURE: return CommitCallback.FAILURE_INCOMPATIBLE;
- case INSTALL_FAILED_CONTAINER_ERROR: return CommitCallback.FAILURE_STORAGE;
- case INSTALL_FAILED_INVALID_INSTALL_LOCATION: return CommitCallback.FAILURE_STORAGE;
- case INSTALL_FAILED_MEDIA_UNAVAILABLE: return CommitCallback.FAILURE_STORAGE;
- case INSTALL_FAILED_VERIFICATION_TIMEOUT: return CommitCallback.FAILURE_ABORTED;
- case INSTALL_FAILED_VERIFICATION_FAILURE: return CommitCallback.FAILURE_ABORTED;
- case INSTALL_FAILED_PACKAGE_CHANGED: return CommitCallback.FAILURE_INVALID;
- case INSTALL_FAILED_UID_CHANGED: return CommitCallback.FAILURE_INVALID;
- case INSTALL_FAILED_VERSION_DOWNGRADE: return CommitCallback.FAILURE_INVALID;
- case INSTALL_PARSE_FAILED_NOT_APK: return CommitCallback.FAILURE_INVALID;
- case INSTALL_PARSE_FAILED_BAD_MANIFEST: return CommitCallback.FAILURE_INVALID;
- case INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION: return CommitCallback.FAILURE_INVALID;
- case INSTALL_PARSE_FAILED_NO_CERTIFICATES: return CommitCallback.FAILURE_INVALID;
- case INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES: return CommitCallback.FAILURE_INVALID;
- case INSTALL_PARSE_FAILED_CERTIFICATE_ENCODING: return CommitCallback.FAILURE_INVALID;
- case INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME: return CommitCallback.FAILURE_INVALID;
- case INSTALL_PARSE_FAILED_BAD_SHARED_USER_ID: return CommitCallback.FAILURE_INVALID;
- case INSTALL_PARSE_FAILED_MANIFEST_MALFORMED: return CommitCallback.FAILURE_INVALID;
- case INSTALL_PARSE_FAILED_MANIFEST_EMPTY: return CommitCallback.FAILURE_INVALID;
- case INSTALL_FAILED_INTERNAL_ERROR: return CommitCallback.FAILURE_UNKNOWN;
- case INSTALL_FAILED_USER_RESTRICTED: return CommitCallback.FAILURE_INCOMPATIBLE;
- case INSTALL_FAILED_DUPLICATE_PERMISSION: return CommitCallback.FAILURE_CONFLICT;
- case INSTALL_FAILED_NO_MATCHING_ABIS: return CommitCallback.FAILURE_INCOMPATIBLE;
- case INSTALL_FAILED_ABORTED: return CommitCallback.FAILURE_ABORTED;
- default: return CommitCallback.FAILURE_UNKNOWN;
+ case INSTALL_SUCCEEDED: return PackageInstaller.STATUS_SUCCESS;
+ case INSTALL_FAILED_ALREADY_EXISTS: return PackageInstaller.STATUS_FAILURE_CONFLICT;
+ case INSTALL_FAILED_INVALID_APK: return PackageInstaller.STATUS_FAILURE_INVALID;
+ case INSTALL_FAILED_INVALID_URI: return PackageInstaller.STATUS_FAILURE_INVALID;
+ case INSTALL_FAILED_INSUFFICIENT_STORAGE: return PackageInstaller.STATUS_FAILURE_STORAGE;
+ case INSTALL_FAILED_DUPLICATE_PACKAGE: return PackageInstaller.STATUS_FAILURE_CONFLICT;
+ case INSTALL_FAILED_NO_SHARED_USER: return PackageInstaller.STATUS_FAILURE_CONFLICT;
+ case INSTALL_FAILED_UPDATE_INCOMPATIBLE: return PackageInstaller.STATUS_FAILURE_CONFLICT;
+ case INSTALL_FAILED_SHARED_USER_INCOMPATIBLE: return PackageInstaller.STATUS_FAILURE_CONFLICT;
+ case INSTALL_FAILED_MISSING_SHARED_LIBRARY: return PackageInstaller.STATUS_FAILURE_INCOMPATIBLE;
+ case INSTALL_FAILED_REPLACE_COULDNT_DELETE: return PackageInstaller.STATUS_FAILURE_CONFLICT;
+ case INSTALL_FAILED_DEXOPT: return PackageInstaller.STATUS_FAILURE_INVALID;
+ case INSTALL_FAILED_OLDER_SDK: return PackageInstaller.STATUS_FAILURE_INCOMPATIBLE;
+ case INSTALL_FAILED_CONFLICTING_PROVIDER: return PackageInstaller.STATUS_FAILURE_CONFLICT;
+ case INSTALL_FAILED_NEWER_SDK: return PackageInstaller.STATUS_FAILURE_INCOMPATIBLE;
+ case INSTALL_FAILED_TEST_ONLY: return PackageInstaller.STATUS_FAILURE_INVALID;
+ case INSTALL_FAILED_CPU_ABI_INCOMPATIBLE: return PackageInstaller.STATUS_FAILURE_INCOMPATIBLE;
+ case INSTALL_FAILED_MISSING_FEATURE: return PackageInstaller.STATUS_FAILURE_INCOMPATIBLE;
+ case INSTALL_FAILED_CONTAINER_ERROR: return PackageInstaller.STATUS_FAILURE_STORAGE;
+ case INSTALL_FAILED_INVALID_INSTALL_LOCATION: return PackageInstaller.STATUS_FAILURE_STORAGE;
+ case INSTALL_FAILED_MEDIA_UNAVAILABLE: return PackageInstaller.STATUS_FAILURE_STORAGE;
+ case INSTALL_FAILED_VERIFICATION_TIMEOUT: return PackageInstaller.STATUS_FAILURE_ABORTED;
+ case INSTALL_FAILED_VERIFICATION_FAILURE: return PackageInstaller.STATUS_FAILURE_ABORTED;
+ case INSTALL_FAILED_PACKAGE_CHANGED: return PackageInstaller.STATUS_FAILURE_INVALID;
+ case INSTALL_FAILED_UID_CHANGED: return PackageInstaller.STATUS_FAILURE_INVALID;
+ case INSTALL_FAILED_VERSION_DOWNGRADE: return PackageInstaller.STATUS_FAILURE_INVALID;
+ case INSTALL_PARSE_FAILED_NOT_APK: return PackageInstaller.STATUS_FAILURE_INVALID;
+ case INSTALL_PARSE_FAILED_BAD_MANIFEST: return PackageInstaller.STATUS_FAILURE_INVALID;
+ case INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION: return PackageInstaller.STATUS_FAILURE_INVALID;
+ case INSTALL_PARSE_FAILED_NO_CERTIFICATES: return PackageInstaller.STATUS_FAILURE_INVALID;
+ case INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES: return PackageInstaller.STATUS_FAILURE_INVALID;
+ case INSTALL_PARSE_FAILED_CERTIFICATE_ENCODING: return PackageInstaller.STATUS_FAILURE_INVALID;
+ case INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME: return PackageInstaller.STATUS_FAILURE_INVALID;
+ case INSTALL_PARSE_FAILED_BAD_SHARED_USER_ID: return PackageInstaller.STATUS_FAILURE_INVALID;
+ case INSTALL_PARSE_FAILED_MANIFEST_MALFORMED: return PackageInstaller.STATUS_FAILURE_INVALID;
+ case INSTALL_PARSE_FAILED_MANIFEST_EMPTY: return PackageInstaller.STATUS_FAILURE_INVALID;
+ case INSTALL_FAILED_INTERNAL_ERROR: return PackageInstaller.STATUS_FAILURE;
+ case INSTALL_FAILED_USER_RESTRICTED: return PackageInstaller.STATUS_FAILURE_INCOMPATIBLE;
+ case INSTALL_FAILED_DUPLICATE_PERMISSION: return PackageInstaller.STATUS_FAILURE_CONFLICT;
+ case INSTALL_FAILED_NO_MATCHING_ABIS: return PackageInstaller.STATUS_FAILURE_INCOMPATIBLE;
+ case INSTALL_FAILED_ABORTED: return PackageInstaller.STATUS_FAILURE_ABORTED;
+ default: return PackageInstaller.STATUS_FAILURE;
+ }
+ }
+
+ /** {@hide} */
+ public static String deleteStatusToString(int status, String msg) {
+ final String str = deleteStatusToString(status);
+ if (msg != null) {
+ return str + ": " + msg;
+ } else {
+ return str;
}
}
@@ -3905,14 +3924,15 @@
}
/** {@hide} */
- public static int deleteStatusToFailureReason(int status) {
+ public static int deleteStatusToPublicStatus(int status) {
switch (status) {
- case DELETE_FAILED_INTERNAL_ERROR: return UninstallCallback.FAILURE_UNKNOWN;
- case DELETE_FAILED_DEVICE_POLICY_MANAGER: return UninstallCallback.FAILURE_BLOCKED;
- case DELETE_FAILED_USER_RESTRICTED: return UninstallCallback.FAILURE_BLOCKED;
- case DELETE_FAILED_OWNER_BLOCKED: return UninstallCallback.FAILURE_BLOCKED;
- case DELETE_FAILED_ABORTED: return UninstallCallback.FAILURE_ABORTED;
- default: return UninstallCallback.FAILURE_UNKNOWN;
+ case DELETE_SUCCEEDED: return PackageInstaller.STATUS_SUCCESS;
+ case DELETE_FAILED_INTERNAL_ERROR: return PackageInstaller.STATUS_FAILURE;
+ case DELETE_FAILED_DEVICE_POLICY_MANAGER: return PackageInstaller.STATUS_FAILURE_BLOCKED;
+ case DELETE_FAILED_USER_RESTRICTED: return PackageInstaller.STATUS_FAILURE_BLOCKED;
+ case DELETE_FAILED_OWNER_BLOCKED: return PackageInstaller.STATUS_FAILURE_BLOCKED;
+ case DELETE_FAILED_ABORTED: return PackageInstaller.STATUS_FAILURE_ABORTED;
+ default: return PackageInstaller.STATUS_FAILURE;
}
}
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 44bf35d..cddefb5 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -434,6 +434,11 @@
pi.reqFeatures = new FeatureInfo[N];
p.reqFeatures.toArray(pi.reqFeatures);
}
+ N = p.featureGroups != null ? p.featureGroups.size() : 0;
+ if (N > 0) {
+ pi.featureGroups = new FeatureGroupInfo[N];
+ p.featureGroups.toArray(pi.featureGroups);
+ }
}
if ((flags&PackageManager.GET_ACTIVITIES) != 0) {
int N = p.activities.size();
@@ -1502,24 +1507,7 @@
XmlUtils.skipCurrentTag(parser);
} else if (tagName.equals("uses-feature")) {
- FeatureInfo fi = new FeatureInfo();
- sa = res.obtainAttributes(attrs,
- com.android.internal.R.styleable.AndroidManifestUsesFeature);
- // Note: don't allow this value to be a reference to a resource
- // that may change.
- fi.name = sa.getNonResourceString(
- com.android.internal.R.styleable.AndroidManifestUsesFeature_name);
- if (fi.name == null) {
- fi.reqGlEsVersion = sa.getInt(
- com.android.internal.R.styleable.AndroidManifestUsesFeature_glEsVersion,
- FeatureInfo.GL_ES_VERSION_UNDEFINED);
- }
- if (sa.getBoolean(
- com.android.internal.R.styleable.AndroidManifestUsesFeature_required,
- true)) {
- fi.flags |= FeatureInfo.FLAG_REQUIRED;
- }
- sa.recycle();
+ FeatureInfo fi = parseUsesFeature(res, attrs);
pkg.reqFeatures = ArrayUtils.add(pkg.reqFeatures, fi);
if (fi.name == null) {
@@ -1531,9 +1519,35 @@
XmlUtils.skipCurrentTag(parser);
} else if (tagName.equals("feature-group")) {
- // Skip this for now until we know what to do with it.
+ FeatureGroupInfo group = new FeatureGroupInfo();
+ ArrayList<FeatureInfo> features = null;
+ final int innerDepth = parser.getDepth();
+ while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+ && (type != XmlPullParser.END_TAG || parser.getDepth() > innerDepth)) {
+ if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
+ continue;
+ }
- XmlUtils.skipCurrentTag(parser);
+ final String innerTagName = parser.getName();
+ if (innerTagName.equals("uses-feature")) {
+ FeatureInfo featureInfo = parseUsesFeature(res, attrs);
+ // FeatureGroups are stricter and mandate that
+ // any <uses-feature> declared are mandatory.
+ featureInfo.flags |= FeatureInfo.FLAG_REQUIRED;
+ features = ArrayUtils.add(features, featureInfo);
+ } else {
+ Slog.w(TAG, "Unknown element under <feature-group>: " + innerTagName +
+ " at " + mArchiveSourcePath + " " +
+ parser.getPositionDescription());
+ }
+ XmlUtils.skipCurrentTag(parser);
+ }
+
+ if (features != null) {
+ group.features = new FeatureInfo[features.size()];
+ group.features = features.toArray(group.features);
+ }
+ pkg.featureGroups = ArrayUtils.add(pkg.featureGroups, group);
} else if (tagName.equals("uses-sdk")) {
if (SDK_VERSION > 0) {
@@ -1851,6 +1865,28 @@
return pkg;
}
+ private FeatureInfo parseUsesFeature(Resources res, AttributeSet attrs)
+ throws XmlPullParserException, IOException {
+ FeatureInfo fi = new FeatureInfo();
+ TypedArray sa = res.obtainAttributes(attrs,
+ com.android.internal.R.styleable.AndroidManifestUsesFeature);
+ // Note: don't allow this value to be a reference to a resource
+ // that may change.
+ fi.name = sa.getNonResourceString(
+ com.android.internal.R.styleable.AndroidManifestUsesFeature_name);
+ if (fi.name == null) {
+ fi.reqGlEsVersion = sa.getInt(
+ com.android.internal.R.styleable.AndroidManifestUsesFeature_glEsVersion,
+ FeatureInfo.GL_ES_VERSION_UNDEFINED);
+ }
+ if (sa.getBoolean(
+ com.android.internal.R.styleable.AndroidManifestUsesFeature_required, true)) {
+ fi.flags |= FeatureInfo.FLAG_REQUIRED;
+ }
+ sa.recycle();
+ return fi;
+ }
+
private boolean parseUsesPermission(Package pkg, Resources res, XmlResourceParser parser,
AttributeSet attrs, String[] outError)
throws XmlPullParserException, IOException {
@@ -4225,6 +4261,9 @@
// Applications requested features
public ArrayList<FeatureInfo> reqFeatures = null;
+ // Applications requested feature groups
+ public ArrayList<FeatureGroupInfo> featureGroups = null;
+
public int installLocation;
/* An app that's required for all users and cannot be uninstalled for a user */
diff --git a/core/java/android/content/pm/PermissionInfo.java b/core/java/android/content/pm/PermissionInfo.java
index af574db..4e7da48 100644
--- a/core/java/android/content/pm/PermissionInfo.java
+++ b/core/java/android/content/pm/PermissionInfo.java
@@ -70,7 +70,7 @@
/**
* Additional flag for {@link #protectionLevel}, corresponding
- * to the <code>development</code> value of
+ * to the <code>appop</code> value of
* {@link android.R.attr#protectionLevel}.
*/
public static final int PROTECTION_FLAG_APPOP = 0x40;
diff --git a/core/java/android/content/pm/VerificationParams.java b/core/java/android/content/pm/VerificationParams.java
index bf1f77f..e5119b6 100644
--- a/core/java/android/content/pm/VerificationParams.java
+++ b/core/java/android/content/pm/VerificationParams.java
@@ -24,7 +24,7 @@
/**
* Represents verification parameters used to verify packages to be installed.
*
- * @deprecated callers should migrate to {@link PackageInstallerParams}.
+ * @deprecated callers should migrate to {@link PackageInstaller}.
* @hide
*/
@Deprecated
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index 8423d09..ba811b7 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -640,10 +640,13 @@
* You can call this to try to enable hardware accelerated drawing for
* your IME. This must be set before {@link #onCreate}, so you
* will typically call it in your constructor. It is not always possible
- * to use hardware acclerated drawing in an IME (for example on low-end
+ * to use hardware accelerated drawing in an IME (for example on low-end
* devices that do not have the resources to support this), so the call
* returns true if it succeeds otherwise false if you will need to draw
* in software. You must be able to handle either case.
+ *
+ * @deprecated Starting in API 21, hardware acceleration is always enabled
+ * on capable devices.
*/
public boolean enableHardwareAcceleration() {
if (mWindow != null) {
diff --git a/core/java/android/net/LinkProperties.java b/core/java/android/net/LinkProperties.java
index 47b74ab..dcb2892 100644
--- a/core/java/android/net/LinkProperties.java
+++ b/core/java/android/net/LinkProperties.java
@@ -17,6 +17,7 @@
package android.net;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.net.ProxyInfo;
import android.os.Parcelable;
import android.os.Parcel;
@@ -55,6 +56,10 @@
private ProxyInfo mHttpProxy;
private int mMtu;
+ private static final int MIN_MTU = 68;
+ private static final int MIN_MTU_V6 = 1280;
+ private static final int MAX_MTU = 10000;
+
// Stores the properties of links that are "stacked" above this link.
// Indexed by interface name to allow modification and to prevent duplicates being added.
private Hashtable<String, LinkProperties> mStackedLinks =
@@ -124,7 +129,7 @@
*
* @return The interface name set for this link or {@code null}.
*/
- public String getInterfaceName() {
+ public @Nullable String getInterfaceName() {
return mIfaceName;
}
@@ -995,4 +1000,17 @@
return new LinkProperties[size];
}
};
+
+ /**
+ * Check the valid MTU range based on IPv4 or IPv6.
+ * @hide
+ */
+ public static boolean isValidMtu(int mtu, boolean ipv6) {
+ if (ipv6) {
+ if ((mtu >= MIN_MTU_V6 && mtu <= MAX_MTU)) return true;
+ } else {
+ if ((mtu >= MIN_MTU && mtu <= MAX_MTU)) return true;
+ }
+ return false;
+ }
}
diff --git a/core/java/android/nfc/INfcAdapter.aidl b/core/java/android/nfc/INfcAdapter.aidl
index ee4d45e..5815fa6 100644
--- a/core/java/android/nfc/INfcAdapter.aidl
+++ b/core/java/android/nfc/INfcAdapter.aidl
@@ -59,5 +59,5 @@
void registerLockscreenDispatch(INfcLockscreenDispatch lockscreenDispatch, in int[] techList);
void addNfcUnlockHandler(INfcUnlockHandler unlockHandler, in int[] techList);
- void removeNfcUnlockHandler(IBinder b);
+ void removeNfcUnlockHandler(INfcUnlockHandler unlockHandler);
}
diff --git a/core/java/android/nfc/NfcAdapter.java b/core/java/android/nfc/NfcAdapter.java
index dde2cf1..6bd5a32 100644
--- a/core/java/android/nfc/NfcAdapter.java
+++ b/core/java/android/nfc/NfcAdapter.java
@@ -311,7 +311,7 @@
final NfcActivityManager mNfcActivityManager;
final Context mContext;
- final HashMap<NfcUnlockHandler, IBinder> mNfcUnlockHandlers;
+ final HashMap<NfcUnlockHandler, INfcUnlockHandler> mNfcUnlockHandlers;
final Object mLock;
/**
@@ -542,7 +542,7 @@
NfcAdapter(Context context) {
mContext = context;
mNfcActivityManager = new NfcActivityManager(this);
- mNfcUnlockHandlers = new HashMap<NfcUnlockHandler, IBinder>();
+ mNfcUnlockHandlers = new HashMap<NfcUnlockHandler, INfcUnlockHandler>();
mLock = new Object();
}
@@ -1498,27 +1498,37 @@
* <p /> The parameter {@code tagTechnologies} determines which Tag technologies will be polled for
* at the lockscreen. Polling for less tag technologies reduces latency, and so it is
* strongly recommended to only provide the Tag technologies that the handler is expected to
- * receive.
+ * receive. There must be at least one tag technology provided, otherwise the unlock handler
+ * is ignored.
*
* @hide
*/
@SystemApi
public boolean addNfcUnlockHandler(final NfcUnlockHandler unlockHandler,
String[] tagTechnologies) {
- try {
- INfcUnlockHandler.Stub iHandler = new INfcUnlockHandler.Stub() {
- @Override
- public boolean onUnlockAttempted(Tag tag) throws RemoteException {
- return unlockHandler.onUnlockAttempted(tag);
- }
- };
+ // If there are no tag technologies, don't bother adding unlock handler
+ if (tagTechnologies.length == 0) {
+ return false;
+ }
+ try {
synchronized (mLock) {
if (mNfcUnlockHandlers.containsKey(unlockHandler)) {
- return true;
+ // update the tag technologies
+ sService.removeNfcUnlockHandler(mNfcUnlockHandlers.get(unlockHandler));
+ mNfcUnlockHandlers.remove(unlockHandler);
}
- sService.addNfcUnlockHandler(iHandler, Tag.getTechCodesFromStrings(tagTechnologies));
- mNfcUnlockHandlers.put(unlockHandler, iHandler.asBinder());
+
+ INfcUnlockHandler.Stub iHandler = new INfcUnlockHandler.Stub() {
+ @Override
+ public boolean onUnlockAttempted(Tag tag) throws RemoteException {
+ return unlockHandler.onUnlockAttempted(tag);
+ }
+ };
+
+ sService.addNfcUnlockHandler(iHandler,
+ Tag.getTechCodesFromStrings(tagTechnologies));
+ mNfcUnlockHandlers.put(unlockHandler, iHandler);
}
} catch (RemoteException e) {
attemptDeadServiceRecovery(e);
@@ -1542,8 +1552,7 @@
try {
synchronized (mLock) {
if (mNfcUnlockHandlers.containsKey(unlockHandler)) {
- sService.removeNfcUnlockHandler(mNfcUnlockHandlers.get(unlockHandler));
- mNfcUnlockHandlers.remove(unlockHandler);
+ sService.removeNfcUnlockHandler(mNfcUnlockHandlers.remove(unlockHandler));
}
return true;
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index f9e7b78..9d1a7bc 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -499,7 +499,12 @@
* Sets all the user-wide restrictions for this user.
* Requires the MANAGE_USERS permission.
* @param restrictions the Bundle containing all the restrictions.
+ * @deprecated use {@link android.app.admin.DevicePolicyManager#addUserRestriction(
+ * android.content.ComponentName, String)} or
+ * {@link android.app.admin.DevicePolicyManager#clearUserRestriction(
+ * android.content.ComponentName, String)} instead.
*/
+ @Deprecated
public void setUserRestrictions(Bundle restrictions) {
setUserRestrictions(restrictions, Process.myUserHandle());
}
@@ -509,7 +514,12 @@
* Requires the MANAGE_USERS permission.
* @param restrictions the Bundle containing all the restrictions.
* @param userHandle the UserHandle of the user for whom to set the restrictions.
+ * @deprecated use {@link android.app.admin.DevicePolicyManager#addUserRestriction(
+ * android.content.ComponentName, String)} or
+ * {@link android.app.admin.DevicePolicyManager#clearUserRestriction(
+ * android.content.ComponentName, String)} instead.
*/
+ @Deprecated
public void setUserRestrictions(Bundle restrictions, UserHandle userHandle) {
try {
mService.setUserRestrictions(restrictions, userHandle.getIdentifier());
@@ -523,7 +533,12 @@
* Requires the MANAGE_USERS permission.
* @param key the key of the restriction
* @param value the value for the restriction
+ * @deprecated use {@link android.app.admin.DevicePolicyManager#addUserRestriction(
+ * android.content.ComponentName, String)} or
+ * {@link android.app.admin.DevicePolicyManager#clearUserRestriction(
+ * android.content.ComponentName, String)} instead.
*/
+ @Deprecated
public void setUserRestriction(String key, boolean value) {
Bundle bundle = getUserRestrictions();
bundle.putBoolean(key, value);
@@ -537,7 +552,12 @@
* @param key the key of the restriction
* @param value the value for the restriction
* @param userHandle the user whose restriction is to be changed.
+ * @deprecated use {@link android.app.admin.DevicePolicyManager#addUserRestriction(
+ * android.content.ComponentName, String)} or
+ * {@link android.app.admin.DevicePolicyManager#clearUserRestriction(
+ * android.content.ComponentName, String)} instead.
*/
+ @Deprecated
public void setUserRestriction(String key, boolean value, UserHandle userHandle) {
Bundle bundle = getUserRestrictions(userHandle);
bundle.putBoolean(key, value);
@@ -718,7 +738,7 @@
/**
* Returns list of the profiles of userHandle including
* userHandle itself.
- * Note that it this returns both enabled and not enabled profiles. See
+ * Note that this returns both enabled and not enabled profiles. See
* {@link #getUserProfiles()} if you need only the enabled ones.
*
* Requires {@link android.Manifest.permission#MANAGE_USERS} permission.
diff --git a/core/java/android/preference/PreferenceFragment.java b/core/java/android/preference/PreferenceFragment.java
index acd7942..e95e6e2 100644
--- a/core/java/android/preference/PreferenceFragment.java
+++ b/core/java/android/preference/PreferenceFragment.java
@@ -258,6 +258,7 @@
*/
public void setPreferenceScreen(PreferenceScreen preferenceScreen) {
if (mPreferenceManager.setPreferences(preferenceScreen) && preferenceScreen != null) {
+ onUnbindPreferences();
mHavePrefs = true;
if (mInitDone) {
postBindPreferences();
@@ -350,6 +351,10 @@
}
/** @hide */
+ protected void onUnbindPreferences() {
+ }
+
+ /** @hide */
public ListView getListView() {
ensureList();
return mList;
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 8886559..e075d8b 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -2667,6 +2667,16 @@
};
/**
+ * These entries are considered common between the personal and the managed profile,
+ * since the managed profile doesn't get to change them.
+ * @hide
+ */
+ public static final String[] CLONE_TO_MANAGED_PROFILE = {
+ DATE_FORMAT,
+ TIME_12_24
+ };
+
+ /**
* When to use Wi-Fi calling
*
* @see android.telephony.TelephonyManager.WifiCallingChoices
@@ -4797,6 +4807,26 @@
};
/**
+ * These entries are considered common between the personal and the managed profile,
+ * since the managed profile doesn't get to change them.
+ * @hide
+ */
+ public static final String[] CLONE_TO_MANAGED_PROFILE = {
+ ACCESSIBILITY_ENABLED,
+ ALLOW_MOCK_LOCATION,
+ ALLOWED_GEOLOCATION_ORIGINS,
+ DEFAULT_INPUT_METHOD,
+ ENABLED_ACCESSIBILITY_SERVICES,
+ ENABLED_INPUT_METHODS,
+ LOCATION_MODE,
+ LOCATION_PROVIDERS_ALLOWED,
+ LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS,
+ SELECTED_INPUT_METHOD_SUBTYPE,
+ SELECTED_SPELL_CHECKER,
+ SELECTED_SPELL_CHECKER_SUBTYPE
+ };
+
+ /**
* Helper method for determining if a location provider is enabled.
*
* @param cr the content resolver to use
@@ -6440,6 +6470,14 @@
public static final String GUEST_USER_ENABLED = "guest_user_enabled";
/**
+ * Whether the NetworkScoringService has been first initialized.
+ * <p>
+ * Type: int (0 for false, 1 for true)
+ * @hide
+ */
+ public static final String NETWORK_SCORING_PROVISIONED = "network_scoring_provisioned";
+
+ /**
* Settings to backup. This is here so that it's in the same place as the settings
* keys and easy to update.
*
diff --git a/core/java/android/service/notification/ZenModeConfig.java b/core/java/android/service/notification/ZenModeConfig.java
index cc09653..872f911 100644
--- a/core/java/android/service/notification/ZenModeConfig.java
+++ b/core/java/android/service/notification/ZenModeConfig.java
@@ -73,9 +73,14 @@
private static final String CONDITION_TAG = "condition";
private static final String CONDITION_ATT_COMPONENT = "component";
private static final String CONDITION_ATT_ID = "id";
+ private static final String CONDITION_ATT_SUMMARY = "summary";
+ private static final String CONDITION_ATT_LINE1 = "line1";
+ private static final String CONDITION_ATT_LINE2 = "line2";
+ private static final String CONDITION_ATT_ICON = "icon";
+ private static final String CONDITION_ATT_STATE = "state";
+ private static final String CONDITION_ATT_FLAGS = "flags";
private static final String EXIT_CONDITION_TAG = "exitCondition";
- private static final String EXIT_CONDITION_ATT_ID = "id";
private static final String EXIT_CONDITION_ATT_COMPONENT = "component";
public boolean allowCalls;
@@ -83,13 +88,13 @@
public int allowFrom = SOURCE_ANYONE;
public String sleepMode;
- public int sleepStartHour;
- public int sleepStartMinute;
+ public int sleepStartHour; // 0-23
+ public int sleepStartMinute; // 0-59
public int sleepEndHour;
public int sleepEndMinute;
public ComponentName[] conditionComponents;
public Uri[] conditionIds;
- public Uri exitConditionId;
+ public Condition exitCondition;
public ComponentName exitConditionComponent;
public ZenModeConfig() { }
@@ -115,7 +120,7 @@
source.readTypedArray(conditionIds, Uri.CREATOR);
}
allowFrom = source.readInt();
- exitConditionId = source.readParcelable(null);
+ exitCondition = source.readParcelable(null);
exitConditionComponent = source.readParcelable(null);
}
@@ -146,7 +151,7 @@
dest.writeInt(0);
}
dest.writeInt(allowFrom);
- dest.writeParcelable(exitConditionId, 0);
+ dest.writeParcelable(exitCondition, 0);
dest.writeParcelable(exitConditionComponent, 0);
}
@@ -163,7 +168,7 @@
.append(conditionComponents == null ? null : TextUtils.join(",", conditionComponents))
.append(",conditionIds=")
.append(conditionIds == null ? null : TextUtils.join(",", conditionIds))
- .append(",exitConditionId=").append(exitConditionId)
+ .append(",exitCondition=").append(exitCondition)
.append(",exitConditionComponent=").append(exitConditionComponent)
.append(']').toString();
}
@@ -196,7 +201,7 @@
&& other.sleepEndMinute == sleepEndMinute
&& Objects.deepEquals(other.conditionComponents, conditionComponents)
&& Objects.deepEquals(other.conditionIds, conditionIds)
- && Objects.equals(other.exitConditionId, exitConditionId)
+ && Objects.equals(other.exitCondition, exitCondition)
&& Objects.equals(other.exitConditionComponent, exitConditionComponent);
}
@@ -205,7 +210,7 @@
return Objects.hash(allowCalls, allowMessages, allowFrom, sleepMode,
sleepStartHour, sleepStartMinute, sleepEndHour, sleepEndMinute,
Arrays.hashCode(conditionComponents), Arrays.hashCode(conditionIds),
- exitConditionId, exitConditionComponent);
+ exitCondition, exitConditionComponent);
}
public boolean isValid() {
@@ -294,9 +299,11 @@
conditionIds.add(conditionId);
}
} else if (EXIT_CONDITION_TAG.equals(tag)) {
- rt.exitConditionId = safeUri(parser, EXIT_CONDITION_ATT_ID);
- rt.exitConditionComponent =
- safeComponentName(parser, EXIT_CONDITION_ATT_COMPONENT);
+ rt.exitCondition = readConditionXml(parser);
+ if (rt.exitCondition != null) {
+ rt.exitConditionComponent =
+ safeComponentName(parser, EXIT_CONDITION_ATT_COMPONENT);
+ }
}
}
}
@@ -333,16 +340,42 @@
out.endTag(null, CONDITION_TAG);
}
}
- if (exitConditionId != null && exitConditionComponent != null) {
+ if (exitCondition != null && exitConditionComponent != null) {
out.startTag(null, EXIT_CONDITION_TAG);
- out.attribute(null, EXIT_CONDITION_ATT_ID, exitConditionId.toString());
out.attribute(null, EXIT_CONDITION_ATT_COMPONENT,
exitConditionComponent.flattenToString());
+ writeConditionXml(exitCondition, out);
out.endTag(null, EXIT_CONDITION_TAG);
}
out.endTag(null, ZEN_TAG);
}
+ public static Condition readConditionXml(XmlPullParser parser) {
+ final Uri id = safeUri(parser, CONDITION_ATT_ID);
+ final String summary = parser.getAttributeValue(null, CONDITION_ATT_SUMMARY);
+ final String line1 = parser.getAttributeValue(null, CONDITION_ATT_LINE1);
+ final String line2 = parser.getAttributeValue(null, CONDITION_ATT_LINE2);
+ final int icon = safeInt(parser, CONDITION_ATT_ICON, -1);
+ final int state = safeInt(parser, CONDITION_ATT_STATE, -1);
+ final int flags = safeInt(parser, CONDITION_ATT_FLAGS, -1);
+ try {
+ return new Condition(id, summary, line1, line2, icon, state, flags);
+ } catch (IllegalArgumentException e) {
+ Slog.w(TAG, "Unable to read condition xml", e);
+ return null;
+ }
+ }
+
+ public static void writeConditionXml(Condition c, XmlSerializer out) throws IOException {
+ out.attribute(null, CONDITION_ATT_ID, c.id.toString());
+ out.attribute(null, CONDITION_ATT_SUMMARY, c.summary);
+ out.attribute(null, CONDITION_ATT_LINE1, c.line1);
+ out.attribute(null, CONDITION_ATT_LINE2, c.line2);
+ out.attribute(null, CONDITION_ATT_ICON, Integer.toString(c.icon));
+ out.attribute(null, CONDITION_ATT_STATE, Integer.toString(c.state));
+ out.attribute(null, CONDITION_ATT_FLAGS, Integer.toString(c.flags));
+ }
+
public static boolean isValidHour(int val) {
return val >= 0 && val < 24;
}
@@ -403,21 +436,31 @@
}
};
- // Built-in countdown conditions, e.g. condition://android/countdown/1399917958951
+ public DowntimeInfo toDowntimeInfo() {
+ final DowntimeInfo downtime = new DowntimeInfo();
+ downtime.startHour = sleepStartHour;
+ downtime.startMinute = sleepStartMinute;
+ downtime.endHour = sleepEndHour;
+ downtime.endMinute = sleepEndMinute;
+ return downtime;
+ }
- private static final String COUNTDOWN_AUTHORITY = "android";
+ // For built-in conditions
+ private static final String SYSTEM_AUTHORITY = "android";
+
+ // Built-in countdown conditions, e.g. condition://android/countdown/1399917958951
private static final String COUNTDOWN_PATH = "countdown";
public static Uri toCountdownConditionId(long time) {
return new Uri.Builder().scheme(Condition.SCHEME)
- .authority(COUNTDOWN_AUTHORITY)
+ .authority(SYSTEM_AUTHORITY)
.appendPath(COUNTDOWN_PATH)
.appendPath(Long.toString(time))
.build();
}
public static long tryParseCountdownConditionId(Uri conditionId) {
- if (!Condition.isValidId(conditionId, COUNTDOWN_AUTHORITY)) return 0;
+ if (!Condition.isValidId(conditionId, SYSTEM_AUTHORITY)) return 0;
if (conditionId.getPathSegments().size() != 2
|| !COUNTDOWN_PATH.equals(conditionId.getPathSegments().get(0))) return 0;
try {
@@ -431,4 +474,68 @@
public static boolean isValidCountdownConditionId(Uri conditionId) {
return tryParseCountdownConditionId(conditionId) != 0;
}
+
+ // Built-in downtime conditions, e.g. condition://android/downtime?start=10.00&end=7.00
+ private static final String DOWNTIME_PATH = "downtime";
+
+ public static Uri toDowntimeConditionId(DowntimeInfo downtime) {
+ return new Uri.Builder().scheme(Condition.SCHEME)
+ .authority(SYSTEM_AUTHORITY)
+ .appendPath(DOWNTIME_PATH)
+ .appendQueryParameter("start", downtime.startHour + "." + downtime.startMinute)
+ .appendQueryParameter("end", downtime.endHour + "." + downtime.endMinute)
+ .build();
+ }
+
+ public static DowntimeInfo tryParseDowntimeConditionId(Uri conditionId) {
+ if (!Condition.isValidId(conditionId, SYSTEM_AUTHORITY)
+ || conditionId.getPathSegments().size() != 1
+ || !DOWNTIME_PATH.equals(conditionId.getPathSegments().get(0))) {
+ return null;
+ }
+ final int[] start = tryParseHourAndMinute(conditionId.getQueryParameter("start"));
+ final int[] end = tryParseHourAndMinute(conditionId.getQueryParameter("end"));
+ if (start == null || end == null) return null;
+ final DowntimeInfo downtime = new DowntimeInfo();
+ downtime.startHour = start[0];
+ downtime.startMinute = start[1];
+ downtime.endHour = end[0];
+ downtime.endMinute = end[1];
+ return downtime;
+ }
+
+ private static int[] tryParseHourAndMinute(String value) {
+ if (TextUtils.isEmpty(value)) return null;
+ final int i = value.indexOf('.');
+ if (i < 1 || i >= value.length() - 1) return null;
+ final int hour = tryParseInt(value.substring(0, i), -1);
+ final int minute = tryParseInt(value.substring(i + 1), -1);
+ return isValidHour(hour) && isValidMinute(minute) ? new int[] { hour, minute } : null;
+ }
+
+ public static boolean isValidDowntimeConditionId(Uri conditionId) {
+ return tryParseDowntimeConditionId(conditionId) != null;
+ }
+
+ public static class DowntimeInfo {
+ public int startHour; // 0-23
+ public int startMinute; // 0-59
+ public int endHour;
+ public int endMinute;
+
+ @Override
+ public int hashCode() {
+ return 0;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (!(o instanceof DowntimeInfo)) return false;
+ final DowntimeInfo other = (DowntimeInfo) o;
+ return startHour == other.startHour
+ && startMinute == other.startMinute
+ && endHour == other.endHour
+ && endMinute == other.endMinute;
+ }
+ }
}
diff --git a/core/java/android/service/trust/TrustAgentService.java b/core/java/android/service/trust/TrustAgentService.java
index 51f07ec..5fe9194 100644
--- a/core/java/android/service/trust/TrustAgentService.java
+++ b/core/java/android/service/trust/TrustAgentService.java
@@ -69,12 +69,6 @@
"[" + getClass().getSimpleName() + "]";
private static final boolean DEBUG = false;
- // Temporary workaround to allow current trust agent implementations to continue working.
- // This and the code guarded by this should be removed before shipping.
- // If true, calls setManagingTrust(true) after onCreate, if it wasn't already set.
- // TODO: Remove this once all agents are updated.
- private static final boolean SET_MANAGED_FOR_LEGACY_AGENTS = true;
-
/**
* The {@link Intent} that must be declared as handled by the service.
*/
@@ -130,11 +124,6 @@
@Override
public void onCreate() {
- // TODO: Remove this once all agents are updated.
- if (SET_MANAGED_FOR_LEGACY_AGENTS) {
- setManagingTrust(true);
- }
-
super.onCreate();
ComponentName component = new ComponentName(this, getClass());
try {
@@ -175,7 +164,7 @@
* set.
*
* @param options Option feature bundle.
- * @return true if the {@link #TrustAgentService()} supports this feature.
+ * @return true if the {@link TrustAgentService} supports this feature.
*/
public boolean onSetTrustAgentFeaturesEnabled(Bundle options) {
return false;
diff --git a/core/java/android/text/Layout.java b/core/java/android/text/Layout.java
index a36f06c7..7dce348 100644
--- a/core/java/android/text/Layout.java
+++ b/core/java/android/text/Layout.java
@@ -273,16 +273,22 @@
// Draw all leading margin spans. Adjust left or right according
// to the paragraph direction of the line.
final int length = spans.length;
+ boolean useFirstLineMargin = isFirstParaLine;
+ for (int n = 0; n < length; n++) {
+ if (spans[n] instanceof LeadingMarginSpan2) {
+ int count = ((LeadingMarginSpan2) spans[n]).getLeadingMarginLineCount();
+ int startLine = getLineForOffset(sp.getSpanStart(spans[n]));
+ // if there is more than one LeadingMarginSpan2, use
+ // the count that is greatest
+ if (i < startLine + count) {
+ useFirstLineMargin = true;
+ break;
+ }
+ }
+ }
for (int n = 0; n < length; n++) {
if (spans[n] instanceof LeadingMarginSpan) {
LeadingMarginSpan margin = (LeadingMarginSpan) spans[n];
- boolean useFirstLineMargin = isFirstParaLine;
- if (margin instanceof LeadingMarginSpan2) {
- int count = ((LeadingMarginSpan2) margin).getLeadingMarginLineCount();
- int startLine = getLineForOffset(sp.getSpanStart(margin));
- useFirstLineMargin = i < startLine + count;
- }
-
if (dir == DIR_RIGHT_TO_LEFT) {
margin.drawLeadingMargin(canvas, paint, right, dir, ltop,
lbaseline, lbottom, buf,
@@ -1535,15 +1541,18 @@
boolean isFirstParaLine = lineStart == 0 ||
spanned.charAt(lineStart - 1) == '\n';
+ boolean useFirstLineMargin = isFirstParaLine;
+ for (int i = 0; i < spans.length; i++) {
+ if (spans[i] instanceof LeadingMarginSpan2) {
+ int spStart = spanned.getSpanStart(spans[i]);
+ int spanLine = getLineForOffset(spStart);
+ int count = ((LeadingMarginSpan2) spans[i]).getLeadingMarginLineCount();
+ // if there is more than one LeadingMarginSpan2, use the count that is greatest
+ useFirstLineMargin |= line < spanLine + count;
+ }
+ }
for (int i = 0; i < spans.length; i++) {
LeadingMarginSpan span = spans[i];
- boolean useFirstLineMargin = isFirstParaLine;
- if (span instanceof LeadingMarginSpan2) {
- int spStart = spanned.getSpanStart(span);
- int spanLine = getLineForOffset(spStart);
- int count = ((LeadingMarginSpan2)span).getLeadingMarginLineCount();
- useFirstLineMargin = line < spanLine + count;
- }
margin += span.getLeadingMargin(useFirstLineMargin);
}
diff --git a/core/java/android/text/StaticLayout.java b/core/java/android/text/StaticLayout.java
index 4144a75..aecf488 100644
--- a/core/java/android/text/StaticLayout.java
+++ b/core/java/android/text/StaticLayout.java
@@ -201,13 +201,12 @@
restWidth -= sp[i].getLeadingMargin(false);
// LeadingMarginSpan2 is odd. The count affects all
- // leading margin spans, not just this particular one,
- // and start from the top of the span, not the top of the
- // paragraph.
+ // leading margin spans, not just this particular one
if (lms instanceof LeadingMarginSpan2) {
LeadingMarginSpan2 lms2 = (LeadingMarginSpan2) lms;
int lmsFirstLine = getLineForOffset(spanned.getSpanStart(lms2));
- firstWidthLineLimit = lmsFirstLine + lms2.getLeadingMarginLineCount();
+ firstWidthLineLimit = Math.max(firstWidthLineLimit,
+ lmsFirstLine + lms2.getLeadingMarginLineCount());
}
}
diff --git a/core/java/android/text/style/LeadingMarginSpan.java b/core/java/android/text/style/LeadingMarginSpan.java
index 2f429ff..96a7cd9 100644
--- a/core/java/android/text/style/LeadingMarginSpan.java
+++ b/core/java/android/text/style/LeadingMarginSpan.java
@@ -28,6 +28,9 @@
* margin spans on a single paragraph; they will be rendered in order, each
* adding its margin to the ones before it. The leading margin is on the right
* for lines in a right-to-left paragraph.
+ * <p>
+ * LeadingMarginSpans should be attached from the first character to the last
+ * character of a single paragraph.
*/
public interface LeadingMarginSpan
extends ParagraphStyle
@@ -69,18 +72,22 @@
/**
- * An extended version of {@link LeadingMarginSpan}, which allows
- * the implementor to specify the number of lines of text to which
- * this object is attached that the "first line of paragraph" margin
- * width will be applied to.
+ * An extended version of {@link LeadingMarginSpan}, which allows the
+ * implementor to specify the number of lines of the paragraph to which
+ * this object is attached that the "first line of paragraph" margin width
+ * will be applied to.
+ * <p>
+ * There should only be one LeadingMarginSpan2 per paragraph. The leading
+ * margin line count affects all LeadingMarginSpans in the paragraph,
+ * adjusting the number of lines to which the first line margin is applied.
+ * <p>
+ * As with LeadingMarginSpans, LeadingMarginSpan2s should be attached from
+ * the beginning to the end of a paragraph.
*/
public interface LeadingMarginSpan2 extends LeadingMarginSpan, WrapTogetherSpan {
/**
- * Returns the number of lines of text to which this object is
+ * Returns the number of lines of the paragraph to which this object is
* attached that the "first line" margin will apply to.
- * Note that if this returns N, the first N lines of the region,
- * not the first N lines of each paragraph, will be given the
- * special margin width.
*/
public int getLeadingMarginLineCount();
};
diff --git a/core/java/android/webkit/WebViewFactory.java b/core/java/android/webkit/WebViewFactory.java
index ec589a5..c7ffedc 100644
--- a/core/java/android/webkit/WebViewFactory.java
+++ b/core/java/android/webkit/WebViewFactory.java
@@ -72,12 +72,17 @@
private static WebViewFactoryProvider sProviderInstance;
private static final Object sProviderLock = new Object();
private static boolean sAddressSpaceReserved = false;
+ private static PackageInfo sPackageInfo;
public static String getWebViewPackageName() {
return AppGlobals.getInitialApplication().getString(
com.android.internal.R.string.config_webViewPackageName);
}
+ public static PackageInfo getLoadedPackageInfo() {
+ return sPackageInfo;
+ }
+
static WebViewFactoryProvider getProvider() {
synchronized (sProviderLock) {
// For now the main purpose of this function (and the factory abstraction) is to keep
@@ -125,9 +130,9 @@
try {
// First fetch the package info so we can log the webview package version.
String packageName = getWebViewPackageName();
- PackageInfo pi = initialApplication.getPackageManager().getPackageInfo(packageName, 0);
- Log.i(LOGTAG, "Loading " + packageName + " version " + pi.versionName +
- " (code " + pi.versionCode + ")");
+ sPackageInfo = initialApplication.getPackageManager().getPackageInfo(packageName, 0);
+ Log.i(LOGTAG, "Loading " + packageName + " version " + sPackageInfo.versionName +
+ " (code " + sPackageInfo.versionCode + ")");
// Construct a package context to load the Java code into the current app.
Context webViewContext = initialApplication.createPackageContext(packageName,
diff --git a/core/java/android/widget/CheckedTextView.java b/core/java/android/widget/CheckedTextView.java
index ccd0480..29a6a7d 100644
--- a/core/java/android/widget/CheckedTextView.java
+++ b/core/java/android/widget/CheckedTextView.java
@@ -53,6 +53,8 @@
private int mBasePadding;
private int mCheckMarkWidth;
+ private int mCheckMarkGravity = Gravity.END;
+
private boolean mNeedRequestlayout;
private static final int[] CHECKED_STATE_SET = {
@@ -83,15 +85,17 @@
}
mCheckMarkTintMode = Drawable.parseTintMode(a.getInt(
- R.styleable.CompoundButton_buttonTintMode, -1), mCheckMarkTintMode);
+ R.styleable.CheckedTextView_checkMarkTintMode, -1), mCheckMarkTintMode);
- if (a.hasValue(R.styleable.CompoundButton_buttonTint)) {
- mCheckMarkTintList = a.getColorStateList(R.styleable.CompoundButton_buttonTint);
+ if (a.hasValue(R.styleable.CheckedTextView_checkMarkTint)) {
+ mCheckMarkTintList = a.getColorStateList(R.styleable.CheckedTextView_checkMarkTint);
mHasCheckMarkTint = true;
applyCheckMarkTint();
}
+ mCheckMarkGravity = a.getInt(R.styleable.CheckedTextView_checkMarkGravity, Gravity.END);
+
boolean checked = a.getBoolean(R.styleable.CheckedTextView_checked, false);
setChecked(checked);
@@ -293,7 +297,7 @@
@Override
protected void internalSetPadding(int left, int top, int right, int bottom) {
super.internalSetPadding(left, top, right, bottom);
- setBasePadding(isLayoutRtl());
+ setBasePadding(isCheckMarkAtStart());
}
@Override
@@ -306,7 +310,7 @@
resetPaddingToInitialValues();
int newPadding = (mCheckMarkDrawable != null) ?
mCheckMarkWidth + mBasePadding : mBasePadding;
- if (isLayoutRtl()) {
+ if (isCheckMarkAtStart()) {
mNeedRequestlayout |= (mPaddingLeft != newPadding);
mPaddingLeft = newPadding;
} else {
@@ -319,14 +323,20 @@
}
}
- private void setBasePadding(boolean isLayoutRtl) {
- if (isLayoutRtl) {
+ private void setBasePadding(boolean checkmarkAtStart) {
+ if (checkmarkAtStart) {
mBasePadding = mPaddingLeft;
} else {
mBasePadding = mPaddingRight;
}
}
+ private boolean isCheckMarkAtStart() {
+ final int gravity = Gravity.getAbsoluteGravity(mCheckMarkGravity, getLayoutDirection());
+ final int hgrav = gravity & Gravity.HORIZONTAL_GRAVITY_MASK;
+ return hgrav == Gravity.LEFT;
+ }
+
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
@@ -347,13 +357,13 @@
break;
}
- final boolean isLayoutRtl = isLayoutRtl();
+ final boolean checkMarkAtStart = isCheckMarkAtStart();
final int width = getWidth();
final int top = y;
final int bottom = top + height;
final int left;
final int right;
- if (isLayoutRtl) {
+ if (checkMarkAtStart) {
left = mBasePadding;
right = left + mCheckMarkWidth;
} else {
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index a536b2d..a82fa65 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -1320,8 +1320,8 @@
attrs, com.android.internal.R.styleable.View, defStyleAttr, defStyleRes);
boolean focusable = mMovement != null || getKeyListener() != null;
- boolean clickable = focusable;
- boolean longClickable = focusable;
+ boolean clickable = focusable || isClickable();
+ boolean longClickable = focusable || isLongClickable();
n = a.getIndexCount();
for (int i = 0; i < n; i++) {
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index ee0d14b..a745b20 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -45,6 +45,7 @@
import android.telephony.ServiceState;
import android.telephony.SignalStrength;
import android.telephony.TelephonyManager;
+import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.Log;
import android.util.LogWriter;
@@ -3744,6 +3745,7 @@
}
public void noteNetworkInterfaceTypeLocked(String iface, int networkType) {
+ if (TextUtils.isEmpty(iface)) return;
if (ConnectivityManager.isNetworkTypeMobile(networkType)) {
mMobileIfaces = includeInStringArray(mMobileIfaces, iface);
if (DEBUG) Slog.d(TAG, "Note mobile iface " + iface + ": " + mMobileIfaces);
diff --git a/core/java/com/android/internal/os/InstallerConnection.java b/core/java/com/android/internal/os/InstallerConnection.java
new file mode 100644
index 0000000..e3f229f
--- /dev/null
+++ b/core/java/com/android/internal/os/InstallerConnection.java
@@ -0,0 +1,207 @@
+/*
+ * Copyright (C) 2008 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.os;
+
+import android.net.LocalSocket;
+import android.net.LocalSocketAddress;
+import android.util.Slog;
+import libcore.io.IoUtils;
+import libcore.io.Streams;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+/**
+ * Represents a connection to {@code installd}. Allows multiple connect and
+ * disconnect cycles.
+ *
+ * @hide for internal use only
+ */
+public class InstallerConnection {
+ private static final String TAG = "InstallerConnection";
+ private static final boolean LOCAL_DEBUG = false;
+
+ private InputStream mIn;
+ private OutputStream mOut;
+ private LocalSocket mSocket;
+
+ private final byte buf[] = new byte[1024];
+
+ public InstallerConnection() {
+ }
+
+ public synchronized String transact(String cmd) {
+ if (!connect()) {
+ Slog.e(TAG, "connection failed");
+ return "-1";
+ }
+
+ if (!writeCommand(cmd)) {
+ /*
+ * If installd died and restarted in the background (unlikely but
+ * possible) we'll fail on the next write (this one). Try to
+ * reconnect and write the command one more time before giving up.
+ */
+ Slog.e(TAG, "write command failed? reconnect!");
+ if (!connect() || !writeCommand(cmd)) {
+ return "-1";
+ }
+ }
+ if (LOCAL_DEBUG) {
+ Slog.i(TAG, "send: '" + cmd + "'");
+ }
+
+ final int replyLength = readReply();
+ if (replyLength > 0) {
+ String s = new String(buf, 0, replyLength);
+ if (LOCAL_DEBUG) {
+ Slog.i(TAG, "recv: '" + s + "'");
+ }
+ return s;
+ } else {
+ if (LOCAL_DEBUG) {
+ Slog.i(TAG, "fail");
+ }
+ return "-1";
+ }
+ }
+
+ public int execute(String cmd) {
+ String res = transact(cmd);
+ try {
+ return Integer.parseInt(res);
+ } catch (NumberFormatException ex) {
+ return -1;
+ }
+ }
+
+ public int dexopt(String apkPath, int uid, boolean isPublic, String instructionSet) {
+ StringBuilder builder = new StringBuilder("dexopt");
+ builder.append(' ');
+ builder.append(apkPath);
+ builder.append(' ');
+ builder.append(uid);
+ builder.append(isPublic ? " 1" : " 0");
+ builder.append(" *"); // No pkgName arg present
+ builder.append(' ');
+ builder.append(instructionSet);
+ return execute(builder.toString());
+ }
+
+ public int patchoat(String apkPath, int uid, boolean isPublic, String instructionSet) {
+ StringBuilder builder = new StringBuilder("patchoat");
+ builder.append(' ');
+ builder.append(apkPath);
+ builder.append(' ');
+ builder.append(uid);
+ builder.append(isPublic ? " 1" : " 0");
+ builder.append(" *"); // No pkgName arg present
+ builder.append(' ');
+ builder.append(instructionSet);
+ return execute(builder.toString());
+ }
+
+ private boolean connect() {
+ if (mSocket != null) {
+ return true;
+ }
+ Slog.i(TAG, "connecting...");
+ try {
+ mSocket = new LocalSocket();
+
+ LocalSocketAddress address = new LocalSocketAddress("installd",
+ LocalSocketAddress.Namespace.RESERVED);
+
+ mSocket.connect(address);
+
+ mIn = mSocket.getInputStream();
+ mOut = mSocket.getOutputStream();
+ } catch (IOException ex) {
+ disconnect();
+ return false;
+ }
+ return true;
+ }
+
+ public void disconnect() {
+ Slog.i(TAG, "disconnecting...");
+ IoUtils.closeQuietly(mSocket);
+ IoUtils.closeQuietly(mIn);
+ IoUtils.closeQuietly(mOut);
+
+ mSocket = null;
+ mIn = null;
+ mOut = null;
+ }
+
+
+ private boolean readFully(byte[] buffer, int len) {
+ try {
+ Streams.readFully(mIn, buffer, 0, len);
+ } catch (IOException ioe) {
+ Slog.e(TAG, "read exception");
+ disconnect();
+ return false;
+ }
+
+ if (LOCAL_DEBUG) {
+ Slog.i(TAG, "read " + len + " bytes");
+ }
+
+ return true;
+ }
+
+ private int readReply() {
+ if (!readFully(buf, 2)) {
+ return -1;
+ }
+
+ final int len = (((int) buf[0]) & 0xff) | ((((int) buf[1]) & 0xff) << 8);
+ if ((len < 1) || (len > buf.length)) {
+ Slog.e(TAG, "invalid reply length (" + len + ")");
+ disconnect();
+ return -1;
+ }
+
+ if (!readFully(buf, len)) {
+ return -1;
+ }
+
+ return len;
+ }
+
+ private boolean writeCommand(String cmdString) {
+ final byte[] cmd = cmdString.getBytes();
+ final int len = cmd.length;
+ if ((len < 1) || (len > buf.length)) {
+ return false;
+ }
+
+ buf[0] = (byte) (len & 0xff);
+ buf[1] = (byte) ((len >> 8) & 0xff);
+ try {
+ mOut.write(buf, 0, 2);
+ mOut.write(cmd, 0, len);
+ } catch (IOException ex) {
+ Slog.e(TAG, "write error");
+ disconnect();
+ return false;
+ }
+ return true;
+ }
+}
diff --git a/core/java/com/android/internal/os/RuntimeInit.java b/core/java/com/android/internal/os/RuntimeInit.java
index 4a26b4b..d35fce4 100644
--- a/core/java/com/android/internal/os/RuntimeInit.java
+++ b/core/java/com/android/internal/os/RuntimeInit.java
@@ -192,13 +192,14 @@
*
* @param className Fully-qualified class name
* @param argv Argument vector for main()
+ * @param classLoader the classLoader to load {@className} with
*/
- private static void invokeStaticMain(String className, String[] argv)
+ private static void invokeStaticMain(String className, String[] argv, ClassLoader classLoader)
throws ZygoteInit.MethodAndArgsCaller {
Class<?> cl;
try {
- cl = Class.forName(className);
+ cl = Class.forName(className, true, classLoader);
} catch (ClassNotFoundException ex) {
throw new RuntimeException(
"Missing class when invoking static main " + className,
@@ -263,7 +264,7 @@
* @param targetSdkVersion target SDK version
* @param argv arg strings
*/
- public static final void zygoteInit(int targetSdkVersion, String[] argv)
+ public static final void zygoteInit(int targetSdkVersion, String[] argv, ClassLoader classLoader)
throws ZygoteInit.MethodAndArgsCaller {
if (DEBUG) Slog.d(TAG, "RuntimeInit: Starting application from zygote");
@@ -272,7 +273,7 @@
commonInit();
nativeZygoteInit();
- applicationInit(targetSdkVersion, argv);
+ applicationInit(targetSdkVersion, argv, classLoader);
}
/**
@@ -290,10 +291,10 @@
throws ZygoteInit.MethodAndArgsCaller {
if (DEBUG) Slog.d(TAG, "RuntimeInit: Starting application from wrapper");
- applicationInit(targetSdkVersion, argv);
+ applicationInit(targetSdkVersion, argv, null);
}
- private static void applicationInit(int targetSdkVersion, String[] argv)
+ private static void applicationInit(int targetSdkVersion, String[] argv, ClassLoader classLoader)
throws ZygoteInit.MethodAndArgsCaller {
// If the application calls System.exit(), terminate the process
// immediately without running any shutdown hooks. It is not possible to
@@ -317,7 +318,7 @@
}
// Remaining arguments are passed to the start class's static main
- invokeStaticMain(args.startClass, args.startArgs);
+ invokeStaticMain(args.startClass, args.startArgs, classLoader);
}
/**
diff --git a/core/java/com/android/internal/os/ZygoteConnection.java b/core/java/com/android/internal/os/ZygoteConnection.java
index 0c48368..43ebb3d 100644
--- a/core/java/com/android/internal/os/ZygoteConnection.java
+++ b/core/java/com/android/internal/os/ZygoteConnection.java
@@ -863,7 +863,7 @@
pipeFd, parsedArgs.remainingArgs);
} else {
RuntimeInit.zygoteInit(parsedArgs.targetSdkVersion,
- parsedArgs.remainingArgs);
+ parsedArgs.remainingArgs, null /* classLoader */);
}
} else {
String className;
diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java
index eea4201..051de6e 100644
--- a/core/java/com/android/internal/os/ZygoteInit.java
+++ b/core/java/com/android/internal/os/ZygoteInit.java
@@ -34,8 +34,11 @@
import android.system.OsConstants;
import android.util.EventLog;
import android.util.Log;
+import android.util.Slog;
import android.webkit.WebViewFactory;
+import dalvik.system.DexFile;
+import dalvik.system.PathClassLoader;
import dalvik.system.VMRuntime;
import libcore.io.IoUtils;
@@ -493,21 +496,69 @@
Process.setArgV0(parsedArgs.niceName);
}
+ final String systemServerClasspath = Os.getenv("SYSTEMSERVERCLASSPATH");
+ if (systemServerClasspath != null) {
+ performSystemServerDexOpt(systemServerClasspath);
+ }
+
if (parsedArgs.invokeWith != null) {
+ String[] args = parsedArgs.remainingArgs;
+ // If we have a non-null system server class path, we'll have to duplicate the
+ // existing arguments and append the classpath to it. ART will handle the classpath
+ // correctly when we exec a new process.
+ if (systemServerClasspath != null) {
+ String[] amendedArgs = new String[args.length + 2];
+ amendedArgs[0] = "-cp";
+ amendedArgs[1] = systemServerClasspath;
+ System.arraycopy(parsedArgs.remainingArgs, 0, amendedArgs, 2, parsedArgs.remainingArgs.length);
+ }
+
WrapperInit.execApplication(parsedArgs.invokeWith,
parsedArgs.niceName, parsedArgs.targetSdkVersion,
- null, parsedArgs.remainingArgs);
+ null, args);
} else {
+ ClassLoader cl = null;
+ if (systemServerClasspath != null) {
+ cl = new PathClassLoader(systemServerClasspath, ClassLoader.getSystemClassLoader());
+ Thread.currentThread().setContextClassLoader(cl);
+ }
+
/*
* Pass the remaining arguments to SystemServer.
*/
- RuntimeInit.zygoteInit(parsedArgs.targetSdkVersion, parsedArgs.remainingArgs);
+ RuntimeInit.zygoteInit(parsedArgs.targetSdkVersion, parsedArgs.remainingArgs, cl);
}
/* should never reach here */
}
/**
+ * Performs dex-opt on the elements of {@code classPath}, if needed. We
+ * choose the instruction set of the current runtime.
+ */
+ private static void performSystemServerDexOpt(String classPath) {
+ final String[] classPathElements = classPath.split(":");
+ final InstallerConnection installer = new InstallerConnection();
+ final String instructionSet = VMRuntime.getRuntime().vmInstructionSet();
+
+ try {
+ for (String classPathElement : classPathElements) {
+ final byte dexopt = DexFile.isDexOptNeededInternal(classPathElement, "*", instructionSet,
+ false /* defer */);
+ if (dexopt == DexFile.DEXOPT_NEEDED) {
+ installer.dexopt(classPathElement, Process.SYSTEM_UID, false, instructionSet);
+ } else if (dexopt == DexFile.PATCHOAT_NEEDED) {
+ installer.patchoat(classPathElement, Process.SYSTEM_UID, false, instructionSet);
+ }
+ }
+ } catch (IOException ioe) {
+ throw new RuntimeException("Error starting system_server", ioe);
+ } finally {
+ installer.disconnect();
+ }
+ }
+
+ /**
* Prepare the arguments and fork for the system server process.
*/
private static boolean startSystemServer(String abiList, String socketName)
diff --git a/core/java/com/android/internal/widget/ActionBarOverlayLayout.java b/core/java/com/android/internal/widget/ActionBarOverlayLayout.java
index ea36e37..5709f659 100644
--- a/core/java/com/android/internal/widget/ActionBarOverlayLayout.java
+++ b/core/java/com/android/internal/widget/ActionBarOverlayLayout.java
@@ -184,6 +184,8 @@
Build.VERSION_CODES.KITKAT;
mFlingEstimator = new OverScroller(context);
+
+ setFocusableInTouchMode(true);
}
@Override
diff --git a/services/core/java/com/android/server/BootReceiver.java b/core/java/com/android/server/BootReceiver.java
similarity index 100%
rename from services/core/java/com/android/server/BootReceiver.java
rename to core/java/com/android/server/BootReceiver.java
diff --git a/core/jni/Android.mk b/core/jni/Android.mk
index 480383b..2106d38 100644
--- a/core/jni/Android.mk
+++ b/core/jni/Android.mk
@@ -246,6 +246,7 @@
libminikin \
libstlport \
libprocessgroup \
+ libnativebridge \
ifeq ($(USE_OPENGL_RENDERER),true)
LOCAL_SHARED_LIBRARIES += libhwui
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 90c66d7..79b8542 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -161,10 +161,8 @@
extern int register_android_text_StaticLayout(JNIEnv *env);
extern int register_android_text_AndroidBidi(JNIEnv *env);
extern int register_android_opengl_classes(JNIEnv *env);
-extern int register_android_server_fingerprint_FingerprintService(JNIEnv* env);
-extern int register_android_server_NetworkManagementSocketTagger(JNIEnv* env);
-extern int register_android_server_Watchdog(JNIEnv* env);
extern int register_android_ddm_DdmHandleNativeHeap(JNIEnv *env);
+extern int register_android_server_NetworkManagementSocketTagger(JNIEnv* env);
extern int register_com_android_internal_os_ZygoteInit(JNIEnv* env);
extern int register_android_backup_BackupDataInput(JNIEnv *env);
extern int register_android_backup_BackupDataOutput(JNIEnv *env);
@@ -804,7 +802,7 @@
"-Xmx", "-Xcompiler-option");
if (skip_compilation) {
addOption("-Xcompiler-option");
- addOption("--compiler-filter=interpret-only");
+ addOption("--compiler-filter=verify-none");
} else {
parseCompilerOption("dalvik.vm.dex2oat-filter", dex2oatCompilerFilterBuf,
"--compiler-filter=", "-Xcompiler-option");
@@ -1338,9 +1336,7 @@
REG_JNI(register_android_media_ToneGenerator),
REG_JNI(register_android_opengl_classes),
- REG_JNI(register_android_server_fingerprint_FingerprintService),
REG_JNI(register_android_server_NetworkManagementSocketTagger),
- REG_JNI(register_android_server_Watchdog),
REG_JNI(register_android_ddm_DdmHandleNativeHeap),
REG_JNI(register_android_backup_BackupDataInput),
REG_JNI(register_android_backup_BackupDataOutput),
diff --git a/core/jni/android_app_NativeActivity.cpp b/core/jni/android_app_NativeActivity.cpp
index 9c44093..633a207 100644
--- a/core/jni/android_app_NativeActivity.cpp
+++ b/core/jni/android_app_NativeActivity.cpp
@@ -38,6 +38,8 @@
#include "android_view_InputChannel.h"
#include "android_view_KeyEvent.h"
+#include "nativebridge/native_bridge.h"
+
#define LOG_TRACE(...)
//#define LOG_TRACE(...) ALOG(LOG_DEBUG, LOG_TAG, __VA_ARGS__)
@@ -251,17 +253,29 @@
const char* pathStr = env->GetStringUTFChars(path, NULL);
NativeCode* code = NULL;
-
+ bool needNativeBridge = false;
+
void* handle = dlopen(pathStr, RTLD_LAZY);
-
+ if (handle == NULL) {
+ if (NativeBridgeIsSupported(pathStr)) {
+ handle = NativeBridgeLoadLibrary(pathStr, RTLD_LAZY);
+ needNativeBridge = true;
+ }
+ }
env->ReleaseStringUTFChars(path, pathStr);
-
+
if (handle != NULL) {
+ void* funcPtr = NULL;
const char* funcStr = env->GetStringUTFChars(funcName, NULL);
- code = new NativeCode(handle, (ANativeActivity_createFunc*)
- dlsym(handle, funcStr));
+ if (needNativeBridge) {
+ funcPtr = NativeBridgeGetTrampoline(handle, funcStr, NULL, 0);
+ } else {
+ funcPtr = dlsym(handle, funcStr);
+ }
+
+ code = new NativeCode(handle, (ANativeActivity_createFunc*)funcPtr);
env->ReleaseStringUTFChars(funcName, funcStr);
-
+
if (code->createActivityFunc == NULL) {
ALOGW("ANativeActivity_onCreate not found");
delete code;
diff --git a/core/res/res/animator/leanback_setup_fragment_close_enter.xml b/core/res/res/animator/leanback_setup_fragment_close_enter.xml
new file mode 100644
index 0000000..1626dd3
--- /dev/null
+++ b/core/res/res/animator/leanback_setup_fragment_close_enter.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2014, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+<set xmlns:android="http://schemas.android.com/apk/res/android">
+ <objectAnimator
+ android:propertyName="alpha"
+ android:valueType="floatType"
+ android:valueFrom="@dimen/leanback_setup_alpha_backward_in_content_start"
+ android:valueTo="@dimen/leanback_setup_alpha_backward_in_content_end"
+ android:duration="@integer/leanback_setup_alpha_backward_in_content_duration"
+ android:startOffset="@integer/leanback_setup_alpha_backward_in_content_delay"/>
+</set>
diff --git a/core/res/res/animator/leanback_setup_fragment_close_exit.xml b/core/res/res/animator/leanback_setup_fragment_close_exit.xml
new file mode 100644
index 0000000..a827df4
--- /dev/null
+++ b/core/res/res/animator/leanback_setup_fragment_close_exit.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2014, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+<set xmlns:android="http://schemas.android.com/apk/res/android">
+ <objectAnimator
+ android:propertyName="alpha"
+ android:valueType="floatType"
+ android:valueFrom="@dimen/leanback_setup_alpha_backward_out_content_start"
+ android:valueTo="@dimen/leanback_setup_alpha_backward_out_content_end"
+ android:duration="@integer/leanback_setup_alpha_backward_out_content_duration"
+ android:startOffset="@integer/leanback_setup_alpha_backward_out_content_delay"/>
+ <objectAnimator
+ android:propertyName="x"
+ android:valueType="floatType"
+ android:valueFrom="@dimen/leanback_setup_translation_backward_out_content_start"
+ android:valueTo="@dimen/leanback_setup_translation_backward_out_content_end"
+ android:duration="@integer/leanback_setup_translation_backward_out_content_duration"
+ android:startOffset="@integer/leanback_setup_translation_backward_out_content_delay"/>
+</set>
diff --git a/core/res/res/animator/leanback_setup_fragment_open_enter.xml b/core/res/res/animator/leanback_setup_fragment_open_enter.xml
new file mode 100644
index 0000000..34b9a57
--- /dev/null
+++ b/core/res/res/animator/leanback_setup_fragment_open_enter.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2014, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+<set xmlns:android="http://schemas.android.com/apk/res/android" >
+ <objectAnimator
+ android:propertyName="alpha"
+ android:valueType="floatType"
+ android:valueFrom="@dimen/leanback_setup_alpha_forward_in_content_start"
+ android:valueTo="@dimen/leanback_setup_alpha_forward_in_content_end"
+ android:duration="@integer/leanback_setup_alpha_forward_in_content_duration"
+ android:startOffset="@integer/leanback_setup_alpha_forward_in_content_delay"/>
+ <objectAnimator
+ android:propertyName="x"
+ android:valueType="floatType"
+ android:valueFrom="@dimen/leanback_setup_translation_forward_in_content_start"
+ android:valueTo="@dimen/leanback_setup_translation_forward_in_content_end"
+ android:duration="@integer/leanback_setup_translation_forward_in_content_duration"
+ android:startOffset="@integer/leanback_setup_translation_forward_in_content_delay" />
+</set>
diff --git a/core/res/res/animator/leanback_setup_fragment_open_exit.xml b/core/res/res/animator/leanback_setup_fragment_open_exit.xml
new file mode 100644
index 0000000..5622db4
--- /dev/null
+++ b/core/res/res/animator/leanback_setup_fragment_open_exit.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2014, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+<set xmlns:android="http://schemas.android.com/apk/res/android">
+ <objectAnimator
+ android:propertyName="alpha"
+ android:valueType="floatType"
+ android:valueFrom="@dimen/leanback_setup_alpha_forward_out_content_start"
+ android:valueTo="@dimen/leanback_setup_alpha_forward_out_content_end"
+ android:duration="@integer/leanback_setup_alpha_forward_out_content_duration"
+ android:startOffset="@integer/leanback_setup_alpha_forward_out_content_delay"/>
+</set>
diff --git a/core/res/res/color/primary_text_leanback_formwizard_dark.xml b/core/res/res/color/primary_text_leanback_formwizard_dark.xml
new file mode 100644
index 0000000..8fe02b2
--- /dev/null
+++ b/core/res/res/color/primary_text_leanback_formwizard_dark.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:state_enabled="false"
+ android:alpha="@dimen/disabled_alpha_leanback_formwizard"
+ android:color="@color/primary_text_leanback_formwizard_default_dark"/>
+ <item android:color="@color/primary_text_leanback_formwizard_default_dark"/>
+</selector>
diff --git a/core/res/res/layout/preference_widget_switch.xml b/core/res/res/layout/preference_widget_switch.xml
index 7395ff2..25e8aa6 100644
--- a/core/res/res/layout/preference_widget_switch.xml
+++ b/core/res/res/layout/preference_widget_switch.xml
@@ -21,4 +21,5 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:focusable="false"
+ android:clickable="false"
android:background="@null" />
diff --git a/core/res/res/layout/select_dialog_multichoice_material.xml b/core/res/res/layout/select_dialog_multichoice_material.xml
index 8b4c59d..01e4cfa 100644
--- a/core/res/res/layout/select_dialog_multichoice_material.xml
+++ b/core/res/res/layout/select_dialog_multichoice_material.xml
@@ -26,4 +26,5 @@
android:paddingStart="16dip"
android:paddingEnd="16dip"
android:checkMark="?android:attr/listChoiceIndicatorMultiple"
+ android:checkMarkGravity="start"
android:ellipsize="marquee" />
diff --git a/core/res/res/layout/select_dialog_singlechoice_material.xml b/core/res/res/layout/select_dialog_singlechoice_material.xml
index 27a6648..0f3c277 100644
--- a/core/res/res/layout/select_dialog_singlechoice_material.xml
+++ b/core/res/res/layout/select_dialog_singlechoice_material.xml
@@ -26,4 +26,5 @@
android:paddingStart="16dip"
android:paddingEnd="16dip"
android:checkMark="?android:attr/listChoiceIndicatorSingle"
+ android:checkMarkGravity="start"
android:ellipsize="marquee" />
diff --git a/core/res/res/values-mcc206-mnc05/config.xml b/core/res/res/values-mcc206-mnc05/config.xml
new file mode 100644
index 0000000..a684aaa
--- /dev/null
+++ b/core/res/res/values-mcc206-mnc05/config.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** 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.
+*/
+-->
+
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array translatable="false" name="config_operatorConsideredNonRoaming">
+ <item>20610</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-mcc206-mnc10/config.xml b/core/res/res/values-mcc206-mnc10/config.xml
new file mode 100644
index 0000000..5c96317
--- /dev/null
+++ b/core/res/res/values-mcc206-mnc10/config.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** 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.
+*/
+-->
+
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array translatable="false" name="config_operatorConsideredNonRoaming">
+ <item>20605</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-mcc208-mnc10/config.xml b/core/res/res/values-mcc208-mnc10/config.xml
index 358bef6..10a6c5d 100644
--- a/core/res/res/values-mcc208-mnc10/config.xml
+++ b/core/res/res/values-mcc208-mnc10/config.xml
@@ -35,4 +35,28 @@
note that empty fields can be ommitted: "name,apn,,,,,,,,,310,260,,DUN" -->
<string translatable="false" name="config_tether_apndata">SFR option modem,websfr,,,,,,,,,208,10,,DUN</string>
+ <string-array translatable="false" name="config_operatorConsideredNonRoaming">
+ <item>21401</item>
+ <item>21402</item>
+ <item>21403</item>
+ <item>21404</item>
+ <item>21405</item>
+ <item>21406</item>
+ <item>21407</item>
+ <item>21408</item>
+ <item>21409</item>
+ <item>21410</item>
+ <item>21411</item>
+ <item>21412</item>
+ <item>21413</item>
+ <item>21414</item>
+ <item>21415</item>
+ <item>21416</item>
+ <item>21417</item>
+ <item>21418</item>
+ <item>21419</item>
+ <item>21420</item>
+ <item>21421</item>
+ </string-array>
+
</resources>
diff --git a/core/res/res/values-mcc208-mnc15/config.xml b/core/res/res/values-mcc208-mnc15/config.xml
new file mode 100644
index 0000000..32b951c
--- /dev/null
+++ b/core/res/res/values-mcc208-mnc15/config.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** 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.
+*/
+-->
+
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array translatable="false" name="config_operatorConsideredNonRoaming">
+ <item>20801</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-mcc214-mnc01/config.xml b/core/res/res/values-mcc214-mnc01/config.xml
index 1b7c462..ecd8124 100644
--- a/core/res/res/values-mcc214-mnc01/config.xml
+++ b/core/res/res/values-mcc214-mnc01/config.xml
@@ -37,4 +37,27 @@
note that empty fields can be ommitted: "name,apn,,,,,,,,,310,260,,DUN" -->
<string translatable="false" name="config_tether_apndata">INTERNET,airtelnet.es,,,vodafone,vodafone,,,,,214,01,1,DUN</string>
+ <string-array translatable="false" name="config_operatorConsideredNonRoaming">
+ <item>21402</item>
+ <item>21403</item>
+ <item>21404</item>
+ <item>21405</item>
+ <item>21406</item>
+ <item>21407</item>
+ <item>21408</item>
+ <item>21409</item>
+ <item>21410</item>
+ <item>21411</item>
+ <item>21412</item>
+ <item>21413</item>
+ <item>21414</item>
+ <item>21415</item>
+ <item>21416</item>
+ <item>21417</item>
+ <item>21418</item>
+ <item>21419</item>
+ <item>21420</item>
+ <item>21421</item>
+ </string-array>
+
</resources>
diff --git a/core/res/res/values-mcc214-mnc02/config.xml b/core/res/res/values-mcc214-mnc02/config.xml
new file mode 100755
index 0000000..c83de57
--- /dev/null
+++ b/core/res/res/values-mcc214-mnc02/config.xml
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** 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.
+*/
+-->
+
+
+<resources>
+ <string-array translatable="false" name="config_operatorConsideredNonRoaming">
+ <item>21401</item>
+ <item>21402</item>
+ <item>21403</item>
+ <item>21404</item>
+ <item>21405</item>
+ <item>21406</item>
+ <item>21407</item>
+ <item>21408</item>
+ <item>21409</item>
+ <item>21410</item>
+ <item>21411</item>
+ <item>21412</item>
+ <item>21413</item>
+ <item>21414</item>
+ <item>21415</item>
+ <item>21416</item>
+ <item>21417</item>
+ <item>21418</item>
+ <item>21419</item>
+ <item>21420</item>
+ <item>21421</item>
+ </string-array>
+
+</resources>
diff --git a/core/res/res/values-mcc214-mnc04/config.xml b/core/res/res/values-mcc214-mnc04/config.xml
index 71301d5..6dfa87b 100644
--- a/core/res/res/values-mcc214-mnc04/config.xml
+++ b/core/res/res/values-mcc214-mnc04/config.xml
@@ -20,6 +20,25 @@
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<!-- Don't use roaming icon for considered operators -->
<string-array translatable="false" name="config_operatorConsideredNonRoaming">
+ <item>21401</item>
+ <item>21402</item>
+ <item>21403</item>
+ <item>21405</item>
+ <item>21406</item>
<item>21407</item>
+ <item>21408</item>
+ <item>21409</item>
+ <item>21410</item>
+ <item>21411</item>
+ <item>21412</item>
+ <item>21413</item>
+ <item>21414</item>
+ <item>21415</item>
+ <item>21416</item>
+ <item>21417</item>
+ <item>21418</item>
+ <item>21419</item>
+ <item>21420</item>
+ <item>21421</item>
</string-array>
</resources>
diff --git a/core/res/res/values-mcc214-mnc05/config.xml b/core/res/res/values-mcc214-mnc05/config.xml
new file mode 100755
index 0000000..9302b0c
--- /dev/null
+++ b/core/res/res/values-mcc214-mnc05/config.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** 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.
+*/
+-->
+
+<resources>
+ <string-array translatable="false" name="config_operatorConsideredNonRoaming">
+ <item>21401</item>
+ <item>21402</item>
+ <item>21403</item>
+ <item>21404</item>
+ <item>21406</item>
+ <item>21407</item>
+ <item>21408</item>
+ <item>21409</item>
+ <item>21410</item>
+ <item>21411</item>
+ <item>21412</item>
+ <item>21413</item>
+ <item>21414</item>
+ <item>21415</item>
+ <item>21416</item>
+ <item>21417</item>
+ <item>21418</item>
+ <item>21419</item>
+ <item>21420</item>
+ <item>21421</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-mcc214-mnc06/config.xml b/core/res/res/values-mcc214-mnc06/config.xml
new file mode 100755
index 0000000..c3f2643
--- /dev/null
+++ b/core/res/res/values-mcc214-mnc06/config.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** 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.
+*/
+-->
+
+<resources>
+ <string-array translatable="false" name="config_operatorConsideredNonRoaming">
+ <item>21401</item>
+ <item>21402</item>
+ <item>21403</item>
+ <item>21404</item>
+ <item>21405</item>
+ <item>21407</item>
+ <item>21408</item>
+ <item>21409</item>
+ <item>21410</item>
+ <item>21411</item>
+ <item>21412</item>
+ <item>21413</item>
+ <item>21414</item>
+ <item>21415</item>
+ <item>21416</item>
+ <item>21417</item>
+ <item>21418</item>
+ <item>21419</item>
+ <item>21420</item>
+ <item>21421</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-mcc214-mnc08/config.xml b/core/res/res/values-mcc214-mnc08/config.xml
new file mode 100755
index 0000000..5af6d5d
--- /dev/null
+++ b/core/res/res/values-mcc214-mnc08/config.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** 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.
+*/
+-->
+
+<resources>
+ <string-array translatable="false" name="config_operatorConsideredNonRoaming">
+ <item>21401</item>
+ <item>21402</item>
+ <item>21403</item>
+ <item>21404</item>
+ <item>21405</item>
+ <item>21406</item>
+ <item>21407</item>
+ <item>21409</item>
+ <item>21410</item>
+ <item>21411</item>
+ <item>21412</item>
+ <item>21413</item>
+ <item>21414</item>
+ <item>21415</item>
+ <item>21416</item>
+ <item>21417</item>
+ <item>21418</item>
+ <item>21419</item>
+ <item>21420</item>
+ <item>21421</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-mcc214-mnc09/config.xml b/core/res/res/values-mcc214-mnc09/config.xml
new file mode 100755
index 0000000..d789771
--- /dev/null
+++ b/core/res/res/values-mcc214-mnc09/config.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** 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.
+*/
+-->
+
+<resources>
+ <string-array translatable="false" name="config_operatorConsideredNonRoaming">
+ <item>21401</item>
+ <item>21402</item>
+ <item>21403</item>
+ <item>21404</item>
+ <item>21405</item>
+ <item>21406</item>
+ <item>21407</item>
+ <item>21408</item>
+ <item>21410</item>
+ <item>21411</item>
+ <item>21412</item>
+ <item>21413</item>
+ <item>21414</item>
+ <item>21415</item>
+ <item>21416</item>
+ <item>21417</item>
+ <item>21418</item>
+ <item>21419</item>
+ <item>21420</item>
+ <item>21421</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-mcc214-mnc10/config.xml b/core/res/res/values-mcc214-mnc10/config.xml
new file mode 100755
index 0000000..b66e1a2
--- /dev/null
+++ b/core/res/res/values-mcc214-mnc10/config.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** 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.
+*/
+-->
+
+<resources>
+ <string-array translatable="false" name="config_operatorConsideredNonRoaming">
+ <item>21401</item>
+ <item>21402</item>
+ <item>21403</item>
+ <item>21404</item>
+ <item>21405</item>
+ <item>21406</item>
+ <item>21407</item>
+ <item>21408</item>
+ <item>21409</item>
+ <item>21411</item>
+ <item>21412</item>
+ <item>21413</item>
+ <item>21414</item>
+ <item>21415</item>
+ <item>21416</item>
+ <item>21417</item>
+ <item>21418</item>
+ <item>21419</item>
+ <item>21420</item>
+ <item>21421</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-mcc214-mnc11/config.xml b/core/res/res/values-mcc214-mnc11/config.xml
new file mode 100755
index 0000000..9fd06db
--- /dev/null
+++ b/core/res/res/values-mcc214-mnc11/config.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** 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.
+*/
+-->
+
+<resources>
+ <string-array translatable="false" name="config_operatorConsideredNonRoaming">
+ <item>21401</item>
+ <item>21402</item>
+ <item>21403</item>
+ <item>21404</item>
+ <item>21405</item>
+ <item>21406</item>
+ <item>21407</item>
+ <item>21408</item>
+ <item>21409</item>
+ <item>21410</item>
+ <item>21412</item>
+ <item>21413</item>
+ <item>21414</item>
+ <item>21415</item>
+ <item>21416</item>
+ <item>21417</item>
+ <item>21418</item>
+ <item>21419</item>
+ <item>21420</item>
+ <item>21421</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-mcc214-mnc12/config.xml b/core/res/res/values-mcc214-mnc12/config.xml
new file mode 100755
index 0000000..7468238
--- /dev/null
+++ b/core/res/res/values-mcc214-mnc12/config.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** 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.
+*/
+-->
+
+<resources>
+ <string-array translatable="false" name="config_operatorConsideredNonRoaming">
+ <item>21401</item>
+ <item>21402</item>
+ <item>21403</item>
+ <item>21404</item>
+ <item>21405</item>
+ <item>21406</item>
+ <item>21407</item>
+ <item>21408</item>
+ <item>21409</item>
+ <item>21410</item>
+ <item>21411</item>
+ <item>21413</item>
+ <item>21414</item>
+ <item>21415</item>
+ <item>21416</item>
+ <item>21417</item>
+ <item>21418</item>
+ <item>21419</item>
+ <item>21420</item>
+ <item>21421</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-mcc214-mnc13/config.xml b/core/res/res/values-mcc214-mnc13/config.xml
new file mode 100755
index 0000000..35ff4ae
--- /dev/null
+++ b/core/res/res/values-mcc214-mnc13/config.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** 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.
+*/
+-->
+
+<resources>
+ <string-array translatable="false" name="config_operatorConsideredNonRoaming">
+ <item>21401</item>
+ <item>21402</item>
+ <item>21403</item>
+ <item>21404</item>
+ <item>21405</item>
+ <item>21406</item>
+ <item>21407</item>
+ <item>21408</item>
+ <item>21409</item>
+ <item>21410</item>
+ <item>21411</item>
+ <item>21412</item>
+ <item>21414</item>
+ <item>21415</item>
+ <item>21416</item>
+ <item>21417</item>
+ <item>21418</item>
+ <item>21419</item>
+ <item>21420</item>
+ <item>21421</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-mcc214-mnc14/config.xml b/core/res/res/values-mcc214-mnc14/config.xml
new file mode 100755
index 0000000..b6a7440
--- /dev/null
+++ b/core/res/res/values-mcc214-mnc14/config.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** 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.
+*/
+-->
+
+<resources>
+ <string-array translatable="false" name="config_operatorConsideredNonRoaming">
+ <item>21401</item>
+ <item>21402</item>
+ <item>21403</item>
+ <item>21404</item>
+ <item>21405</item>
+ <item>21406</item>
+ <item>21407</item>
+ <item>21408</item>
+ <item>21409</item>
+ <item>21410</item>
+ <item>21411</item>
+ <item>21412</item>
+ <item>21413</item>
+ <item>21415</item>
+ <item>21416</item>
+ <item>21417</item>
+ <item>21418</item>
+ <item>21419</item>
+ <item>21420</item>
+ <item>21421</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-mcc214-mnc15/config.xml b/core/res/res/values-mcc214-mnc15/config.xml
new file mode 100755
index 0000000..8296410
--- /dev/null
+++ b/core/res/res/values-mcc214-mnc15/config.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** 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.
+*/
+-->
+
+<resources>
+ <string-array translatable="false" name="config_operatorConsideredNonRoaming">
+ <item>21401</item>
+ <item>21402</item>
+ <item>21403</item>
+ <item>21404</item>
+ <item>21405</item>
+ <item>21406</item>
+ <item>21407</item>
+ <item>21408</item>
+ <item>21409</item>
+ <item>21410</item>
+ <item>21411</item>
+ <item>21412</item>
+ <item>21413</item>
+ <item>21414</item>
+ <item>21416</item>
+ <item>21417</item>
+ <item>21418</item>
+ <item>21419</item>
+ <item>21420</item>
+ <item>21421</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-mcc214-mnc16/config.xml b/core/res/res/values-mcc214-mnc16/config.xml
new file mode 100755
index 0000000..1aaf577
--- /dev/null
+++ b/core/res/res/values-mcc214-mnc16/config.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** 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.
+*/
+-->
+
+<resources>
+ <string-array translatable="false" name="config_operatorConsideredNonRoaming">
+ <item>21401</item>
+ <item>21402</item>
+ <item>21403</item>
+ <item>21404</item>
+ <item>21405</item>
+ <item>21406</item>
+ <item>21407</item>
+ <item>21408</item>
+ <item>21409</item>
+ <item>21410</item>
+ <item>21411</item>
+ <item>21412</item>
+ <item>21413</item>
+ <item>21414</item>
+ <item>21415</item>
+ <item>21417</item>
+ <item>21418</item>
+ <item>21419</item>
+ <item>21420</item>
+ <item>21421</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-mcc214-mnc17/config.xml b/core/res/res/values-mcc214-mnc17/config.xml
new file mode 100755
index 0000000..be92a32
--- /dev/null
+++ b/core/res/res/values-mcc214-mnc17/config.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** 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.
+*/
+-->
+
+<resources>
+ <string-array translatable="false" name="config_operatorConsideredNonRoaming">
+ <item>21401</item>
+ <item>21402</item>
+ <item>21403</item>
+ <item>21404</item>
+ <item>21405</item>
+ <item>21406</item>
+ <item>21407</item>
+ <item>21408</item>
+ <item>21409</item>
+ <item>21410</item>
+ <item>21411</item>
+ <item>21412</item>
+ <item>21413</item>
+ <item>21414</item>
+ <item>21415</item>
+ <item>21416</item>
+ <item>21418</item>
+ <item>21419</item>
+ <item>21420</item>
+ <item>21421</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-mcc214-mnc18/config.xml b/core/res/res/values-mcc214-mnc18/config.xml
new file mode 100755
index 0000000..078d7e2
--- /dev/null
+++ b/core/res/res/values-mcc214-mnc18/config.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** 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.
+*/
+-->
+
+<resources>
+ <string-array translatable="false" name="config_operatorConsideredNonRoaming">
+ <item>21401</item>
+ <item>21402</item>
+ <item>21403</item>
+ <item>21404</item>
+ <item>21405</item>
+ <item>21406</item>
+ <item>21407</item>
+ <item>21408</item>
+ <item>21409</item>
+ <item>21410</item>
+ <item>21411</item>
+ <item>21412</item>
+ <item>21413</item>
+ <item>21414</item>
+ <item>21415</item>
+ <item>21416</item>
+ <item>21417</item>
+ <item>21419</item>
+ <item>21420</item>
+ <item>21421</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-mcc214-mnc19/config.xml b/core/res/res/values-mcc214-mnc19/config.xml
new file mode 100755
index 0000000..d194687
--- /dev/null
+++ b/core/res/res/values-mcc214-mnc19/config.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** 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.
+*/
+-->
+
+<resources>
+ <string-array translatable="false" name="config_operatorConsideredNonRoaming">
+ <item>21401</item>
+ <item>21402</item>
+ <item>21403</item>
+ <item>21404</item>
+ <item>21405</item>
+ <item>21406</item>
+ <item>21407</item>
+ <item>21408</item>
+ <item>21409</item>
+ <item>21410</item>
+ <item>21411</item>
+ <item>21412</item>
+ <item>21413</item>
+ <item>21414</item>
+ <item>21415</item>
+ <item>21416</item>
+ <item>21417</item>
+ <item>21418</item>
+ <item>21420</item>
+ <item>21421</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-mcc214-mnc20/config.xml b/core/res/res/values-mcc214-mnc20/config.xml
new file mode 100755
index 0000000..6aaf970
--- /dev/null
+++ b/core/res/res/values-mcc214-mnc20/config.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** 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.
+*/
+-->
+
+<resources>
+ <string-array translatable="false" name="config_operatorConsideredNonRoaming">
+ <item>21401</item>
+ <item>21402</item>
+ <item>21403</item>
+ <item>21404</item>
+ <item>21405</item>
+ <item>21406</item>
+ <item>21407</item>
+ <item>21408</item>
+ <item>21409</item>
+ <item>21410</item>
+ <item>21411</item>
+ <item>21412</item>
+ <item>21413</item>
+ <item>21414</item>
+ <item>21415</item>
+ <item>21416</item>
+ <item>21417</item>
+ <item>21418</item>
+ <item>21419</item>
+ <item>21421</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-mcc214-mnc21/config.xml b/core/res/res/values-mcc214-mnc21/config.xml
new file mode 100755
index 0000000..f890b14
--- /dev/null
+++ b/core/res/res/values-mcc214-mnc21/config.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** 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.
+*/
+-->
+
+<resources>
+ <string-array translatable="false" name="config_operatorConsideredNonRoaming">
+ <item>21401</item>
+ <item>21402</item>
+ <item>21403</item>
+ <item>21404</item>
+ <item>21405</item>
+ <item>21406</item>
+ <item>21407</item>
+ <item>21408</item>
+ <item>21409</item>
+ <item>21410</item>
+ <item>21411</item>
+ <item>21412</item>
+ <item>21413</item>
+ <item>21414</item>
+ <item>21415</item>
+ <item>21416</item>
+ <item>21417</item>
+ <item>21418</item>
+ <item>21419</item>
+ <item>21420</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-mcc222-mnc10/config.xml b/core/res/res/values-mcc222-mnc10/config.xml
index 24dd71c..67467a0 100644
--- a/core/res/res/values-mcc222-mnc10/config.xml
+++ b/core/res/res/values-mcc222-mnc10/config.xml
@@ -35,4 +35,29 @@
"name,apn,proxy,port,username,password,server,mmsc,mmsproxy,mmsport,mcc,mnc,auth,type"
note that empty fields can be ommitted: "name,apn,,,,,,,,,310,260,,DUN" -->
<string translatable="false" name="config_tether_apndata">Tethering Internet,web.omnitel.it,,,,,,,,,222,10,,DUN</string>
+
+ <string-array translatable="false" name="config_operatorConsideredNonRoaming">
+ <item>21401</item>
+ <item>21402</item>
+ <item>21403</item>
+ <item>21404</item>
+ <item>21405</item>
+ <item>21406</item>
+ <item>21407</item>
+ <item>21408</item>
+ <item>21409</item>
+ <item>21410</item>
+ <item>21411</item>
+ <item>21412</item>
+ <item>21413</item>
+ <item>21414</item>
+ <item>21415</item>
+ <item>21416</item>
+ <item>21417</item>
+ <item>21418</item>
+ <item>21419</item>
+ <item>21420</item>
+ <item>21421</item>
+ </string-array>
+
</resources>
diff --git a/core/res/res/values-mcc234-mnc10/config.xml b/core/res/res/values-mcc234-mnc10/config.xml
new file mode 100644
index 0000000..b704d3f
--- /dev/null
+++ b/core/res/res/values-mcc234-mnc10/config.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** 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.
+*/
+-->
+
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array translatable="false" name="config_operatorConsideredNonRoaming">
+ <item>23420</item>
+ <item>23426</item>
+ <item>23430</item>
+ <item>23431</item>
+ <item>23432</item>
+ <item>23433</item>
+ <item>23434</item>
+ <item>23486</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-mcc234-mnc20/config.xml b/core/res/res/values-mcc234-mnc20/config.xml
index d602c9f..1ed53dc 100644
--- a/core/res/res/values-mcc234-mnc20/config.xml
+++ b/core/res/res/values-mcc234-mnc20/config.xml
@@ -24,5 +24,15 @@
<!-- Configure mobile network MTU. Carrier specific value is set here.
-->
<integer name="config_mobile_mtu">1440</integer>
+ <string-array translatable="false" name="config_operatorConsideredNonRoaming">
+ <item>23410</item>
+ <item>23426</item>
+ <item>23430</item>
+ <item>23431</item>
+ <item>23432</item>
+ <item>23433</item>
+ <item>23434</item>
+ <item>23486</item>
+ </string-array>
</resources>
diff --git a/core/res/res/values-mcc234-mnc26/config.xml b/core/res/res/values-mcc234-mnc26/config.xml
new file mode 100644
index 0000000..8d259de
--- /dev/null
+++ b/core/res/res/values-mcc234-mnc26/config.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** 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.
+*/
+-->
+
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array translatable="false" name="config_operatorConsideredNonRoaming">
+ <item>23410</item>
+ <item>23420</item>
+ <item>23430</item>
+ <item>23431</item>
+ <item>23432</item>
+ <item>23433</item>
+ <item>23434</item>
+ <item>23486</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-mcc302-mnc220/config.xml b/core/res/res/values-mcc302-mnc220/config.xml
new file mode 100644
index 0000000..5259152
--- /dev/null
+++ b/core/res/res/values-mcc302-mnc220/config.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** 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.
+*/
+-->
+
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string-array translatable="false" name="config_operatorConsideredNonRoaming">
+ <item>302370</item>
+ <item>302610</item>
+ <item>302660</item>
+ <item>302720</item>
+ <item>302780</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-mcc302-mnc370/config.xml b/core/res/res/values-mcc302-mnc370/config.xml
index 4fb2232..9cfe31f 100644
--- a/core/res/res/values-mcc302-mnc370/config.xml
+++ b/core/res/res/values-mcc302-mnc370/config.xml
@@ -40,4 +40,12 @@
-->
<integer name="config_mobile_mtu">1410</integer>
+ <string-array translatable="false" name="config_operatorConsideredNonRoaming">
+ <item>302220</item>
+ <item>302610</item>
+ <item>302660</item>
+ <item>302720</item>
+ <item>302780</item>
+ </string-array>
+
</resources>
diff --git a/core/res/res/values-mcc302-mnc660/config.xml b/core/res/res/values-mcc302-mnc660/config.xml
index 76f7968..000f897 100644
--- a/core/res/res/values-mcc302-mnc660/config.xml
+++ b/core/res/res/values-mcc302-mnc660/config.xml
@@ -40,4 +40,12 @@
-->
<integer name="config_mobile_mtu">1430</integer>
+ <string-array translatable="false" name="config_operatorConsideredNonRoaming">
+ <item>302220</item>
+ <item>302370</item>
+ <item>302610</item>
+ <item>302720</item>
+ <item>302780</item>
+ </string-array>
+
</resources>
diff --git a/core/res/res/values-mcc302-mnc720/config.xml b/core/res/res/values-mcc302-mnc720/config.xml
index 4eceffc..dfc58fe 100644
--- a/core/res/res/values-mcc302-mnc720/config.xml
+++ b/core/res/res/values-mcc302-mnc720/config.xml
@@ -40,4 +40,12 @@
-->
<integer name="config_mobile_mtu">1430</integer>
+ <string-array translatable="false" name="config_operatorConsideredNonRoaming">
+ <item>302220</item>
+ <item>302370</item>
+ <item>302610</item>
+ <item>302660</item>
+ <item>302780</item>
+ </string-array>
+
</resources>
diff --git a/core/res/res/values-mcc310-mnc150/config.xml b/core/res/res/values-mcc310-mnc150/config.xml
new file mode 100644
index 0000000..f1936f4
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc150/config.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** 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.
+*/
+-->
+
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Show roaming icon though same named operators. -->
+ <string-array translatable="false" name="config_sameNamedOperatorConsideredRoaming">
+ <item>310110</item>
+ <item>310140</item>
+ <item>310400</item>
+ <item>310470</item>
+ </string-array>
+ <string-array translatable="false" name="config_operatorConsideredNonRoaming">
+ <item>311</item>
+ <item>312</item>
+ <item>313</item>
+ <item>314</item>
+ <item>315</item>
+ <item>316</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc170 b/core/res/res/values-mcc310-mnc170
new file mode 120000
index 0000000..cfced17
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc170
@@ -0,0 +1 @@
+./values-mcc310-mnc150
\ No newline at end of file
diff --git a/core/res/res/values-mcc310-mnc380 b/core/res/res/values-mcc310-mnc380
new file mode 120000
index 0000000..cfced17
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc380
@@ -0,0 +1 @@
+./values-mcc310-mnc150
\ No newline at end of file
diff --git a/core/res/res/values-mcc310-mnc410/config.xml b/core/res/res/values-mcc310-mnc410/config.xml
index 73aa1ce..c7ae8c8 100644
--- a/core/res/res/values-mcc310-mnc410/config.xml
+++ b/core/res/res/values-mcc310-mnc410/config.xml
@@ -24,5 +24,20 @@
<!-- Configure mobile network MTU. Carrier specific value is set here.
-->
<integer name="config_mobile_mtu">1410</integer>
-
+ <!-- Show roaming icon though same named operators. -->
+ <string-array translatable="false" name="config_sameNamedOperatorConsideredRoaming">
+ <item>310110</item>
+ <item>310140</item>
+ <item>310400</item>
+ <item>310470</item>
+ </string-array>
+ <!-- BEGIN Motorola, xnk746, Feb-12-2014, IKVPREL2KK-1468 -->
+ <string-array translatable="false" name="config_operatorConsideredNonRoaming">
+ <item>311</item>
+ <item>312</item>
+ <item>313</item>
+ <item>314</item>
+ <item>315</item>
+ <item>316</item>
+ </string-array>
</resources>
diff --git a/core/res/res/values-mcc340-mnc01/config.xml b/core/res/res/values-mcc340-mnc01/config.xml
index bbab4ad..bb491ed 100644
--- a/core/res/res/values-mcc340-mnc01/config.xml
+++ b/core/res/res/values-mcc340-mnc01/config.xml
@@ -35,4 +35,10 @@
"name,apn,proxy,port,username,password,server,mmsc,mmsproxy,mmsport,mcc,mnc,auth,type"
note that empty fields can be ommitted: "name,apn,,,,,,,,,310,260,,DUN" -->
<string translatable="false" name="config_tether_apndata">Orangeweb,orangeweb,,,orange,orange,,,,,340,01,1,DUN</string>
+
+ <string-array translatable="false" name="config_operatorConsideredNonRoaming">
+ <item>20801</item>
+ <item>20815</item>
+ </string-array>
+
</resources>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 7f68492..89bda82 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -3163,6 +3163,17 @@
result to valid color values. Saturate(S + D) -->
<enum name="add" value="16" />
</attr>
+ <!-- Gravity for aligning a CheckedTextView's checkmark to one side or the other. -->
+ <attr name="checkMarkGravity">
+ <!-- Push object to the left of its container, not changing its size. -->
+ <flag name="left" value="0x03" />
+ <!-- Push object to the right of its container, not changing its size. -->
+ <flag name="right" value="0x05" />
+ <!-- Push object to the beginning of its container, not changing its size. -->
+ <flag name="start" value="0x00800003" />
+ <!-- Push object to the end of its container, not changing its size. -->
+ <flag name="end" value="0x00800005" />
+ </attr>
</declare-styleable>
<declare-styleable name="EditText">
</declare-styleable>
@@ -7276,9 +7287,8 @@
<!-- Component name of an activity that allows the user to modify
the settings for this service. -->
<attr name="settingsActivity" />
- <!-- Component name of an xml file that describes the structure of TV content ratings that
- this service uses. -->
- <attr name="contentRatingSystemXml" format="reference" />
+ <!-- Reference to an XML document that describes TV content rating. -->
+ <attr name="tvContentRatingDescription" format="reference" />
</declare-styleable>
<declare-styleable name="ResolverDrawerLayout">
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index c268d97..7d4c37e 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -1250,7 +1250,9 @@
application.
<p>This appears as a child tag of the root
- {@link #AndroidManifest manifest} tag. -->
+ {@link #AndroidManifest manifest} tag.
+
+ @deprecated Use <code>feature-group</code> instead.-->
<declare-styleable name="AndroidManifestUsesConfiguration" parent="AndroidManifest">
<!-- The type of touch screen used by an application. -->
<attr name="reqTouchScreen" />
diff --git a/core/res/res/values/colors_leanback.xml b/core/res/res/values/colors_leanback.xml
index dc0c673..e52a861e 100644
--- a/core/res/res/values/colors_leanback.xml
+++ b/core/res/res/values/colors_leanback.xml
@@ -25,4 +25,5 @@
<color name="primary_text_leanback_light">#cc222222</color>
<color name="secondary_text_leanback_light">#99222222</color>
+ <color name="primary_text_leanback_formwizard_default_dark">#ffeeeeee</color>
</resources>
diff --git a/core/res/res/values/dimens_leanback.xml b/core/res/res/values/dimens_leanback.xml
new file mode 100644
index 0000000..c824a2a
--- /dev/null
+++ b/core/res/res/values/dimens_leanback.xml
@@ -0,0 +1,86 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2014 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android">
+ <!-- Default alpha value for disabled elements. -->
+ <item name="disabled_alpha_leanback_formwizard" format="float" type="dimen">0.2</item>
+ <!-- The duration of most animations related to screen content transitions -->
+ <integer name="leanback_setup_base_animation_duration">500</integer>
+ <item name="leanback_setup_alpha_animiation_max_opacity" format="float" type="dimen">1.0</item>
+ <item name="leanback_setup_alpha_animiation_min_opacity" format="float" type="dimen">0.0</item>
+ <!-- Where stable, on-screen content rests -->
+ <dimen name="leanback_setup_translation_content_resting_point">0dp</dimen>
+ <integer name="leanback_setup_translation_content_resting_point_v4">0</integer>
+ <!-- The screen position at which content enters/exits. If you're over the edge of the cliff, we can't see you. -->
+ <dimen name="leanback_setup_translation_content_cliff">100dp</dimen>
+ <integer name="leanback_setup_translation_content_cliff_v4">200</integer>
+
+ <!-- Opacity animation for activity background -->
+ <!-- The opacity of the background of the new activity background when the alpha animation starts-->
+ <item name="leanback_setup_alpha_activity_in_bkg_start" format="float" type="dimen">@dimen/leanback_setup_alpha_animiation_min_opacity</item>
+ <!-- The opacity of the background of the new activity background when the alpha animation ends-->
+ <item name="leanback_setup_alpha_activity_in_bkg_end" format="float" type="dimen">@dimen/leanback_setup_alpha_animiation_max_opacity</item>
+ <integer name="leanback_setup_alpha_activity_in_bkg_delay">0</integer>
+ <integer name="leanback_setup_alpha_activity_in_bkg_duration">@integer/leanback_setup_base_animation_duration</integer>
+
+ <item name="leanback_setup_alpha_activity_out_bkg_start" format="float" type="dimen">@dimen/leanback_setup_alpha_animiation_max_opacity</item>
+ <!-- The opacity of the background of the new activity background when the alpha animation ends-->
+ <item name="leanback_setup_alpha_activity_out_bkg_end" format="float" type="dimen">@dimen/leanback_setup_alpha_animiation_min_opacity</item>
+ <integer name="leanback_setup_alpha_activity_out_bkg_delay">0</integer>
+ <integer name="leanback_setup_alpha_activity_out_bkg_duration">@integer/leanback_setup_base_animation_duration</integer>
+
+ <!-- Content forward animation configuration values -->
+ <!-- Parameter for alpha animation of new content coming on to the screen when we're moving "forward" -->
+ <!-- Initial opacity of the new content that is coming on to the screen -->
+ <item name="leanback_setup_alpha_forward_in_content_start" format="float" type="dimen">@dimen/leanback_setup_alpha_animiation_min_opacity</item>
+ <item name="leanback_setup_alpha_forward_in_content_end" format="float" type="dimen">@dimen/leanback_setup_alpha_animiation_max_opacity</item>
+ <integer name="leanback_setup_alpha_forward_in_content_delay">0</integer>
+ <integer name="leanback_setup_alpha_forward_in_content_duration">@integer/leanback_setup_base_animation_duration</integer>
+
+ <item name="leanback_setup_alpha_forward_out_content_start" format="float" type="dimen">@dimen/leanback_setup_alpha_animiation_max_opacity</item>
+ <item name="leanback_setup_alpha_forward_out_content_end" format="float" type="dimen">@dimen/leanback_setup_alpha_animiation_min_opacity</item>
+ <integer name="leanback_setup_alpha_forward_out_content_delay">0</integer>
+ <integer name="leanback_setup_alpha_forward_out_content_duration">@integer/leanback_setup_base_animation_duration</integer>
+
+ <!-- Position animation of incoming content during a "forward" transition -->
+ <dimen name="leanback_setup_translation_forward_in_content_start">@dimen/leanback_setup_translation_content_cliff</dimen>
+ <dimen name="leanback_setup_translation_forward_in_content_start_v4">@integer/leanback_setup_translation_content_cliff_v4</dimen>
+ <dimen name="leanback_setup_translation_forward_in_content_end">@dimen/leanback_setup_translation_content_resting_point</dimen>
+ <dimen name="leanback_setup_translation_forward_in_content_end_v4">@integer/leanback_setup_translation_content_resting_point_v4</dimen>
+ <integer name="leanback_setup_translation_forward_in_content_delay">0</integer>
+ <integer name="leanback_setup_translation_forward_in_content_duration">@integer/leanback_setup_base_animation_duration</integer>
+
+ <!-- Content backward animation configuration values -->
+ <!-- Alpha animation values for the content that will be displayed after the transition is complete, this is the content coming in. -->
+ <item name="leanback_setup_alpha_backward_in_content_start" format="float" type="dimen">@dimen/leanback_setup_alpha_animiation_min_opacity</item>
+ <item name="leanback_setup_alpha_backward_in_content_end" format="float" type="dimen">@dimen/leanback_setup_alpha_animiation_max_opacity</item>
+ <integer name="leanback_setup_alpha_backward_in_content_delay">0</integer>
+ <integer name="leanback_setup_alpha_backward_in_content_duration">@integer/leanback_setup_base_animation_duration</integer>
+
+ <!-- Alpha animiation values for the content that is displayed when the transition starts, this is the content going away. -->
+ <item name="leanback_setup_alpha_backward_out_content_start" format="float" type="dimen">@dimen/leanback_setup_alpha_animiation_max_opacity</item>
+ <item name="leanback_setup_alpha_backward_out_content_end" format="float" type="dimen">@dimen/leanback_setup_alpha_animiation_min_opacity</item>
+ <integer name="leanback_setup_alpha_backward_out_content_delay">0</integer>
+ <integer name="leanback_setup_alpha_backward_out_content_duration">@integer/leanback_setup_base_animation_duration</integer>
+
+ <!-- Position animation for content that is displayed when the transition starts, this is the content going away. -->
+ <dimen name="leanback_setup_translation_backward_out_content_start">@dimen/leanback_setup_translation_content_resting_point</dimen>
+ <dimen name="leanback_setup_translation_backward_out_content_start_v4">@integer/leanback_setup_translation_content_resting_point_v4</dimen>
+ <dimen name="leanback_setup_translation_backward_out_content_end">@dimen/leanback_setup_translation_content_cliff</dimen>
+ <dimen name="leanback_setup_translation_backward_out_content_end_v4">@integer/leanback_setup_translation_content_cliff_v4</dimen>
+ <integer name="leanback_setup_translation_backward_out_content_delay">0</integer>
+ <integer name="leanback_setup_translation_backward_out_content_duration">@integer/leanback_setup_base_animation_duration</integer>
+</resources>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 60aacf2..e894a9c 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2267,7 +2267,7 @@
<public type="attr" name="windowReenterTransition" />
<public type="attr" name="windowSharedElementReturnTransition" />
<public type="attr" name="windowSharedElementReenterTransition" />
- <public type="attr" name="contentRatingSystemXml"/>
+ <public type="attr" name="tvContentRatingDescription"/>
<public type="attr" name="datePickerMode"/>
<public type="attr" name="timePickerMode"/>
<public type="attr" name="inset" />
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 5011421..6262f13 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -4844,24 +4844,28 @@
<string name="day_of_week_label_typeface">sans-serif</string>
<!-- Notify use that they are in Lock-to-app -->
- <string name="lock_to_app_toast">You are in lock-to-app mode. To exit, touch and hold the Recents button</string>
+ <string name="lock_to_app_toast">To unpin this screen, touch and hold Back and Recents at the same time.</string>
+ <!-- Notify use that they are in Lock-to-app in accessibility mode -->
+ <string name="lock_to_app_toast_accessible">To unpin this screen, touch and hold Recents.</string>
<!-- Notify user that they are locked in lock-to-app mode -->
- <string name="lock_to_app_toast_locked">You are in Lock-to-App mode.</string>
+ <string name="lock_to_app_toast_locked">Screen is pinned. Unpinning isn\'t allowed by your organization.</string>
<!-- Lock-to-app dialog title. -->
- <string name="lock_to_app_title">Use lock-to-app?</string>
+ <string name="lock_to_app_title">Use screen pinning?</string>
<!-- Lock-to-app dialog description. -->
- <string name="lock_to_app_description">Lock-to-app locks the display in a single app.\n\nTo exit, touch and hold the Recents button.</string>
+ <string name="lock_to_app_description">Screen pinning locks the display in a single view.\n\nTo exit, touch and hold Back and Recents at the same time.</string>
+ <!-- Lock-to-app dialog description when in accessibility mode. -->
+ <string name="lock_to_app_description_accessible">Screen pinning locks the display in a single view.\n\nTo exit, touch and hold Recents.</string>
<!-- Lock-to-app negative response. -->
<string name="lock_to_app_negative">NO, THANKS</string>
<!-- Lock-to-app positive response. -->
<string name="lock_to_app_positive">START</string>
<!-- Starting lock-to-app indication. -->
- <string name="lock_to_app_start">Locked to app</string>
+ <string name="lock_to_app_start">Screen pinned</string>
<!-- Exting lock-to-app indication. -->
- <string name="lock_to_app_exit">No longer locked to app</string>
+ <string name="lock_to_app_exit">Screen unpinned</string>
<!-- Lock-to-app checkbox for lock on exit -->
- <string name="lock_to_app_use_screen_lock">Ask for %1$s before exiting</string>
+ <string name="lock_to_app_use_screen_lock">Ask for %1$s before unpinning</string>
<!-- Lock-to-app unlock pin string -->
<string name="lock_to_app_unlock_pin">PIN</string>
@@ -4872,35 +4876,35 @@
<!-- Range specific TV content rating system strings for AM TV -->
<string name="display_name_amtvrs" translatable="false">AM-TV-RS</string>
- <string name="description_amtvrs">Range specific TV content rating system strings for Armenia</string>
+ <string name="description_amtvrs" translatable="false">Range specific TV content rating system strings for Armenia</string>
<string name="display_name_amtvrs_y" translatable="false">Y</string>
<string name="display_name_amtvrs_y7" translatable="false">Y7</string>
<string name="display_name_amtvrs_ga" translatable="false">GA</string>
<string name="display_name_amtvrs_tw" translatable="false">TW</string>
<string name="display_name_amtvrs_t" translatable="false">T</string>
<string name="display_name_amtvrs_a" translatable="false">A</string>
- <string name="description_amtvrs_y">Suitable for ages 2-11</string>
- <string name="description_amtvrs_y7">Suitable for ages 7-16</string>
- <string name="description_amtvrs_ga">Suitable for general audiences</string>
- <string name="description_amtvrs_tw">Suitable for teens ages 9 and up</string>
- <string name="description_amtvrs_t">Suitable for teens ages 12 and up</string>
- <string name="description_amtvrs_a">Suitable only for adults ages 18 and up</string>
+ <string name="description_amtvrs_y" translatable="false">Suitable for ages 2-11</string>
+ <string name="description_amtvrs_y7" translatable="false">Suitable for ages 7-16</string>
+ <string name="description_amtvrs_ga" translatable="false">Suitable for general audiences</string>
+ <string name="description_amtvrs_tw" translatable="false">Suitable for teens ages 9 and up</string>
+ <string name="description_amtvrs_t" translatable="false">Suitable for teens ages 12 and up</string>
+ <string name="description_amtvrs_a" translatable="false">Suitable only for adults ages 18 and up</string>
<!-- Age specific TV content rating system strings for AM TV -->
<string name="display_name_amtvas" translatable="false">AM-TV-AS</string>
- <string name="description_amtvas">Age specific TV content rating system strings for Armenia</string>
+ <string name="description_amtvas" translatable="false">Age specific TV content rating system strings for Armenia</string>
<string name="display_name_amtvas_ec" translatable="false">EC</string>
<string name="display_name_amtvas_e" translatable="false">E</string>
<string name="display_name_amtvas_e9" translatable="false">E9</string>
<string name="display_name_amtvas_t" translatable="false">T</string>
<string name="display_name_amtvas_m" translatable="false">M</string>
<string name="display_name_amtvas_ao" translatable="false">AO</string>
- <string name="description_amtvas_ec">Suitable for ages 2 and up</string>
- <string name="description_amtvas_e">Suitable for ages 5 and up</string>
- <string name="description_amtvas_e9">Suitable for ages 9 and up</string>
- <string name="description_amtvas_t">Suitable for ages 12 and up</string>
- <string name="description_amtvas_m">Suitable for ages 16 and up</string>
- <string name="description_amtvas_ao">Suitable for ages 17 and up</string>
+ <string name="description_amtvas_ec" translatable="false">Suitable for ages 2 and up</string>
+ <string name="description_amtvas_e" translatable="false">Suitable for ages 5 and up</string>
+ <string name="description_amtvas_e9" translatable="false">Suitable for ages 9 and up</string>
+ <string name="description_amtvas_t" translatable="false">Suitable for ages 12 and up</string>
+ <string name="description_amtvas_m" translatable="false">Suitable for ages 16 and up</string>
+ <string name="description_amtvas_ao" translatable="false">Suitable for ages 17 and up</string>
<!-- TV content rating system strings for AR TV -->
<string name="display_name_artv" translatable="false">AR-TV</string>
@@ -4908,10 +4912,10 @@
<string name="display_name_artv_13" translatable="false">Apto para mayores de 13 años</string>
<string name="display_name_artv_16" translatable="false">Apto para mayores de 16 años</string>
<string name="display_name_artv_18" translatable="false">Apto para mayores de 18 años</string>
- <string name="description_artv_all">Suitable for all audiences. Programs may contain mild violence, language and mature situations</string>
- <string name="description_artv_13">Suitable for ages 13 and up. Programs may contain mild to moderate language and mild violence and sexual references</string>
- <string name="description_artv_16">Suitable for ages 16 and up. Programs may contain more intensive violence and coarse language, partial nudity and moderate sexual references</string>
- <string name="description_artv_18">Suitable for mature audiences only. Programs contain strong violence, coarse language and explicit sexual references</string>
+ <string name="description_artv_all" translatable="false">Suitable for all audiences. Programs may contain mild violence, language and mature situations</string>
+ <string name="description_artv_13" translatable="false">Suitable for ages 13 and up. Programs may contain mild to moderate language and mild violence and sexual references</string>
+ <string name="description_artv_16" translatable="false">Suitable for ages 16 and up. Programs may contain more intensive violence and coarse language, partial nudity and moderate sexual references</string>
+ <string name="description_artv_18" translatable="false">Suitable for mature audiences only. Programs contain strong violence, coarse language and explicit sexual references</string>
<!-- TV content rating system strings for AU TV -->
<string name="display_name_autv" translatable="false">Australian TV Classification</string>
@@ -4922,13 +4926,13 @@
<string name="display_name_autv_ma15" translatable="false">MA 15+</string>
<string name="display_name_autv_r18" translatable="false">R 18+</string>
<string name="display_name_autv_x18" translatable="false">X 18+</string>
- <string name="description_autv_ctc">This has advertising approval, but is not yet classified</string>
- <string name="description_autv_g">The content is very mild in impact, and suitable for everyone</string>
- <string name="description_autv_pg">The content is mild in impact, but it may contain content that children find confusing or upsetting and may require the guidance or parents and guardians</string>
- <string name="description_autv_m">The content is moderate in impact, and it is recommended for teenagers aged 15 years and over</string>
- <string name="description_autv_ma15">The content is strong in impact, and it is legally restricted to persons 15 years and over</string>
- <string name="description_autv_r18">The content is high in impact, and it is restricted to adults</string>
- <string name="description_autv_x18">The content is restricted to adults. This classification is a special and legally restricted category which contains only sexually explicit content</string>
+ <string name="description_autv_ctc" translatable="false">This has advertising approval, but is not yet classified</string>
+ <string name="description_autv_g" translatable="false">The content is very mild in impact, and suitable for everyone</string>
+ <string name="description_autv_pg" translatable="false">The content is mild in impact, but it may contain content that children find confusing or upsetting and may require the guidance or parents and guardians</string>
+ <string name="description_autv_m" translatable="false">The content is moderate in impact, and it is recommended for teenagers aged 15 years and over</string>
+ <string name="description_autv_ma15" translatable="false">The content is strong in impact, and it is legally restricted to persons 15 years and over</string>
+ <string name="description_autv_r18" translatable="false">The content is high in impact, and it is restricted to adults</string>
+ <string name="description_autv_x18" translatable="false">The content is restricted to adults. This classification is a special and legally restricted category which contains only sexually explicit content</string>
<!-- TV content rating system strings for BG TV -->
<string name="display_name_bgtv" translatable="false">BG-TV</string>
@@ -4937,11 +4941,11 @@
<string name="display_name_bgtv_c" translatable="false">C</string>
<string name="display_name_bgtv_d" translatable="false">D</string>
<string name="display_name_bgtv_x" translatable="false">X</string>
- <string name="description_bgtv_a">Recommended to children. When the film confirms the ideals of humanism or popularizes the national and world cultures or contributes to upbringing children</string>
- <string name="description_bgtv_b">No restrictive recommendations from the Committee. When the film is in no way contrary to the universal rules of morality in this country, has no restrictive recommendations from the Committee and does not fall in rating A</string>
- <string name="description_bgtv_c">No persons under the age of 12 are admitted unless accompanied by an adult. When the film contains certain erotic scenes or scenes with drinking, taking drugs or stimulants or a few scenes of violence</string>
- <string name="description_bgtv_d">No persons under the age of 16 are admitted. When the film contains quite a number of erotic scenes or scenes with drinking, taking drugs or stimulants or a considerable number of scenes showing violence</string>
- <string name="description_bgtv_x">No persons under the age of 18 are admitted. When the film is naturalistically erotic or shows violence in an ostentatious manner</string>
+ <string name="description_bgtv_a" translatable="false">Recommended to children. When the film confirms the ideals of humanism or popularizes the national and world cultures or contributes to upbringing children</string>
+ <string name="description_bgtv_b" translatable="false">No restrictive recommendations from the Committee. When the film is in no way contrary to the universal rules of morality in this country, has no restrictive recommendations from the Committee and does not fall in rating A</string>
+ <string name="description_bgtv_c" translatable="false">No persons under the age of 12 are admitted unless accompanied by an adult. When the film contains certain erotic scenes or scenes with drinking, taking drugs or stimulants or a few scenes of violence</string>
+ <string name="description_bgtv_d" translatable="false">No persons under the age of 16 are admitted. When the film contains quite a number of erotic scenes or scenes with drinking, taking drugs or stimulants or a considerable number of scenes showing violence</string>
+ <string name="description_bgtv_x" translatable="false">No persons under the age of 18 are admitted. When the film is naturalistically erotic or shows violence in an ostentatious manner</string>
<!-- TV content rating system strings for BR TV -->
<string name="display_name_brtv" translatable="false">Brazil Content Rating</string>
@@ -4951,12 +4955,12 @@
<string name="display_name_brtv_14" translatable="false">14 anos</string>
<string name="display_name_brtv_16" translatable="false">16 anos</string>
<string name="display_name_brtv_18" translatable="false">18 anos</string>
- <string name="description_brtv_l">Content is suitable for all audiences</string>
- <string name="description_brtv_10">Content suitable for viewers over the age of 10</string>
- <string name="description_brtv_12">Content suitable for viewers over the age of 12</string>
- <string name="description_brtv_14">Content suitable for viewers over the age of 14</string>
- <string name="description_brtv_16">Content suitable for viewers over the age of 16</string>
- <string name="description_brtv_18">Content suitable for viewers over the age of 18</string>
+ <string name="description_brtv_l" translatable="false">Content is suitable for all audiences</string>
+ <string name="description_brtv_10" translatable="false">Content suitable for viewers over the age of 10</string>
+ <string name="description_brtv_12" translatable="false">Content suitable for viewers over the age of 12</string>
+ <string name="description_brtv_14" translatable="false">Content suitable for viewers over the age of 14</string>
+ <string name="description_brtv_16" translatable="false">Content suitable for viewers over the age of 16</string>
+ <string name="description_brtv_18" translatable="false">Content suitable for viewers over the age of 18</string>
<!-- TV content rating system strings for CA TV -->
<string name="display_name_catv" translatable="false">Canadian TV Classification System</string>
@@ -4967,20 +4971,20 @@
<string name="display_name_catv_pg" translatable="false">PG</string>
<string name="display_name_catv_14" translatable="false">14+</string>
<string name="display_name_catv_18" translatable="false">18+</string>
- <string name="description_catv_exempt">Shows which are exempt from ratings (such as news and sports programming) will not display an on-screen rating at all</string>
- <string name="description_catv_c">Programming suitable for children ages of 2-7 years. No profanity or sexual content of any level allowed. Contains little violence</string>
- <string name="description_catv_c8">Suitable for children ages 8+. Low level violence and fantasy horror is allowed. No foul language is allowed, but occasional "socially offensive and discriminatory" language is allowed if in the context of the story. No sexual content of any level allowed</string>
- <string name="description_catv_g">Suitable for general audiences. Programming suitable for the entire family with mild violence, and mild profanity and/or censored language</string>
- <string name="description_catv_pg">Parental guidance. Moderate violence and moderate profanity is allowed, as is brief nudity and sexual references if important to the context of the story</string>
- <string name="description_catv_14">Programming intended for viewers ages 14 and older. May contain strong violence and strong profanity, and depictions of sexual activity as long as they are within the context of a story</string>
- <string name="description_catv_18">Programming intended for viewers ages 18 and older. May contain explicit violence and sexual activity</string>
+ <string name="description_catv_exempt" translatable="false">Shows which are exempt from ratings (such as news and sports programming) will not display an on-screen rating at all</string>
+ <string name="description_catv_c" translatable="false">Programming suitable for children ages of 2-7 years. No profanity or sexual content of any level allowed. Contains little violence</string>
+ <string name="description_catv_c8" translatable="false">Suitable for children ages 8+. Low level violence and fantasy horror is allowed. No foul language is allowed, but occasional "socially offensive and discriminatory" language is allowed if in the context of the story. No sexual content of any level allowed</string>
+ <string name="description_catv_g" translatable="false">Suitable for general audiences. Programming suitable for the entire family with mild violence, and mild profanity and/or censored language</string>
+ <string name="description_catv_pg" translatable="false">Parental guidance. Moderate violence and moderate profanity is allowed, as is brief nudity and sexual references if important to the context of the story</string>
+ <string name="description_catv_14" translatable="false">Programming intended for viewers ages 14 and older. May contain strong violence and strong profanity, and depictions of sexual activity as long as they are within the context of a story</string>
+ <string name="description_catv_18" translatable="false">Programming intended for viewers ages 18 and older. May contain explicit violence and sexual activity</string>
<!-- TV content rating system strings for CH TV -->
<string name="display_name_chtv" translatable="false">CH-TV</string>
<string name="display_name_chtv_all" translatable="false">All ages</string>
<string name="display_name_chtv_red" translatable="false">Red rectangle</string>
- <string name="description_chtv_all">This program is suitable for all ages</string>
- <string name="description_chtv_red">This program contains scenes that may hurt sensitive people, therefore the red symbol will be displayed</string>
+ <string name="description_chtv_all" translatable="false">This program is suitable for all ages</string>
+ <string name="description_chtv_red" translatable="false">This program contains scenes that may hurt sensitive people, therefore the red symbol will be displayed</string>
<!-- TV content rating system strings for CL TV -->
<string name="display_name_cltv" translatable="false">CL-TV</string>
@@ -4991,13 +4995,13 @@
<string name="display_name_cltv_f" translatable="false">F</string>
<string name="display_name_cltv_r" translatable="false">R</string>
<string name="display_name_cltv_a" translatable="false">A</string>
- <string name="description_cltv_i">Programs suitable for all children</string>
- <string name="description_cltv_i7">Programs recommended for children ages 7 or older</string>
- <string name="description_cltv_i10">Programs recommended for children ages 10 or older</string>
- <string name="description_cltv_i12">Programs recommended for children and teens ages 12 or older</string>
- <string name="description_cltv_f">Programs suitable for a general audience, with content appropriate for all ages</string>
- <string name="description_cltv_r">Programs may content not suitable for children not accompanied by an adult</string>
- <string name="description_cltv_a">Programs suitable for adult audiences only (ages 18 or older), may contain coarse language, and sexual or explicit situations</string>
+ <string name="description_cltv_i" translatable="false">Programs suitable for all children</string>
+ <string name="description_cltv_i7" translatable="false">Programs recommended for children ages 7 or older</string>
+ <string name="description_cltv_i10" translatable="false">Programs recommended for children ages 10 or older</string>
+ <string name="description_cltv_i12" translatable="false">Programs recommended for children and teens ages 12 or older</string>
+ <string name="description_cltv_f" translatable="false">Programs suitable for a general audience, with content appropriate for all ages</string>
+ <string name="description_cltv_r" translatable="false">Programs may content not suitable for children not accompanied by an adult</string>
+ <string name="description_cltv_a" translatable="false">Programs suitable for adult audiences only (ages 18 or older), may contain coarse language, and sexual or explicit situations</string>
<!-- TV content rating system strings for DE TV -->
<string name="display_name_detv" translatable="false">DE-TV</string>
@@ -5005,10 +5009,10 @@
<string name="display_name_detv_12" translatable="false">ab 12 Jahren</string>
<string name="display_name_detv_16" translatable="false">ab 16 Jahren</string>
<string name="display_name_detv_18" translatable="false">ab 18 Jahren</string>
- <string name="description_detv_all">The program is suitable for all ages</string>
- <string name="description_detv_12">The program is not suitable for viewers under the age of 12</string>
- <string name="description_detv_16">The program is not suitable for viewers under the age of 16</string>
- <string name="description_detv_18">The program is not suitable for viewers under the age of 18</string>
+ <string name="description_detv_all" translatable="false">The program is suitable for all ages</string>
+ <string name="description_detv_12" translatable="false">The program is not suitable for viewers under the age of 12</string>
+ <string name="description_detv_16" translatable="false">The program is not suitable for viewers under the age of 16</string>
+ <string name="description_detv_18" translatable="false">The program is not suitable for viewers under the age of 18</string>
<!-- TV content rating system strings for DK TV -->
<string name="display_name_dktv" translatable="false">DK-TV</string>
@@ -5016,10 +5020,10 @@
<string name="display_name_dktv_y" translatable="false">Yellow symbol</string>
<string name="display_name_dktv_r" translatable="false">Red symbol</string>
<string name="display_name_dktv_b" translatable="false">Blue symbol</string>
- <string name="description_dktv_g">programs suitable for all ages</string>
- <string name="description_dktv_y">programs suitable children accompanied by an adult</string>
- <string name="description_dktv_r">programs containing material with more intensive content </string>
- <string name="description_dktv_b">programs containing explicit content and strictly for adults only</string>
+ <string name="description_dktv_g" translatable="false">programs suitable for all ages</string>
+ <string name="description_dktv_y" translatable="false">programs suitable children accompanied by an adult</string>
+ <string name="description_dktv_r" translatable="false">programs containing material with more intensive content </string>
+ <string name="description_dktv_b" translatable="false">programs containing explicit content and strictly for adults only</string>
<!-- TV content rating system strings for ES TV -->
<string name="display_name_estv" translatable="false">ES-TV</string>
@@ -5030,13 +5034,13 @@
<string name="display_name_estv_12" translatable="false">12</string>
<string name="display_name_estv_16" translatable="false">16</string>
<string name="display_name_estv_18" translatable="false">18</string>
- <string name="description_estv_tp">Recommended for all ages</string>
- <string name="description_estv_i">Specially recommended for preschoolers and kids</string>
- <string name="description_estv_7">Recommended for people older than 7 years old</string>
- <string name="description_estv_7i">Recommended for kids older than 7 years old</string>
- <string name="description_estv_12">Recommended for people older than 12 years old</string>
- <string name="description_estv_16">Recommended for people older than 16 years old</string>
- <string name="description_estv_18">Recommended for people older than 18 years old</string>
+ <string name="description_estv_tp" translatable="false">Recommended for all ages</string>
+ <string name="description_estv_i" translatable="false">Specially recommended for preschoolers and kids</string>
+ <string name="description_estv_7" translatable="false">Recommended for people older than 7 years old</string>
+ <string name="description_estv_7i" translatable="false">Recommended for kids older than 7 years old</string>
+ <string name="description_estv_12" translatable="false">Recommended for people older than 12 years old</string>
+ <string name="description_estv_16" translatable="false">Recommended for people older than 16 years old</string>
+ <string name="description_estv_18" translatable="false">Recommended for people older than 18 years old</string>
<!-- TV content rating system strings for FI TV -->
<string name="display_name_fitv" translatable="false">FI-TV</string>
@@ -5045,11 +5049,11 @@
<string name="display_name_fitv_k12" translatable="false">K12</string>
<string name="display_name_fitv_k16" translatable="false">K16</string>
<string name="display_name_fitv_k18" translatable="false">K18</string>
- <string name="description_fitv_s">Allowed at all times</string>
- <string name="description_fitv_k7">Not recommended for children under 7</string>
- <string name="description_fitv_k12">Not recommended for children under 12</string>
- <string name="description_fitv_k16">Not recommended for children under 16</string>
- <string name="description_fitv_k18">Not recommended for children under 18</string>
+ <string name="description_fitv_s" translatable="false">Allowed at all times</string>
+ <string name="description_fitv_k7" translatable="false">Not recommended for children under 7</string>
+ <string name="description_fitv_k12" translatable="false">Not recommended for children under 12</string>
+ <string name="description_fitv_k16" translatable="false">Not recommended for children under 16</string>
+ <string name="description_fitv_k18" translatable="false">Not recommended for children under 18</string>
<!-- TV content rating system strings for FR TV -->
<string name="display_name_frtv" translatable="false">FR-TV</string>
@@ -5058,11 +5062,11 @@
<string name="display_name_frtv_12" translatable="false">Déconseillé aux -12 ans</string>
<string name="display_name_frtv_16" translatable="false">Déconseillé aux -16 ans</string>
<string name="display_name_frtv_18" translatable="false">Déconseillé aux -18 ans</string>
- <string name="description_frtv_all">Appropriate for all ages</string>
- <string name="description_frtv_10">Not recommended for children under 10</string>
- <string name="description_frtv_12">Not recommended for children under 12</string>
- <string name="description_frtv_16">Not recommended for children under 16</string>
- <string name="description_frtv_18">Not recommended for persons under 18</string>
+ <string name="description_frtv_all" translatable="false">Appropriate for all ages</string>
+ <string name="description_frtv_10" translatable="false">Not recommended for children under 10</string>
+ <string name="description_frtv_12" translatable="false">Not recommended for children under 12</string>
+ <string name="description_frtv_16" translatable="false">Not recommended for children under 16</string>
+ <string name="description_frtv_18" translatable="false">Not recommended for persons under 18</string>
<!-- TV content rating system strings for GR TV -->
<string name="display_name_grtv" translatable="false">GR-TV</string>
@@ -5071,20 +5075,20 @@
<string name="display_name_grtv_12" translatable="false">White triangle in orange background</string>
<string name="display_name_grtv_15" translatable="false">White square in purple background</string>
<string name="display_name_grtv_18" translatable="false">White X in red background</string>
- <string name="description_grtv_all">Suitable for all ages</string>
- <string name="description_grtv_10">Parental consent suggested</string>
- <string name="description_grtv_12">Required parental consent</string>
- <string name="description_grtv_15">Suitable for minors over the age of 15</string>
- <string name="description_grtv_18">Suitable only for adults profanity before midnight is punishable by fine, except when used in the context of the program</string>
+ <string name="description_grtv_all" translatable="false">Suitable for all ages</string>
+ <string name="description_grtv_10" translatable="false">Parental consent suggested</string>
+ <string name="description_grtv_12" translatable="false">Required parental consent</string>
+ <string name="description_grtv_15" translatable="false">Suitable for minors over the age of 15</string>
+ <string name="description_grtv_18" translatable="false">Suitable only for adults profanity before midnight is punishable by fine, except when used in the context of the program</string>
<!-- TV content rating system strings for HK TV -->
<string name="display_name_hktv" translatable="false">HK-TV</string>
<string name="display_name_hktv_g" translatable="false">G</string>
<string name="display_name_hktv_pg" translatable="false">PG</string>
<string name="display_name_hktv_m" translatable="false">M</string>
- <string name="description_hktv_g">For general audiences</string>
- <string name="description_hktv_pg">Programs are unsuitable for children, parental guidance is recommended</string>
- <string name="description_hktv_m">Programs are recommended only for adult viewers above the age of 18</string>
+ <string name="description_hktv_g" translatable="false">For general audiences</string>
+ <string name="description_hktv_pg" translatable="false">Programs are unsuitable for children, parental guidance is recommended</string>
+ <string name="description_hktv_m" translatable="false">Programs are recommended only for adult viewers above the age of 18</string>
<!-- TV content rating system strings for HU TV -->
<string name="display_name_hutv" translatable="false">HU-TV</string>
@@ -5094,12 +5098,12 @@
<string name="display_name_hutv_12" translatable="false">12</string>
<string name="display_name_hutv_16" translatable="false">16</string>
<string name="display_name_hutv_18" translatable="false">18</string>
- <string name="description_hutv_u">Programs can be viewed by any age</string>
- <string name="description_hutv_cf">Programs recommended for children. It is an optional rating, there is no obligation for broadcasters to indicate it</string>
- <string name="description_hutv_6">Programs not recommended for children below the age of 6, may not contain any violence or sexual content</string>
- <string name="description_hutv_12">Programs not recommended for children below the age of 12, may contain light sexual content or explicit language</string>
- <string name="description_hutv_16">Programs not recommended for teens and children below the age of 16, may contain more intensive violence and sexual content</string>
- <string name="description_hutv_18">The program is recommended only for adult viewers (for ages 18 and up), may contain explicit violence and explicit sexual content</string>
+ <string name="description_hutv_u" translatable="false">Programs can be viewed by any age</string>
+ <string name="description_hutv_cf" translatable="false">Programs recommended for children. It is an optional rating, there is no obligation for broadcasters to indicate it</string>
+ <string name="description_hutv_6" translatable="false">Programs not recommended for children below the age of 6, may not contain any violence or sexual content</string>
+ <string name="description_hutv_12" translatable="false">Programs not recommended for children below the age of 12, may contain light sexual content or explicit language</string>
+ <string name="description_hutv_16" translatable="false">Programs not recommended for teens and children below the age of 16, may contain more intensive violence and sexual content</string>
+ <string name="description_hutv_18" translatable="false">The program is recommended only for adult viewers (for ages 18 and up), may contain explicit violence and explicit sexual content</string>
<!-- TV content rating system strings for ID TV -->
<string name="display_name_idtv" translatable="false">ID-TV</string>
@@ -5111,14 +5115,14 @@
<string name="display_name_idtv_r" translatable="false">R</string>
<string name="display_name_idtv_r_bo" translatable="false">R-BO</string>
<string name="display_name_idtv_d" translatable="false">D</string>
- <string name="description_idtv_p">Suitable for children from ages 2 through 11</string>
- <string name="description_idtv_a">Suitable for teens and children from ages 7 through 16</string>
- <string name="description_idtv_a_bo">Suitable for children ages 5 through 10, with parental guidance or permission</string>
- <string name="description_idtv_su">Suitable for general audiences</string>
- <string name="description_idtv_bo">Parental guidance suggested for ages 5 and under</string>
- <string name="description_idtv_r">Suitable for teens from ages 13 through 17</string>
- <string name="description_idtv_r_bo">Suitable for teens with parental guidance or permission</string>
- <string name="description_idtv_d">Suitable for viewers over 18 and older only</string>
+ <string name="description_idtv_p" translatable="false">Suitable for children from ages 2 through 11</string>
+ <string name="description_idtv_a" translatable="false">Suitable for teens and children from ages 7 through 16</string>
+ <string name="description_idtv_a_bo" translatable="false">Suitable for children ages 5 through 10, with parental guidance or permission</string>
+ <string name="description_idtv_su" translatable="false">Suitable for general audiences</string>
+ <string name="description_idtv_bo" translatable="false">Parental guidance suggested for ages 5 and under</string>
+ <string name="description_idtv_r" translatable="false">Suitable for teens from ages 13 through 17</string>
+ <string name="description_idtv_r_bo" translatable="false">Suitable for teens with parental guidance or permission</string>
+ <string name="description_idtv_d" translatable="false">Suitable for viewers over 18 and older only</string>
<!-- TV content rating system strings for IE TV -->
<string name="display_name_ietv" translatable="false">RTÉ programme classifications</string>
@@ -5127,11 +5131,11 @@
<string name="display_name_ietv_ya" translatable="false">YA</string>
<string name="display_name_ietv_ps" translatable="false">PS</string>
<string name="display_name_ietv_ma" translatable="false">MA</string>
- <string name="description_ietv_ga">Suitable for all ages</string>
- <string name="description_ietv_ch">Suitable for children ages 5 to 10, may contain comedic violence or action fantasy violence</string>
- <string name="description_ietv_ya">Suitable for adolescent audiences, may contain thematic elements that would appeal to teenagers</string>
- <string name="description_ietv_ps">Suitable for more mature viewers, more mature themes may be present</string>
- <string name="description_ietv_ma">Most restrictive classification, allowing for heavy subject matter and coarse language</string>
+ <string name="description_ietv_ga" translatable="false">Suitable for all ages</string>
+ <string name="description_ietv_ch" translatable="false">Suitable for children ages 5 to 10, may contain comedic violence or action fantasy violence</string>
+ <string name="description_ietv_ya" translatable="false">Suitable for adolescent audiences, may contain thematic elements that would appeal to teenagers</string>
+ <string name="description_ietv_ps" translatable="false">Suitable for more mature viewers, more mature themes may be present</string>
+ <string name="description_ietv_ma" translatable="false">Most restrictive classification, allowing for heavy subject matter and coarse language</string>
<!-- TV content rating system strings for IL TV -->
<string name="display_name_iltv" translatable="false">IL-TV</string>
@@ -5140,11 +5144,11 @@
<string name="display_name_iltv_15" translatable="false">15+</string>
<string name="display_name_iltv_18" translatable="false">18+</string>
<string name="display_name_iltv_e" translatable="false">E</string>
- <string name="description_iltv_g">General audience; anyone, regardless of age, can view the program, usually news and children\'s programming</string>
- <string name="description_iltv_12">Suitable for teens and children ages 12 and over, no child under 12 are permitted to view the program</string>
- <string name="description_iltv_15">Suitable for teens ages 15 and over, no child under 15 may view the programme</string>
- <string name="description_iltv_18">Suitable for adults only, no minors may view the programme</string>
- <string name="description_iltv_e">Exempt from classification</string>
+ <string name="description_iltv_g" translatable="false">General audience; anyone, regardless of age, can view the program, usually news and children\'s programming</string>
+ <string name="description_iltv_12" translatable="false">Suitable for teens and children ages 12 and over, no child under 12 are permitted to view the program</string>
+ <string name="description_iltv_15" translatable="false">Suitable for teens ages 15 and over, no child under 15 may view the programme</string>
+ <string name="description_iltv_18" translatable="false">Suitable for adults only, no minors may view the programme</string>
+ <string name="description_iltv_e" translatable="false">Exempt from classification</string>
<!-- TV content rating system strings for IN TV -->
<string name="display_name_intv" translatable="false">IN-TV</string>
@@ -5152,10 +5156,10 @@
<string name="display_name_intv_u/a" translatable="false">U/A</string>
<string name="display_name_intv_a" translatable="false">A</string>
<string name="display_name_intv_s" translatable="false">S</string>
- <string name="description_intv_u">Unrestricted public exhibition</string>
- <string name="description_intv_u/a">Unrestricted public exhibition, but with a caution regarding parental guidance to those under 12 years of age</string>
- <string name="description_intv_a">Public exhibition restricted to adults 18 years of age and older only</string>
- <string name="description_intv_s">Public exhibition restricted to members of any profession or any class of persons</string>
+ <string name="description_intv_u" translatable="false">Unrestricted public exhibition</string>
+ <string name="description_intv_u/a" translatable="false">Unrestricted public exhibition, but with a caution regarding parental guidance to those under 12 years of age</string>
+ <string name="description_intv_a" translatable="false">Public exhibition restricted to adults 18 years of age and older only</string>
+ <string name="description_intv_s" translatable="false">Public exhibition restricted to members of any profession or any class of persons</string>
<!-- TV content rating system strings for IS TV -->
<string name="display_name_istv" translatable="false">IS-TV</string>
@@ -5166,13 +5170,13 @@
<string name="display_name_istv_14" translatable="false">14</string>
<string name="display_name_istv_16" translatable="false">16</string>
<string name="display_name_istv_18" translatable="false">18</string>
- <string name="description_istv_l">Programs suitable for all ages</string>
- <string name="description_istv_7">Programs suitable for ages 7 and older</string>
- <string name="description_istv_10">Programs suitable for ages 10 and older</string>
- <string name="description_istv_12">Programs suitable for ages 12 and older</string>
- <string name="description_istv_14">Programs suitable for ages 14 and older</string>
- <string name="description_istv_16">Programs suitable for ages 16 and older</string>
- <string name="description_istv_18">Programs suitable for ages 18 and older</string>
+ <string name="description_istv_l" translatable="false">Programs suitable for all ages</string>
+ <string name="description_istv_7" translatable="false">Programs suitable for ages 7 and older</string>
+ <string name="description_istv_10" translatable="false">Programs suitable for ages 10 and older</string>
+ <string name="description_istv_12" translatable="false">Programs suitable for ages 12 and older</string>
+ <string name="description_istv_14" translatable="false">Programs suitable for ages 14 and older</string>
+ <string name="description_istv_16" translatable="false">Programs suitable for ages 16 and older</string>
+ <string name="description_istv_18" translatable="false">Programs suitable for ages 18 and older</string>
<!-- TV content rating system strings for KR TV -->
<string name="display_name_krtv" translatable="false">KR-TV</string>
@@ -5181,11 +5185,11 @@
<string name="display_name_krtv_12" translatable="false">12세이상시청가</string>
<string name="display_name_krtv_15" translatable="false">15세이상시청가</string>
<string name="display_name_krtv_19" translatable="false">19세이상시청가</string>
- <string name="description_krtv_all">Appropriate for all ages</string>
- <string name="description_krtv_7">May contain material inappropriate for children younger than 7, and parental discretion should be used</string>
- <string name="description_krtv_12">May deemed inappropriate for those younger than 12, and parental discretion should be used</string>
- <string name="description_krtv_15">May be inappropriate for children under 15, and that parental discretion should be used</string>
- <string name="description_krtv_19">For adults only</string>
+ <string name="description_krtv_all" translatable="false">Appropriate for all ages</string>
+ <string name="description_krtv_7" translatable="false">May contain material inappropriate for children younger than 7, and parental discretion should be used</string>
+ <string name="description_krtv_12" translatable="false">May deemed inappropriate for those younger than 12, and parental discretion should be used</string>
+ <string name="description_krtv_15" translatable="false">May be inappropriate for children under 15, and that parental discretion should be used</string>
+ <string name="description_krtv_19" translatable="false">For adults only</string>
<!-- TV content rating system strings for MV TV -->
<string name="display_name_mvtv" translatable="false">MV-TV</string>
@@ -5198,15 +5202,15 @@
<string name="display_name_mvtv_18" translatable="false">18+</string>
<string name="display_name_mvtv_21" translatable="false">21+</string>
<string name="display_name_mvtv_x" translatable="false">X</string>
- <string name="description_mvtv_y">Young children</string>
- <string name="description_mvtv_g">General viewing for all ages</string>
- <string name="description_mvtv_pg">Parental guidance is required unaccompanied children</string>
- <string name="description_mvtv_pg-12">Parental guidance is required for children under the age of 12</string>
- <string name="description_mvtv_12">Teens and children aged 12 and older may watch, otherwise restricted</string>
- <string name="description_mvtv_15">Restricted to viewers aged 15 and above</string>
- <string name="description_mvtv_18">Restricted to viewers aged 18 and above</string>
- <string name="description_mvtv_21">Restricted to viewers aged 21 and above</string>
- <string name="description_mvtv_x">Most restrictive classification, only adults ages 25 and above may view</string>
+ <string name="description_mvtv_y" translatable="false">Young children</string>
+ <string name="description_mvtv_g" translatable="false">General viewing for all ages</string>
+ <string name="description_mvtv_pg" translatable="false">Parental guidance is required unaccompanied children</string>
+ <string name="description_mvtv_pg-12" translatable="false">Parental guidance is required for children under the age of 12</string>
+ <string name="description_mvtv_12" translatable="false">Teens and children aged 12 and older may watch, otherwise restricted</string>
+ <string name="description_mvtv_15" translatable="false">Restricted to viewers aged 15 and above</string>
+ <string name="description_mvtv_18" translatable="false">Restricted to viewers aged 18 and above</string>
+ <string name="description_mvtv_21" translatable="false">Restricted to viewers aged 21 and above</string>
+ <string name="description_mvtv_x" translatable="false">Most restrictive classification, only adults ages 25 and above may view</string>
<!-- TV content rating system strings for MX TV -->
<string name="display_name_mxtv" translatable="false">MX-TV</string>
@@ -5216,21 +5220,21 @@
<string name="display_name_mxtv_c" translatable="false">C</string>
<string name="display_name_mxtv_d" translatable="false">D</string>
<string name="display_name_mxtv_rc" translatable="false">RC</string>
- <string name="description_mxtv_a">Appropriate for all ages, parental guidance is recommended for children under 7 years</string>
- <string name="description_mxtv_b">Designed for ages 12 and older, may contain some sexual situations, mild violence, and mild language</string>
- <string name="description_mxtv_b-15">Designed for ages 15 and up, slightly more intensive than the \'A\' and \'B\' ratings</string>
- <string name="description_mxtv_c">Designed to be viewed by adults aged 18 or older only, generally more intensive content </string>
- <string name="description_mxtv_d">Designed to be viewed only by mature adults (at least 21 years of age and over), contains extreme content matter</string>
- <string name="description_mxtv_rc">Banned from public television in Mexico</string>
+ <string name="description_mxtv_a" translatable="false">Appropriate for all ages, parental guidance is recommended for children under 7 years</string>
+ <string name="description_mxtv_b" translatable="false">Designed for ages 12 and older, may contain some sexual situations, mild violence, and mild language</string>
+ <string name="description_mxtv_b-15" translatable="false">Designed for ages 15 and up, slightly more intensive than the \'A\' and \'B\' ratings</string>
+ <string name="description_mxtv_c" translatable="false">Designed to be viewed by adults aged 18 or older only, generally more intensive content </string>
+ <string name="description_mxtv_d" translatable="false">Designed to be viewed only by mature adults (at least 21 years of age and over), contains extreme content matter</string>
+ <string name="description_mxtv_rc" translatable="false">Banned from public television in Mexico</string>
<!-- TV content rating system strings for MY TV -->
<string name="display_name_mytv" translatable="false">MY-TV</string>
<string name="display_name_mytv_u" translatable="false">U</string>
<string name="display_name_mytv_p13" translatable="false">P13</string>
<string name="display_name_mytv_18" translatable="false">18</string>
- <string name="description_mytv_u">General viewing for all ages, can be broadcast anytime</string>
- <string name="description_mytv_p13">For viewers ages 13 and above, children under 13 needs parental guidance, can be broadcast anytime, but some elements may only be broadcast at night</string>
- <string name="description_mytv_18">For viewers ages 18 and above only</string>
+ <string name="description_mytv_u" translatable="false">General viewing for all ages, can be broadcast anytime</string>
+ <string name="description_mytv_p13" translatable="false">For viewers ages 13 and above, children under 13 needs parental guidance, can be broadcast anytime, but some elements may only be broadcast at night</string>
+ <string name="description_mytv_18" translatable="false">For viewers ages 18 and above only</string>
<!-- TV content rating system strings for NL TV -->
<string name="display_name_nltv" translatable="false">NICAM</string>
@@ -5245,31 +5249,31 @@
<string name="display_name_nltv_9" translatable="false">Let op met kinderen tot 9 jaar</string>
<string name="display_name_nltv_12" translatable="false">Let op met kinderen tot 12 jaar</string>
<string name="display_name_nltv_16" translatable="false">Let op met kinderen tot 16 jaar</string>
- <string name="description_nltv_v">Violence\nApplicable to NL_TV_AL, NL_TV_6, NL_TV_9, NL_TV_12, NL_TV_16</string>
- <string name="description_nltv_f">Scary or Disturbing ContentViolence\nApplicable to NL_TV_AL, NL_TV_6, NL_TV_9, NL_TV_12, NL_TV_16</string>
- <string name="description_nltv_s">Sexual Content\nApplicable to NL_TV_AL, NL_TV_6, NL_TV_9, NL_TV_12, NL_TV_16</string>
- <string name="description_nltv_d">Discrimination\nApplicable to NL_TV_AL, NL_TV_6, NL_TV_9, NL_TV_12, NL_TV_16</string>
- <string name="description_nltv_da">Drug and/or Alcohol abuse\nApplicable to NL_TV_AL, NL_TV_6, NL_TV_9, NL_TV_12, NL_TV_16</string>
- <string name="description_nltv_l">Bad Language\nApplicable to NL_TV_AL, NL_TV_6, NL_TV_9, NL_TV_12, NL_TV_16</string>
- <string name="description_nltv_al">All Ages</string>
- <string name="description_nltv_6">Parental advisory for children under 6</string>
- <string name="description_nltv_9">Parental advisory for children under 9</string>
- <string name="description_nltv_12">Parental advisory for children under 12</string>
- <string name="description_nltv_16">Parental advisory for children under 16</string>
+ <string name="description_nltv_v" translatable="false">Violence\nApplicable to NL_TV_AL, NL_TV_6, NL_TV_9, NL_TV_12, NL_TV_16</string>
+ <string name="description_nltv_f" translatable="false">Scary or Disturbing ContentViolence\nApplicable to NL_TV_AL, NL_TV_6, NL_TV_9, NL_TV_12, NL_TV_16</string>
+ <string name="description_nltv_s" translatable="false">Sexual Content\nApplicable to NL_TV_AL, NL_TV_6, NL_TV_9, NL_TV_12, NL_TV_16</string>
+ <string name="description_nltv_d" translatable="false">Discrimination\nApplicable to NL_TV_AL, NL_TV_6, NL_TV_9, NL_TV_12, NL_TV_16</string>
+ <string name="description_nltv_da" translatable="false">Drug and/or Alcohol abuse\nApplicable to NL_TV_AL, NL_TV_6, NL_TV_9, NL_TV_12, NL_TV_16</string>
+ <string name="description_nltv_l" translatable="false">Bad Language\nApplicable to NL_TV_AL, NL_TV_6, NL_TV_9, NL_TV_12, NL_TV_16</string>
+ <string name="description_nltv_al" translatable="false">All Ages</string>
+ <string name="description_nltv_6" translatable="false">Parental advisory for children under 6</string>
+ <string name="description_nltv_9" translatable="false">Parental advisory for children under 9</string>
+ <string name="description_nltv_12" translatable="false">Parental advisory for children under 12</string>
+ <string name="description_nltv_16" translatable="false">Parental advisory for children under 16</string>
<!-- TV content rating system strings for NZF(Free) TV -->
<string name="display_name_nzftv" translatable="false">NZ-Free-TV</string>
- <string name="description_nzftv">TV content rating system for free-to-air channels in New Zealand</string>
+ <string name="description_nzftv" translatable="false">TV content rating system for free-to-air channels in New Zealand</string>
<string name="display_name_nzftv_g" translatable="false">G</string>
<string name="display_name_nzftv_pgr" translatable="false">PGR</string>
<string name="display_name_nzftv_ao" translatable="false">AO</string>
- <string name="description_nzftv_g">These exclude material likely to harm children under 14 and can screen at any time. Programmes may not necessarily be designed for younger viewers, but must not contain material likely to cause them undue distress or discomfort</string>
- <string name="description_nzftv_pgr">Programmes more suited to more mature viewers. These are not necessarily unsuitable for children, but viewer discretion is advised, and parents and guardians are encouraged to supervise younger viewers</string>
- <string name="description_nzftv_ao">Contain material of an adult nature handled in such a way that it is unsuitable for children</string>
+ <string name="description_nzftv_g" translatable="false">These exclude material likely to harm children under 14 and can screen at any time. Programmes may not necessarily be designed for younger viewers, but must not contain material likely to cause them undue distress or discomfort</string>
+ <string name="description_nzftv_pgr" translatable="false">Programmes more suited to more mature viewers. These are not necessarily unsuitable for children, but viewer discretion is advised, and parents and guardians are encouraged to supervise younger viewers</string>
+ <string name="description_nzftv_ao" translatable="false">Contain material of an adult nature handled in such a way that it is unsuitable for children</string>
<!-- TV content rating system strings for NZP(Pay) TV -->
<string name="display_name_nzptv" translatable="false">NZ-Pay-TV</string>
- <string name="description_nzptv">TV content rating system for Pay TV channels in New Zealand</string>
+ <string name="description_nzptv" translatable="false">TV content rating system for Pay TV channels in New Zealand</string>
<string name="display_name_nzptv_c" translatable="false">C</string>
<string name="display_name_nzptv_v" translatable="false">V</string>
<string name="display_name_nzptv_l" translatable="false">L</string>
@@ -5279,46 +5283,46 @@
<string name="display_name_nzptv_m" translatable="false">M</string>
<string name="display_name_nzptv_16" translatable="false">16</string>
<string name="display_name_nzptv_18" translatable="false">18</string>
- <string name="description_nzptv_c">Content may offend\nApplicable to NZ_PTV_PG, NZ_PTV_M, NZ_PTV_16, NZ_PTV_18</string>
- <string name="description_nzptv_v">Violence\nApplicable to NZ_PTV_PG, NZ_PTV_M, NZ_PTV_16, NZ_PTV_18</string>
- <string name="description_nzptv_l">Language\nApplicable to NZ_PTV_PG, NZ_PTV_M, NZ_PTV_16, NZ_PTV_18</string>
- <string name="description_nzptv_s">Sexual content\nApplicable to NZ_PTV_PG, NZ_PTV_M, NZ_PTV_16, NZ_PTV_18</string>
- <string name="description_nzptv_g">suitable for general audiences</string>
- <string name="description_nzptv_pg">Parental guidance recommended for under 10</string>
- <string name="description_nzptv_m">Suitable for mature audiences 13 and up</string>
- <string name="description_nzptv_16">Suitable for viewers 16 and up</string>
- <string name="description_nzptv_18">Suitable for viewers 18 and up</string>
+ <string name="description_nzptv_c" translatable="false">Content may offend\nApplicable to NZ_PTV_PG, NZ_PTV_M, NZ_PTV_16, NZ_PTV_18</string>
+ <string name="description_nzptv_v" translatable="false">Violence\nApplicable to NZ_PTV_PG, NZ_PTV_M, NZ_PTV_16, NZ_PTV_18</string>
+ <string name="description_nzptv_l" translatable="false">Language\nApplicable to NZ_PTV_PG, NZ_PTV_M, NZ_PTV_16, NZ_PTV_18</string>
+ <string name="description_nzptv_s" translatable="false">Sexual content\nApplicable to NZ_PTV_PG, NZ_PTV_M, NZ_PTV_16, NZ_PTV_18</string>
+ <string name="description_nzptv_g" translatable="false">suitable for general audiences</string>
+ <string name="description_nzptv_pg" translatable="false">Parental guidance recommended for under 10</string>
+ <string name="description_nzptv_m" translatable="false">Suitable for mature audiences 13 and up</string>
+ <string name="description_nzptv_16" translatable="false">Suitable for viewers 16 and up</string>
+ <string name="description_nzptv_18" translatable="false">Suitable for viewers 18 and up</string>
<!-- TV content rating system strings for PE TV -->
<string name="display_name_petv" translatable="false">PE-TV</string>
- <string name="description_petv">TV content rating system for some Peruvian channels in Peru</string>
+ <string name="description_petv" translatable="false">TV content rating system for some Peruvian channels in Peru</string>
<string name="display_name_petv_a" translatable="false">Apt</string>
<string name="display_name_petv_14" translatable="false">14</string>
<string name="display_name_petv_18" translatable="false">18</string>
- <string name="description_petv_a">Suitable for all audiences</string>
- <string name="description_petv_14">Suitable for people aged 14 and above only</string>
- <string name="description_petv_18">Suitable for people aged 18 and above only</string>
+ <string name="description_petv_a" translatable="false">Suitable for all audiences</string>
+ <string name="description_petv_14" translatable="false">Suitable for people aged 14 and above only</string>
+ <string name="description_petv_18" translatable="false">Suitable for people aged 18 and above only</string>
<!-- TV content rating system strings for America TV in PE -->
<string name="display_name_peatv" translatable="false">PE-ATV</string>
- <string name="description_peatv">TV content rating system for America Television in Peru that uses its own rating system</string>
+ <string name="description_peatv" translatable="false">TV content rating system for America Television in Peru that uses its own rating system</string>
<string name="display_name_peatv_gp" translatable="false">GP</string>
<string name="display_name_peatv_pg" translatable="false">PG</string>
<string name="display_name_peatv_14" translatable="false">TV-14</string>
<string name="display_name_peatv_18" translatable="false">TV-18</string>
- <string name="description_peatv_gp">General audience</string>
- <string name="description_peatv_pg">Parental guidance required for under 6</string>
- <string name="description_peatv_14">Suitable for people aged 14 and above only</string>
- <string name="description_peatv_18">Suitable for people aged 18 and above only</string>
+ <string name="description_peatv_gp" translatable="false">General audience</string>
+ <string name="description_peatv_pg" translatable="false">Parental guidance required for under 6</string>
+ <string name="description_peatv_14" translatable="false">Suitable for people aged 14 and above only</string>
+ <string name="description_peatv_18" translatable="false">Suitable for people aged 18 and above only</string>
<!-- TV content rating system strings for PH TV -->
<string name="display_name_phtv" translatable="false">MTRCB</string>
<string name="display_name_phtv_g" translatable="false">G</string>
<string name="display_name_phtv_pg" translatable="false">PG</string>
<string name="display_name_phtv_spg" translatable="false">SPG</string>
- <string name="description_phtv_g">Suitable for all public viewers</string>
- <string name="description_phtv_pg">Programmes rated PG may contain scenes or other content that are unsuitable for children without the guidance of a parent</string>
- <string name="description_phtv_spg">Contains mature themes or moderate to intense violence, which may be deemed unfit for children to watch without strict parental supervision</string>
+ <string name="description_phtv_g" translatable="false">Suitable for all public viewers</string>
+ <string name="description_phtv_pg" translatable="false">Programmes rated PG may contain scenes or other content that are unsuitable for children without the guidance of a parent</string>
+ <string name="description_phtv_spg" translatable="false">Contains mature themes or moderate to intense violence, which may be deemed unfit for children to watch without strict parental supervision</string>
<!-- TV content rating system strings for PL TV -->
<string name="display_name_pltv" translatable="false">PL-TV</string>
@@ -5327,11 +5331,11 @@
<string name="display_name_pltv_12" translatable="false">12</string>
<string name="display_name_pltv_16" translatable="false">16</string>
<string name="display_name_pltv_18" translatable="false">18</string>
- <string name="description_pltv_g">Positive or neutral view of the world, little to no violence, non-sexual love, and no sexual content</string>
- <string name="description_pltv_7">Age 7 and above. May additionally contain some mild language, bloodless violence, and a more negative view of the world</string>
- <string name="description_pltv_12">Age 12 and above. May contain some foul language, some violence, and some sexual content</string>
- <string name="description_pltv_16">Age 16 and above. Deviant social behaviour, world filled with violence and sexuality, simplified picture of adulthood, display of physical force, especially in controversial social context, immoral behaviour without ethic dilemma, putting the blame on the victim, excessive concentration on material possessions</string>
- <string name="description_pltv_18">Age 18 and above. One-sided display of the joys of adult life without showing responsibilities, social justification of violent behaviour, excessive vulgarity, use of racial slurs and social stereotypes, explicit sexual content, praise of aggression or vulgarity</string>
+ <string name="description_pltv_g" translatable="false">Positive or neutral view of the world, little to no violence, non-sexual love, and no sexual content</string>
+ <string name="description_pltv_7" translatable="false">Age 7 and above. May additionally contain some mild language, bloodless violence, and a more negative view of the world</string>
+ <string name="description_pltv_12" translatable="false">Age 12 and above. May contain some foul language, some violence, and some sexual content</string>
+ <string name="description_pltv_16" translatable="false">Age 16 and above. Deviant social behaviour, world filled with violence and sexuality, simplified picture of adulthood, display of physical force, especially in controversial social context, immoral behaviour without ethic dilemma, putting the blame on the victim, excessive concentration on material possessions</string>
+ <string name="description_pltv_18" translatable="false">Age 18 and above. One-sided display of the joys of adult life without showing responsibilities, social justification of violent behaviour, excessive vulgarity, use of racial slurs and social stereotypes, explicit sexual content, praise of aggression or vulgarity</string>
<!-- TV content rating system strings for PT TV -->
<string name="display_name_pttv" translatable="false">PT-TV</string>
@@ -5339,10 +5343,10 @@
<string name="display_name_pttv_10" translatable="false">10</string>
<string name="display_name_pttv_12" translatable="false">12</string>
<string name="display_name_pttv_16" translatable="false">16</string>
- <string name="description_pttv_t">Suitable for all</string>
- <string name="description_pttv_10">May not be suitable for children under 10, parental guidance advised</string>
- <string name="description_pttv_12">May not be suitable for children under 12, parental guidance advised</string>
- <string name="description_pttv_16">Not suitable for children under 16</string>
+ <string name="description_pttv_t" translatable="false">Suitable for all</string>
+ <string name="description_pttv_10" translatable="false">May not be suitable for children under 10, parental guidance advised</string>
+ <string name="description_pttv_12" translatable="false">May not be suitable for children under 12, parental guidance advised</string>
+ <string name="description_pttv_16" translatable="false">Not suitable for children under 16</string>
<!-- TV content rating system strings for RO TV -->
<string name="display_name_rotv" translatable="false">RO-TV</string>
@@ -5352,12 +5356,12 @@
<string name="display_name_rotv_12" translatable="false">12</string>
<string name="display_name_rotv_15" translatable="false">15</string>
<string name="display_name_rotv_18" translatable="false">18</string>
- <string name="description_rotv_y">Young Ages</string>
- <string name="description_rotv_g">General Exhibition</string>
- <string name="description_rotv_ap">Parental guidance is recommended for children below the age of 12</string>
- <string name="description_rotv_12">Forbidden for children under 12 years of age</string>
- <string name="description_rotv_15">Forbidden for children under 15 years of age</string>
- <string name="description_rotv_18">Forbidden for children under 18 years of age</string>
+ <string name="description_rotv_y" translatable="false">Young Ages</string>
+ <string name="description_rotv_g" translatable="false">General Exhibition</string>
+ <string name="description_rotv_ap" translatable="false">Parental guidance is recommended for children below the age of 12</string>
+ <string name="description_rotv_12" translatable="false">Forbidden for children under 12 years of age</string>
+ <string name="description_rotv_15" translatable="false">Forbidden for children under 15 years of age</string>
+ <string name="description_rotv_18" translatable="false">Forbidden for children under 18 years of age</string>
<!-- TV content rating system strings for RU TV -->
<string name="display_name_rutv" translatable="false">RU-TV</string>
@@ -5366,11 +5370,11 @@
<string name="display_name_rutv_12" translatable="false">12+</string>
<string name="display_name_rutv_16" translatable="false">16+</string>
<string name="display_name_rutv_18" translatable="false">18+</string>
- <string name="description_rutv_0">Can be watched by Any Age</string>
- <string name="description_rutv_6">Only kids the age of 6 or older can watch</string>
- <string name="description_rutv_12">Only kids the age of 12 or older can watch</string>
- <string name="description_rutv_16">Only teens the age of 16 or older can watch</string>
- <string name="description_rutv_18">Restricted to children ONLY people 18 or older</string>
+ <string name="description_rutv_0" translatable="false">Can be watched by Any Age</string>
+ <string name="description_rutv_6" translatable="false">Only kids the age of 6 or older can watch</string>
+ <string name="description_rutv_12" translatable="false">Only kids the age of 12 or older can watch</string>
+ <string name="description_rutv_16" translatable="false">Only teens the age of 16 or older can watch</string>
+ <string name="description_rutv_18" translatable="false">Restricted to children ONLY people 18 or older</string>
<!-- TV content rating system strings for RS TV -->
<string name="display_name_rstv" translatable="false">RS-TV</string>
@@ -5381,27 +5385,27 @@
<string name="display_name_rstv_16" translatable="false">16</string>
<string name="display_name_rstv_17" translatable="false">17</string>
<string name="display_name_rstv_18" translatable="false">18</string>
- <string name="description_rstv_g">Program suitable for all ages</string>
- <string name="description_rstv_12">Program not suitable for children under the age of 12 </string>
- <string name="description_rstv_14">Program not suitable for children/teens under the age of 14</string>
- <string name="description_rstv_15">Program not suitable for children/teens under the age of 15</string>
- <string name="description_rstv_16">Program not suitable for children/teens under the age of 16</string>
- <string name="description_rstv_17">Program not suitable for children/teens under the age of 17</string>
- <string name="description_rstv_18">Program not suitable for minors under the age of 18</string>
+ <string name="description_rstv_g" translatable="false">Program suitable for all ages</string>
+ <string name="description_rstv_12" translatable="false">Program not suitable for children under the age of 12 </string>
+ <string name="description_rstv_14" translatable="false">Program not suitable for children/teens under the age of 14</string>
+ <string name="description_rstv_15" translatable="false">Program not suitable for children/teens under the age of 15</string>
+ <string name="description_rstv_16" translatable="false">Program not suitable for children/teens under the age of 16</string>
+ <string name="description_rstv_17" translatable="false">Program not suitable for children/teens under the age of 17</string>
+ <string name="description_rstv_18" translatable="false">Program not suitable for minors under the age of 18</string>
<!-- TV content rating system strings for SGF(Free-to-Air) TV -->
<string name="display_name_sgftv" translatable="false">SG-Free-TV</string>
<string name="display_name_sgftv_pg" translatable="false">PG</string>
<string name="display_name_sgftv_pg13" translatable="false">PG13</string>
- <string name="description_sgftv_pg">Suitable for most but parents should guide their young</string>
- <string name="description_sgftv_pg13">Parental Guidance Strongly Cautioned - Suitable for 13 And Above</string>
+ <string name="description_sgftv_pg" translatable="false">Suitable for most but parents should guide their young</string>
+ <string name="description_sgftv_pg13" translatable="false">Parental Guidance Strongly Cautioned - Suitable for 13 And Above</string>
<!-- TV content rating system strings for SGP(Pay TV) TV -->
<string name="display_name_sgptv" translatable="false">SG-Pay-TV</string>
<string name="display_name_sgptv_nc16" translatable="false">NC16</string>
<string name="display_name_sgptv_m18" translatable="false">M18</string>
- <string name="description_sgptv_nc16">No Children Under 16</string>
- <string name="description_sgptv_m18">Nobody under age 18 is admitted</string>
+ <string name="description_sgptv_nc16" translatable="false">No Children Under 16</string>
+ <string name="description_sgptv_m18" translatable="false">Nobody under age 18 is admitted</string>
<!-- TV content rating system strings for SI TV -->
<string name="display_name_sitv" translatable="false">SI-TV</string>
@@ -5409,10 +5413,10 @@
<string name="display_name_sitv_12" translatable="false">+12</string>
<string name="display_name_sitv_15" translatable="false">+15</string>
<string name="display_name_sitv_ad" translatable="false">AD</string>
- <string name="description_sitv_vs">Parental guidance suggested (for children under 6)</string>
- <string name="description_sitv_12">Content suitable for teens over 12 years</string>
- <string name="description_sitv_15">Content suitable for teens over 15 years</string>
- <string name="description_sitv_ad">Content exclusively for adults</string>
+ <string name="description_sitv_vs" translatable="false">Parental guidance suggested (for children under 6)</string>
+ <string name="description_sitv_12" translatable="false">Content suitable for teens over 12 years</string>
+ <string name="description_sitv_15" translatable="false">Content suitable for teens over 15 years</string>
+ <string name="description_sitv_ad" translatable="false">Content exclusively for adults</string>
<!-- TV content rating system strings for TH TV -->
<string name="display_name_thtv" translatable="false">TH-TV</string>
@@ -5422,12 +5426,12 @@
<string name="display_name_thtv_pg13" translatable="false">PG13</string>
<string name="display_name_thtv_pg18" translatable="false">PG18</string>
<string name="display_name_thtv_adults" translatable="false">Adults</string>
- <string name="description_thtv_primary">Content suitable for primary school aged children</string>
- <string name="description_thtv_children">Content suitable for children between 6-12 years old</string>
- <string name="description_thtv_general">Content suitable for general audiences</string>
- <string name="description_thtv_pg13">Content suitable for people aged 13 and above, but can be watched by those who are under the recommended age if parental guidance is provided</string>
- <string name="description_thtv_pg18">Content suitable for people aged above 18 years old; those who are younger that 18 must be provided with parental guidance</string>
- <string name="description_thtv_adults">Content unsuitable for children and youngsters</string>
+ <string name="description_thtv_primary" translatable="false">Content suitable for primary school aged children</string>
+ <string name="description_thtv_children" translatable="false">Content suitable for children between 6-12 years old</string>
+ <string name="description_thtv_general" translatable="false">Content suitable for general audiences</string>
+ <string name="description_thtv_pg13" translatable="false">Content suitable for people aged 13 and above, but can be watched by those who are under the recommended age if parental guidance is provided</string>
+ <string name="description_thtv_pg18" translatable="false">Content suitable for people aged above 18 years old; those who are younger that 18 must be provided with parental guidance</string>
+ <string name="description_thtv_adults" translatable="false">Content unsuitable for children and youngsters</string>
<!-- TV content rating system strings for TR TV -->
<string name="display_name_trtv" translatable="false">TR-TV</string>
@@ -5435,10 +5439,10 @@
<string name="display_name_trtv_7" translatable="false">7+</string>
<string name="display_name_trtv_13" translatable="false">13+</string>
<string name="display_name_trtv_18" translatable="false">18+</string>
- <string name="description_trtv_g">General audience. Suitable for all ages</string>
- <string name="description_trtv_7">Suitable for ages 7 and over</string>
- <string name="description_trtv_13">Suitable for ages 13 and over</string>
- <string name="description_trtv_18">Suitable for ages 13 and over</string>
+ <string name="description_trtv_g" translatable="false">General audience. Suitable for all ages</string>
+ <string name="description_trtv_7" translatable="false">Suitable for ages 7 and over</string>
+ <string name="description_trtv_13" translatable="false">Suitable for ages 13 and over</string>
+ <string name="description_trtv_18" translatable="false">Suitable for ages 13 and over</string>
<!-- TV content rating system strings for TW TV -->
<string name="display_name_twtv" translatable="false">TW-TV</string>
@@ -5446,23 +5450,23 @@
<string name="display_name_twtv_p" translatable="false">Protected category</string>
<string name="display_name_twtv_pg" translatable="false">Parental guidance category</string>
<string name="display_name_twtv_r" translatable="false">Restricted category</string>
- <string name="description_twtv_g">For all ages</string>
- <string name="description_twtv_p">Not suitable for children under 6 years old. People aged 6 but under 12 require guidance from accompanying adults to watch</string>
- <string name="description_twtv_pg">Not suitable for people under 12 years of age. Parental guidance is required for people aged 12 but under 18</string>
- <string name="description_twtv_r">For adults only and people under 18 years of age must not watch</string>
+ <string name="description_twtv_g" translatable="false">For all ages</string>
+ <string name="description_twtv_p" translatable="false">Not suitable for children under 6 years old. People aged 6 but under 12 require guidance from accompanying adults to watch</string>
+ <string name="description_twtv_pg" translatable="false">Not suitable for people under 12 years of age. Parental guidance is required for people aged 12 but under 18</string>
+ <string name="description_twtv_r" translatable="false">For adults only and people under 18 years of age must not watch</string>
<!-- TV content rating system strings for UA TV -->
<string name="display_name_uatv" translatable="false">UA-TV</string>
<string name="display_name_uatv_green circle" translatable="false">Green Circle</string>
<string name="display_name_uatv_yellow triangle" translatable="false">Yellow Triangle</string>
<string name="display_name_uatv_red square" translatable="false">Red Square</string>
- <string name="description_uatv_green circle">This program does not have age restrictions</string>
- <string name="description_uatv_yellow triangle">Children must view this program with parents. In it program there are fragments, which unsuitable for children</string>
- <string name="description_uatv_red square">This program is only for adult viewers. In it there are scenes with nudity, drug use, or violence</string>
+ <string name="description_uatv_green circle" translatable="false">This program does not have age restrictions</string>
+ <string name="description_uatv_yellow triangle" translatable="false">Children must view this program with parents. In it program there are fragments, which unsuitable for children</string>
+ <string name="description_uatv_red square" translatable="false">This program is only for adult viewers. In it there are scenes with nudity, drug use, or violence</string>
<!-- TV content rating system strings for US TV -->
<string name="display_name_ustv" translatable="false">US-TV</string>
- <string name="description_ustv">TV content rating system for United States</string>
+ <string name="description_ustv" translatable="false">TV content rating system for United States</string>
<string name="display_name_ustv_d" translatable="false">D</string>
<string name="display_name_ustv_l" translatable="false">L</string>
<string name="display_name_ustv_s" translatable="false">S</string>
@@ -5474,26 +5478,26 @@
<string name="display_name_ustv_pg" translatable="false">TV-PG</string>
<string name="display_name_ustv_14" translatable="false">TV-14</string>
<string name="display_name_ustv_ma" translatable="false">TV-MA</string>
- <string name="description_ustv_d">Suggestive dialogue (Usually means talks about sex)\nApplicable to US_TV_PG, US_TV_14, US_TV</string>
- <string name="description_ustv_l">Coarse language\nApplicable to US_TV_PG, US_TV_14</string>
- <string name="description_ustv_s">Sexual content\nApplicable to US_TV_PG, US_TV_14, US_TV_MA</string>
- <string name="description_ustv_v">Violence\nApplicable to US_TV_PG, US_TV_14, US_TV_MA</string>
- <string name="description_ustv_fv">Fantasy violence (Children\'s programming only)\nApplicable to US_TV_Y7</string>
- <string name="description_ustv_y">This program is designed to be appropriate for all children</string>
- <string name="description_ustv_y7">This program is designed for children age 7 and above</string>
- <string name="description_ustv_g">Most parents would find this program suitable for all ages</string>
- <string name="description_ustv_pg">This program contains material that parents may find unsuitable for younger children</string>
- <string name="description_ustv_14">This program contains some material that many parents would find unsuitable for children under 14 years of age</string>
- <string name="description_ustv_ma">This program is specifically designed to be viewed by adults and therefore may be unsuitable for children under 17</string>
+ <string name="description_ustv_d" translatable="false">Suggestive dialogue (Usually means talks about sex)\nApplicable to US_TV_PG, US_TV_14, US_TV</string>
+ <string name="description_ustv_l" translatable="false">Coarse language\nApplicable to US_TV_PG, US_TV_14</string>
+ <string name="description_ustv_s" translatable="false">Sexual content\nApplicable to US_TV_PG, US_TV_14, US_TV_MA</string>
+ <string name="description_ustv_v" translatable="false">Violence\nApplicable to US_TV_PG, US_TV_14, US_TV_MA</string>
+ <string name="description_ustv_fv" translatable="false">Fantasy violence (Children\'s programming only)\nApplicable to US_TV_Y7</string>
+ <string name="description_ustv_y" translatable="false">This program is designed to be appropriate for all children</string>
+ <string name="description_ustv_y7" translatable="false">This program is designed for children age 7 and above</string>
+ <string name="description_ustv_g" translatable="false">Most parents would find this program suitable for all ages</string>
+ <string name="description_ustv_pg" translatable="false">This program contains material that parents may find unsuitable for younger children</string>
+ <string name="description_ustv_14" translatable="false">This program contains some material that many parents would find unsuitable for children under 14 years of age</string>
+ <string name="description_ustv_ma" translatable="false">This program is specifically designed to be viewed by adults and therefore may be unsuitable for children under 17</string>
<!-- TV content rating system strings for VE TV -->
<string name="display_name_vetv" translatable="false">VE-TV</string>
<string name="display_name_vetv_tu" translatable="false">TU</string>
<string name="display_name_vetv_su" translatable="false">SU</string>
<string name="display_name_vetv_a" translatable="false">A</string>
- <string name="description_vetv_tu">For all ages</string>
- <string name="description_vetv_su">Parental guidance for young viewers</string>
- <string name="description_vetv_a">Mature viewers</string>
+ <string name="description_vetv_tu" translatable="false">For all ages</string>
+ <string name="description_vetv_su" translatable="false">Parental guidance for young viewers</string>
+ <string name="description_vetv_a" translatable="false">Mature viewers</string>
<!-- TV content rating system strings for ZA TV -->
<string name="display_name_zatv" translatable="false">ZA-TV</string>
@@ -5509,19 +5513,22 @@
<string name="display_name_zatv_16" translatable="false">16</string>
<string name="display_name_zatv_18" translatable="false">18</string>
<string name="display_name_zatv_r18" translatable="false">R18</string>
- <string name="description_zatv_d">Drug\nApplicable to ZA_TV_F, ZA_TV_PG, ZA_TV_13, ZA_TV_16, ZA_TV_18, ZA_TV_R18</string>
- <string name="description_zatv_v">Violence\nApplicable to ZA_TV_F, ZA_TV_PG, ZA_TV_13, ZA_TV_16, ZA_TV_18, ZA_TV_R18</string>
- <string name="description_zatv_n">Nudity\nApplicable to ZA_TV_F, ZA_TV_PG, ZA_TV_13, ZA_TV_16, ZA_TV_18, ZA_TV_R18</string>
- <string name="description_zatv_p">Prejudice\nApplicable to ZA_TV_F, ZA_TV_PG, ZA_TV_13, ZA_TV_16, ZA_TV_18, ZA_TV_R18</string>
- <string name="description_zatv_s">Sex\nApplicable to ZA_TV_F, ZA_TV_PG, ZA_TV_13, ZA_TV_16, ZA_TV_18, ZA_TV_R18</string>
- <string name="description_zatv_l">Language\nApplicable to ZA_TV_F, ZA_TV_PG, ZA_TV_13, ZA_TV_16, ZA_TV_18, ZA_TV_R18</string>
- <string name="description_zatv_f">This is a program/film that does not contain any obscenity, and is suitable for family viewing. A logo must be displayed in the corner of the screen for 30 seconds after each commercial break</string>
- <string name="description_zatv_pg">Children under 6 may watch this program/film, but must be accompanied by an adult. This program contains an adult related theme, which might include very mild language, violence and sexual innuendo. A logo must be displayed in the corner of the screen for one minute after each commercial break</string>
- <string name="description_zatv_13">Children under 13 are prohibited from watching this program/film. This program contains mild language, violence and sexual innuendo. A logo must be displayed in the corner of the screen for two minutes after each commercial break</string>
- <string name="description_zatv_16">Children under 16 are prohibited from watching this program/film. It contains moderate violence, language, and some sexual situations. In the case of television, this program may only be broadcast after 9pm-4:30am. A logo must be displayed in the corner of the screen for five minutes after each commercial break. A full-screen warning must be issued before the start of the program. If the program is longer than an hour, a warning must be displayed every half an hour</string>
- <string name="description_zatv_18">Children under 18 are prohibited from watching this program/film. It contains extreme violence, language and/or graphic sexual content. In the case of television, this program may only be broadcast from 10pm-4:30am. A logo must be displayed in the corner of the screen for the duration of the program. A full-screen warning must be issued before the start of the program and after each commercial break</string>
- <string name="description_zatv_r18">This is reserved for films of an extreme sexual nature (pornography). R18 films may only be distributed in the form of video and DVD in a controlled environment (e.g. Adult Shops). No public viewing of this film may take place. R18 films may not be broadcast on television and in cinemas</string>
+ <string name="description_zatv_d" translatable="false">Drug\nApplicable to ZA_TV_F, ZA_TV_PG, ZA_TV_13, ZA_TV_16, ZA_TV_18, ZA_TV_R18</string>
+ <string name="description_zatv_v" translatable="false">Violence\nApplicable to ZA_TV_F, ZA_TV_PG, ZA_TV_13, ZA_TV_16, ZA_TV_18, ZA_TV_R18</string>
+ <string name="description_zatv_n" translatable="false">Nudity\nApplicable to ZA_TV_F, ZA_TV_PG, ZA_TV_13, ZA_TV_16, ZA_TV_18, ZA_TV_R18</string>
+ <string name="description_zatv_p" translatable="false">Prejudice\nApplicable to ZA_TV_F, ZA_TV_PG, ZA_TV_13, ZA_TV_16, ZA_TV_18, ZA_TV_R18</string>
+ <string name="description_zatv_s" translatable="false">Sex\nApplicable to ZA_TV_F, ZA_TV_PG, ZA_TV_13, ZA_TV_16, ZA_TV_18, ZA_TV_R18</string>
+ <string name="description_zatv_l" translatable="false">Language\nApplicable to ZA_TV_F, ZA_TV_PG, ZA_TV_13, ZA_TV_16, ZA_TV_18, ZA_TV_R18</string>
+ <string name="description_zatv_f" translatable="false">This is a program/film that does not contain any obscenity, and is suitable for family viewing. A logo must be displayed in the corner of the screen for 30 seconds after each commercial break</string>
+ <string name="description_zatv_pg" translatable="false">Children under 6 may watch this program/film, but must be accompanied by an adult. This program contains an adult related theme, which might include very mild language, violence and sexual innuendo. A logo must be displayed in the corner of the screen for one minute after each commercial break</string>
+ <string name="description_zatv_13" translatable="false">Children under 13 are prohibited from watching this program/film. This program contains mild language, violence and sexual innuendo. A logo must be displayed in the corner of the screen for two minutes after each commercial break</string>
+ <string name="description_zatv_16" translatable="false">Children under 16 are prohibited from watching this program/film. It contains moderate violence, language, and some sexual situations. In the case of television, this program may only be broadcast after 9pm-4:30am. A logo must be displayed in the corner of the screen for five minutes after each commercial break. A full-screen warning must be issued before the start of the program. If the program is longer than an hour, a warning must be displayed every half an hour</string>
+ <string name="description_zatv_18" translatable="false">Children under 18 are prohibited from watching this program/film. It contains extreme violence, language and/or graphic sexual content. In the case of television, this program may only be broadcast from 10pm-4:30am. A logo must be displayed in the corner of the screen for the duration of the program. A full-screen warning must be issued before the start of the program and after each commercial break</string>
+ <string name="description_zatv_r18" translatable="false">This is reserved for films of an extreme sexual nature (pornography). R18 films may only be distributed in the form of video and DVD in a controlled environment (e.g. Adult Shops). No public viewing of this film may take place. R18 films may not be broadcast on television and in cinemas</string>
<!-- [CHAR_LIMIT=NONE] Battery saver: Feature description -->
<string name="battery_saver_description">To help improve battery life, battery saver reduces your device’s performance and limits vibration and most background data. Email, messaging, and other apps that rely on syncing may not update unless you open them.\n\nBattery saver turns off automatically when your device is charging</string>
+
+ <!-- [CHAR_LIMIT=NONE] Zen mode: Condition summary for built-in downtime condition, if active -->
+ <string name="downtime_condition_summary">Until your downtime ends at <xliff:g id="formattedTime" example="10.00 PM">%1$s</xliff:g></string>
</resources>
diff --git a/core/res/res/values/styles_leanback.xml b/core/res/res/values/styles_leanback.xml
index 256ef00..da83c36 100644
--- a/core/res/res/values/styles_leanback.xml
+++ b/core/res/res/values/styles_leanback.xml
@@ -39,26 +39,36 @@
<style name="TextAppearance.Leanback.FormWizard" parent="@style/TextAppearance.Material">
<item name="textSize">18sp</item>
<item name="fontFamily">sans-serif-light</item>
+ <item name="textColor">?attr/textColorPrimary</item>
</style>
<style name="TextAppearance.Leanback.FormWizard.Small" parent="@style/TextAppearance.Material.Small">
<item name="textSize">18sp</item>
<item name="fontFamily">sans-serif-light</item>
+ <item name="textColor">?attr/textColorPrimary</item>
</style>
<style name="TextAppearance.Leanback.FormWizard.Medium" parent="@style/TextAppearance.Material.Medium">
<item name="textSize">36sp</item>
<item name="fontFamily">sans-serif-thin</item>
+ <item name="textColor">?attr/textColorPrimary</item>
</style>
<style name="TextAppearance.Leanback.FormWizard.Large" parent="@style/TextAppearance.Material.Large">
<item name="textSize">56sp</item>
<item name="fontFamily">sans-serif-thin</item>
+ <item name="textColor">?attr/textColorPrimary</item>
</style>
<style name="TextAppearance.Leanback.FormWizard.ListItem" parent="@style/TextAppearance.Material.Subhead">
- <item name="textSize">18sp</item>
+ <item name="textSize">16sp</item>
<item name="fontFamily">sans-serif-condensed</item>
</style>
+ <style name="WindowAnimationStyle.Leanback.Setup" parent="@style/Animation.Material.Activity">
+ <item name="android:fragmentOpenEnterAnimation">@animator/leanback_setup_fragment_open_enter</item>
+ <item name="android:fragmentOpenExitAnimation">@animator/leanback_setup_fragment_open_exit</item>
+ <item name="android:fragmentCloseEnterAnimation">@animator/leanback_setup_fragment_close_enter</item>
+ <item name="android:fragmentCloseExitAnimation">@animator/leanback_setup_fragment_close_exit</item>
+ </style>
</resources>
diff --git a/core/res/res/values/styles_material.xml b/core/res/res/values/styles_material.xml
index 97d4bf6..f028222 100644
--- a/core/res/res/values/styles_material.xml
+++ b/core/res/res/values/styles_material.xml
@@ -426,7 +426,6 @@
<style name="Widget.Material.Button" parent="Widget.Button">
<item name="background">@drawable/btn_default_material</item>
<item name="textAppearance">?attr/textAppearanceButton</item>
- <item name="textColor">?attr/textColorPrimary</item>
<item name="minHeight">48dip</item>
<item name="minWidth">88dip</item>
<item name="stateListAnimator">@anim/button_state_list_anim_material</item>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 220f5ab..2cd0948 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -610,9 +610,11 @@
<java-symbol type="string" name="last_month" />
<java-symbol type="string" name="launchBrowserDefault" />
<java-symbol type="string" name="lock_to_app_toast" />
+ <java-symbol type="string" name="lock_to_app_toast_accessible" />
<java-symbol type="string" name="lock_to_app_toast_locked" />
<java-symbol type="string" name="lock_to_app_title" />
<java-symbol type="string" name="lock_to_app_description" />
+ <java-symbol type="string" name="lock_to_app_description_accessible" />
<java-symbol type="string" name="lock_to_app_negative" />
<java-symbol type="string" name="lock_to_app_positive" />
<java-symbol type="layout" name="lock_to_app_checkbox" />
@@ -1901,6 +1903,7 @@
<java-symbol type="string" name="timepicker_transition_mid_radius_multiplier" />
<java-symbol type="string" name="timepicker_transition_end_radius_multiplier" />
<java-symbol type="string" name="battery_saver_description" />
+ <java-symbol type="string" name="downtime_condition_summary" />
<java-symbol type="string" name="item_is_selected" />
<java-symbol type="string" name="day_of_week_label_typeface" />
@@ -1979,4 +1982,7 @@
<java-symbol type="string" name="time_picker_am_label" />
<java-symbol type="string" name="time_picker_pm_label" />
<java-symbol type="dimen" name="text_size_small_material" />
+ <java-symbol type="attr" name="checkMarkGravity" />
+ <java-symbol type="layout" name="select_dialog_singlechoice_material" />
+ <java-symbol type="layout" name="select_dialog_multichoice_material" />
</resources>
diff --git a/core/res/res/values/themes_leanback.xml b/core/res/res/values/themes_leanback.xml
index 321b827..0a2c0a4 100644
--- a/core/res/res/values/themes_leanback.xml
+++ b/core/res/res/values/themes_leanback.xml
@@ -57,5 +57,7 @@
<item name="textAppearanceLarge">@style/TextAppearance.Leanback.FormWizard.Large</item>
<item name="textAppearanceListItem">@style/TextAppearance.Leanback.FormWizard.ListItem</item>
<item name="textAppearance">@style/TextAppearance.Leanback.FormWizard</item>
+ <item name="textColorPrimary">@color/primary_text_leanback_formwizard_dark</item>
+ <item name="windowAnimationStyle">@style/WindowAnimationStyle.Leanback.Setup</item>
</style>
</resources>
diff --git a/graphics/java/android/graphics/drawable/AnimatedRotateDrawable.java b/graphics/java/android/graphics/drawable/AnimatedRotateDrawable.java
index e3c03a9..0bc4fdf 100644
--- a/graphics/java/android/graphics/drawable/AnimatedRotateDrawable.java
+++ b/graphics/java/android/graphics/drawable/AnimatedRotateDrawable.java
@@ -275,7 +275,7 @@
final int res = a.getResourceId(R.styleable.AnimatedRotateDrawable_drawable, 0);
Drawable drawable = null;
if (res > 0) {
- drawable = r.getDrawable(res);
+ drawable = r.getDrawable(res, theme);
}
a.recycle();
diff --git a/graphics/java/android/graphics/drawable/AnimatedStateListDrawable.java b/graphics/java/android/graphics/drawable/AnimatedStateListDrawable.java
index 2cb7b03..14aa570 100644
--- a/graphics/java/android/graphics/drawable/AnimatedStateListDrawable.java
+++ b/graphics/java/android/graphics/drawable/AnimatedStateListDrawable.java
@@ -119,11 +119,11 @@
*
* @param fromId Unique identifier of the starting keyframe
* @param toId Unique identifier of the ending keyframe
- * @param transition An animatable drawable to use as a transition, may not be null
+ * @param transition An {@link Animatable} drawable to use as a transition, may not be null
* @param reversible Whether the transition can be reversed
*/
- public void addTransition(int fromId, int toId, @NonNull Drawable transition,
- boolean reversible) {
+ public <T extends Drawable & Animatable> void addTransition(int fromId, int toId,
+ @NonNull T transition, boolean reversible) {
if (transition == null) {
throw new IllegalArgumentException("Transition drawable must not be null");
}
@@ -427,7 +427,7 @@
final Drawable dr;
if (drawableRes != 0) {
- dr = r.getDrawable(drawableRes);
+ dr = r.getDrawable(drawableRes, theme);
} else {
int type;
while ((type = parser.next()) == XmlPullParser.TEXT) {
@@ -473,7 +473,7 @@
final Drawable dr;
if (drawableRes != 0) {
- dr = r.getDrawable(drawableRes);
+ dr = r.getDrawable(drawableRes, theme);
} else {
int type;
while ((type = parser.next()) == XmlPullParser.TEXT) {
diff --git a/graphics/java/android/graphics/drawable/AnimationDrawable.java b/graphics/java/android/graphics/drawable/AnimationDrawable.java
index cef3377..5318fa7 100644
--- a/graphics/java/android/graphics/drawable/AnimationDrawable.java
+++ b/graphics/java/android/graphics/drawable/AnimationDrawable.java
@@ -313,7 +313,7 @@
Drawable dr;
if (drawableRes != 0) {
- dr = r.getDrawable(drawableRes);
+ dr = r.getDrawable(drawableRes, theme);
} else {
while ((type=parser.next()) == XmlPullParser.TEXT) {
// Empty
diff --git a/graphics/java/android/graphics/drawable/BitmapDrawable.java b/graphics/java/android/graphics/drawable/BitmapDrawable.java
index f5e63ae..40adf94 100644
--- a/graphics/java/android/graphics/drawable/BitmapDrawable.java
+++ b/graphics/java/android/graphics/drawable/BitmapDrawable.java
@@ -16,6 +16,7 @@
package android.graphics.drawable;
+import android.annotation.NonNull;
import android.content.res.ColorStateList;
import android.content.res.Resources;
import android.content.res.Resources.Theme;
@@ -27,6 +28,7 @@
import android.graphics.ColorFilter;
import android.graphics.Insets;
import android.graphics.Matrix;
+import android.graphics.Outline;
import android.graphics.Paint;
import android.graphics.PixelFormat;
import android.graphics.PorterDuff;
@@ -598,6 +600,16 @@
}
@Override
+ public void getOutline(@NonNull Outline outline) {
+ super.getOutline(outline);
+ if (mBitmapState.mBitmap.hasAlpha()) {
+ // Bitmaps with alpha can't report a non-0 alpha,
+ // since they may not fill their rectangular bounds
+ outline.setAlpha(0.0f);
+ }
+ }
+
+ @Override
public void setAlpha(int alpha) {
final int oldAlpha = mBitmapState.mPaint.getAlpha();
if (alpha != oldAlpha) {
diff --git a/graphics/java/android/graphics/drawable/RotateDrawable.java b/graphics/java/android/graphics/drawable/RotateDrawable.java
index 983eb3b..63b9e02 100644
--- a/graphics/java/android/graphics/drawable/RotateDrawable.java
+++ b/graphics/java/android/graphics/drawable/RotateDrawable.java
@@ -436,7 +436,7 @@
com.android.internal.R.styleable.RotateDrawable_drawable, 0);
Drawable drawable = null;
if (res > 0) {
- drawable = r.getDrawable(res);
+ drawable = r.getDrawable(res, theme);
}
a.recycle();
diff --git a/graphics/java/android/graphics/drawable/VectorDrawable.java b/graphics/java/android/graphics/drawable/VectorDrawable.java
index 766e681..9ac6927 100644
--- a/graphics/java/android/graphics/drawable/VectorDrawable.java
+++ b/graphics/java/android/graphics/drawable/VectorDrawable.java
@@ -49,7 +49,7 @@
import java.util.Stack;
/**
- * This lets you create a drawable based on an XML vector graphic It can be
+ * This lets you create a drawable based on an XML vector graphic. It can be
* defined in an XML file with the <code><vector></code> element.
* <p/>
* The vector drawable has the following elements:
diff --git a/graphics/java/android/graphics/drawable/shapes/Shape.java b/graphics/java/android/graphics/drawable/shapes/Shape.java
index 589fbaa..eab8666 100644
--- a/graphics/java/android/graphics/drawable/shapes/Shape.java
+++ b/graphics/java/android/graphics/drawable/shapes/Shape.java
@@ -16,6 +16,7 @@
package android.graphics.drawable.shapes;
+import android.annotation.NonNull;
import android.graphics.Canvas;
import android.graphics.Outline;
import android.graphics.Paint;
@@ -93,11 +94,12 @@
protected void onResize(float width, float height) {}
/**
- * Compute the Outline of the shape.
+ * Compute the Outline of the shape and return it in the supplied Outline
+ * parameter. The default implementation does nothing and {@code outline} is not changed.
*
- * The default implementation does not supply an outline.
+ * @param outline The Outline to be populated with the result. Should not be null.
*/
- public void getOutline(Outline outline) {}
+ public void getOutline(@NonNull Outline outline) {}
@Override
public Shape clone() throws CloneNotSupportedException {
diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp
index 7661d58..2a4dec0 100644
--- a/libs/androidfw/ResourceTypes.cpp
+++ b/libs/androidfw/ResourceTypes.cpp
@@ -802,11 +802,16 @@
const String8 ResStringPool::string8ObjectAt(size_t idx) const
{
size_t len;
- const char *str = (const char*)string8At(idx, &len);
+ const char *str = string8At(idx, &len);
if (str != NULL) {
- return String8(str);
+ return String8(str, len);
}
- return String8(stringAt(idx, &len));
+
+ const char16_t *str16 = stringAt(idx, &len);
+ if (str16 != NULL) {
+ return String8(str16, len);
+ }
+ return String8();
}
const ResStringPool_span* ResStringPool::styleAt(const ResStringPool_ref& ref) const
@@ -2691,6 +2696,9 @@
case ResTable_config::DENSITY_XXHIGH:
res.append("xxhdpi");
break;
+ case ResTable_config::DENSITY_XXXHIGH:
+ res.append("xxxhdpi");
+ break;
case ResTable_config::DENSITY_NONE:
res.append("nodpi");
break;
@@ -6204,11 +6212,6 @@
if (mError != 0) {
printf("mError=0x%x (%s)\n", mError, strerror(mError));
}
-#if 0
- char localeStr[RESTABLE_MAX_LOCALE_LEN];
- mParams.getBcp47Locale(localeStr);
- printf("mParams=%s,\n" localeStr);
-#endif
size_t pgCount = mPackageGroups.size();
printf("Package Groups (%d)\n", (int)pgCount);
for (size_t pgIndex=0; pgIndex<pgCount; pgIndex++) {
diff --git a/libs/androidfw/tests/ResTable_test.cpp b/libs/androidfw/tests/ResTable_test.cpp
index 8016a82..89d271d0 100644
--- a/libs/androidfw/tests/ResTable_test.cpp
+++ b/libs/androidfw/tests/ResTable_test.cpp
@@ -195,4 +195,20 @@
ASSERT_EQ(uint32_t(400), val.data);
}
+TEST(ResTableTest, emptyTableHasSensibleDefaults) {
+ const int32_t assetCookie = 1;
+
+ ResTable table;
+ ASSERT_EQ(NO_ERROR, table.addEmpty(assetCookie));
+
+ // Adding an empty table gives us one table!
+ ASSERT_EQ(uint32_t(1), table.getTableCount());
+
+ // Adding an empty table doesn't mean we get packages.
+ ASSERT_EQ(uint32_t(0), table.getBasePackageCount());
+
+ Res_value val;
+ ASSERT_LT(table.getResource(base::R::integer::number1, &val, MAY_NOT_BE_BAG), 0);
+}
+
}
diff --git a/libs/hwui/Animator.cpp b/libs/hwui/Animator.cpp
index 5ecd77a..78d569d 100644
--- a/libs/hwui/Animator.cpp
+++ b/libs/hwui/Animator.cpp
@@ -14,8 +14,6 @@
* limitations under the License.
*/
-#define LOG_TAG "RT-Animator"
-
#include "Animator.h"
#include <inttypes.h>
diff --git a/libs/hwui/DamageAccumulator.cpp b/libs/hwui/DamageAccumulator.cpp
index 15bed58..054a164 100644
--- a/libs/hwui/DamageAccumulator.cpp
+++ b/libs/hwui/DamageAccumulator.cpp
@@ -14,8 +14,6 @@
* limitations under the License.
*/
-#define LOG_TAG "DamageAccumulator"
-
#include "DamageAccumulator.h"
#include <cutils/log.h>
@@ -26,12 +24,6 @@
namespace android {
namespace uirenderer {
-NullDamageAccumulator NullDamageAccumulator::sInstance;
-
-NullDamageAccumulator* NullDamageAccumulator::instance() {
- return &sInstance;
-}
-
enum TransformType {
TransformInvalid = 0,
TransformRenderNode,
@@ -60,6 +52,30 @@
mHead->type = TransformNone;
}
+static void computeTransformImpl(const DirtyStack* currentFrame, Matrix4* outMatrix) {
+ if (currentFrame->prev != currentFrame) {
+ computeTransformImpl(currentFrame->prev, outMatrix);
+ }
+ switch (currentFrame->type) {
+ case TransformRenderNode:
+ currentFrame->renderNode->applyViewPropertyTransforms(*outMatrix);
+ break;
+ case TransformMatrix4:
+ outMatrix->multiply(*currentFrame->matrix4);
+ break;
+ case TransformNone:
+ // nothing to be done
+ break;
+ default:
+ LOG_ALWAYS_FATAL("Tried to compute transform with an invalid type: %d", currentFrame->type);
+ }
+}
+
+void DamageAccumulator::computeCurrentTransform(Matrix4* outMatrix) const {
+ outMatrix->loadIdentity();
+ computeTransformImpl(mHead, outMatrix);
+}
+
void DamageAccumulator::pushCommon() {
if (!mHead->next) {
DirtyStack* nextFrame = (DirtyStack*) mAllocator.alloc(sizeof(DirtyStack));
diff --git a/libs/hwui/DamageAccumulator.h b/libs/hwui/DamageAccumulator.h
index 90d9425..6f0bd8c 100644
--- a/libs/hwui/DamageAccumulator.h
+++ b/libs/hwui/DamageAccumulator.h
@@ -31,18 +31,7 @@
class RenderNode;
class Matrix4;
-class IDamageAccumulator {
-public:
- virtual void pushTransform(const RenderNode* transform) = 0;
- virtual void pushTransform(const Matrix4* transform) = 0;
- virtual void popTransform() = 0;
- virtual void dirty(float left, float top, float right, float bottom) = 0;
- virtual void peekAtDirty(SkRect* dest) = 0;
-protected:
- virtual ~IDamageAccumulator() {}
-};
-
-class DamageAccumulator : public IDamageAccumulator {
+class DamageAccumulator {
PREVENT_COPY_AND_ASSIGN(DamageAccumulator);
public:
DamageAccumulator();
@@ -51,17 +40,19 @@
// Push a transform node onto the stack. This should be called prior
// to any dirty() calls. Subsequent calls to dirty()
// will be affected by the transform when popTransform() is called.
- virtual void pushTransform(const RenderNode* transform);
- virtual void pushTransform(const Matrix4* transform);
+ void pushTransform(const RenderNode* transform);
+ void pushTransform(const Matrix4* transform);
// Pops a transform node from the stack, propagating the dirty rect
// up to the parent node. Returns the IDamageTransform that was just applied
- virtual void popTransform();
+ void popTransform();
- virtual void dirty(float left, float top, float right, float bottom);
+ void dirty(float left, float top, float right, float bottom);
// Returns the current dirty area, *NOT* transformed by pushed transforms
- virtual void peekAtDirty(SkRect* dest);
+ void peekAtDirty(SkRect* dest);
+
+ void computeCurrentTransform(Matrix4* outMatrix) const;
void finish(SkRect* totalDirty);
@@ -74,24 +65,6 @@
DirtyStack* mHead;
};
-class NullDamageAccumulator : public IDamageAccumulator {
- PREVENT_COPY_AND_ASSIGN(NullDamageAccumulator);
-public:
- virtual void pushTransform(const RenderNode* transform) { }
- virtual void pushTransform(const Matrix4* transform) { }
- virtual void popTransform() { }
- virtual void dirty(float left, float top, float right, float bottom) { }
- virtual void peekAtDirty(SkRect* dest) { dest->setEmpty(); }
-
- ANDROID_API static NullDamageAccumulator* instance();
-
-private:
- NullDamageAccumulator() {}
- ~NullDamageAccumulator() {}
-
- static NullDamageAccumulator sInstance;
-};
-
} /* namespace uirenderer */
} /* namespace android */
diff --git a/libs/hwui/DisplayListRenderer.cpp b/libs/hwui/DisplayListRenderer.cpp
index f0a6e55..94162fc 100644
--- a/libs/hwui/DisplayListRenderer.cpp
+++ b/libs/hwui/DisplayListRenderer.cpp
@@ -66,7 +66,7 @@
"prepareDirty called a second time during a recording!");
mDisplayListData = new DisplayListData();
- initializeSaveStack(0, 0, getWidth(), getHeight());
+ initializeSaveStack(0, 0, getWidth(), getHeight(), Vector3());
mDirtyClip = opaque;
mRestoreSaveCount = -1;
diff --git a/libs/hwui/Layer.cpp b/libs/hwui/Layer.cpp
index d05cabc..8639ae1 100644
--- a/libs/hwui/Layer.cpp
+++ b/libs/hwui/Layer.cpp
@@ -53,6 +53,7 @@
deferredList = NULL;
convexMask = NULL;
caches.resourceCache.incrementRefcount(this);
+ rendererLightPosDirty = true;
}
Layer::~Layer() {
@@ -80,6 +81,17 @@
}
}
+void Layer::updateLightPosFromRenderer(const OpenGLRenderer& rootRenderer) {
+ if (renderer && rendererLightPosDirty) {
+ // re-init renderer's light position, based upon last cached location in window
+ Vector3 lightPos = rootRenderer.getLightCenter();
+ cachedInvTransformInWindow.mapPoint3d(lightPos);
+ renderer->initLight(lightPos, rootRenderer.getLightRadius(),
+ rootRenderer.getAmbientShadowAlpha(), rootRenderer.getSpotShadowAlpha());
+ rendererLightPosDirty = false;
+ }
+}
+
bool Layer::resize(const uint32_t width, const uint32_t height) {
uint32_t desiredWidth = computeIdealWidth(width);
uint32_t desiredHeight = computeIdealWidth(height);
@@ -203,7 +215,8 @@
}
}
-void Layer::defer() {
+void Layer::defer(const OpenGLRenderer& rootRenderer) {
+ updateLightPosFromRenderer(rootRenderer);
const float width = layer.getWidth();
const float height = layer.getHeight();
@@ -253,7 +266,8 @@
}
}
-void Layer::render() {
+void Layer::render(const OpenGLRenderer& rootRenderer) {
+ updateLightPosFromRenderer(rootRenderer);
renderer->setViewport(layer.getWidth(), layer.getHeight());
renderer->prepareDirty(dirtyRect.left, dirtyRect.top, dirtyRect.right, dirtyRect.bottom,
!isBlend());
diff --git a/libs/hwui/Layer.h b/libs/hwui/Layer.h
index 0bf05d0..38c29c7 100644
--- a/libs/hwui/Layer.h
+++ b/libs/hwui/Layer.h
@@ -86,6 +86,11 @@
regionRect.translate(layer.left, layer.top);
}
+ void setWindowTransform(Matrix4& windowTransform) {
+ cachedInvTransformInWindow.loadInverse(windowTransform);
+ rendererLightPosDirty = true;
+ }
+
void updateDeferred(RenderNode* renderNode, int left, int top, int right, int bottom);
inline uint32_t getWidth() const {
@@ -257,10 +262,10 @@
return transform;
}
- void defer();
+ void defer(const OpenGLRenderer& rootRenderer);
void cancelDefer();
void flush();
- void render();
+ void render(const OpenGLRenderer& rootRenderer);
/**
* Bounds of the layer.
@@ -304,6 +309,7 @@
private:
void requireRenderer();
+ void updateLightPosFromRenderer(const OpenGLRenderer& rootRenderer);
Caches& caches;
@@ -383,6 +389,12 @@
mat4 transform;
/**
+ * Cached transform of layer in window, updated only on creation / resize
+ */
+ mat4 cachedInvTransformInWindow;
+ bool rendererLightPosDirty;
+
+ /**
* Used to defer display lists when the layer is updated with a
* display list.
*/
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index 3fcfbc1..721ab3d 100755
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -188,8 +188,7 @@
void OpenGLRenderer::setupFrameState(float left, float top,
float right, float bottom, bool opaque) {
mCaches.clearGarbage();
-
- initializeSaveStack(left, top, right, bottom);
+ initializeSaveStack(left, top, right, bottom, mLightCenter);
mOpaque = opaque;
mTilingClip.set(left, top, right, bottom);
}
@@ -481,9 +480,9 @@
}
if (CC_UNLIKELY(inFrame || mCaches.drawDeferDisabled)) {
- layer->render();
+ layer->render(*this);
} else {
- layer->defer();
+ layer->defer(*this);
}
if (inFrame) {
diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h
index fc95c18..e9ca5d9 100755
--- a/libs/hwui/OpenGLRenderer.h
+++ b/libs/hwui/OpenGLRenderer.h
@@ -345,8 +345,10 @@
}
#endif
- const Vector3& getLightCenter() const { return mLightCenter; }
+ const Vector3& getLightCenter() const { return currentSnapshot()->getRelativeLightCenter(); }
float getLightRadius() const { return mLightRadius; }
+ uint8_t getAmbientShadowAlpha() const { return mAmbientShadowAlpha; }
+ uint8_t getSpotShadowAlpha() const { return mSpotShadowAlpha; }
protected:
/**
diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp
index e3732a1..0db6198 100644
--- a/libs/hwui/RenderNode.cpp
+++ b/libs/hwui/RenderNode.cpp
@@ -116,6 +116,7 @@
void RenderNode::prepareTree(TreeInfo& info) {
ATRACE_CALL();
+ LOG_ALWAYS_FATAL_IF(!info.damageAccumulator, "DamageAccumulator missing");
prepareTreeImpl(info);
}
@@ -163,16 +164,26 @@
return;
}
+ bool transformUpdateNeeded = false;
if (!mLayer) {
mLayer = LayerRenderer::createRenderLayer(info.renderState, getWidth(), getHeight());
applyLayerPropertiesToLayer(info);
damageSelf(info);
+ transformUpdateNeeded = true;
} else if (mLayer->layer.getWidth() != getWidth() || mLayer->layer.getHeight() != getHeight()) {
if (!LayerRenderer::resizeLayer(mLayer, getWidth(), getHeight())) {
LayerRenderer::destroyLayer(mLayer);
mLayer = 0;
}
damageSelf(info);
+ transformUpdateNeeded = true;
+ }
+
+ if (transformUpdateNeeded) {
+ // update the transform in window of the layer to reset its origin wrt light source position
+ Matrix4 windowTransform;
+ info.damageAccumulator->computeCurrentTransform(&windowTransform);
+ mLayer->setWindowTransform(windowTransform);
}
SkRect dirty;
@@ -406,7 +417,7 @@
* If true3dTransform is set to true, the transform applied to the input matrix will use true 4x4
* matrix computation instead of the Skia 3x3 matrix + camera hackery.
*/
-void RenderNode::applyViewPropertyTransforms(mat4& matrix, bool true3dTransform) {
+void RenderNode::applyViewPropertyTransforms(mat4& matrix, bool true3dTransform) const {
if (properties().getLeft() != 0 || properties().getTop() != 0) {
matrix.translate(properties().getLeft(), properties().getTop());
}
diff --git a/libs/hwui/RenderNode.h b/libs/hwui/RenderNode.h
index fa310e0..afa17d5 100644
--- a/libs/hwui/RenderNode.h
+++ b/libs/hwui/RenderNode.h
@@ -174,6 +174,8 @@
// UI thread only!
ANDROID_API void addAnimator(const sp<BaseRenderNodeAnimator>& animator);
+ void applyViewPropertyTransforms(mat4& matrix, bool true3dTransform = false) const;
+
private:
typedef key_value_pair_t<float, DrawRenderNodeOp*> ZDrawRenderNodeOpPair;
@@ -189,8 +191,6 @@
kPositiveZChildren
};
- void applyViewPropertyTransforms(mat4& matrix, bool true3dTransform = false);
-
void computeOrderingImpl(DrawRenderNodeOp* opState,
const SkPath* outlineOfProjectionSurface,
Vector<DrawRenderNodeOp*>* compositedChildrenOfProjectionSurface,
diff --git a/libs/hwui/Snapshot.cpp b/libs/hwui/Snapshot.cpp
index 6f19275..ecc47d2 100644
--- a/libs/hwui/Snapshot.cpp
+++ b/libs/hwui/Snapshot.cpp
@@ -55,7 +55,8 @@
, empty(false)
, alpha(s->alpha)
, roundRectClipState(s->roundRectClipState)
- , mViewportData(s->mViewportData) {
+ , mViewportData(s->mViewportData)
+ , mRelativeLightCenter(s->mRelativeLightCenter) {
if (saveFlags & SkCanvas::kMatrix_SaveFlag) {
mTransformRoot.load(*s->transform);
transform = &mTransformRoot;
@@ -200,6 +201,13 @@
///////////////////////////////////////////////////////////////////////////////
void Snapshot::resetTransform(float x, float y, float z) {
+ // before resetting, map current light pos with inverse of current transform
+ Vector3 center = mRelativeLightCenter;
+ mat4 inverse;
+ inverse.loadInverse(*transform);
+ inverse.mapPoint3d(center);
+ mRelativeLightCenter = center;
+
transform = &mTransformRoot;
transform->loadTranslate(x, y, z);
}
diff --git a/libs/hwui/Snapshot.h b/libs/hwui/Snapshot.h
index 98e2440..ad4ee9d 100644
--- a/libs/hwui/Snapshot.h
+++ b/libs/hwui/Snapshot.h
@@ -161,6 +161,9 @@
int getViewportHeight() const { return mViewportData.mHeight; }
const Matrix4& getOrthoMatrix() const { return mViewportData.mOrthoMatrix; }
+ const Vector3& getRelativeLightCenter() const { return mRelativeLightCenter; }
+ void setRelativeLightCenter(const Vector3& lightCenter) { mRelativeLightCenter = lightCenter; }
+
/**
* Sets (and replaces) the current clipping outline
*/
@@ -302,6 +305,7 @@
SkRegion mClipRegionRoot;
ViewportData mViewportData;
+ Vector3 mRelativeLightCenter;
}; // class Snapshot
diff --git a/libs/hwui/StatefulBaseRenderer.cpp b/libs/hwui/StatefulBaseRenderer.cpp
index dc41157..06c5ab4 100644
--- a/libs/hwui/StatefulBaseRenderer.cpp
+++ b/libs/hwui/StatefulBaseRenderer.cpp
@@ -35,11 +35,12 @@
}
void StatefulBaseRenderer::initializeSaveStack(float clipLeft, float clipTop,
- float clipRight, float clipBottom) {
+ float clipRight, float clipBottom, const Vector3& lightCenter) {
mSnapshot = new Snapshot(mFirstSnapshot,
SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
mSnapshot->setClip(clipLeft, clipTop, clipRight, clipBottom);
mSnapshot->fbo = getTargetFbo();
+ mSnapshot->setRelativeLightCenter(lightCenter);
mSaveCount = 1;
}
diff --git a/libs/hwui/StatefulBaseRenderer.h b/libs/hwui/StatefulBaseRenderer.h
index 6d83b4c..3957d36 100644
--- a/libs/hwui/StatefulBaseRenderer.h
+++ b/libs/hwui/StatefulBaseRenderer.h
@@ -52,7 +52,8 @@
* the render target.
*/
virtual void setViewport(int width, int height);
- void initializeSaveStack(float clipLeft, float clipTop, float clipRight, float clipBottom);
+ void initializeSaveStack(float clipLeft, float clipTop, float clipRight, float clipBottom,
+ const Vector3& lightCenter);
// getters
bool hasRectToRectTransform() const {
diff --git a/libs/hwui/TessellationCache.cpp b/libs/hwui/TessellationCache.cpp
index c5fc21f..0a9aeb8 100644
--- a/libs/hwui/TessellationCache.cpp
+++ b/libs/hwui/TessellationCache.cpp
@@ -14,9 +14,6 @@
* limitations under the License.
*/
-#define LOG_TAG "OpenGLRenderer"
-#define ATRACE_TAG ATRACE_TAG_VIEW
-
#include <utils/JenkinsHash.h>
#include <utils/Trace.h>
diff --git a/libs/hwui/TreeInfo.h b/libs/hwui/TreeInfo.h
index de09755..331f157 100644
--- a/libs/hwui/TreeInfo.h
+++ b/libs/hwui/TreeInfo.h
@@ -65,7 +65,7 @@
, frameTimeMs(0)
, animationHook(NULL)
, prepareTextures(mode == MODE_FULL)
- , damageAccumulator(NullDamageAccumulator::instance())
+ , damageAccumulator(NULL)
, renderState(renderState)
, renderer(NULL)
, errorHandler(NULL)
@@ -88,8 +88,9 @@
// TODO: Remove this? Currently this is used to signal to stop preparing
// textures if we run out of cache space.
bool prepareTextures;
- // Must not be null
- IDamageAccumulator* damageAccumulator;
+
+ // Must not be null during actual usage
+ DamageAccumulator* damageAccumulator;
RenderState& renderState;
// The renderer that will be drawing the next frame. Use this to push any
// layer updates or similar. May be NULL.
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index 756f660..e673b0d 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -14,8 +14,6 @@
* limitations under the License.
*/
-#define LOG_TAG "CanvasContext"
-
#include "CanvasContext.h"
#include <private/hwui/DrawGlInfo.h>
diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp
index 986e808..d9b96f6c 100644
--- a/libs/hwui/renderthread/RenderProxy.cpp
+++ b/libs/hwui/renderthread/RenderProxy.cpp
@@ -14,8 +14,6 @@
* limitations under the License.
*/
-#define LOG_TAG "RenderProxy"
-
#include "RenderProxy.h"
#include "CanvasContext.h"
diff --git a/libs/hwui/renderthread/RenderThread.cpp b/libs/hwui/renderthread/RenderThread.cpp
index 03e98d5..403e164 100644
--- a/libs/hwui/renderthread/RenderThread.cpp
+++ b/libs/hwui/renderthread/RenderThread.cpp
@@ -14,10 +14,11 @@
* limitations under the License.
*/
-#define LOG_TAG "RenderThread"
-
#include "RenderThread.h"
+#if defined(HAVE_PTHREADS)
+#include <sys/resource.h>
+#endif
#include <gui/DisplayEventReceiver.h>
#include <utils/Log.h>
@@ -244,6 +245,9 @@
}
bool RenderThread::threadLoop() {
+#if defined(HAVE_PTHREADS)
+ setpriority(PRIO_PROCESS, 0, PRIORITY_DISPLAY);
+#endif
initThreadLocals();
int timeoutMillis = -1;
diff --git a/libs/hwui/thread/TaskManager.cpp b/libs/hwui/thread/TaskManager.cpp
index 3d2b0d9..cb5401c 100644
--- a/libs/hwui/thread/TaskManager.cpp
+++ b/libs/hwui/thread/TaskManager.cpp
@@ -15,6 +15,9 @@
*/
#include <sys/sysinfo.h>
+#if defined(HAVE_PTHREADS)
+#include <sys/resource.h>
+#endif
#include "TaskManager.h"
#include "Task.h"
@@ -79,6 +82,13 @@
// Thread
///////////////////////////////////////////////////////////////////////////////
+status_t TaskManager::WorkerThread::readyToRun() {
+#if defined(HAVE_PTHREADS)
+ setpriority(PRIO_PROCESS, 0, PRIORITY_FOREGROUND);
+#endif
+ return NO_ERROR;
+}
+
bool TaskManager::WorkerThread::threadLoop() {
mSignal.wait();
Vector<TaskWrapper> tasks;
diff --git a/libs/hwui/thread/TaskManager.h b/libs/hwui/thread/TaskManager.h
index f2a216f..5a933ab 100644
--- a/libs/hwui/thread/TaskManager.h
+++ b/libs/hwui/thread/TaskManager.h
@@ -84,6 +84,7 @@
void exit();
private:
+ virtual status_t readyToRun();
virtual bool threadLoop();
// Lock for the list of tasks
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index bd50142..6a76a71 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -1672,24 +1672,6 @@
/**
* @hide
- * Checks whether any local or remote media playback is active.
- * Local playback refers to playback for instance on the device's speakers or wired headphones.
- * Remote playback refers to playback for instance on a wireless display mirroring the
- * devices's, or on a device using a Cast-like protocol.
- * @return true if media playback, from which the device is aware, is active.
- */
- public boolean isLocalOrRemoteMusicActive() {
- IAudioService service = getService();
- try {
- return service.isLocalOrRemoteMusicActive();
- } catch (RemoteException e) {
- Log.e(TAG, "Dead object in isLocalOrRemoteMusicActive()", e);
- return false;
- }
- }
-
- /**
- * @hide
* Checks whether the current audio focus is exclusive.
* @return true if the top of the audio focus stack requested focus
* with {@link #AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE}
diff --git a/media/java/android/media/AudioManagerInternal.java b/media/java/android/media/AudioManagerInternal.java
new file mode 100644
index 0000000..6f1bdef
--- /dev/null
+++ b/media/java/android/media/AudioManagerInternal.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.media;
+
+import com.android.server.LocalServices;
+
+/**
+ * Class for system services to access extra AudioManager functionality. The
+ * AudioService is responsible for registering an implementation with
+ * {@link LocalServices}.
+ *
+ * @hide
+ */
+public abstract class AudioManagerInternal {
+
+ public abstract void adjustStreamVolumeForUid(int streamType, int direction, int flags,
+ String callingPackage, int uid);
+
+ public abstract void setStreamVolumeForUid(int streamType, int direction, int flags,
+ String callingPackage, int uid);
+}
diff --git a/media/java/android/media/AudioService.java b/media/java/android/media/AudioService.java
index 6d0bfee..0af2457 100644
--- a/media/java/android/media/AudioService.java
+++ b/media/java/android/media/AudioService.java
@@ -79,6 +79,7 @@
import com.android.internal.telephony.ITelephony;
import com.android.internal.util.XmlUtils;
+import com.android.server.LocalServices;
import org.xmlpull.v1.XmlPullParserException;
@@ -481,11 +482,6 @@
final RemoteCallbackList<IAudioRoutesObserver> mRoutesObservers
= new RemoteCallbackList<IAudioRoutesObserver>();
- /**
- * A fake stream type to match the notion of remote media playback
- */
- public final static int STREAM_REMOTE_MUSIC = -200;
-
// Devices for which the volume is fixed and VolumePanel slider should be disabled
int mFixedVolumeDevices = AudioSystem.DEVICE_OUT_HDMI |
AudioSystem.DEVICE_OUT_DGTL_DOCK_HEADSET |
@@ -635,6 +631,7 @@
mMasterVolumeRamp = context.getResources().getIntArray(
com.android.internal.R.array.config_masterVolumeRamp);
+ LocalServices.addService(AudioManagerInternal.class, new AudioServiceInternal());
}
public void systemReady() {
@@ -896,27 +893,6 @@
///////////////////////////////////////////////////////////////////////////
// IPC methods
///////////////////////////////////////////////////////////////////////////
- /** @see AudioManager#isLocalOrRemoteMusicActive() */
- public boolean isLocalOrRemoteMusicActive() {
- if (AudioSystem.isStreamActive(AudioSystem.STREAM_MUSIC, 0)) {
- // local / wired / BT playback active
- if (DEBUG_VOL) Log.d(TAG, "isLocalOrRemoteMusicActive(): local");
- return true;
- }
- if (mMediaFocusControl.checkUpdateRemoteStateIfActive(AudioSystem.STREAM_MUSIC)) {
- // remote "cast-like" playback active
- if (DEBUG_VOL) Log.d(TAG, "isLocalOrRemoteMusicActive(): has PLAYBACK_TYPE_REMOTE");
- return true;
- }
- if (AudioSystem.isStreamActiveRemotely(AudioSystem.STREAM_MUSIC, 0)) {
- // remote submix playback active
- if (DEBUG_VOL) Log.d(TAG, "isLocalOrRemoteMusicActive(): remote submix");
- return true;
- }
- if (DEBUG_VOL) Log.d(TAG, "isLocalOrRemoteMusicActive(): no");
- return false;
- }
-
/** @see AudioManager#adjustVolume(int, int) */
public void adjustSuggestedStreamVolume(int direction, int suggestedStreamType, int flags,
String callingPackage) {
@@ -930,9 +906,8 @@
}
final int resolvedStream = mStreamVolumeAlias[streamType];
- // Play sounds on STREAM_RING and STREAM_REMOTE_MUSIC only.
- if ((streamType != STREAM_REMOTE_MUSIC) &&
- (flags & AudioManager.FLAG_PLAY_SOUND) != 0 &&
+ // Play sounds on STREAM_RING only.
+ if ((flags & AudioManager.FLAG_PLAY_SOUND) != 0 &&
resolvedStream != AudioSystem.STREAM_RING) {
flags &= ~AudioManager.FLAG_PLAY_SOUND;
}
@@ -945,17 +920,17 @@
if (DEBUG_VOL) Log.d(TAG, "Volume controller suppressed adjustment");
}
- if (streamType == STREAM_REMOTE_MUSIC) {
- // TODO bounce it to MediaSessionService to find an appropriate
- // session
- } else {
- adjustStreamVolume(streamType, direction, flags, callingPackage);
- }
+ adjustStreamVolume(streamType, direction, flags, callingPackage);
}
/** @see AudioManager#adjustStreamVolume(int, int, int) */
public void adjustStreamVolume(int streamType, int direction, int flags,
String callingPackage) {
+ adjustStreamVolume(streamType, direction, flags, callingPackage, Binder.getCallingUid());
+ }
+
+ private void adjustStreamVolume(int streamType, int direction, int flags,
+ String callingPackage, int uid) {
if (mUseFixedVolume) {
return;
}
@@ -984,8 +959,8 @@
return;
}
- if (mAppOps.noteOp(STEAM_VOLUME_OPS[streamTypeAlias], Binder.getCallingUid(),
- callingPackage) != AppOpsManager.MODE_ALLOWED) {
+ if (mAppOps.noteOp(STEAM_VOLUME_OPS[streamTypeAlias], uid, callingPackage)
+ != AppOpsManager.MODE_ALLOWED) {
return;
}
@@ -1161,6 +1136,11 @@
/** @see AudioManager#setStreamVolume(int, int, int) */
public void setStreamVolume(int streamType, int index, int flags, String callingPackage) {
+ setStreamVolume(streamType, index, flags, callingPackage, Binder.getCallingUid());
+ }
+
+ private void setStreamVolume(int streamType, int index, int flags, String callingPackage,
+ int uid) {
if (mUseFixedVolume) {
return;
}
@@ -1179,8 +1159,8 @@
return;
}
- if (mAppOps.noteOp(STEAM_VOLUME_OPS[streamTypeAlias], Binder.getCallingUid(),
- callingPackage) != AppOpsManager.MODE_ALLOWED) {
+ if (mAppOps.noteOp(STEAM_VOLUME_OPS[streamTypeAlias], uid, callingPackage)
+ != AppOpsManager.MODE_ALLOWED) {
return;
}
@@ -1885,10 +1865,6 @@
}
}
int streamType = getActiveStreamType(AudioManager.USE_DEFAULT_STREAM_TYPE);
- if (streamType == STREAM_REMOTE_MUSIC) {
- // here handle remote media playback the same way as local playback
- streamType = AudioManager.STREAM_MUSIC;
- }
int device = getDeviceForStream(streamType);
int index = mStreamStates[mStreamVolumeAlias[streamType]].getIndex(device);
setStreamVolumeInt(mStreamVolumeAlias[streamType], index, device, true);
@@ -2973,12 +2949,6 @@
if (DEBUG_VOL)
Log.v(TAG, "getActiveStreamType: Forcing STREAM_MUSIC stream active");
return AudioSystem.STREAM_MUSIC;
- } else
- if (mMediaFocusControl.checkUpdateRemoteStateIfActive(AudioSystem.STREAM_MUSIC))
- {
- if (DEBUG_VOL)
- Log.v(TAG, "getActiveStreamType: Forcing STREAM_REMOTE_MUSIC");
- return STREAM_REMOTE_MUSIC;
} else {
if (DEBUG_VOL)
Log.v(TAG, "getActiveStreamType: Forcing STREAM_RING b/c default");
@@ -2992,19 +2962,8 @@
break;
case PLATFORM_TELEVISION:
if (suggestedStreamType == AudioManager.USE_DEFAULT_STREAM_TYPE) {
- if (isAfMusicActiveRecently(DEFAULT_STREAM_TYPE_OVERRIDE_DELAY_MS)) {
- if (DEBUG_VOL) Log.v(TAG, "getActiveStreamType: forcing STREAM_MUSIC");
+ // TV always defaults to STREAM_MUSIC
return AudioSystem.STREAM_MUSIC;
- } else if (mMediaFocusControl.checkUpdateRemoteStateIfActive(
- AudioSystem.STREAM_MUSIC)) {
- if (DEBUG_VOL)
- Log.v(TAG, "getActiveStreamType: Forcing STREAM_REMOTE_MUSIC");
- return STREAM_REMOTE_MUSIC;
- } else {
- if (DEBUG_VOL) Log.v(TAG,
- "getActiveStreamType: using STREAM_MUSIC as default");
- return AudioSystem.STREAM_MUSIC;
- }
}
break;
default:
@@ -3027,12 +2986,6 @@
if (isAfMusicActiveRecently(DEFAULT_STREAM_TYPE_OVERRIDE_DELAY_MS)) {
if (DEBUG_VOL) Log.v(TAG, "getActiveStreamType: forcing STREAM_MUSIC");
return AudioSystem.STREAM_MUSIC;
- } else
- if (mMediaFocusControl.checkUpdateRemoteStateIfActive(AudioSystem.STREAM_MUSIC))
- {
- if (DEBUG_VOL)
- Log.v(TAG, "getActiveStreamType: Forcing STREAM_REMOTE_MUSIC");
- return STREAM_REMOTE_MUSIC;
} else {
if (DEBUG_VOL) Log.v(TAG,
"getActiveStreamType: using STREAM_NOTIFICATION as default");
@@ -5347,6 +5300,24 @@
}
}
+ /**
+ * Interface for system components to get some extra functionality through
+ * LocalServices.
+ */
+ final class AudioServiceInternal extends AudioManagerInternal {
+ @Override
+ public void adjustStreamVolumeForUid(int streamType, int direction, int flags,
+ String callingPackage, int uid) {
+ adjustStreamVolume(streamType, direction, flags, callingPackage, uid);
+ }
+
+ @Override
+ public void setStreamVolumeForUid(int streamType, int direction, int flags,
+ String callingPackage, int uid) {
+ setStreamVolume(streamType, direction, flags, callingPackage, uid);
+ }
+ }
+
//==========================================================================================
// Audio policy management
//==========================================================================================
diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl
index 7318660..6477055 100644
--- a/media/java/android/media/IAudioService.aidl
+++ b/media/java/android/media/IAudioService.aidl
@@ -37,8 +37,6 @@
*/
interface IAudioService {
- boolean isLocalOrRemoteMusicActive();
-
void adjustSuggestedStreamVolume(int direction, int suggestedStreamType, int flags,
String callingPackage);
diff --git a/media/java/android/media/RemoteController.java b/media/java/android/media/RemoteController.java
index 0aaaf46..adc8391 100644
--- a/media/java/android/media/RemoteController.java
+++ b/media/java/android/media/RemoteController.java
@@ -793,7 +793,7 @@
final ComponentName listenerComponent = new ComponentName(mContext,
mOnClientUpdateListener.getClass());
mSessionManager.addActiveSessionsListener(mSessionListener, listenerComponent,
- UserHandle.myUserId());
+ UserHandle.myUserId(), null);
mSessionListener.onActiveSessionsChanged(mSessionManager
.getActiveSessions(listenerComponent));
if (DEBUG) {
diff --git a/media/java/android/media/session/ISessionController.aidl b/media/java/android/media/session/ISessionController.aidl
index 3518458..5764bd1 100644
--- a/media/java/android/media/session/ISessionController.aidl
+++ b/media/java/android/media/session/ISessionController.aidl
@@ -48,8 +48,8 @@
PendingIntent getLaunchPendingIntent();
long getFlags();
ParcelableVolumeInfo getVolumeAttributes();
- void adjustVolume(int direction, int flags);
- void setVolumeTo(int value, int flags);
+ void adjustVolume(int direction, int flags, String packageName);
+ void setVolumeTo(int value, int flags, String packageName);
IMediaRouterDelegate createMediaRouterDelegate(IMediaRouterStateCallback callback);
diff --git a/media/java/android/media/session/ISessionControllerCallback.aidl b/media/java/android/media/session/ISessionControllerCallback.aidl
index 78cd699..cf31767 100644
--- a/media/java/android/media/session/ISessionControllerCallback.aidl
+++ b/media/java/android/media/session/ISessionControllerCallback.aidl
@@ -27,6 +27,7 @@
*/
oneway interface ISessionControllerCallback {
void onEvent(String event, in Bundle extras);
+ void onSessionDestroyed();
// These callbacks are for the TransportController
void onPlaybackStateChanged(in PlaybackState state);
diff --git a/media/java/android/media/session/MediaController.java b/media/java/android/media/session/MediaController.java
index f6e189a..63c85f8 100644
--- a/media/java/android/media/session/MediaController.java
+++ b/media/java/android/media/session/MediaController.java
@@ -63,6 +63,7 @@
private static final int MSG_UPDATE_QUEUE = 5;
private static final int MSG_UPDATE_QUEUE_TITLE = 6;
private static final int MSG_UPDATE_EXTRAS = 7;
+ private static final int MSG_DESTROYED = 8;
private final ISessionController mSessionBinder;
@@ -310,7 +311,7 @@
*/
public void setVolumeTo(int value, int flags) {
try {
- mSessionBinder.setVolumeTo(value, flags);
+ mSessionBinder.setVolumeTo(value, flags, mContext.getPackageName());
} catch (RemoteException e) {
Log.wtf(TAG, "Error calling setVolumeTo.", e);
}
@@ -331,7 +332,7 @@
*/
public void adjustVolume(int direction, int flags) {
try {
- mSessionBinder.adjustVolume(direction, flags);
+ mSessionBinder.adjustVolume(direction, flags, mContext.getPackageName());
} catch (RemoteException e) {
Log.wtf(TAG, "Error calling adjustVolumeBy.", e);
}
@@ -508,6 +509,13 @@
*/
public static abstract class Callback {
/**
+ * Override to handle the session being destroyed. The session is no
+ * longer valid after this call and calls to it will be ignored.
+ */
+ public void onSessionDestroyed() {
+ }
+
+ /**
* Override to handle custom events sent by the session owner without a
* specified interface. Controllers should only handle these for
* sessions they own.
@@ -863,6 +871,14 @@
}
@Override
+ public void onSessionDestroyed() {
+ MediaController controller = mController.get();
+ if (controller != null) {
+ controller.postMessage(MSG_DESTROYED, null, null);
+ }
+ }
+
+ @Override
public void onEvent(String event, Bundle extras) {
MediaController controller = mController.get();
if (controller != null) {
@@ -955,6 +971,9 @@
case MSG_UPDATE_VOLUME:
mCallback.onVolumeInfoChanged((VolumeInfo) msg.obj);
break;
+ case MSG_DESTROYED:
+ mCallback.onSessionDestroyed();
+ break;
}
}
diff --git a/media/java/android/media/session/MediaSessionLegacyHelper.java b/media/java/android/media/session/MediaSessionLegacyHelper.java
index a182982..aa196a9 100644
--- a/media/java/android/media/session/MediaSessionLegacyHelper.java
+++ b/media/java/android/media/session/MediaSessionLegacyHelper.java
@@ -290,7 +290,6 @@
if (DEBUG) {
Log.d(TAG, "addMediaButtonListener already added " + pi);
}
- return;
}
holder.mMediaButtonListener = new MediaButtonListener(pi, context);
// TODO determine if handling transport performer commands should also
@@ -468,7 +467,11 @@
mSessions.remove(mPi);
} else if (mCb == null) {
mCb = new SessionCallback();
- mSession.setCallback(mCb);
+ Handler handler = null;
+ if (Looper.myLooper() == null) {
+ handler = new Handler(Looper.getMainLooper());
+ }
+ mSession.setCallback(mCb, handler);
}
}
diff --git a/media/java/android/media/session/MediaSessionManager.java b/media/java/android/media/session/MediaSessionManager.java
index 84983b9..c2fb5a3 100644
--- a/media/java/android/media/session/MediaSessionManager.java
+++ b/media/java/android/media/session/MediaSessionManager.java
@@ -23,6 +23,7 @@
import android.media.AudioManager;
import android.media.IRemoteVolumeController;
import android.media.session.ISessionManager;
+import android.os.Handler;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.ServiceManager;
@@ -37,11 +38,11 @@
/**
* Provides support for interacting with {@link MediaSession media sessions}
- * that applications have published to express their ongoing media playback state.
+ * that applications have published to express their ongoing media playback
+ * state.
* <p>
* Use <code>Context.getSystemService(Context.MEDIA_SESSION_SERVICE)</code> to
* get an instance of this class.
- * <p>
*
* @see MediaSession
* @see MediaController
@@ -133,15 +134,17 @@
* the calling app. You may also retrieve this list if your app is an
* enabled notification listener using the
* {@link NotificationListenerService} APIs, in which case you must pass the
- * {@link ComponentName} of your enabled listener.
+ * {@link ComponentName} of your enabled listener. Updates will be posted to
+ * the thread that registered the listener.
*
* @param sessionListener The listener to add.
* @param notificationListener The enabled notification listener component.
* May be null.
*/
- public void addActiveSessionsListener(SessionListener sessionListener,
- ComponentName notificationListener) {
- addActiveSessionsListener(sessionListener, notificationListener, UserHandle.myUserId());
+ public void addActiveSessionsListener(@NonNull SessionListener sessionListener,
+ @Nullable ComponentName notificationListener) {
+ addActiveSessionsListener(sessionListener, notificationListener, UserHandle.myUserId(),
+ null);
}
/**
@@ -157,13 +160,18 @@
* @param notificationListener The enabled notification listener component.
* May be null.
* @param userId The userId to listen for changes on.
+ * @param handler The handler to post updates on.
* @hide
*/
public void addActiveSessionsListener(@NonNull SessionListener sessionListener,
- @Nullable ComponentName notificationListener, int userId) {
+ @Nullable ComponentName notificationListener, int userId, @Nullable Handler handler) {
if (sessionListener == null) {
throw new IllegalArgumentException("listener may not be null");
}
+ if (handler == null) {
+ handler = new Handler();
+ }
+ sessionListener.setHandler(handler);
try {
mService.addSessionsListener(sessionListener.mStub, notificationListener, userId);
} catch (RemoteException e) {
@@ -253,6 +261,7 @@
*/
public static abstract class SessionListener {
private final Context mContext;
+ private Handler mHandler;
public SessionListener(Context context) {
mContext = context;
@@ -269,15 +278,27 @@
public abstract void onActiveSessionsChanged(
@Nullable List<MediaController> controllers);
+ private final void setHandler(Handler handler) {
+ mHandler = handler;
+ }
+
private final IActiveSessionsListener.Stub mStub = new IActiveSessionsListener.Stub() {
@Override
- public void onActiveSessionsChanged(List<MediaSession.Token> tokens) {
- ArrayList<MediaController> controllers = new ArrayList<MediaController>();
- int size = tokens.size();
- for (int i = 0; i < size; i++) {
- controllers.add(new MediaController(mContext, tokens.get(i)));
+ public void onActiveSessionsChanged(final List<MediaSession.Token> tokens) {
+ if (mHandler != null) {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ ArrayList<MediaController> controllers
+ = new ArrayList<MediaController>();
+ int size = tokens.size();
+ for (int i = 0; i < size; i++) {
+ controllers.add(new MediaController(mContext, tokens.get(i)));
+ }
+ SessionListener.this.onActiveSessionsChanged(controllers);
+ }
+ });
}
- SessionListener.this.onActiveSessionsChanged(controllers);
}
};
}
diff --git a/media/java/android/media/tv/TvContentRating.java b/media/java/android/media/tv/TvContentRating.java
index 015daee..405ef22 100644
--- a/media/java/android/media/tv/TvContentRating.java
+++ b/media/java/android/media/tv/TvContentRating.java
@@ -34,42 +34,87 @@
* strings, or a TV input service defined strings.
* TV input service defined strings are in an xml file defined in <code><{@link
* android.R.styleable#TvInputService tv-input}></code> with the {@link
- * android.R.attr#contentRatingSystemXml contentRatingSystemXml} attribute by the TV input service.
+ * android.R.attr#tvContentRatingDescription tvContentRatingDescription} attribute by the TV input
+ * service.
*
- * <h3> Content Rating System XML format </h3>
- * The XML file for publishing content rating system should follow the DTD bellow:
+ * <h3> Example: Rating system definition for the TV Parental Guidelines</h3>
+ * The following XML example shows how the TV Parental Guidelines in United States can be defined:
* <p><pre class="prettyprint">
- * <?xml version="1.0" encoding="UTF-8"?>
- * <!DOCTYPE rating-systems [
- * <!ELEMENT rating-system-definitions (rating-system-definition+)>
- * <!ATTLIST rating-system-definitions
- * version CDATA #REQUIRED>
- * <!ELEMENT rating-system-definition (
- * (sub-rating-definition*, rating-definition, sub-rating-definition*)+, order*)>
- * <!ATTLIST rating-system-definition
- * id ID #REQUIRED
- * displayName CDATA #IMPLIED
- * description CDATA #IMPLIED
- * country CDATA #IMPLIED>
- * <!ELEMENT sub-rating-definition EMPTY>
- * <!ATTLIST sub-rating-definition
- * id ID #REQUIRED
- * displayName CDATA #IMPLIED
- * icon CDATA #IMPLIED
- * description CDATA #IMPLIED>
- * <!ELEMENT rating-definition (sub-rating*))>
- * <!ATTLIST rating-definition
- * id ID #REQUIRED
- * displayName CDATA #IMPLIED
- * icon CDATA #IMPLIED
- * description CDATA #IMPLIED>
- * <!ELEMENT sub-rating EMPTY>
- * <!ATTLIST sub-rating id IDREF #REQUIRED>
- * <!ELEMENT order (rating, rating+)>
- * <!ELEMENT rating EMPTY>
- * <!ATTLIST rating id IDREF #REQUIRED>
- * ]>
- * </pre></p>
+ * {@literal
+ * <?xml version="1.0" encoding="UTF-8"?>
+ * <rating-system-definitions version="1.0">
+ * <rating-system-definition id="US_TV"
+ * displayName="US-TV"
+ * description="The TV Parental Guidelines"
+ * country="US">
+ * <sub-rating-definition id="US_TV_D"
+ * displayName="D"
+ * description="Suggestive dialogue (Usually means talks about sex)" />
+ * <sub-rating-definition id="US_TV_L"
+ * displayName="L"
+ * description="Coarse language" />
+ * <sub-rating-definition id="US_TV_S"
+ * displayName="S"
+ * description="Sexual content" />
+ * <sub-rating-definition id="US_TV_V"
+ * displayName="V"
+ * description="Violence" />
+ * <sub-rating-definition id="US_TV_FV"
+ * displayName="FV"
+ * description="Fantasy violence (Children\'s programming only)" />
+ *
+ * <rating-definition id="US_TV_Y"
+ * displayName="TV-Y"
+ * description="This program is designed to be appropriate for all children"
+ * ageHint="0" />
+ * <rating-definition id="US_TV_Y7"
+ * displayName="TV-Y7"
+ * description="This program is designed for children age 7 and above"
+ * ageHint="7">
+ * <sub-rating id="US_TV_FV" />
+ * </rating-definition>
+ * <rating-definition id="US_TV_G"
+ * displayName="TV-G"
+ * description="Most parents would find this program suitable for all ages"
+ * ageHint="0" />
+ * <rating-definition id="US_TV_PG"
+ * displayName="TV-PG"
+ * description="This program contains material that parents may find unsuitable for younger children"
+ * ageHint="14">
+ * <sub-rating id="US_TV_D" />
+ * <sub-rating id="US_TV_L" />
+ * <sub-rating id="US_TV_S" />
+ * <sub-rating id="US_TV_V" />
+ * </rating-definition>
+ * <rating-definition id="US_TV_14"
+ * displayName="TV-14"
+ * description="This program contains some material that many parents would find unsuitable for children under 14 years of age"
+ * ageHint="14">
+ * <sub-rating id="US_TV_D" />
+ * <sub-rating id="US_TV_L" />
+ * <sub-rating id="US_TV_S" />
+ * <sub-rating id="US_TV_V" />
+ * </rating-definition>
+ * <rating-definition id="US_TV_MA"
+ * displayName="TV-MA"
+ * description="This program is specifically designed to be viewed by adults and therefore may be unsuitable for children under 17"
+ * ageHint="17">
+ * <sub-rating id="US_TV_L" />
+ * <sub-rating id="US_TV_S" />
+ * <sub-rating id="US_TV_V" />
+ * </rating-definition>
+ * <order>
+ * <rating id="US_TV_Y" />
+ * <rating id="US_TV_Y7" />
+ * </order>
+ * <order>
+ * <rating id="US_TV_G" />
+ * <rating id="US_TV_PG" />
+ * <rating id="US_TV_14" />
+ * <rating id="US_TV_MA" />
+ * </order>
+ * </rating-system-definition>
+ * </rating-system-definitions>}</pre></p>
*
* <h3>System defined rating strings</h3>
*
diff --git a/media/java/android/media/tv/TvInputInfo.java b/media/java/android/media/tv/TvInputInfo.java
index a1ca871..6ed7580 100644
--- a/media/java/android/media/tv/TvInputInfo.java
+++ b/media/java/android/media/tv/TvInputInfo.java
@@ -246,17 +246,17 @@
Log.d(TAG, "Settings activity loaded. [" + input.mSettingsActivity + "] for "
+ si.name);
}
- int contentRatingSystemXml = sa.getResourceId(
- com.android.internal.R.styleable.TvInputService_contentRatingSystemXml, -1);
- if (contentRatingSystemXml != -1) {
+ int tvContentRatingDescription = sa.getResourceId(
+ com.android.internal.R.styleable.TvInputService_tvContentRatingDescription, -1);
+ if (tvContentRatingDescription != -1) {
input.mRatingSystemXmlUri = new Uri.Builder()
.scheme(ContentResolver.SCHEME_ANDROID_RESOURCE)
.authority(si.packageName)
- .appendPath(Integer.toString(contentRatingSystemXml))
+ .appendPath(Integer.toString(tvContentRatingDescription))
.build();
if (DEBUG) {
- Log.d(TAG, "Content rating xml loaded. [" + contentRatingSystemXml + "] for "
- + si.name);
+ Log.d(TAG, "Content rating xml loaded. [" + tvContentRatingDescription
+ + "] for " + si.name);
}
}
sa.recycle();
diff --git a/packages/DocumentsUI/src/com/android/documentsui/IconUtils.java b/packages/DocumentsUI/src/com/android/documentsui/IconUtils.java
index 3038323..b2e38fc 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/IconUtils.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/IconUtils.java
@@ -210,7 +210,7 @@
return pm.getDrawable(info.packageName, icon, info.applicationInfo);
}
} else {
- return context.getResources().getDrawable(icon);
+ return context.getDrawable(icon);
}
}
return null;
@@ -218,19 +218,17 @@
public static Drawable loadMimeIcon(
Context context, String mimeType, String authority, String docId, int mode) {
- final Resources res = context.getResources();
-
if (Document.MIME_TYPE_DIR.equals(mimeType)) {
// TODO: eventually move these hacky assets into that package
if ("com.android.providers.media.documents".equals(authority)
&& docId.startsWith("album")) {
- return res.getDrawable(R.drawable.ic_doc_album);
+ return context.getDrawable(R.drawable.ic_doc_album);
}
if (mode == DocumentsActivity.State.MODE_GRID) {
- return res.getDrawable(R.drawable.ic_grid_folder);
+ return context.getDrawable(R.drawable.ic_grid_folder);
} else {
- return res.getDrawable(R.drawable.ic_doc_folder);
+ return context.getDrawable(R.drawable.ic_doc_folder);
}
}
@@ -238,16 +236,14 @@
}
public static Drawable loadMimeIcon(Context context, String mimeType) {
- final Resources res = context.getResources();
-
if (Document.MIME_TYPE_DIR.equals(mimeType)) {
- return res.getDrawable(R.drawable.ic_doc_folder);
+ return context.getDrawable(R.drawable.ic_doc_folder);
}
// Look for exact match first
Integer resId = sMimeIcons.get(mimeType);
if (resId != null) {
- return res.getDrawable(resId);
+ return context.getDrawable(resId);
}
if (mimeType == null) {
@@ -258,15 +254,15 @@
// Otherwise look for partial match
final String typeOnly = mimeType.split("/")[0];
if ("audio".equals(typeOnly)) {
- return res.getDrawable(R.drawable.ic_doc_audio);
+ return context.getDrawable(R.drawable.ic_doc_audio);
} else if ("image".equals(typeOnly)) {
- return res.getDrawable(R.drawable.ic_doc_image);
+ return context.getDrawable(R.drawable.ic_doc_image);
} else if ("text".equals(typeOnly)) {
- return res.getDrawable(R.drawable.ic_doc_text);
+ return context.getDrawable(R.drawable.ic_doc_text);
} else if ("video".equals(typeOnly)) {
- return res.getDrawable(R.drawable.ic_doc_video);
+ return context.getDrawable(R.drawable.ic_doc_video);
} else {
- return res.getDrawable(R.drawable.ic_doc_generic);
+ return context.getDrawable(R.drawable.ic_doc_generic);
}
}
@@ -276,7 +272,7 @@
final TypedValue outValue = new TypedValue();
context.getTheme().resolveAttribute(tintAttrId, outValue, true);
- final Drawable icon = res.getDrawable(drawableId);
+ final Drawable icon = context.getDrawable(drawableId);
icon.mutate();
icon.setTintList(res.getColorStateList(outValue.resourceId));
return icon;
diff --git a/packages/DocumentsUI/src/com/android/documentsui/RecentsCreateFragment.java b/packages/DocumentsUI/src/com/android/documentsui/RecentsCreateFragment.java
index 82c3048..dd75dbd 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/RecentsCreateFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/RecentsCreateFragment.java
@@ -221,8 +221,7 @@
final DocumentStack stack = getItem(position);
iconMime.setImageDrawable(stack.root.loadIcon(context));
- final Drawable crumb = context.getResources()
- .getDrawable(R.drawable.ic_breadcrumb_arrow);
+ final Drawable crumb = context.getDrawable(R.drawable.ic_breadcrumb_arrow);
crumb.setBounds(0, 0, crumb.getIntrinsicWidth(), crumb.getIntrinsicHeight());
final SpannableStringBuilder builder = new SpannableStringBuilder();
diff --git a/packages/DocumentsUI/src/com/android/documentsui/model/RootInfo.java b/packages/DocumentsUI/src/com/android/documentsui/model/RootInfo.java
index a358798..fcfe518 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/model/RootInfo.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/model/RootInfo.java
@@ -205,7 +205,7 @@
public Drawable loadIcon(Context context) {
if (derivedIcon != 0) {
- return context.getResources().getDrawable(derivedIcon);
+ return context.getDrawable(derivedIcon);
} else {
return IconUtils.loadPackageIcon(context, authority, icon);
}
diff --git a/packages/Keyguard/res/values-h560dp/dimens.xml b/packages/Keyguard/res/values-h560dp/dimens.xml
new file mode 100644
index 0000000..1683113
--- /dev/null
+++ b/packages/Keyguard/res/values-h560dp/dimens.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2014 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+
+<resources>
+ <dimen name="widget_big_font_size">96dp</dimen>
+</resources>
\ No newline at end of file
diff --git a/packages/Keyguard/res/values/dimens.xml b/packages/Keyguard/res/values/dimens.xml
index e822c82..bfc0531 100644
--- a/packages/Keyguard/res/values/dimens.xml
+++ b/packages/Keyguard/res/values/dimens.xml
@@ -161,7 +161,7 @@
<dimen name="bottom_text_spacing_digital">-10dp</dimen>
<dimen name="label_font_size">14dp</dimen>
<dimen name="widget_label_font_size">16sp</dimen>
- <dimen name="widget_big_font_size">96dp</dimen>
+ <dimen name="widget_big_font_size">88dp</dimen>
<dimen name="big_font_size">120dp</dimen>
<!-- The y translation to apply at the start in appear animations. -->
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 5dc7d26..87c015c 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -20,6 +20,7 @@
import java.security.SecureRandom;
import java.util.HashMap;
import java.util.HashSet;
+import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
@@ -94,6 +95,9 @@
// Each defined user has their own settings
protected final SparseArray<DatabaseHelper> mOpenHelpers = new SparseArray<DatabaseHelper>();
+ // Keep the list of managed profiles synced here
+ private List<UserInfo> mManagedProfiles = null;
+
// Over this size we don't reject loading or saving settings but
// we do consider them broken/malicious and don't keep them in
// memory at least:
@@ -119,6 +123,9 @@
private static final String DROPBOX_TAG_USERLOG = "restricted_profile_ssaid";
+ static final HashSet<String> sSecureCloneToManagedKeys;
+ static final HashSet<String> sSystemCloneToManagedKeys;
+
static {
// Keys (name column) from the 'secure' table that are now in the owner user's 'global'
// table, shared across all users
@@ -142,6 +149,15 @@
UserManager.ENSURE_VERIFY_APPS);
sRestrictedKeys.put(Settings.Global.PREFERRED_NETWORK_MODE,
UserManager.DISALLOW_CONFIG_MOBILE_NETWORKS);
+
+ sSecureCloneToManagedKeys = new HashSet<String>();
+ for (int i = 0; i < Settings.Secure.CLONE_TO_MANAGED_PROFILE.length; i++) {
+ sSecureCloneToManagedKeys.add(Settings.Secure.CLONE_TO_MANAGED_PROFILE[i]);
+ }
+ sSystemCloneToManagedKeys = new HashSet<String>();
+ for (int i = 0; i < Settings.System.CLONE_TO_MANAGED_PROFILE.length; i++) {
+ sSystemCloneToManagedKeys.add(Settings.System.CLONE_TO_MANAGED_PROFILE[i]);
+ }
}
private boolean settingMovedToGlobal(final String name) {
@@ -362,18 +378,22 @@
IntentFilter userFilter = new IntentFilter();
userFilter.addAction(Intent.ACTION_USER_REMOVED);
+ userFilter.addAction(Intent.ACTION_USER_ADDED);
getContext().registerReceiver(new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
+ final int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE,
+ UserHandle.USER_OWNER);
if (intent.getAction().equals(Intent.ACTION_USER_REMOVED)) {
- final int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE,
- UserHandle.USER_OWNER);
- if (userHandle != UserHandle.USER_OWNER) {
- onUserRemoved(userHandle);
- }
+ onUserRemoved(userHandle);
+ } else if (intent.getAction().equals(Intent.ACTION_USER_ADDED)) {
+ onProfilesChanged();
}
}
}, userFilter);
+
+ onProfilesChanged();
+
return true;
}
@@ -391,6 +411,32 @@
sSystemCaches.delete(userHandle);
sSecureCaches.delete(userHandle);
sKnownMutationsInFlight.delete(userHandle);
+ onProfilesChanged();
+ }
+ }
+
+ /**
+ * Updates the list of managed profiles. It assumes that only the primary user
+ * can have managed profiles. Modify this code if that changes in the future.
+ */
+ void onProfilesChanged() {
+ synchronized (this) {
+ mManagedProfiles = mUserManager.getProfiles(UserHandle.USER_OWNER);
+ if (mManagedProfiles != null) {
+ // Remove the primary user from the list
+ for (int i = mManagedProfiles.size() - 1; i >= 0; i--) {
+ if (mManagedProfiles.get(i).id == UserHandle.USER_OWNER) {
+ mManagedProfiles.remove(i);
+ }
+ }
+ // If there are no managed profiles, reset the variable
+ if (mManagedProfiles.size() == 0) {
+ mManagedProfiles = null;
+ }
+ }
+ if (LOCAL_LOGV) {
+ Slog.d(TAG, "Managed Profiles = " + mManagedProfiles);
+ }
}
}
@@ -601,6 +647,24 @@
}
/**
+ * Checks if the calling user is a managed profile of the primary user.
+ * Currently only the primary user (USER_OWNER) can have managed profiles.
+ * @param callingUser the user trying to read/write settings
+ * @return true if it is a managed profile of the primary user
+ */
+ private boolean isManagedProfile(int callingUser) {
+ synchronized (this) {
+ if (mManagedProfiles == null) return false;
+ for (int i = mManagedProfiles.size() - 1; i >= 0; i--) {
+ if (mManagedProfiles.get(i).id == callingUser) {
+ return true;
+ }
+ }
+ return false;
+ }
+ }
+
+ /**
* Fast path that avoids the use of chatty remoted Cursors.
*/
@Override
@@ -625,12 +689,18 @@
// Get methods
if (Settings.CALL_METHOD_GET_SYSTEM.equals(method)) {
if (LOCAL_LOGV) Slog.v(TAG, "call(system:" + request + ") for " + callingUser);
+ if (isManagedProfile(callingUser) && sSystemCloneToManagedKeys.contains(request)) {
+ callingUser = UserHandle.USER_OWNER;
+ }
dbHelper = getOrEstablishDatabase(callingUser);
cache = sSystemCaches.get(callingUser);
return lookupValue(dbHelper, TABLE_SYSTEM, cache, request);
}
if (Settings.CALL_METHOD_GET_SECURE.equals(method)) {
if (LOCAL_LOGV) Slog.v(TAG, "call(secure:" + request + ") for " + callingUser);
+ if (isManagedProfile(callingUser) && sSecureCloneToManagedKeys.contains(request)) {
+ callingUser = UserHandle.USER_OWNER;
+ }
dbHelper = getOrEstablishDatabase(callingUser);
cache = sSecureCaches.get(callingUser);
return lookupValue(dbHelper, TABLE_SECURE, cache, request);
@@ -667,13 +737,70 @@
values.put(Settings.NameValueTable.NAME, request);
values.put(Settings.NameValueTable.VALUE, newValue);
if (Settings.CALL_METHOD_PUT_SYSTEM.equals(method)) {
- if (LOCAL_LOGV) Slog.v(TAG, "call_put(system:" + request + "=" + newValue + ") for " + callingUser);
+ if (LOCAL_LOGV) {
+ Slog.v(TAG, "call_put(system:" + request + "=" + newValue + ") for "
+ + callingUser);
+ }
+ // Extra check for USER_OWNER to optimize for the 99%
+ if (callingUser != UserHandle.USER_OWNER && isManagedProfile(callingUser)) {
+ if (sSystemCloneToManagedKeys.contains(request)) {
+ // Don't write these settings
+ return null;
+ }
+ }
insertForUser(Settings.System.CONTENT_URI, values, callingUser);
+ // Clone the settings to the managed profiles so that notifications can be sent out
+ if (callingUser == UserHandle.USER_OWNER && mManagedProfiles != null
+ && sSystemCloneToManagedKeys.contains(request)) {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ for (int i = mManagedProfiles.size() - 1; i >= 0; i--) {
+ if (LOCAL_LOGV) {
+ Slog.v(TAG, "putting to additional user "
+ + mManagedProfiles.get(i).id);
+ }
+ insertForUser(Settings.System.CONTENT_URI, values,
+ mManagedProfiles.get(i).id);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
} else if (Settings.CALL_METHOD_PUT_SECURE.equals(method)) {
- if (LOCAL_LOGV) Slog.v(TAG, "call_put(secure:" + request + "=" + newValue + ") for " + callingUser);
+ if (LOCAL_LOGV) {
+ Slog.v(TAG, "call_put(secure:" + request + "=" + newValue + ") for "
+ + callingUser);
+ }
+ // Extra check for USER_OWNER to optimize for the 99%
+ if (callingUser != UserHandle.USER_OWNER && isManagedProfile(callingUser)) {
+ if (sSecureCloneToManagedKeys.contains(request)) {
+ // Don't write these settings
+ return null;
+ }
+ }
insertForUser(Settings.Secure.CONTENT_URI, values, callingUser);
+ // Clone the settings to the managed profiles so that notifications can be sent out
+ if (callingUser == UserHandle.USER_OWNER && mManagedProfiles != null
+ && sSecureCloneToManagedKeys.contains(request)) {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ for (int i = mManagedProfiles.size() - 1; i >= 0; i--) {
+ if (LOCAL_LOGV) {
+ Slog.v(TAG, "putting to additional user "
+ + mManagedProfiles.get(i).id);
+ }
+ insertForUser(Settings.Secure.CONTENT_URI, values,
+ mManagedProfiles.get(i).id);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
} else if (Settings.CALL_METHOD_PUT_GLOBAL.equals(method)) {
- if (LOCAL_LOGV) Slog.v(TAG, "call_put(global:" + request + "=" + newValue + ") for " + callingUser);
+ if (LOCAL_LOGV) {
+ Slog.v(TAG, "call_put(global:" + request + "=" + newValue + ") for "
+ + callingUser);
+ }
insertForUser(Settings.Global.CONTENT_URI, values, callingUser);
} else {
Slog.w(TAG, "call() with invalid method: " + method);
diff --git a/packages/SystemUI/res/layout/split_clock_view.xml b/packages/SystemUI/res/layout/split_clock_view.xml
index d9ba35d..87b7051 100644
--- a/packages/SystemUI/res/layout/split_clock_view.xml
+++ b/packages/SystemUI/res/layout/split_clock_view.xml
@@ -34,5 +34,15 @@
android:layout_height="wrap_content"
android:singleLine="true"
android:textAppearance="@style/TextAppearance.StatusBar.Expanded.Clock"
+ android:textSize="@dimen/qs_time_collapsed_size"
+ />
+
+ <!-- Empty text view so we have the same height when expanded/collapsed-->
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:visibility="invisible"
+ android:singleLine="true"
+ android:textAppearance="@style/TextAppearance.StatusBar.Expanded.Clock"
/>
</com.android.systemui.statusbar.policy.SplitClockView>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/status_bar_expanded.xml b/packages/SystemUI/res/layout/status_bar_expanded.xml
index 4eab9c7..cc449c5 100644
--- a/packages/SystemUI/res/layout/status_bar_expanded.xml
+++ b/packages/SystemUI/res/layout/status_bar_expanded.xml
@@ -50,6 +50,10 @@
android:visibility="gone"
/>
+ <include
+ layout="@layout/keyguard_bottom_area"
+ android:visibility="gone" />
+
<com.android.systemui.statusbar.phone.NotificationsQuickSettingsContainer
android:layout_width="match_parent"
android:layout_height="match_parent"
@@ -115,10 +119,6 @@
<include layout="@layout/status_bar_expanded_header" />
- <include
- layout="@layout/keyguard_bottom_area"
- android:visibility="gone" />
-
<com.android.systemui.statusbar.AlphaOptimizedView
android:id="@+id/qs_navbar_scrim"
android:layout_height="96dp"
diff --git a/packages/SystemUI/res/values-h560dp-xhdpi/config.xml b/packages/SystemUI/res/values-h560dp-xhdpi/config.xml
new file mode 100644
index 0000000..cf2017f
--- /dev/null
+++ b/packages/SystemUI/res/values-h560dp-xhdpi/config.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+ ~ Copyright (C) 2014 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+<resources>
+ <!-- The maximum count of notifications on Keyguard. The rest will be collapsed in an overflow
+ card. -->
+ <integer name="keyguard_max_notification_count">3</integer>
+</resources>
+
diff --git a/packages/SystemUI/res/values-h560dp-xhdpi/dimens.xml b/packages/SystemUI/res/values-h560dp-xhdpi/dimens.xml
new file mode 100644
index 0000000..f6dbc3d
--- /dev/null
+++ b/packages/SystemUI/res/values-h560dp-xhdpi/dimens.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2014 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+
+<resources>
+ <fraction name="keyguard_clock_y_fraction_max">32.5%</fraction>
+ <fraction name="keyguard_clock_y_fraction_min">24%</fraction>
+</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/res/values-h560dp/config.xml b/packages/SystemUI/res/values-h560dp-xxhdpi/config.xml
similarity index 100%
rename from packages/SystemUI/res/values-h560dp/config.xml
rename to packages/SystemUI/res/values-h560dp-xxhdpi/config.xml
diff --git a/packages/SystemUI/res/values-h560dp-xxhdpi/dimens.xml b/packages/SystemUI/res/values-h560dp-xxhdpi/dimens.xml
new file mode 100644
index 0000000..905e9e3
--- /dev/null
+++ b/packages/SystemUI/res/values-h560dp-xxhdpi/dimens.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2014 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+
+<resources>
+ <fraction name="keyguard_clock_y_fraction_max">32.5%</fraction>
+ <fraction name="keyguard_clock_y_fraction_min">19.8%</fraction>
+</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index a09dfe1..5415d19 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -323,7 +323,7 @@
<dimen name="volume_panel_z">3dp</dimen>
<!-- Distance between notifications and header when they are considered to be colliding. -->
- <dimen name="header_notifications_collide_distance">24dp</dimen>
+ <dimen name="header_notifications_collide_distance">48dp</dimen>
<!-- Distance the user needs to drag vertically such that a swipe is accepted to unlock the
device. -->
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 50eb3e5..c24fcae 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -678,6 +678,12 @@
<!-- Related to user switcher --><skip/>
+ <!-- Accessibility label for the button that opens the user switcher. -->
+ <string name="accessibility_multi_user_switch_switcher">Switch user</string>
+
+ <!-- Accessibility label for the button that opens the quick contact of the user. -->
+ <string name="accessibility_multi_user_switch_quick_contact">Show profile</string>
+
<!-- Label for the adding a new user in the user switcher [CHAR LIMIT=35] -->
<string name="user_add_user">Add user</string>
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
index 21b62e9..aee558f 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
@@ -395,7 +395,7 @@
// Scroll the view into position (just center it in the curve)
if (scrollToNewPosition) {
- float newScroll = mLayoutAlgorithm.getStackScrollForTaskIndex(t) - 0.5f;
+ float newScroll = mLayoutAlgorithm.getStackScrollForTask(t) - 0.5f;
newScroll = mStackScroller.getBoundedStackScroll(newScroll);
mStackScroller.animateScroll(mStackScroller.getStackScroll(), newScroll, postScrollRunnable);
} else {
@@ -681,8 +681,28 @@
// Notify the callback that we've removed the task and it can clean up after it
mCb.onTaskViewDismissed(removedTask);
+ // Get the stack scroll of the task to anchor to (since we are removing something, the front
+ // most task will be our anchor task)
+ Task anchorTask = null;
+ float prevAnchorTaskScroll = 0;
+ boolean pullStackForward = stack.getTaskCount() > 0;
+ if (pullStackForward) {
+ anchorTask = mStack.getFrontMostTask();
+ prevAnchorTaskScroll = mLayoutAlgorithm.getStackScrollForTask(anchorTask);
+ }
+
// Update the min/max scroll and animate other task views into their new positions
updateMinMaxScroll(true, mConfig.launchedWithAltTab, mConfig.launchedFromHome);
+
+ // Offset the stack by as much as the anchor task would otherwise move back
+ if (pullStackForward) {
+ float anchorTaskScroll = mLayoutAlgorithm.getStackScrollForTask(anchorTask);
+ mStackScroller.setStackScroll(mStackScroller.getStackScroll() + (anchorTaskScroll
+ - prevAnchorTaskScroll));
+ mStackScroller.boundScroll();
+ }
+
+ // Animate all the tasks into place
requestSynchronizeStackViewsWithModel(200);
// Update the new front most task
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewLayoutAlgorithm.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewLayoutAlgorithm.java
index 495d00b..667faa7 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewLayoutAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewLayoutAlgorithm.java
@@ -202,7 +202,7 @@
/**
* Returns the scroll to such task top = 1f;
*/
- float getStackScrollForTaskIndex(Task t) {
+ float getStackScrollForTask(Task t) {
return mTaskProgressMap.get(t.key);
}
diff --git a/packages/SystemUI/src/com/android/systemui/settings/BrightnessController.java b/packages/SystemUI/src/com/android/systemui/settings/BrightnessController.java
index 2113c68..9fbcd7f 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/BrightnessController.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/BrightnessController.java
@@ -59,6 +59,7 @@
private boolean mAutomatic;
private boolean mListening;
+ private boolean mExternalChange;
public interface BrightnessStateChangeCallback {
public void onBrightnessLevelChanged();
@@ -86,19 +87,24 @@
@Override
public void onChange(boolean selfChange, Uri uri) {
if (selfChange) return;
- if (BRIGHTNESS_MODE_URI.equals(uri)) {
- updateMode();
- updateSlider();
- } else if (BRIGHTNESS_URI.equals(uri) && !mAutomatic) {
- updateSlider();
- } else if (BRIGHTNESS_ADJ_URI.equals(uri) && mAutomatic) {
- updateSlider();
- } else {
- updateMode();
- updateSlider();
- }
- for (BrightnessStateChangeCallback cb : mChangeCallbacks) {
- cb.onBrightnessLevelChanged();
+ try {
+ mExternalChange = true;
+ if (BRIGHTNESS_MODE_URI.equals(uri)) {
+ updateMode();
+ updateSlider();
+ } else if (BRIGHTNESS_URI.equals(uri) && !mAutomatic) {
+ updateSlider();
+ } else if (BRIGHTNESS_ADJ_URI.equals(uri) && mAutomatic) {
+ updateSlider();
+ } else {
+ updateMode();
+ updateSlider();
+ }
+ for (BrightnessStateChangeCallback cb : mChangeCallbacks) {
+ cb.onBrightnessLevelChanged();
+ }
+ } finally {
+ mExternalChange = false;
}
}
@@ -191,6 +197,8 @@
@Override
public void onChanged(ToggleSlider view, boolean tracking, boolean automatic, int value) {
updateIcon(mAutomatic);
+ if (mExternalChange) return;
+
if (!mAutomatic) {
final int val = value + mMinimumBacklight;
setBrightness(val);
diff --git a/packages/SystemUI/src/com/android/systemui/settings/BrightnessDialog.java b/packages/SystemUI/src/com/android/systemui/settings/BrightnessDialog.java
index d113139..a1704ff 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/BrightnessDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/BrightnessDialog.java
@@ -40,35 +40,27 @@
super.onCreate(savedInstanceState);
final Window window = getWindow();
- final WindowManager.LayoutParams lp = window.getAttributes();
- // Offset from the top
- lp.y = getResources().getDimensionPixelOffset(R.dimen.volume_panel_top);
- lp.type = WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY;
- lp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
-
- window.setAttributes(lp);
window.setGravity(Gravity.TOP);
window.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
window.requestFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.quick_settings_brightness_dialog);
+
+ final ImageView icon = (ImageView) findViewById(R.id.brightness_icon);
+ final ToggleSlider slider = (ToggleSlider) findViewById(R.id.brightness_slider);
+ mBrightnessController = new BrightnessController(this, icon, slider);
}
@Override
protected void onStart() {
super.onStart();
-
- final ImageView icon = (ImageView) findViewById(R.id.brightness_icon);
- final ToggleSlider slider = (ToggleSlider) findViewById(R.id.brightness_slider);
- mBrightnessController = new BrightnessController(this, icon, slider);
mBrightnessController.registerCallbacks();
}
@Override
protected void onStop() {
super.onStop();
-
mBrightnessController.unregisterCallbacks();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java
index 99487ff..ca290e4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java
@@ -662,7 +662,6 @@
}
public void reset() {
- super.reset();
setTintColor(0);
setShowingLegacyBackground(false);
setBelowSpeedBump(false);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
index f4857eb..947d70d5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
@@ -1220,8 +1220,6 @@
row.setClearable(sbn.isClearable());
- row.setDrawingCacheEnabled(true);
-
if (MULTIUSER_DEBUG) {
TextView debug = (TextView) row.findViewById(R.id.debug_info);
if (debug != null) {
@@ -1737,6 +1735,9 @@
: null;
// Reapply the RemoteViews
+ if (entry.row != null) {
+ entry.row.resetHeight();
+ }
contentView.reapply(mContext, entry.expanded, mOnClickHandler);
if (bigContentView != null && entry.getBigContentView() != null) {
bigContentView.reapply(mContext, entry.getBigContentView(),
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
index 43b7707..9ac20a6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
@@ -88,9 +88,14 @@
mExpansionDisabled = false;
mPublicLayout.reset();
mPrivateLayout.reset();
+ resetHeight();
+ logExpansionEvent(false, wasExpanded);
+ }
+
+ public void resetHeight() {
mMaxExpandHeight = 0;
mWasReset = true;
- logExpansionEvent(false, wasExpanded);
+ onHeightReset();
}
@Override
@@ -178,20 +183,26 @@
* @param expand whether the system wants this notification to be expanded.
*/
public void setSystemExpanded(boolean expand) {
- final boolean wasExpanded = isExpanded();
- mIsSystemExpanded = expand;
- notifyHeightChanged();
- logExpansionEvent(false, wasExpanded);
+ if (expand != mIsSystemExpanded) {
+ final boolean wasExpanded = isExpanded();
+ mIsSystemExpanded = expand;
+ notifyHeightChanged();
+ logExpansionEvent(false, wasExpanded);
+ }
}
/**
* @param expansionDisabled whether to prevent notification expansion
*/
public void setExpansionDisabled(boolean expansionDisabled) {
- final boolean wasExpanded = isExpanded();
- mExpansionDisabled = expansionDisabled;
- logExpansionEvent(false, wasExpanded);
- notifyHeightChanged();
+ if (expansionDisabled != mExpansionDisabled) {
+ final boolean wasExpanded = isExpanded();
+ mExpansionDisabled = expansionDisabled;
+ logExpansionEvent(false, wasExpanded);
+ if (wasExpanded != isExpanded()) {
+ notifyHeightChanged();
+ }
+ }
}
/**
@@ -368,9 +379,8 @@
mPrivateLayout.notifyContentUpdated();
}
- public boolean isShowingLayoutLayouted() {
- NotificationContentView showingLayout = getShowingLayout();
- return showingLayout.getWidth() != 0;
+ public boolean isMaxExpandHeightInitialized() {
+ return mMaxExpandHeight != 0;
}
private NotificationContentView getShowingLayout() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java
index df64edf..127ff6c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java
@@ -255,8 +255,10 @@
public void setBelowSpeedBump(boolean below) {
}
- public void reset() {
- mOnHeightChangedListener.onReset(this);
+ public void onHeightReset() {
+ if (mOnHeightChangedListener != null) {
+ mOnHeightChangedListener.onReset(this);
+ }
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
index b8ac3e7..3e2a398 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
@@ -25,9 +25,12 @@
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.res.Configuration;
+import android.os.AsyncTask;
import android.os.RemoteException;
import android.os.UserHandle;
+import android.phone.PhoneManager;
import android.provider.MediaStore;
+import android.telecomm.TelecommManager;
import android.util.AttributeSet;
import android.util.Log;
import android.util.TypedValue;
@@ -246,7 +249,18 @@
}
public void launchPhone() {
- mActivityStarter.startActivity(PHONE_INTENT, false /* dismissShade */);
+ TelecommManager tm = TelecommManager.from(mContext);
+ if (tm.isInAPhoneCall()) {
+ final PhoneManager pm = (PhoneManager) mContext.getSystemService(Context.PHONE_SERVICE);
+ AsyncTask.execute(new Runnable() {
+ @Override
+ public void run() {
+ pm.showCallScreen(false /* showDialpad */);
+ }
+ });
+ } else {
+ mActivityStarter.startActivity(PHONE_INTENT, false /* dismissShade */);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/MultiUserSwitch.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/MultiUserSwitch.java
index 47325c8..d7144da 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/MultiUserSwitch.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/MultiUserSwitch.java
@@ -21,10 +21,13 @@
import android.os.UserHandle;
import android.os.UserManager;
import android.provider.ContactsContract;
+import android.text.TextUtils;
import android.util.AttributeSet;
import android.view.View;
+import android.view.accessibility.AccessibilityEvent;
import android.widget.FrameLayout;
+import com.android.systemui.R;
import com.android.systemui.qs.QSPanel;
import com.android.systemui.statusbar.policy.KeyguardUserSwitcher;
@@ -36,9 +39,11 @@
private QSPanel mQsPanel;
private KeyguardUserSwitcher mKeyguardUserSwitcher;
private boolean mKeyguardMode;
+ final UserManager mUserManager;
public MultiUserSwitch(Context context, AttributeSet attrs) {
super(context, attrs);
+ mUserManager = UserManager.get(getContext());
}
@Override
@@ -80,6 +85,22 @@
}
@Override
+ public void onPopulateAccessibilityEvent(AccessibilityEvent event) {
+ super.onPopulateAccessibilityEvent(event);
+
+ if (isClickable()) {
+ final UserManager um = UserManager.get(getContext());
+ String text = mContext.getString(um.isUserSwitcherEnabled()
+ ? R.string.accessibility_multi_user_switch_switcher
+ : R.string.accessibility_multi_user_switch_quick_contact);
+ if (!TextUtils.isEmpty(text)) {
+ event.getText().add(text);
+ }
+ }
+
+ }
+
+ @Override
public boolean hasOverlappingRendering() {
return false;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
index bac46be..42ae0c9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -24,6 +24,7 @@
import android.content.Context;
import android.content.res.Configuration;
import android.util.AttributeSet;
+import android.util.MathUtils;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.View;
@@ -104,7 +105,6 @@
private boolean mQsFullyExpanded;
private boolean mKeyguardShowing;
private boolean mDozing;
- private boolean mKeyguardStatusBarTransparent;
private int mStatusBarState;
private float mInitialHeightOnTouch;
private float mInitialTouchX;
@@ -117,6 +117,7 @@
private int mQsPeekHeight;
private boolean mStackScrollerOverscrolling;
private boolean mQsExpansionFromOverscroll;
+ private float mLastOverscroll;
private boolean mQsExpansionEnabled = true;
private ValueAnimator mQsExpansionAnimator;
private FlingAnimationUtils mFlingAnimationUtils;
@@ -273,11 +274,10 @@
requestScrollerTopPaddingUpdate(false /* animate */);
}
} else {
- if (!mStackScrollerOverscrolling) {
- setQsExpansion(mQsMinExpansionHeight);
- }
+ setQsExpansion(mQsMinExpansionHeight + mLastOverscroll);
positionClockAndNotifications();
mNotificationStackScroller.setStackHeight(getExpandedHeight());
+ updateHeader();
}
mNotificationStackScroller.updateIsSmallScreen(
mHeader.getCollapsedHeight() + mQsPeekHeight);
@@ -552,8 +552,8 @@
}
private float getQsExpansionFraction() {
- return (mQsExpansionHeight - mQsMinExpansionHeight)
- / (getTempQsMaxExpansion() - mQsMinExpansionHeight);
+ return Math.min(1f, (mQsExpansionHeight - mQsMinExpansionHeight)
+ / (getTempQsMaxExpansion() - mQsMinExpansionHeight));
}
@Override
@@ -602,6 +602,10 @@
&& event.getY(event.getActionIndex()) < mStatusBarMinHeight) {
mTwoFingerQsExpand = true;
requestPanelHeightUpdate();
+
+ // Normally, we start listening when the panel is expanded, but here we need to start
+ // earlier so the state is already up to date when dragging down.
+ setListening(true);
}
super.onTouchEvent(event);
return true;
@@ -621,6 +625,11 @@
mInitialHeightOnTouch = mQsExpansionHeight;
mInitialTouchY = event.getX();
mInitialTouchX = event.getY();
+
+ // If we interrupt an expansion gesture here, make sure to update the state correctly.
+ if (mIsExpanding) {
+ onExpandingFinished();
+ }
}
}
@@ -718,6 +727,7 @@
float rounded = amount >= 1f ? amount : 0f;
mStackScrollerOverscrolling = rounded != 0f && isRubberbanded;
mQsExpansionFromOverscroll = rounded != 0f;
+ mLastOverscroll = rounded;
updateQsState();
setQsExpansion(mQsMinExpansionHeight + rounded);
}
@@ -984,15 +994,7 @@
requestScrollerTopPaddingUpdate(false /* animate */);
updateNotificationScrim(height);
if (mKeyguardShowing) {
- float alpha = getQsExpansionFraction();
- alpha *= 2;
- alpha = Math.min(1, alpha);
- alpha = 1 - alpha;
- mKeyguardStatusBarTransparent = alpha == 0f;
- updateKeyguardStatusBarVisibility();
- if (!mKeyguardStatusBarTransparent) {
- mKeyguardStatusBar.setAlpha(alpha);
- }
+ updateHeaderKeyguard();
}
if (mStatusBarState == StatusBarState.SHADE && mQsExpanded
&& !mStackScrollerOverscrolling && mQsScrimEnabled) {
@@ -1025,7 +1027,9 @@
}
private float calculateQsTopPadding() {
- if (mKeyguardShowing) {
+ // We can only do the smoother transition on Keyguard when we also are not collapsing from a
+ // scrolled quick settings.
+ if (mKeyguardShowing && mScrollYOverride == -1) {
return interpolate(getQsExpansionFraction(),
mNotificationStackScroller.getIntrinsicPadding() - mNotificationTopPadding,
mQsMaxExpansionHeight);
@@ -1081,6 +1085,7 @@
}
return;
}
+ mScrollView.setBlockFlinging(true);
ValueAnimator animator = ValueAnimator.ofFloat(mQsExpansionHeight, target);
mFlingAnimationUtils.apply(animator, mQsExpansionHeight, target, vel);
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@@ -1092,6 +1097,7 @@
animator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
+ mScrollView.setBlockFlinging(false);
mScrollYOverride = -1;
mQsExpansionAnimator = null;
if (onFinishRunnable != null) {
@@ -1339,27 +1345,25 @@
}
private void updateHeaderKeyguard() {
- float alpha;
+ float alphaNotifications;
if (mStatusBar.getBarState() == StatusBarState.KEYGUARD) {
// When on Keyguard, we hide the header as soon as the top card of the notification
// stack scroller is close enough (collision distance) to the bottom of the header.
- alpha = getNotificationsTopY()
+ alphaNotifications = getNotificationsTopY()
/
- (mQsMinExpansionHeight + mNotificationsHeaderCollideDistance);
-
+ (mKeyguardStatusBar.getHeight() + mNotificationsHeaderCollideDistance);
} else {
// In SHADE_LOCKED, the top card is already really close to the header. Hide it as
// soon as we start translating the stack.
- alpha = getNotificationsTopY() / mQsMinExpansionHeight;
+ alphaNotifications = getNotificationsTopY() / mKeyguardStatusBar.getHeight();
}
- alpha = Math.max(0, Math.min(alpha, 1));
- alpha = (float) Math.pow(alpha, 0.75);
- if (!mQsExpanded) {
- mKeyguardStatusBar.setAlpha(alpha);
- }
- mKeyguardBottomArea.setAlpha(alpha);
+ alphaNotifications = MathUtils.constrain(alphaNotifications, 0, 1);
+ alphaNotifications = (float) Math.pow(alphaNotifications, 0.75);
+ float alphaQsExpansion = 1 - Math.min(1, getQsExpansionFraction() * 2);
+ mKeyguardStatusBar.setAlpha(Math.min(alphaNotifications, alphaQsExpansion));
+ mKeyguardBottomArea.setAlpha(Math.min(1 - getQsExpansionFraction(), alphaNotifications));
setQsTranslation(mQsExpansionHeight);
}
@@ -1703,8 +1707,7 @@
}
private void updateKeyguardStatusBarVisibility() {
- mKeyguardStatusBar.setVisibility(mKeyguardShowing && !mKeyguardStatusBarTransparent
- && !mDozing ? VISIBLE : INVISIBLE);
+ mKeyguardStatusBar.setVisibility(mKeyguardShowing && !mDozing ? VISIBLE : INVISIBLE);
}
public void setDozing(boolean dozing) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ObservableScrollView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ObservableScrollView.java
index ee6b1a9..53361dc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ObservableScrollView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ObservableScrollView.java
@@ -33,6 +33,7 @@
private boolean mHandlingTouchEvent;
private float mLastX;
private float mLastY;
+ private boolean mBlockFlinging;
public ObservableScrollView(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -109,6 +110,17 @@
maxOverScrollX, maxOverScrollY, isTouchEvent);
}
+ public void setBlockFlinging(boolean blockFlinging) {
+ mBlockFlinging = blockFlinging;
+ }
+
+ @Override
+ public void fling(int velocityY) {
+ if (!mBlockFlinging) {
+ super.fling(velocityY);
+ }
+ }
+
@Override
protected void onOverScrolled(int scrollX, int scrollY, boolean clampedX, boolean clampedY) {
super.onOverScrolled(scrollX, scrollY, clampedX, clampedY);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
index c8e943e..decaeb6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
@@ -54,6 +54,11 @@
private float mInitialOffsetOnTouch;
private float mExpandedFraction = 0;
protected float mExpandedHeight = 0;
+ private boolean mPanelClosedOnDown;
+ private boolean mHasLayoutedSinceDown;
+ private float mUpdateFlingVelocity;
+ private boolean mUpdateFlingOnLayout;
+ private boolean mTouching;
private boolean mJustPeeked;
private boolean mClosing;
protected boolean mTracking;
@@ -76,7 +81,6 @@
PanelBar mBar;
- protected int mMaxPanelHeight = -1;
private String mViewName;
private float mInitialTouchY;
private float mInitialTouchX;
@@ -226,6 +230,10 @@
mInitialOffsetOnTouch = mExpandedHeight;
mTouchSlopExceeded = false;
mJustPeeked = false;
+ mPanelClosedOnDown = mExpandedHeight == 0.0f;
+ mHasLayoutedSinceDown = false;
+ mUpdateFlingOnLayout = false;
+ mTouching = true;
if (mVelocityTracker == null) {
initVelocityTracker();
}
@@ -316,6 +324,10 @@
boolean expand = flingExpands(vel, vectorVel);
onTrackingStopped(expand);
fling(vel, expand);
+ mUpdateFlingOnLayout = expand && mPanelClosedOnDown && !mHasLayoutedSinceDown;
+ if (mUpdateFlingOnLayout) {
+ mUpdateFlingVelocity = vel;
+ }
} else {
boolean expands = onEmptySpaceClick(mInitialTouchX);
onTrackingStopped(expands);
@@ -325,6 +337,7 @@
mVelocityTracker.recycle();
mVelocityTracker = null;
}
+ mTouching = false;
break;
}
return !waitForTouchSlop || mTracking;
@@ -383,6 +396,10 @@
mInitialTouchX = x;
mTouchSlopExceeded = false;
mJustPeeked = false;
+ mPanelClosedOnDown = mExpandedHeight == 0.0f;
+ mHasLayoutedSinceDown = false;
+ mUpdateFlingOnLayout = false;
+ mTouching = true;
initVelocityTracker();
trackMovement(event);
break;
@@ -415,6 +432,10 @@
}
}
break;
+ case MotionEvent.ACTION_CANCEL:
+ case MotionEvent.ACTION_UP:
+ mTouching = false;
+ break;
}
return false;
}
@@ -444,7 +465,6 @@
protected void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
loadDimens();
- mMaxPanelHeight = -1;
}
/**
@@ -524,27 +544,6 @@
return mViewName;
}
- @Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- super.onMeasure(widthMeasureSpec, heightMeasureSpec);
-
- if (DEBUG) logf("onMeasure(%d, %d) -> (%d, %d)",
- widthMeasureSpec, heightMeasureSpec, getMeasuredWidth(), getMeasuredHeight());
-
- // Did one of our children change size?
- int newHeight = getMeasuredHeight();
- if (newHeight > mMaxPanelHeight) {
- // we only adapt the max height if it's bigger
- mMaxPanelHeight = newHeight;
- // If the user isn't actively poking us, let's rubberband to the content
- if (!mTracking && mHeightAnimator == null
- && mExpandedHeight > 0 && mExpandedHeight != mMaxPanelHeight
- && mMaxPanelHeight > 0 && mPeekAnimator == null) {
- mExpandedHeight = mMaxPanelHeight;
- }
- }
- }
-
public void setExpandedHeight(float height) {
if (DEBUG) logf("setExpandedHeight(%.1f)", height);
setExpandedHeightInternal(height + getOverExpansionPixels());
@@ -552,10 +551,14 @@
@Override
protected void onLayout (boolean changed, int left, int top, int right, int bottom) {
- if (DEBUG) logf("onLayout: changed=%s, bottom=%d eh=%d fh=%d", changed?"T":"f", bottom,
- (int)mExpandedHeight, mMaxPanelHeight);
super.onLayout(changed, left, top, right, bottom);
requestPanelHeightUpdate();
+ mHasLayoutedSinceDown = true;
+ if (mUpdateFlingOnLayout) {
+ abortAnimations();
+ fling(mUpdateFlingVelocity, true);
+ mUpdateFlingOnLayout = false;
+ }
}
protected void requestPanelHeightUpdate() {
@@ -567,7 +570,8 @@
&& mExpandedHeight > 0
&& currentMaxPanelHeight != mExpandedHeight
&& !mPeekPending
- && mPeekAnimator == null) {
+ && mPeekAnimator == null
+ && !mTouching) {
setExpandedHeight(currentMaxPanelHeight);
}
}
@@ -615,10 +619,7 @@
*
* @return the default implementation simply returns the maximum height.
*/
- protected int getMaxPanelHeight() {
- mMaxPanelHeight = Math.max(mMaxPanelHeight, getHeight());
- return mMaxPanelHeight;
- }
+ protected abstract int getMaxPanelHeight();
public void setExpandedFraction(float frac) {
setExpandedHeight(getMaxPanelHeight() * frac);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
index e9c4822..7a9cbef 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -91,6 +91,8 @@
import android.view.ViewStub;
import android.view.ViewTreeObserver;
import android.view.WindowManager;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityManager;
import android.view.animation.AccelerateDecelerateInterpolator;
import android.view.animation.AccelerateInterpolator;
import android.view.animation.Animation;
@@ -137,6 +139,7 @@
import com.android.systemui.statusbar.policy.FlashlightController;
import com.android.systemui.statusbar.policy.HeadsUpNotificationView;
import com.android.systemui.statusbar.policy.HotspotControllerImpl;
+import com.android.systemui.statusbar.policy.KeyButtonView;
import com.android.systemui.statusbar.policy.KeyguardMonitor;
import com.android.systemui.statusbar.policy.KeyguardUserSwitcher;
import com.android.systemui.statusbar.policy.LocationControllerImpl;
@@ -210,6 +213,9 @@
public static final int FADE_KEYGUARD_START_DELAY = 100;
public static final int FADE_KEYGUARD_DURATION = 300;
+ /** Allow some time inbetween the long press for back and recents. */
+ private static final int LOCK_TO_APP_GESTURE_TOLERENCE = 200;
+
PhoneStatusBarPolicy mIconPolicy;
// These are no longer handled by the policy, because we need custom strategies for them
@@ -1054,10 +1060,13 @@
}
};
- private View.OnLongClickListener mLockToAppClickListener = new View.OnLongClickListener() {
+ private long mLastLockToAppLongPress;
+ private AccessibilityManager mAccessibilityManager;
+ private View.OnLongClickListener mLongPressBackRecentsListener =
+ new View.OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
- toggleLockedApp();
+ handleLongPressBackRecents(v);
return true;
}
};
@@ -1106,7 +1115,9 @@
mNavigationBarView.getRecentsButton().setOnClickListener(mRecentsClickListener);
mNavigationBarView.getRecentsButton().setOnTouchListener(mRecentsPreloadOnTouchListener);
mNavigationBarView.getRecentsButton().setLongClickable(true);
- mNavigationBarView.getRecentsButton().setOnLongClickListener(mLockToAppClickListener);
+ mNavigationBarView.getRecentsButton().setOnLongClickListener(mLongPressBackRecentsListener);
+ mNavigationBarView.getBackButton().setLongClickable(true);
+ mNavigationBarView.getBackButton().setOnLongClickListener(mLongPressBackRecentsListener);
mNavigationBarView.getHomeButton().setOnTouchListener(mHomeActionListener);
updateSearchPanel();
}
@@ -2030,6 +2041,10 @@
return mLeaveOpenOnKeyguardHide;
}
+ public boolean isQsExpanded() {
+ return mNotificationPanel.isQsExpanded();
+ }
+
/**
* All changes to the status bar and notifications funnel through here and are batched.
*/
@@ -3769,15 +3784,58 @@
mStackScroller.setAnimationsEnabled(true);
}
- public void toggleLockedApp() {
- Log.d(TAG, "Trying to toggle lock-to-app");
+ /**
+ * This handles long-press of both back and recents. They are
+ * handled together to capture them both being long-pressed
+ * at the same time to exit screen pinning (lock task).
+ *
+ * When accessibility mode is on, only a long-press from recents
+ * is required to exit.
+ *
+ * In all other circumstances we try to pass through long-press events
+ * for Back, so that apps can still use it. Which can be from two things.
+ * 1) Not currently in screen pinning (lock task).
+ * 2) Back is long-pressed without recents.
+ */
+ private void handleLongPressBackRecents(View v) {
try {
+ boolean sendBackLongPress = false;
IActivityManager activityManager = ActivityManagerNative.getDefault();
- if (activityManager.isInLockTaskMode()) {
- activityManager.stopLockTaskModeOnCurrent();
+ if (mAccessibilityManager == null) {
+ mAccessibilityManager = (AccessibilityManager)
+ mContext.getSystemService(Context.ACCESSIBILITY_SERVICE);
+ }
+ boolean isAccessiblityEnabled = mAccessibilityManager.isEnabled();
+ if (activityManager.isInLockTaskMode() && !isAccessiblityEnabled) {
+ long time = System.currentTimeMillis();
+ // If we recently long-pressed the other button then they were
+ // long-pressed 'together'
+ if ((time - mLastLockToAppLongPress) < LOCK_TO_APP_GESTURE_TOLERENCE) {
+ activityManager.stopLockTaskModeOnCurrent();
+ } else if ((v.getId() == R.id.back)
+ && !mNavigationBarView.getRecentsButton().isPressed()) {
+ // If we aren't pressing recents right now then they presses
+ // won't be together, so send the standard long-press action.
+ sendBackLongPress = true;
+ }
+ mLastLockToAppLongPress = time;
+ } else {
+ // If this is back still need to handle sending the long-press event.
+ if (v.getId() == R.id.back) {
+ sendBackLongPress = true;
+ } else if (isAccessiblityEnabled && activityManager.isInLockTaskMode()) {
+ // When in accessibility mode a long press that is recents (not back)
+ // should stop lock task.
+ activityManager.stopLockTaskModeOnCurrent();
+ }
+ }
+ if (sendBackLongPress) {
+ KeyButtonView keyButtonView = (KeyButtonView) v;
+ keyButtonView.sendEvent(KeyEvent.ACTION_DOWN, KeyEvent.FLAG_LONG_PRESS);
+ keyButtonView.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_LONG_CLICKED);
}
} catch (RemoteException e) {
- Log.d(TAG, "Unable to toggle Lock-to-app", e);
+ Log.d(TAG, "Unable to reach activity manager", e);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java
index 5c9f3ca..39b2022 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java
@@ -24,6 +24,7 @@
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
+import android.util.TypedValue;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewOutlineProvider;
@@ -54,7 +55,7 @@
private View mSystemIconsSuperContainer;
private View mDateGroup;
private View mClock;
- private View mTime;
+ private TextView mTime;
private View mAmPm;
private MultiUserSwitch mMultiUserSwitch;
private ImageView mMultiUserAvatar;
@@ -86,6 +87,9 @@
private int mMultiUserSwitchWidthCollapsed;
private int mMultiUserSwitchWidthExpanded;
+ private int mClockCollapsedSize;
+ private int mClockExpandedSize;
+
/**
* In collapsed QS, the clock and avatar are scaled down a bit post-layout to allow for a nice
* transition. These values determine that factor.
@@ -121,7 +125,7 @@
mSystemIconsSuperContainer.setOnClickListener(this);
mDateGroup = findViewById(R.id.date_group);
mClock = findViewById(R.id.clock);
- mTime = findViewById(R.id.time_view);
+ mTime = (TextView) findViewById(R.id.time_view);
mAmPm = findViewById(R.id.am_pm_view);
mMultiUserSwitch = (MultiUserSwitch) findViewById(R.id.multi_user_switch);
mMultiUserAvatar = (ImageView) findViewById(R.id.multi_user_avatar);
@@ -152,8 +156,6 @@
boolean rtl = getLayoutDirection() == LAYOUT_DIRECTION_RTL;
mTime.setPivotX(rtl ? mTime.getWidth() : 0);
mTime.setPivotY(mTime.getBaseline());
- mAmPm.setPivotX(rtl ? mAmPm.getWidth() : 0);
- mAmPm.setPivotY(mAmPm.getBaseline());
updateAmPmTranslation();
}
});
@@ -206,9 +208,10 @@
mAvatarCollapsedScaleFactor =
getResources().getDimensionPixelSize(R.dimen.multi_user_avatar_collapsed_size)
/ (float) mMultiUserAvatar.getLayoutParams().width;
- mClockCollapsedScaleFactor =
- (float) getResources().getDimensionPixelSize(R.dimen.qs_time_collapsed_size)
- / (float) getResources().getDimensionPixelSize(R.dimen.qs_time_expanded_size);
+ mClockCollapsedSize = getResources().getDimensionPixelSize(R.dimen.qs_time_collapsed_size);
+ mClockExpandedSize = getResources().getDimensionPixelSize(R.dimen.qs_time_expanded_size);
+ mClockCollapsedScaleFactor = (float) mClockCollapsedSize / (float) mClockExpandedSize;
+
}
public void setActivityStarter(ActivityStarter activityStarter) {
@@ -333,17 +336,14 @@
}
private void updateClockScale() {
- mAmPm.setScaleX(mClockCollapsedScaleFactor);
- mAmPm.setScaleY(mClockCollapsedScaleFactor);
- mTime.setScaleX(getTimeScale());
- mTime.setScaleY(getTimeScale());
+ mTime.setTextSize(TypedValue.COMPLEX_UNIT_PX, mExpanded
+ ? mClockExpandedSize
+ : mClockCollapsedSize);
+ mTime.setScaleX(1f);
+ mTime.setScaleY(1f);
updateAmPmTranslation();
}
- private float getTimeScale() {
- return !mExpanded ? mClockCollapsedScaleFactor : 1f;
- }
-
private void updateAmPmTranslation() {
boolean rtl = getLayoutDirection() == LAYOUT_DIRECTION_RTL;
mAmPm.setTranslationX((rtl ? 1 : -1) * mTime.getWidth() * (1 - mTime.getScaleX()));
@@ -366,7 +366,7 @@
mAlarmStatus.setText(KeyguardStatusView.formatNextAlarm(getContext(), nextAlarm));
}
mAlarmShowing = nextAlarm != null;
- updateVisibilities();
+ updateEverything();
requestCaptureValues();
}
@@ -512,10 +512,13 @@
}
public void setShowEmergencyCallsOnly(boolean show) {
- mShowEmergencyCallsOnly = show;
- if (mExpanded) {
- updateVisibilities();
- requestCaptureValues();
+ boolean changed = show != mShowEmergencyCallsOnly;
+ if (changed) {
+ mShowEmergencyCallsOnly = show;
+ if (mExpanded) {
+ updateEverything();
+ requestCaptureValues();
+ }
}
}
@@ -526,8 +529,8 @@
}
private void captureLayoutValues(LayoutValues target) {
- target.timeScale = mTime.getScaleX();
- target.clockY = mClock.getTop();
+ target.timeScale = mExpanded ? 1f : mClockCollapsedScaleFactor;
+ target.clockY = mClock.getBottom();
target.dateY = mDateGroup.getTop();
target.emergencyCallsOnlyAlpha = getAlphaForVisibility(mEmergencyCallsOnly);
target.alarmStatusAlpha = getAlphaForVisibility(mAlarmStatus);
@@ -552,7 +555,7 @@
}
private void applyAlpha(View v, float alpha) {
- if (v == null) {
+ if (v == null || v.getVisibility() == View.GONE) {
return;
}
if (alpha == 0f) {
@@ -566,7 +569,7 @@
private void applyLayoutValues(LayoutValues values) {
mTime.setScaleX(values.timeScale);
mTime.setScaleY(values.timeScale);
- mClock.setY(values.clockY);
+ mClock.setY(values.clockY - mClock.getHeight());
mDateGroup.setY(values.dateY);
mAlarmStatus.setY(values.dateY - mAlarmStatus.getPaddingTop());
mMultiUserAvatar.setScaleX(values.avatarScale);
@@ -595,6 +598,10 @@
applyAlpha(mBatteryLevel, values.batteryLevelAlpha);
applyAlpha(mSettingsButton, values.settingsAlpha);
applyAlpha(mSignalCluster, values.signalClusterAlpha);
+ if (!mExpanded) {
+ mTime.setScaleX(1f);
+ mTime.setScaleY(1f);
+ }
updateAmPmTranslation();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index ce1e176..f427ec4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -188,6 +188,7 @@
@Override
public void run() {
mStatusBarWindowManager.setKeyguardOccluded(true);
+ reset();
}
});
return;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
index 2c5bcb79..e1fd779 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
@@ -28,7 +28,6 @@
import android.view.ViewRootImpl;
import android.widget.FrameLayout;
-import com.android.systemui.ExpandHelper;
import com.android.systemui.R;
import com.android.systemui.statusbar.BaseStatusBar;
import com.android.systemui.statusbar.DragDownHelper;
@@ -122,6 +121,7 @@
MotionEvent cancellation = MotionEvent.obtain(ev);
cancellation.setAction(MotionEvent.ACTION_CANCEL);
mStackScrollLayout.onInterceptTouchEvent(cancellation);
+ mNotificationPanel.onInterceptTouchEvent(cancellation);
cancellation.recycle();
}
return intercept;
@@ -130,7 +130,7 @@
@Override
public boolean onTouchEvent(MotionEvent ev) {
boolean handled = false;
- if (mService.getBarState() == StatusBarState.KEYGUARD) {
+ if (mService.getBarState() == StatusBarState.KEYGUARD && !mService.isQsExpanded()) {
handled = mDragDownHelper.onTouchEvent(ev);
}
if (!handled) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java
index 330b599..e9581fc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java
@@ -56,12 +56,12 @@
public void run() {
if (isPressed()) {
// Log.d("KeyButtonView", "longpressed: " + this);
- if (mCode != 0) {
- sendEvent(KeyEvent.ACTION_DOWN, KeyEvent.FLAG_LONG_PRESS);
- sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_LONG_CLICKED);
- } else {
+ if (isLongClickable()) {
// Just an old-fashioned ImageView
performLongClick();
+ } else {
+ sendEvent(KeyEvent.ACTION_DOWN, KeyEvent.FLAG_LONG_PRESS);
+ sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_LONG_CLICKED);
}
}
}
@@ -216,7 +216,7 @@
return true;
}
- void sendEvent(int action, int flags) {
+ public void sendEvent(int action, int flags) {
sendEvent(action, flags, SystemClock.uptimeMillis());
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeController.java
index 6c4fb7a..7d102ba 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeController.java
@@ -16,7 +16,6 @@
package com.android.systemui.statusbar.policy;
-import android.net.Uri;
import android.service.notification.Condition;
public interface ZenModeController {
@@ -25,15 +24,15 @@
void setZen(int zen);
int getZen();
void requestConditions(boolean request);
- void setExitConditionId(Uri exitConditionId);
- Uri getExitConditionId();
+ void setExitCondition(Condition exitCondition);
+ Condition getExitCondition();
long getNextAlarm();
void setUserId(int userId);
boolean isZenAvailable();
public static class Callback {
public void onZenChanged(int zen) {}
- public void onExitConditionChanged(Uri exitConditionId) {}
+ public void onExitConditionChanged(Condition exitCondition) {}
public void onConditionsChanged(Condition[] conditions) {}
public void onNextAlarmChanged() {}
public void onZenAvailableChanged(boolean available) {}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java
index b33e502..b0c8f26 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java
@@ -122,20 +122,20 @@
}
@Override
- public void setExitConditionId(Uri exitConditionId) {
+ public void setExitCondition(Condition exitCondition) {
try {
- mNoMan.setZenModeCondition(exitConditionId);
+ mNoMan.setZenModeCondition(exitCondition);
} catch (RemoteException e) {
// noop
}
}
@Override
- public Uri getExitConditionId() {
+ public Condition getExitCondition() {
try {
final ZenModeConfig config = mNoMan.getZenModeConfig();
if (config != null) {
- return config.exitConditionId;
+ return config.exitCondition;
}
} catch (RemoteException e) {
// noop
@@ -186,10 +186,10 @@
}
private void fireExitConditionChanged() {
- final Uri exitConditionId = getExitConditionId();
- if (DEBUG) Slog.d(TAG, "exitConditionId changed: " + exitConditionId);
+ final Condition exitCondition = getExitCondition();
+ if (DEBUG) Slog.d(TAG, "exitCondition changed: " + exitCondition);
for (Callback cb : mCallbacks) {
- cb.onExitConditionChanged(exitConditionId);
+ cb.onExitConditionChanged(exitCondition);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
index 462452b..1469d73 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
@@ -280,7 +280,7 @@
}
private void updatePadding(boolean dimmed) {
- mPaddingBetweenElements = dimmed
+ mPaddingBetweenElements = dimmed && mStackScrollAlgorithm.shouldScaleDimmed()
? mPaddingBetweenElementsDimmed
: mPaddingBetweenElementsNormal;
mBottomStackSlowDownHeight = mStackScrollAlgorithm.getBottomStackSlowDownLength();
@@ -1914,6 +1914,7 @@
@Override
public void onReset(ExpandableView view) {
mRequestViewResizeAnimationOnLayout = true;
+ mStackScrollAlgorithm.onReset(view);
}
private void updateScrollPositionOnExpandInBottom(ExpandableView view) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java
index f984339b3..fe855d9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java
@@ -17,6 +17,7 @@
package com.android.systemui.statusbar.stack;
import android.content.Context;
+import android.util.DisplayMetrics;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
@@ -73,6 +74,7 @@
private int mCollapseSecondCardPadding;
private boolean mIsSmallScreen;
private int mMaxNotificationHeight;
+ private boolean mScaleDimmed;
public StackScrollAlgorithm(Context context) {
initConstants(context);
@@ -80,7 +82,7 @@
}
private void updatePadding(boolean dimmed) {
- mPaddingBetweenElements = dimmed
+ mPaddingBetweenElements = dimmed && mScaleDimmed
? mPaddingBetweenElementsDimmed
: mPaddingBetweenElementsNormal;
mTopStackTotalSize = mTopStackSlowDownLength + mPaddingBetweenElements
@@ -125,8 +127,13 @@
R.dimen.notification_material_rounded_rect_radius);
mCollapseSecondCardPadding = context.getResources().getDimensionPixelSize(
R.dimen.notification_collapse_second_card_padding);
+ mScaleDimmed = context.getResources().getDisplayMetrics().densityDpi
+ >= DisplayMetrics.DENSITY_XXHIGH;
}
+ public boolean shouldScaleDimmed() {
+ return mScaleDimmed;
+ }
public void getStackScrollState(AmbientState ambientState, StackScrollState resultState) {
// The state of the local variables are saved in an algorithmState to easily subdivide it
@@ -271,7 +278,7 @@
childViewState.dark = dark;
childViewState.hideSensitive = hideSensitive;
boolean isActivatedChild = activatedChild == child;
- childViewState.scale = !dimmed || isActivatedChild
+ childViewState.scale = !mScaleDimmed || !dimmed || isActivatedChild
? 1.0f
: DIMMED_SCALE;
if (dimmed && activatedChild != null) {
@@ -751,39 +758,42 @@
// current height.
mFirstChildMaxHeight = mFirstChildWhileExpanding.getActualHeight();
} else {
-
- // We are expanding the shade, expand it to its full height.
- if (!isMaxSizeInitialized(mFirstChildWhileExpanding)) {
-
- // This child was not layouted yet, wait for a layout pass
- mFirstChildWhileExpanding
- .addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
- @Override
- public void onLayoutChange(View v, int left, int top, int right,
- int bottom, int oldLeft, int oldTop, int oldRight,
- int oldBottom) {
- if (mFirstChildWhileExpanding != null) {
- mFirstChildMaxHeight = getMaxAllowedChildHeight(
- mFirstChildWhileExpanding);
- } else {
- mFirstChildMaxHeight = 0;
- }
- v.removeOnLayoutChangeListener(this);
- }
- });
- } else {
- mFirstChildMaxHeight = getMaxAllowedChildHeight(mFirstChildWhileExpanding);
- }
+ updateFirstChildMaxSizeToMaxHeight();
}
} else {
mFirstChildMaxHeight = 0;
}
}
+ private void updateFirstChildMaxSizeToMaxHeight() {
+ // We are expanding the shade, expand it to its full height.
+ if (!isMaxSizeInitialized(mFirstChildWhileExpanding)) {
+
+ // This child was not layouted yet, wait for a layout pass
+ mFirstChildWhileExpanding
+ .addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
+ @Override
+ public void onLayoutChange(View v, int left, int top, int right,
+ int bottom, int oldLeft, int oldTop, int oldRight,
+ int oldBottom) {
+ if (mFirstChildWhileExpanding != null) {
+ mFirstChildMaxHeight = getMaxAllowedChildHeight(
+ mFirstChildWhileExpanding);
+ } else {
+ mFirstChildMaxHeight = 0;
+ }
+ v.removeOnLayoutChangeListener(this);
+ }
+ });
+ } else {
+ mFirstChildMaxHeight = getMaxAllowedChildHeight(mFirstChildWhileExpanding);
+ }
+ }
+
private boolean isMaxSizeInitialized(ExpandableView child) {
if (child instanceof ExpandableNotificationRow) {
ExpandableNotificationRow row = (ExpandableNotificationRow) child;
- return row.isShowingLayoutLayouted();
+ return row.isMaxExpandHeightInitialized();
}
return child == null || child.getWidth() != 0;
}
@@ -818,6 +828,12 @@
updatePadding(dimmed);
}
+ public void onReset(ExpandableView view) {
+ if (view.equals(mFirstChildWhileExpanding)) {
+ updateFirstChildMaxSizeToMaxHeight();
+ }
+ }
+
class StackScrollAlgorithmState {
/**
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumePanel.java b/packages/SystemUI/src/com/android/systemui/volume/VolumePanel.java
index 5233da2..b05c242 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumePanel.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumePanel.java
@@ -112,7 +112,8 @@
// Pseudo stream type for master volume
private static final int STREAM_MASTER = -100;
- // Pseudo stream type for remote volume is defined in AudioService.STREAM_REMOTE_MUSIC
+ // Pseudo stream type for remote volume
+ private static final int STREAM_REMOTE_MUSIC = -200;
private static final AudioAttributes VIBRATION_ATTRIBUTES = new AudioAttributes.Builder()
.setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
@@ -192,7 +193,7 @@
R.drawable.ic_audio_vol,
R.drawable.ic_audio_vol_mute,
false),
- RemoteStream(AudioService.STREAM_REMOTE_MUSIC,
+ RemoteStream(STREAM_REMOTE_MUSIC,
R.string.volume_icon_description_media, //FIXME should have its own description
R.drawable.ic_media_route_on_holo_dark,
R.drawable.ic_media_route_disabled_holo_dark,
@@ -521,7 +522,7 @@
private boolean isMuted(int streamType) {
if (streamType == STREAM_MASTER) {
return mAudioManager.isMasterMute();
- } else if (streamType == AudioService.STREAM_REMOTE_MUSIC) {
+ } else if (streamType == STREAM_REMOTE_MUSIC) {
// TODO do we need to support a distinct mute property for remote?
return false;
} else {
@@ -532,7 +533,7 @@
private int getStreamMaxVolume(int streamType) {
if (streamType == STREAM_MASTER) {
return mAudioManager.getMasterMaxVolume();
- } else if (streamType == AudioService.STREAM_REMOTE_MUSIC) {
+ } else if (streamType == STREAM_REMOTE_MUSIC) {
if (mStreamControls != null) {
StreamControl sc = mStreamControls.get(streamType);
if (sc != null && sc.controller != null) {
@@ -549,7 +550,7 @@
private int getStreamVolume(int streamType) {
if (streamType == STREAM_MASTER) {
return mAudioManager.getMasterVolume();
- } else if (streamType == AudioService.STREAM_REMOTE_MUSIC) {
+ } else if (streamType == STREAM_REMOTE_MUSIC) {
if (mStreamControls != null) {
StreamControl sc = mStreamControls.get(streamType);
if (sc != null && sc.controller != null) {
@@ -564,7 +565,7 @@
}
private void setStreamVolume(StreamControl sc, int index, int flags) {
- if (sc.streamType == AudioService.STREAM_REMOTE_MUSIC) {
+ if (sc.streamType == STREAM_REMOTE_MUSIC) {
if (sc.controller != null) {
sc.controller.setVolumeTo(index, flags);
} else {
@@ -690,7 +691,7 @@
private void updateSliderEnabled(final StreamControl sc, boolean muted, boolean fixedVolume) {
final boolean wasEnabled = sc.seekbarView.isEnabled();
final boolean isRinger = isNotificationOrRing(sc.streamType);
- if (sc.streamType == AudioService.STREAM_REMOTE_MUSIC) {
+ if (sc.streamType == STREAM_REMOTE_MUSIC) {
// never disable touch interactions for remote playback, the muting is not tied to
// the state of the phone.
sc.seekbarView.setEnabled(!fixedVolume);
@@ -805,7 +806,7 @@
public void postRemoteSliderVisibility(boolean visible) {
obtainMessage(MSG_SLIDER_VISIBILITY_CHANGED,
- AudioService.STREAM_REMOTE_MUSIC, visible ? 1 : 0).sendToTarget();
+ STREAM_REMOTE_MUSIC, visible ? 1 : 0).sendToTarget();
}
/**
@@ -980,7 +981,7 @@
break;
}
- case AudioService.STREAM_REMOTE_MUSIC: {
+ case STREAM_REMOTE_MUSIC: {
if (controller == null && sc != null) {
// If we weren't passed one try using the last one set.
controller = sc.controller;
@@ -1003,7 +1004,7 @@
}
if (sc != null) {
- if (streamType == AudioService.STREAM_REMOTE_MUSIC && controller != sc.controller) {
+ if (streamType == STREAM_REMOTE_MUSIC && controller != sc.controller) {
if (sc.controller != null) {
sc.controller.removeCallback(mMediaControllerCb);
}
@@ -1021,7 +1022,7 @@
}
if (!isShowing()) {
- int stream = (streamType == AudioService.STREAM_REMOTE_MUSIC) ? -1 : streamType;
+ int stream = (streamType == STREAM_REMOTE_MUSIC) ? -1 : streamType;
// when the stream is for remote playback, use -1 to reset the stream type evaluation
mAudioManager.forceVolumeControlStream(stream);
mDialog.show();
@@ -1031,7 +1032,7 @@
}
// Do a little vibrate if applicable (only when going into vibrate mode)
- if ((streamType != AudioService.STREAM_REMOTE_MUSIC) &&
+ if ((streamType != STREAM_REMOTE_MUSIC) &&
((flags & AudioManager.FLAG_VIBRATE) != 0) &&
mAudioManager.isStreamAffectedByRingerMode(streamType) &&
mAudioManager.getRingerMode() == AudioManager.RINGER_MODE_VIBRATE) {
@@ -1094,10 +1095,10 @@
if (((flags & AudioManager.FLAG_SHOW_UI) != 0) || isShowing()) {
synchronized (this) {
- if (mActiveStreamType != AudioService.STREAM_REMOTE_MUSIC) {
- reorderSliders(AudioService.STREAM_REMOTE_MUSIC);
+ if (mActiveStreamType != STREAM_REMOTE_MUSIC) {
+ reorderSliders(STREAM_REMOTE_MUSIC);
}
- onShowVolumeChanged(AudioService.STREAM_REMOTE_MUSIC, flags, controller);
+ onShowVolumeChanged(STREAM_REMOTE_MUSIC, flags, controller);
}
} else {
if (LOGD) Log.d(mTag, "not calling onShowVolumeChanged(), no FLAG_SHOW_UI or no UI");
@@ -1111,9 +1112,9 @@
protected void onRemoteVolumeUpdateIfShown() {
if (LOGD) Log.d(mTag, "onRemoteVolumeUpdateIfShown()");
if (isShowing()
- && (mActiveStreamType == AudioService.STREAM_REMOTE_MUSIC)
+ && (mActiveStreamType == STREAM_REMOTE_MUSIC)
&& (mStreamControls != null)) {
- onShowVolumeChanged(AudioService.STREAM_REMOTE_MUSIC, 0, null);
+ onShowVolumeChanged(STREAM_REMOTE_MUSIC, 0, null);
}
}
@@ -1122,7 +1123,7 @@
*/
private void clearRemoteStreamController() {
if (mStreamControls != null) {
- StreamControl sc = mStreamControls.get(AudioService.STREAM_REMOTE_MUSIC);
+ StreamControl sc = mStreamControls.get(STREAM_REMOTE_MUSIC);
if (sc != null) {
if (sc.controller != null) {
sc.controller.removeCallback(mMediaControllerCb);
@@ -1133,10 +1134,10 @@
}
/**
- * Handler for MSG_SLIDER_VISIBILITY_CHANGED
- * Hide or show a slider
- * @param streamType can be a valid stream type value, or VolumePanel.STREAM_MASTER,
- * or AudioService.STREAM_REMOTE_MUSIC
+ * Handler for MSG_SLIDER_VISIBILITY_CHANGED Hide or show a slider
+ *
+ * @param streamType can be a valid stream type value, or
+ * VolumePanel.STREAM_MASTER, or VolumePanel.STREAM_REMOTE_MUSIC
* @param visible
*/
synchronized protected void onSliderVisibilityChanged(int streamType, int visible) {
diff --git a/packages/SystemUI/src/com/android/systemui/volume/ZenModePanel.java b/packages/SystemUI/src/com/android/systemui/volume/ZenModePanel.java
index 0d837c7..c99e1fd 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/ZenModePanel.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/ZenModePanel.java
@@ -97,14 +97,16 @@
private Callback mCallback;
private ZenModeController mController;
private boolean mRequestingConditions;
- private Uri mExitConditionId;
+ private Condition mExitCondition;
+ private String mExitConditionText;
private int mBucketIndex = -1;
private boolean mExpanded;
private boolean mHidden = false;
private int mSessionZen;
- private Uri mSessionExitConditionId;
- private String mExitConditionText;
+ private Condition mSessionExitCondition;
private long mNextAlarm;
+ private Condition[] mConditions;
+ private Condition mTimeCondition;
public ZenModePanel(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -161,7 +163,7 @@
super.onAttachedToWindow();
if (DEBUG) Log.d(mTag, "onAttachedToWindow");
mSessionZen = getSelectedZen(-1);
- mSessionExitConditionId = mExitConditionId;
+ mSessionExitCondition = copy(mExitCondition);
refreshExitConditionText();
refreshNextAlarm();
updateWidgets();
@@ -172,7 +174,7 @@
super.onDetachedFromWindow();
if (DEBUG) Log.d(mTag, "onDetachedFromWindow");
mSessionZen = -1;
- mSessionExitConditionId = null;
+ mSessionExitCondition = null;
setExpanded(false);
}
@@ -199,17 +201,16 @@
mController.requestConditions(mRequestingConditions);
}
if (mRequestingConditions) {
- Condition timeCondition = parseExistingTimeCondition(mExitConditionId);
- if (timeCondition != null) {
+ mTimeCondition = parseExistingTimeCondition(mExitCondition);
+ if (mTimeCondition != null) {
mBucketIndex = -1;
} else {
mBucketIndex = DEFAULT_BUCKET_INDEX;
- timeCondition = newTimeCondition(MINUTE_BUCKETS[mBucketIndex]);
+ mTimeCondition = newTimeCondition(MINUTE_BUCKETS[mBucketIndex]);
}
if (DEBUG) Log.d(mTag, "Initial bucket index: " + mBucketIndex);
- handleUpdateConditions(new Condition[0]); // ensures forever exists
- bind(timeCondition, mZenConditions.getChildAt(TIME_CONDITION_INDEX));
- checkForDefault();
+ mConditions = null; // reset conditions
+ handleUpdateConditions();
} else {
mZenConditions.removeAllViews();
}
@@ -217,31 +218,47 @@
public void init(ZenModeController controller) {
mController = controller;
- setExitConditionId(mController.getExitConditionId());
+ setExitCondition(mController.getExitCondition());
refreshExitConditionText();
mSessionZen = getSelectedZen(-1);
handleUpdateZen(mController.getZen());
- if (DEBUG) Log.d(mTag, "init mExitConditionId=" + mExitConditionId);
+ if (DEBUG) Log.d(mTag, "init mExitCondition=" + mExitCondition);
mZenConditions.removeAllViews();
mController.addCallback(mZenCallback);
}
- private void setExitConditionId(Uri exitConditionId) {
- if (Objects.equals(mExitConditionId, exitConditionId)) return;
- mExitConditionId = exitConditionId;
+ private void setExitCondition(Condition exitCondition) {
+ if (sameConditionId(mExitCondition, exitCondition)) return;
+ mExitCondition = exitCondition;
refreshExitConditionText();
updateWidgets();
}
+ private Uri getExitConditionId() {
+ return getConditionId(mExitCondition);
+ }
+
+ private static Uri getConditionId(Condition condition) {
+ return condition != null ? condition.id : null;
+ }
+
+ private static boolean sameConditionId(Condition lhs, Condition rhs) {
+ return lhs == null ? rhs == null : rhs != null && lhs.id.equals(rhs.id);
+ }
+
+ private static Condition copy(Condition condition) {
+ return condition == null ? null : condition.copy();
+ }
+
private void refreshExitConditionText() {
final String forever = mContext.getString(R.string.zen_mode_forever);
- if (mExitConditionId == null) {
+ if (mExitCondition == null) {
mExitConditionText = forever;
- } else if (ZenModeConfig.isValidCountdownConditionId(mExitConditionId)) {
- final Condition condition = parseExistingTimeCondition(mExitConditionId);
+ } else if (ZenModeConfig.isValidCountdownConditionId(mExitCondition.id)) {
+ final Condition condition = parseExistingTimeCondition(mExitCondition);
mExitConditionText = condition != null ? condition.summary : forever;
} else {
- mExitConditionText = "(until condition ends)"; // TODO persist current description
+ mExitConditionText = mExitCondition.summary;
}
}
@@ -296,7 +313,7 @@
mZenConditions.setVisibility(!zenOff && expanded ? VISIBLE : GONE);
mAlarmWarning.setVisibility(zenNone && expanded && hasNextAlarm ? VISIBLE : GONE);
if (showAlarmWarning) {
- final long exitTime = ZenModeConfig.tryParseCountdownConditionId(mExitConditionId);
+ final long exitTime = ZenModeConfig.tryParseCountdownConditionId(getExitConditionId());
final long now = System.currentTimeMillis();
final boolean alarmToday = time(mNextAlarm).yearDay == time(now).yearDay;
final String skeleton = (alarmToday ? "" : "E")
@@ -330,8 +347,9 @@
return t;
}
- private Condition parseExistingTimeCondition(Uri conditionId) {
- final long time = ZenModeConfig.tryParseCountdownConditionId(conditionId);
+ private Condition parseExistingTimeCondition(Condition condition) {
+ if (condition == null) return null;
+ final long time = ZenModeConfig.tryParseCountdownConditionId(condition.id);
if (time == 0) return null;
final long span = time - System.currentTimeMillis();
if (span <= 0 || span > MAX_BUCKET_MINUTES * MINUTES_MS) return null;
@@ -365,15 +383,36 @@
}
private void handleUpdateConditions(Condition[] conditions) {
- final int newCount = conditions == null ? 0 : conditions.length;
- if (DEBUG) Log.d(mTag, "handleUpdateConditions newCount=" + newCount);
- for (int i = mZenConditions.getChildCount(); i >= newCount + FIRST_CONDITION_INDEX; i--) {
+ mConditions = conditions;
+ handleUpdateConditions();
+ }
+
+ private void handleUpdateConditions() {
+ final int conditionCount = mConditions == null ? 0 : mConditions.length;
+ if (DEBUG) Log.d(mTag, "handleUpdateConditions conditionCount=" + conditionCount);
+ for (int i = mZenConditions.getChildCount() - 1; i >= FIRST_CONDITION_INDEX; i--) {
mZenConditions.removeViewAt(i);
}
+ // forever
bind(null, mZenConditions.getChildAt(FOREVER_CONDITION_INDEX));
- for (int i = 0; i < newCount; i++) {
- bind(conditions[i], mZenConditions.getChildAt(FIRST_CONDITION_INDEX + i));
+ // countdown
+ bind(mTimeCondition, mZenConditions.getChildAt(TIME_CONDITION_INDEX));
+ // provider conditions
+ boolean foundDowntime = false;
+ for (int i = 0; i < conditionCount; i++) {
+ bind(mConditions[i], mZenConditions.getChildAt(FIRST_CONDITION_INDEX + i));
+ foundDowntime |= isDowntime(mConditions[i]);
}
+ // ensure downtime exists, if active
+ if (isDowntime(mSessionExitCondition) && !foundDowntime) {
+ bind(mSessionExitCondition, null);
+ }
+ // ensure something is selected
+ checkForDefault();
+ }
+
+ private static boolean isDowntime(Condition c) {
+ return ZenModeConfig.isValidDowntimeConditionId(getConditionId(c));
}
private ConditionTag getConditionTagAt(int index) {
@@ -385,7 +424,7 @@
for (int i = 0; i < mZenConditions.getChildCount(); i++) {
if (getConditionTagAt(i).rb.isChecked()) {
if (DEBUG) Log.d(mTag, "Not selecting a default, checked="
- + getConditionTagAt(i).conditionId);
+ + getConditionTagAt(i).condition);
return;
}
}
@@ -394,20 +433,20 @@
if (favoriteIndex == -1) {
getConditionTagAt(FOREVER_CONDITION_INDEX).rb.setChecked(true);
} else {
- final Condition c = newTimeCondition(MINUTE_BUCKETS[favoriteIndex]);
+ mTimeCondition = newTimeCondition(MINUTE_BUCKETS[favoriteIndex]);
mBucketIndex = favoriteIndex;
- bind(c, mZenConditions.getChildAt(TIME_CONDITION_INDEX));
+ bind(mTimeCondition, mZenConditions.getChildAt(TIME_CONDITION_INDEX));
getConditionTagAt(TIME_CONDITION_INDEX).rb.setChecked(true);
}
}
- private void handleExitConditionChanged(Uri exitCondition) {
- setExitConditionId(exitCondition);
- if (DEBUG) Log.d(mTag, "handleExitConditionChanged " + mExitConditionId);
+ private void handleExitConditionChanged(Condition exitCondition) {
+ setExitCondition(exitCondition);
+ if (DEBUG) Log.d(mTag, "handleExitConditionChanged " + mExitCondition);
final int N = mZenConditions.getChildCount();
for (int i = 0; i < N; i++) {
final ConditionTag tag = getConditionTagAt(i);
- tag.rb.setChecked(Objects.equals(tag.conditionId, exitCondition));
+ tag.rb.setChecked(sameConditionId(tag.condition, mExitCondition));
}
}
@@ -427,23 +466,23 @@
if (tag.rb == null) {
tag.rb = (RadioButton) row.findViewById(android.R.id.checkbox);
}
- tag.conditionId = condition != null ? condition.id : null;
+ tag.condition = condition;
tag.rb.setEnabled(enabled);
- if (mSessionExitConditionId != null && mSessionExitConditionId.equals(tag.conditionId)) {
+ if (sameConditionId(mSessionExitCondition, tag.condition)) {
tag.rb.setChecked(true);
}
tag.rb.setOnCheckedChangeListener(new OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
if (mExpanded && isChecked) {
- if (DEBUG) Log.d(mTag, "onCheckedChanged " + tag.conditionId);
+ if (DEBUG) Log.d(mTag, "onCheckedChanged " + tag.condition);
final int N = mZenConditions.getChildCount();
for (int i = 0; i < N; i++) {
ConditionTag childTag = getConditionTagAt(i);
if (childTag == tag) continue;
childTag.rb.setChecked(false);
}
- select(tag.conditionId);
+ select(tag.condition);
fireInteraction();
}
}
@@ -479,7 +518,7 @@
}
});
- final long time = ZenModeConfig.tryParseCountdownConditionId(tag.conditionId);
+ final long time = ZenModeConfig.tryParseCountdownConditionId(getConditionId(tag.condition));
if (time > 0) {
if (mBucketIndex > -1) {
button1.setEnabled(mBucketIndex > 0);
@@ -504,7 +543,8 @@
final int N = MINUTE_BUCKETS.length;
if (mBucketIndex == -1) {
// not on a known index, search for the next or prev bucket by time
- final long time = ZenModeConfig.tryParseCountdownConditionId(tag.conditionId);
+ final Uri conditionId = getConditionId(tag.condition);
+ final long time = ZenModeConfig.tryParseCountdownConditionId(conditionId);
final long now = System.currentTimeMillis();
for (int i = 0; i < N; i++) {
int j = up ? i : N - 1 - i;
@@ -525,24 +565,25 @@
mBucketIndex = Math.max(0, Math.min(N - 1, mBucketIndex + (up ? 1 : -1)));
newCondition = newTimeCondition(MINUTE_BUCKETS[mBucketIndex]);
}
- bind(newCondition, row);
+ mTimeCondition = newCondition;
+ bind(mTimeCondition, row);
tag.rb.setChecked(true);
- select(newCondition.id);
+ select(mTimeCondition);
fireInteraction();
}
- private void select(Uri conditionId) {
- if (DEBUG) Log.d(mTag, "select " + conditionId);
+ private void select(Condition condition) {
+ if (DEBUG) Log.d(mTag, "select " + condition);
if (mController != null) {
- mController.setExitConditionId(conditionId);
+ mController.setExitCondition(condition);
}
- setExitConditionId(conditionId);
- if (conditionId == null) {
+ setExitCondition(condition);
+ if (condition == null) {
mFavorites.setMinuteIndex(-1);
- } else if (ZenModeConfig.isValidCountdownConditionId(conditionId) && mBucketIndex != -1) {
+ } else if (ZenModeConfig.isValidCountdownConditionId(condition.id) && mBucketIndex != -1) {
mFavorites.setMinuteIndex(mBucketIndex);
}
- mSessionExitConditionId = conditionId;
+ mSessionExitCondition = copy(condition);
}
private void fireMoreSettings() {
@@ -574,8 +615,8 @@
}
@Override
- public void onExitConditionChanged(Uri exitConditionId) {
- mHandler.obtainMessage(H.EXIT_CONDITION_CHANGED, exitConditionId).sendToTarget();
+ public void onExitConditionChanged(Condition exitCondition) {
+ mHandler.obtainMessage(H.EXIT_CONDITION_CHANGED, exitCondition).sendToTarget();
}
@Override
@@ -598,9 +639,8 @@
public void handleMessage(Message msg) {
if (msg.what == UPDATE_CONDITIONS) {
handleUpdateConditions((Condition[]) msg.obj);
- checkForDefault();
} else if (msg.what == EXIT_CONDITION_CHANGED) {
- handleExitConditionChanged((Uri) msg.obj);
+ handleExitConditionChanged((Condition) msg.obj);
} else if (msg.what == UPDATE_ZEN) {
handleUpdateZen(msg.arg1);
} else if (msg.what == NEXT_ALARM_CHANGED) {
@@ -618,7 +658,7 @@
// used as the view tag on condition rows
private static class ConditionTag {
RadioButton rb;
- Uri conditionId;
+ Condition condition;
}
private final class Favorites implements OnSharedPreferenceChangeListener {
diff --git a/rs/java/android/renderscript/Allocation.java b/rs/java/android/renderscript/Allocation.java
index ac76ddf..7db85f2 100644
--- a/rs/java/android/renderscript/Allocation.java
+++ b/rs/java/android/renderscript/Allocation.java
@@ -1341,9 +1341,13 @@
* @param dimX The new size of the allocation.
*
* @deprecated RenderScript objects should be immutable once created. The
- * replacement is to create a new allocation and copy the contents.
+ * replacement is to create a new allocation and copy the contents. This
+ * function will throw an exception if API 21 or higher is used.
*/
public synchronized void resize(int dimX) {
+ if (mRS.getApplicationContext().getApplicationInfo().targetSdkVersion >= 21) {
+ throw new RSRuntimeException("Resize is not allowed in API 21+.");
+ }
if ((mType.getY() > 0)|| (mType.getZ() > 0) || mType.hasFaces() || mType.hasMipmaps()) {
throw new RSInvalidStateException("Resize only support for 1D allocations at this time.");
}
diff --git a/services/appwidget/Android.mk b/services/appwidget/Android.mk
index ca38f2f..e9bab4a 100644
--- a/services/appwidget/Android.mk
+++ b/services/appwidget/Android.mk
@@ -7,4 +7,6 @@
LOCAL_SRC_FILES += \
$(call all-java-files-under,java)
+LOCAL_JAVA_LIBRARIES := services.core
+
include $(BUILD_STATIC_JAVA_LIBRARY)
diff --git a/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java
index b2d1b71..c44474d 100644
--- a/services/backup/java/com/android/server/backup/BackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/BackupManagerService.java
@@ -8599,7 +8599,15 @@
skip = true;
}
- if (!skip && mAutoRestore && mProvisioned) {
+ if (!mAutoRestore || !mProvisioned) {
+ if (DEBUG) {
+ Slog.w(TAG, "Non-restorable state: auto=" + mAutoRestore
+ + " prov=" + mProvisioned);
+ }
+ skip = true;
+ }
+
+ if (!skip) {
try {
// okay, we're going to attempt a restore of this package from this restore set.
// The eventual message back into the Package Manager to run the post-install
@@ -8632,7 +8640,7 @@
if (skip) {
// Auto-restore disabled or no way to attempt a restore; just tell the Package
// Manager to proceed with the post-install handling for this package.
- if (DEBUG) Slog.v(TAG, "Skipping");
+ if (DEBUG) Slog.v(TAG, "Finishing install immediately");
try {
mPackageManagerBinder.finishPackageInstall(token);
} catch (RemoteException e) { /* can't happen */ }
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index dd5a7ea..02695c5 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -1534,11 +1534,17 @@
return;
}
- if (mtu < 68 || mtu > 10000) {
+ if (LinkProperties.isValidMtu(mtu, newLp.hasGlobalIPv6Address())) {
loge("Unexpected mtu value: " + mtu + ", " + iface);
return;
}
+ // Cannot set MTU without interface name
+ if (TextUtils.isEmpty(iface)) {
+ loge("Setting MTU size with null iface.");
+ return;
+ }
+
try {
if (VDBG) log("Setting MTU size: " + iface + ", " + mtu);
mNetd.setMtu(iface, mtu);
diff --git a/services/core/java/com/android/server/NetworkScoreService.java b/services/core/java/com/android/server/NetworkScoreService.java
index ab4d4dc..395e365 100644
--- a/services/core/java/com/android/server/NetworkScoreService.java
+++ b/services/core/java/com/android/server/NetworkScoreService.java
@@ -17,9 +17,9 @@
package com.android.server;
import android.Manifest.permission;
+import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
-import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.net.INetworkScoreCache;
import android.net.INetworkScoreService;
@@ -30,6 +30,7 @@
import android.os.Binder;
import android.os.RemoteException;
import android.os.UserHandle;
+import android.provider.Settings;
import android.text.TextUtils;
import android.util.Log;
@@ -51,9 +52,6 @@
public class NetworkScoreService extends INetworkScoreService.Stub {
private static final String TAG = "NetworkScoreService";
- /** SharedPreference bit set to true after the service is first initialized. */
- private static final String PREF_SCORING_PROVISIONED = "is_provisioned";
-
private final Context mContext;
private final Map<Integer, INetworkScoreCache> mScoreCaches;
@@ -65,8 +63,8 @@
/** Called when the system is ready to run third-party code but before it actually does so. */
void systemReady() {
- SharedPreferences prefs = mContext.getSharedPreferences(TAG, Context.MODE_PRIVATE);
- if (!prefs.getBoolean(PREF_SCORING_PROVISIONED, false)) {
+ ContentResolver cr = mContext.getContentResolver();
+ if (Settings.Global.getInt(cr, Settings.Global.NETWORK_SCORING_PROVISIONED, 0) == 0) {
// On first run, we try to initialize the scorer to the one configured at build time.
// This will be a no-op if the scorer isn't actually valid.
String defaultPackage = mContext.getResources().getString(
@@ -74,7 +72,7 @@
if (!TextUtils.isEmpty(defaultPackage)) {
NetworkScorerAppManager.setActiveScorer(mContext, defaultPackage);
}
- prefs.edit().putBoolean(PREF_SCORING_PROVISIONED, true).apply();
+ Settings.Global.putInt(cr, Settings.Global.NETWORK_SCORING_PROVISIONED, 1);
}
}
diff --git a/core/java/com/android/server/SystemService.java b/services/core/java/com/android/server/SystemService.java
similarity index 100%
rename from core/java/com/android/server/SystemService.java
rename to services/core/java/com/android/server/SystemService.java
diff --git a/core/java/com/android/server/SystemServiceManager.java b/services/core/java/com/android/server/SystemServiceManager.java
similarity index 100%
rename from core/java/com/android/server/SystemServiceManager.java
rename to services/core/java/com/android/server/SystemServiceManager.java
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index ad2704a..ecd8f11 100755
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -2031,7 +2031,7 @@
ApplicationInfo info = mContext.getPackageManager().getApplicationInfo(
"android", STOCK_PM_FLAGS);
- mSystemThread.installSystemApplicationInfo(info);
+ mSystemThread.installSystemApplicationInfo(info, getClass().getClassLoader());
synchronized (this) {
ProcessRecord app = newProcessRecordLocked(info, info.processName, false, 0);
diff --git a/services/core/java/com/android/server/am/LockTaskNotify.java b/services/core/java/com/android/server/am/LockTaskNotify.java
index 6f9b23d..cf65243 100644
--- a/services/core/java/com/android/server/am/LockTaskNotify.java
+++ b/services/core/java/com/android/server/am/LockTaskNotify.java
@@ -19,6 +19,7 @@
import android.content.Context;
import android.os.Handler;
import android.os.Message;
+import android.view.accessibility.AccessibilityManager;
import android.widget.Toast;
import com.android.internal.R;
@@ -32,9 +33,12 @@
private final Context mContext;
private final H mHandler;
+ private AccessibilityManager mAccessibilityManager;
public LockTaskNotify(Context context) {
mContext = context;
+ mAccessibilityManager = (AccessibilityManager)
+ mContext.getSystemService(Context.ACCESSIBILITY_SERVICE);
mHandler = new H();
}
@@ -45,6 +49,9 @@
public void handleShowToast(boolean isLocked) {
String text = mContext.getString(isLocked
? R.string.lock_to_app_toast_locked : R.string.lock_to_app_toast);
+ if (!isLocked && mAccessibilityManager.isEnabled()) {
+ text = mContext.getString(R.string.lock_to_app_toast_accessible);
+ }
Toast.makeText(mContext, text, Toast.LENGTH_LONG).show();
}
diff --git a/services/core/java/com/android/server/am/LockToAppRequestDialog.java b/services/core/java/com/android/server/am/LockToAppRequestDialog.java
index 0847b52..12dcf7e 100644
--- a/services/core/java/com/android/server/am/LockToAppRequestDialog.java
+++ b/services/core/java/com/android/server/am/LockToAppRequestDialog.java
@@ -13,6 +13,7 @@
import android.provider.Settings.SettingNotFoundException;
import android.util.Slog;
import android.view.WindowManager;
+import android.view.accessibility.AccessibilityManager;
import android.widget.CheckBox;
import com.android.internal.R;
@@ -33,8 +34,12 @@
private ILockSettings mLockSettingsService;
+ private AccessibilityManager mAccessibilityService;
+
public LockToAppRequestDialog(Context context, ActivityManagerService activityManagerService) {
mContext = context;
+ mAccessibilityService = (AccessibilityManager)
+ mContext.getSystemService(Context.ACCESSIBILITY_SERVICE);
mService = activityManagerService;
}
@@ -78,7 +83,9 @@
final int unlockStringId = getLockString(task.userId);
final Resources r = Resources.getSystem();
- final String description= r.getString(R.string.lock_to_app_description);
+ final String description= r.getString(mAccessibilityService.isEnabled()
+ ? R.string.lock_to_app_description_accessible
+ : R.string.lock_to_app_description);
AlertDialog.Builder builder = new AlertDialog.Builder(mContext)
.setTitle(r.getString(R.string.lock_to_app_title))
.setMessage(description)
diff --git a/services/core/java/com/android/server/media/MediaSessionRecord.java b/services/core/java/com/android/server/media/MediaSessionRecord.java
index e549ead..f820a3c 100644
--- a/services/core/java/com/android/server/media/MediaSessionRecord.java
+++ b/services/core/java/com/android/server/media/MediaSessionRecord.java
@@ -22,6 +22,7 @@
import android.content.Intent;
import android.content.pm.ParceledListSlice;
import android.media.AudioManager;
+import android.media.AudioManagerInternal;
import android.media.MediaMetadata;
import android.media.Rating;
import android.media.VolumeProvider;
@@ -52,6 +53,8 @@
import android.util.Slog;
import android.view.KeyEvent;
+import com.android.server.LocalServices;
+
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.UUID;
@@ -111,6 +114,7 @@
// Volume handling fields
private AudioAttributes mAudioAttrs;
private AudioManager mAudioManager;
+ private AudioManagerInternal mAudioManagerInternal;
private int mVolumeType = MediaSession.PLAYBACK_TYPE_LOCAL;
private int mVolumeControlType = VolumeProvider.VOLUME_CONTROL_ABSOLUTE;
private int mMaxVolume = 0;
@@ -134,6 +138,7 @@
mService = service;
mHandler = new MessageHandler(handler.getLooper());
mAudioManager = (AudioManager) service.getContext().getSystemService(Context.AUDIO_SERVICE);
+ mAudioManagerInternal = LocalServices.getService(AudioManagerInternal.class);
mAudioAttrs = new AudioAttributes.Builder().setUsage(AudioAttributes.USAGE_MEDIA).build();
}
@@ -227,7 +232,7 @@
*
* @param direction The direction to adjust volume in.
*/
- public void adjustVolume(int direction, int flags) {
+ public void adjustVolume(int direction, int flags, String packageName, int uid) {
if (isPlaybackActive(false)) {
flags &= ~AudioManager.FLAG_PLAY_SOUND;
}
@@ -238,7 +243,8 @@
}
if (mVolumeType == MediaSession.PLAYBACK_TYPE_LOCAL) {
int stream = AudioAttributes.toLegacyStreamType(mAudioAttrs);
- mAudioManager.adjustStreamVolume(stream, direction, flags);
+ mAudioManagerInternal.adjustStreamVolumeForUid(stream, direction, flags, packageName,
+ uid);
} else {
if (mVolumeControlType == VolumeProvider.VOLUME_CONTROL_FIXED) {
// Nothing to do, the volume cannot be changed
@@ -262,10 +268,10 @@
}
}
- public void setVolumeTo(int value, int flags) {
+ public void setVolumeTo(int value, int flags, String packageName, int uid) {
if (mVolumeType == MediaSession.PLAYBACK_TYPE_LOCAL) {
int stream = AudioAttributes.toLegacyStreamType(mAudioAttrs);
- mAudioManager.setStreamVolume(stream, value, flags);
+ mAudioManagerInternal.setStreamVolumeForUid(stream, value, flags, packageName, uid);
} else {
if (mVolumeControlType != VolumeProvider.VOLUME_CONTROL_ABSOLUTE) {
// Nothing to do. The volume can't be set directly.
@@ -397,6 +403,7 @@
return;
}
mDestroyed = true;
+ mHandler.post(MessageHandler.MSG_DESTROYED);
}
}
@@ -575,6 +582,29 @@
}
}
+ private void pushSessionDestroyed() {
+ synchronized (mLock) {
+ // This is the only method that may be (and can only be) called
+ // after the session is destroyed.
+ if (!mDestroyed) {
+ return;
+ }
+ for (int i = mControllerCallbacks.size() - 1; i >= 0; i--) {
+ ISessionControllerCallback cb = mControllerCallbacks.get(i);
+ try {
+ cb.onSessionDestroyed();
+ } catch (DeadObjectException e) {
+ Log.w(TAG, "Removing dead callback in pushEvent.", e);
+ mControllerCallbacks.remove(i);
+ } catch (RemoteException e) {
+ Log.w(TAG, "unexpected exception in pushEvent.", e);
+ }
+ }
+ // After notifying clear all listeners
+ mControllerCallbacks.clear();
+ }
+ }
+
private PlaybackState getStateWithUpdatedPosition() {
PlaybackState state = mPlaybackState;
long duration = -1;
@@ -919,6 +949,16 @@
@Override
public void registerCallbackListener(ISessionControllerCallback cb) {
synchronized (mLock) {
+ // If this session is already destroyed tell the caller and
+ // don't add them.
+ if (mDestroyed) {
+ try {
+ cb.onSessionDestroyed();
+ } catch (Exception e) {
+ // ignored
+ }
+ return;
+ }
if (getControllerCbIndexForCb(cb) < 0) {
mControllerCallbacks.add(cb);
if (DEBUG) {
@@ -984,20 +1024,22 @@
}
@Override
- public void adjustVolume(int direction, int flags) {
+ public void adjustVolume(int direction, int flags, String packageName) {
+ int uid = Binder.getCallingUid();
final long token = Binder.clearCallingIdentity();
try {
- MediaSessionRecord.this.adjustVolume(direction, flags);
+ MediaSessionRecord.this.adjustVolume(direction, flags, packageName, uid);
} finally {
Binder.restoreCallingIdentity(token);
}
}
@Override
- public void setVolumeTo(int value, int flags) {
+ public void setVolumeTo(int value, int flags, String packageName) {
+ int uid = Binder.getCallingUid();
final long token = Binder.clearCallingIdentity();
try {
- MediaSessionRecord.this.setVolumeTo(value, flags);
+ MediaSessionRecord.this.setVolumeTo(value, flags, packageName, uid);
} finally {
Binder.restoreCallingIdentity(token);
}
@@ -1123,6 +1165,7 @@
private static final int MSG_SEND_EVENT = 6;
private static final int MSG_UPDATE_SESSION_STATE = 7;
private static final int MSG_UPDATE_VOLUME = 8;
+ private static final int MSG_DESTROYED = 9;
public MessageHandler(Looper looper) {
super(looper);
@@ -1154,6 +1197,8 @@
case MSG_UPDATE_VOLUME:
pushVolumeUpdate();
break;
+ case MSG_DESTROYED:
+ pushSessionDestroyed();
}
}
diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java
index 0514f48..1221aa4 100644
--- a/services/core/java/com/android/server/media/MediaSessionService.java
+++ b/services/core/java/com/android/server/media/MediaSessionService.java
@@ -812,7 +812,8 @@
Log.e(TAG, "Error adjusting default volume.", e);
}
} else {
- session.adjustVolume(direction, flags);
+ session.adjustVolume(direction, flags, getContext().getPackageName(),
+ UserHandle.myUserId());
if (session.getPlaybackType() == MediaSession.PLAYBACK_TYPE_REMOTE
&& mRvc != null) {
try {
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index ad2bb92..bb5243c 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -1060,9 +1060,11 @@
final NetworkIdentity ident = NetworkIdentity.buildNetworkIdentity(mContext, state);
final String baseIface = state.linkProperties.getInterfaceName();
- connIdents.add(Pair.create(baseIface, ident));
- if (powerSave) {
- connIfaces.add(baseIface);
+ if (baseIface != null) {
+ connIdents.add(Pair.create(baseIface, ident));
+ if (powerSave) {
+ connIfaces.add(baseIface);
+ }
}
// Stacked interfaces are considered to have same identity as
@@ -1070,9 +1072,11 @@
final List<LinkProperties> stackedLinks = state.linkProperties.getStackedLinks();
for (LinkProperties stackedLink : stackedLinks) {
final String stackedIface = stackedLink.getInterfaceName();
- connIdents.add(Pair.create(stackedIface, ident));
- if (powerSave) {
- connIfaces.add(stackedIface);
+ if (stackedIface != null) {
+ connIdents.add(Pair.create(stackedIface, ident));
+ if (powerSave) {
+ connIfaces.add(stackedIface);
+ }
}
}
}
diff --git a/services/core/java/com/android/server/net/NetworkStatsService.java b/services/core/java/com/android/server/net/NetworkStatsService.java
index e35ca46..f995dee 100644
--- a/services/core/java/com/android/server/net/NetworkStatsService.java
+++ b/services/core/java/com/android/server/net/NetworkStatsService.java
@@ -896,10 +896,12 @@
// Traffic occurring on the base interface is always counted for
// both total usage and UID details.
final String baseIface = state.linkProperties.getInterfaceName();
- findOrCreateNetworkIdentitySet(mActiveIfaces, baseIface).add(ident);
- findOrCreateNetworkIdentitySet(mActiveUidIfaces, baseIface).add(ident);
- if (isMobile) {
- mobileIfaces.add(baseIface);
+ if (baseIface != null) {
+ findOrCreateNetworkIdentitySet(mActiveIfaces, baseIface).add(ident);
+ findOrCreateNetworkIdentitySet(mActiveUidIfaces, baseIface).add(ident);
+ if (isMobile) {
+ mobileIfaces.add(baseIface);
+ }
}
// Traffic occurring on stacked interfaces is usually clatd,
@@ -909,15 +911,16 @@
final List<LinkProperties> stackedLinks = state.linkProperties.getStackedLinks();
for (LinkProperties stackedLink : stackedLinks) {
final String stackedIface = stackedLink.getInterfaceName();
- findOrCreateNetworkIdentitySet(mActiveUidIfaces, stackedIface).add(ident);
- if (isMobile) {
- mobileIfaces.add(stackedIface);
+ if (stackedIface != null) {
+ findOrCreateNetworkIdentitySet(mActiveUidIfaces, stackedIface).add(ident);
+ if (isMobile) {
+ mobileIfaces.add(stackedIface);
+ }
}
}
}
}
- mobileIfaces.remove(null);
mMobileIfaces = mobileIfaces.toArray(new String[mobileIfaces.size()]);
}
diff --git a/services/core/java/com/android/server/notification/ConditionProviders.java b/services/core/java/com/android/server/notification/ConditionProviders.java
index a06daf6..189131c 100644
--- a/services/core/java/com/android/server/notification/ConditionProviders.java
+++ b/services/core/java/com/android/server/notification/ConditionProviders.java
@@ -51,8 +51,9 @@
= new ArrayMap<IBinder, IConditionListener>();
private final ArrayList<ConditionRecord> mRecords = new ArrayList<ConditionRecord>();
private final CountdownConditionProvider mCountdown = new CountdownConditionProvider();
+ private final DowntimeConditionProvider mDowntime = new DowntimeConditionProvider();
- private Uri mExitConditionId;
+ private Condition mExitCondition;
private ComponentName mExitConditionComponent;
public ConditionProviders(Context context, Handler handler,
@@ -97,6 +98,7 @@
}
}
mCountdown.dump(pw, filter);
+ mDowntime.dump(pw, filter);
}
@Override
@@ -110,6 +112,10 @@
mCountdown.attachBase(mContext);
registerService(mCountdown.asInterface(), CountdownConditionProvider.COMPONENT,
UserHandle.USER_OWNER);
+ mDowntime.attachBase(mContext);
+ registerService(mDowntime.asInterface(), DowntimeConditionProvider.COMPONENT,
+ UserHandle.USER_OWNER);
+ mDowntime.setCallback(new DowntimeCallback());
}
@Override
@@ -125,7 +131,7 @@
if (info.component.equals(mExitConditionComponent)) {
// ensure record exists, we'll wire it up and subscribe below
final ConditionRecord manualRecord =
- getRecordLocked(mExitConditionId, mExitConditionComponent);
+ getRecordLocked(mExitCondition.id, mExitConditionComponent);
manualRecord.isManual = true;
}
final int N = mRecords.size();
@@ -149,11 +155,11 @@
if (!r.component.equals(removed.component)) continue;
if (r.isManual) {
// removing the current manual condition, exit zen
- mZenModeHelper.setZenMode(Global.ZEN_MODE_OFF);
+ mZenModeHelper.setZenMode(Global.ZEN_MODE_OFF, "manualServiceRemoved");
}
if (r.isAutomatic) {
// removing an automatic condition, exit zen
- mZenModeHelper.setZenMode(Global.ZEN_MODE_OFF);
+ mZenModeHelper.setZenMode(Global.ZEN_MODE_OFF, "automaticServiceRemoved");
}
mRecords.remove(i);
}
@@ -249,7 +255,8 @@
} else if (DEBUG) {
Slog.d(TAG, "Exit zen: manual condition false: " + c);
}
- mZenModeHelper.setZenMode(Settings.Global.ZEN_MODE_OFF);
+ mZenModeHelper.setZenMode(Settings.Global.ZEN_MODE_OFF,
+ "manualConditionExit");
unsubscribeLocked(r);
r.isManual = false;
}
@@ -263,33 +270,46 @@
} else if (DEBUG) {
Slog.d(TAG, "Exit zen: automatic condition false: " + c);
}
- mZenModeHelper.setZenMode(Settings.Global.ZEN_MODE_OFF);
+ mZenModeHelper.setZenMode(Settings.Global.ZEN_MODE_OFF,
+ "automaticConditionExit");
} else if (c.state == Condition.STATE_TRUE) {
Slog.d(TAG, "Enter zen: automatic condition true: " + c);
- mZenModeHelper.setZenMode(Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS);
+ mZenModeHelper.setZenMode(Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS,
+ "automaticConditionEnter");
}
}
}
}
}
- public void setZenModeCondition(Uri conditionId, String reason) {
- if (DEBUG) Slog.d(TAG, "setZenModeCondition " + conditionId);
+ public void setZenModeCondition(Condition condition, String reason) {
+ if (DEBUG) Slog.d(TAG, "setZenModeCondition " + condition);
synchronized(mMutex) {
ComponentName conditionComponent = null;
- if (ZenModeConfig.isValidCountdownConditionId(conditionId)) {
- // constructed by the client, make sure the record exists...
- final ConditionRecord r = getRecordLocked(conditionId,
- CountdownConditionProvider.COMPONENT);
- if (r.info == null) {
- // ... and is associated with the in-process service
- r.info = checkServiceTokenLocked(mCountdown.asInterface());
+ if (condition != null) {
+ if (ZenModeConfig.isValidCountdownConditionId(condition.id)) {
+ // constructed by the client, make sure the record exists...
+ final ConditionRecord r = getRecordLocked(condition.id,
+ CountdownConditionProvider.COMPONENT);
+ if (r.info == null) {
+ // ... and is associated with the in-process service
+ r.info = checkServiceTokenLocked(mCountdown.asInterface());
+ }
+ }
+ if (ZenModeConfig.isValidDowntimeConditionId(condition.id)) {
+ // constructed by the client, make sure the record exists...
+ final ConditionRecord r = getRecordLocked(condition.id,
+ DowntimeConditionProvider.COMPONENT);
+ if (r.info == null) {
+ // ... and is associated with the in-process service
+ r.info = checkServiceTokenLocked(mDowntime.asInterface());
+ }
}
}
final int N = mRecords.size();
for (int i = 0; i < N; i++) {
final ConditionRecord r = mRecords.get(i);
- final boolean idEqual = r.id.equals(conditionId);
+ final boolean idEqual = condition != null && r.id.equals(condition.id);
if (r.isManual && !idEqual) {
// was previous manual condition, unsubscribe
unsubscribeLocked(r);
@@ -303,10 +323,10 @@
conditionComponent = r.component;
}
}
- if (!Objects.equals(mExitConditionId, conditionId)) {
- mExitConditionId = conditionId;
+ if (!Objects.equals(mExitCondition, condition)) {
+ mExitCondition = condition;
mExitConditionComponent = conditionComponent;
- ZenLog.traceExitCondition(mExitConditionId, mExitConditionComponent, reason);
+ ZenLog.traceExitCondition(mExitCondition, mExitConditionComponent, reason);
saveZenConfigLocked();
}
}
@@ -318,6 +338,7 @@
RemoteException re = null;
if (provider != null) {
try {
+ Slog.d(TAG, "Subscribing to " + r.id + " with " + provider);
provider.onSubscribe(r.id);
} catch (RemoteException e) {
Slog.w(TAG, "Error subscribing to " + r, e);
@@ -436,12 +457,13 @@
return;
}
synchronized (mMutex) {
- final boolean changingExit = !Objects.equals(mExitConditionId, config.exitConditionId);
- mExitConditionId = config.exitConditionId;
+ final boolean changingExit = !Objects.equals(mExitCondition, config.exitCondition);
+ mExitCondition = config.exitCondition;
mExitConditionComponent = config.exitConditionComponent;
if (changingExit) {
- ZenLog.traceExitCondition(mExitConditionId, mExitConditionComponent, "config");
+ ZenLog.traceExitCondition(mExitCondition, mExitConditionComponent, "config");
}
+ mDowntime.setConfig(config);
if (config.conditionComponents == null || config.conditionIds == null
|| config.conditionComponents.length != config.conditionIds.length) {
if (DEBUG) Slog.d(TAG, "loadZenConfig: no conditions");
@@ -488,7 +510,7 @@
config.conditionIds[i] = r.id;
}
}
- config.exitConditionId = mExitConditionId;
+ config.exitCondition = mExitCondition;
config.exitConditionComponent = mExitConditionComponent;
if (DEBUG) Slog.d(TAG, "Setting zen config to: " + config);
mZenModeHelper.setConfig(config);
@@ -510,6 +532,26 @@
}
}
+ private class DowntimeCallback implements DowntimeConditionProvider.Callback {
+ @Override
+ public void onDowntimeChanged(boolean inDowntime) {
+ final int mode = mZenModeHelper.getZenMode();
+ final ZenModeConfig config = mZenModeHelper.getConfig();
+ // enter downtime
+ if (inDowntime && mode == Global.ZEN_MODE_OFF && config != null) {
+ final Condition condition = mDowntime.createCondition(config.toDowntimeInfo(),
+ Condition.STATE_TRUE);
+ mZenModeHelper.setZenMode(Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS, "downtimeEnter");
+ setZenModeCondition(condition, "downtime");
+ }
+ // exit downtime
+ if (!inDowntime && mode == Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS
+ && mDowntime.isDowntimeCondition(mExitCondition)) {
+ mZenModeHelper.setZenMode(Global.ZEN_MODE_OFF, "downtimeExit");
+ }
+ }
+ }
+
private static class ConditionRecord {
public final Uri id;
public final ComponentName component;
diff --git a/services/core/java/com/android/server/notification/CountdownConditionProvider.java b/services/core/java/com/android/server/notification/CountdownConditionProvider.java
index aaf7cfc..37aacaa 100644
--- a/services/core/java/com/android/server/notification/CountdownConditionProvider.java
+++ b/services/core/java/com/android/server/notification/CountdownConditionProvider.java
@@ -29,6 +29,7 @@
import android.service.notification.IConditionProvider;
import android.service.notification.ZenModeConfig;
import android.text.format.DateUtils;
+import android.util.Log;
import android.util.Slog;
import com.android.server.notification.NotificationManagerService.DumpFilter;
@@ -38,8 +39,8 @@
/** Built-in zen condition provider for simple time-based conditions */
public class CountdownConditionProvider extends ConditionProviderService {
- private static final String TAG = "CountdownConditionProvider";
- private static final boolean DEBUG = false;
+ private static final String TAG = "CountdownConditions";
+ private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
public static final ComponentName COMPONENT =
new ComponentName("android", CountdownConditionProvider.class.getName());
diff --git a/services/core/java/com/android/server/notification/DowntimeConditionProvider.java b/services/core/java/com/android/server/notification/DowntimeConditionProvider.java
new file mode 100644
index 0000000..317ebef
--- /dev/null
+++ b/services/core/java/com/android/server/notification/DowntimeConditionProvider.java
@@ -0,0 +1,289 @@
+/**
+ * Copyright (c) 2014, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.notification;
+
+import android.app.AlarmManager;
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.net.Uri;
+import android.service.notification.Condition;
+import android.service.notification.ConditionProviderService;
+import android.service.notification.IConditionProvider;
+import android.service.notification.ZenModeConfig;
+import android.service.notification.ZenModeConfig.DowntimeInfo;
+import android.text.format.DateFormat;
+import android.util.ArraySet;
+import android.util.Log;
+import android.util.Slog;
+
+import com.android.internal.R;
+import com.android.server.notification.NotificationManagerService.DumpFilter;
+
+import java.io.PrintWriter;
+import java.text.SimpleDateFormat;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.Locale;
+import java.util.Objects;
+
+/** Built-in zen condition provider for managing downtime */
+public class DowntimeConditionProvider extends ConditionProviderService {
+ private static final String TAG = "DowntimeConditions";
+ private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+
+ public static final ComponentName COMPONENT =
+ new ComponentName("android", DowntimeConditionProvider.class.getName());
+
+ private static final String ENTER_ACTION = TAG + ".enter";
+ private static final int ENTER_CODE = 100;
+ private static final String EXIT_ACTION = TAG + ".exit";
+ private static final int EXIT_CODE = 101;
+ private static final String EXTRA_TIME = "time";
+
+ private final Calendar mCalendar = Calendar.getInstance();
+ private final Context mContext = this;
+ private final ArraySet<Integer> mDays = new ArraySet<Integer>();
+
+ private boolean mConnected;
+ private boolean mInDowntime;
+ private ZenModeConfig mConfig;
+ private Callback mCallback;
+
+ public DowntimeConditionProvider() {
+ if (DEBUG) Slog.d(TAG, "new DowntimeConditionProvider()");
+ }
+
+ public void dump(PrintWriter pw, DumpFilter filter) {
+ pw.println(" DowntimeConditionProvider:");
+ pw.print(" mConnected="); pw.println(mConnected);
+ pw.print(" mInDowntime="); pw.println(mInDowntime);
+ }
+
+ public void attachBase(Context base) {
+ attachBaseContext(base);
+ }
+
+ public IConditionProvider asInterface() {
+ return (IConditionProvider) onBind(null);
+ }
+
+ public void setCallback(Callback callback) {
+ mCallback = callback;
+ }
+
+ @Override
+ public void onConnected() {
+ if (DEBUG) Slog.d(TAG, "onConnected");
+ mConnected = true;
+ final IntentFilter filter = new IntentFilter();
+ filter.addAction(ENTER_ACTION);
+ filter.addAction(EXIT_ACTION);
+ filter.addAction(Intent.ACTION_TIME_CHANGED);
+ filter.addAction(Intent.ACTION_TIMEZONE_CHANGED);
+ mContext.registerReceiver(mReceiver, filter);
+ init();
+ }
+
+ @Override
+ public void onDestroy() {
+ if (DEBUG) Slog.d(TAG, "onDestroy");
+ mConnected = false;
+ }
+
+ @Override
+ public void onRequestConditions(int relevance) {
+ if (DEBUG) Slog.d(TAG, "onRequestConditions relevance=" + relevance);
+ if ((relevance & Condition.FLAG_RELEVANT_NOW) != 0) {
+ if (mInDowntime && mConfig != null) {
+ notifyCondition(createCondition(mConfig.toDowntimeInfo(), Condition.STATE_TRUE));
+ }
+ }
+ }
+
+ @Override
+ public void onSubscribe(Uri conditionId) {
+ if (DEBUG) Slog.d(TAG, "onSubscribe conditionId=" + conditionId);
+ final DowntimeInfo downtime = ZenModeConfig.tryParseDowntimeConditionId(conditionId);
+ if (downtime != null && mConfig != null) {
+ final int state = mConfig.toDowntimeInfo().equals(downtime) && mInDowntime
+ ? Condition.STATE_TRUE : Condition.STATE_FALSE;
+ if (DEBUG) Slog.d(TAG, "notify condition state: " + Condition.stateToString(state));
+ notifyCondition(createCondition(downtime, state));
+ }
+ }
+
+ @Override
+ public void onUnsubscribe(Uri conditionId) {
+ if (DEBUG) Slog.d(TAG, "onUnsubscribe conditionId=" + conditionId);
+ }
+
+ public void setConfig(ZenModeConfig config) {
+ if (Objects.equals(mConfig, config)) return;
+ if (DEBUG) Slog.d(TAG, "setConfig");
+ mConfig = config;
+ if (mConnected) {
+ init();
+ }
+ }
+
+ public boolean isInDowntime() {
+ return mInDowntime;
+ }
+
+ public Condition createCondition(DowntimeInfo downtime, int state) {
+ if (downtime == null) return null;
+ final Uri id = ZenModeConfig.toDowntimeConditionId(downtime);
+ final String skeleton = DateFormat.is24HourFormat(mContext) ? "Hm" : "hma";
+ final Locale locale = Locale.getDefault();
+ final String pattern = DateFormat.getBestDateTimePattern(locale, skeleton);
+ final long time = getTime(System.currentTimeMillis(), downtime.endHour, downtime.endMinute);
+ final String formatted = new SimpleDateFormat(pattern, locale).format(new Date(time));
+ final String summary = mContext.getString(R.string.downtime_condition_summary, formatted);
+ return new Condition(id, summary, "", "", 0, state, Condition.FLAG_RELEVANT_NOW);
+ }
+
+ public boolean isDowntimeCondition(Condition condition) {
+ return condition != null && ZenModeConfig.isValidDowntimeConditionId(condition.id);
+ }
+
+ private void init() {
+ updateDays();
+ reevaluateDowntime();
+ updateAlarms();
+ }
+
+ private void updateDays() {
+ mDays.clear();
+ if (mConfig != null) {
+ final int[] days = ZenModeConfig.tryParseDays(mConfig.sleepMode);
+ for (int i = 0; days != null && i < days.length; i++) {
+ mDays.add(days[i]);
+ }
+ }
+ }
+
+ private boolean isInDowntime(long time) {
+ if (mConfig == null || mDays.size() == 0) return false;
+ final long start = getTime(time, mConfig.sleepStartHour, mConfig.sleepStartMinute);
+ long end = getTime(time, mConfig.sleepEndHour, mConfig.sleepEndMinute);
+ if (start == end) return false;
+ if (end < start) {
+ end = addDays(end, 1);
+ }
+ return isInDowntime(-1, time, start, end) || isInDowntime(0, time, start, end);
+ }
+
+ private boolean isInDowntime(int daysOffset, long time, long start, long end) {
+ final int day = ((getDayOfWeek(time) + daysOffset - 1) % Calendar.SATURDAY) + 1;
+ start = addDays(start, daysOffset);
+ end = addDays(end, daysOffset);
+ return mDays.contains(day) && time >= start && time < end;
+ }
+
+ private void reevaluateDowntime() {
+ final boolean inDowntime = isInDowntime(System.currentTimeMillis());
+ if (DEBUG) Slog.d(TAG, "inDowntime=" + inDowntime);
+ if (inDowntime == mInDowntime) return;
+ Slog.i(TAG, (inDowntime ? "Entering" : "Exiting" ) + " downtime");
+ mInDowntime = inDowntime;
+ ZenLog.traceDowntime(mInDowntime, getDayOfWeek(System.currentTimeMillis()), mDays);
+ fireDowntimeChanged();
+ }
+
+ private void fireDowntimeChanged() {
+ if (mCallback != null) {
+ mCallback.onDowntimeChanged(mInDowntime);
+ }
+ }
+
+ private void updateAlarms() {
+ if (mConfig == null) return;
+ updateAlarm(ENTER_ACTION, ENTER_CODE, mConfig.sleepStartHour, mConfig.sleepStartMinute);
+ updateAlarm(EXIT_ACTION, EXIT_CODE, mConfig.sleepEndHour, mConfig.sleepEndMinute);
+ }
+
+ private int getDayOfWeek(long time) {
+ mCalendar.setTimeInMillis(time);
+ return mCalendar.get(Calendar.DAY_OF_WEEK);
+ }
+
+ private long getTime(long millis, int hour, int min) {
+ mCalendar.setTimeInMillis(millis);
+ mCalendar.set(Calendar.HOUR_OF_DAY, hour);
+ mCalendar.set(Calendar.MINUTE, min);
+ mCalendar.set(Calendar.SECOND, 0);
+ mCalendar.set(Calendar.MILLISECOND, 0);
+ return mCalendar.getTimeInMillis();
+ }
+
+ private long addDays(long time, int days) {
+ mCalendar.setTimeInMillis(time);
+ mCalendar.add(Calendar.DATE, days);
+ return mCalendar.getTimeInMillis();
+ }
+
+ private void updateAlarm(String action, int requestCode, int hr, int min) {
+ final AlarmManager alarms = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
+ final long now = System.currentTimeMillis();
+ mCalendar.setTimeInMillis(now);
+ mCalendar.set(Calendar.HOUR_OF_DAY, hr);
+ mCalendar.set(Calendar.MINUTE, min);
+ mCalendar.set(Calendar.SECOND, 0);
+ mCalendar.set(Calendar.MILLISECOND, 0);
+ long time = mCalendar.getTimeInMillis();
+ if (time <= now) {
+ time = addDays(time, 1);
+ }
+ final PendingIntent pendingIntent = PendingIntent.getBroadcast(mContext, requestCode,
+ new Intent(action).putExtra(EXTRA_TIME, time), PendingIntent.FLAG_UPDATE_CURRENT);
+ alarms.cancel(pendingIntent);
+ if (mConfig.sleepMode != null) {
+ if (DEBUG) Slog.d(TAG, String.format("Scheduling %s for %s, %s in the future, now=%s",
+ action, ts(time), time - now, ts(now)));
+ alarms.setExact(AlarmManager.RTC_WAKEUP, time, pendingIntent);
+ }
+ }
+
+ private static String ts(long time) {
+ return new Date(time) + " (" + time + ")";
+ }
+
+ private BroadcastReceiver mReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ final String action = intent.getAction();
+ final long now = System.currentTimeMillis();
+ if (ENTER_ACTION.equals(action) || EXIT_ACTION.equals(action)) {
+ final long schTime = intent.getLongExtra(EXTRA_TIME, 0);
+ if (DEBUG) Slog.d(TAG, String.format("%s scheduled for %s, fired at %s, delta=%s",
+ action, ts(schTime), ts(now), now - schTime));
+ } else {
+ if (DEBUG) Slog.d(TAG, action + " fired at " + now);
+ }
+ reevaluateDowntime();
+ updateAlarms();
+ }
+ };
+
+ public interface Callback {
+ void onDowntimeChanged(boolean inDowntime);
+ }
+}
diff --git a/services/core/java/com/android/server/notification/ManagedServices.java b/services/core/java/com/android/server/notification/ManagedServices.java
index 36be21f..f647037 100644
--- a/services/core/java/com/android/server/notification/ManagedServices.java
+++ b/services/core/java/com/android/server/notification/ManagedServices.java
@@ -41,6 +41,7 @@
import android.provider.Settings;
import android.text.TextUtils;
import android.util.ArraySet;
+import android.util.Log;
import android.util.Slog;
import android.util.SparseArray;
@@ -64,7 +65,7 @@
*/
abstract public class ManagedServices {
protected final String TAG = getClass().getSimpleName();
- protected static final boolean DEBUG = true;
+ protected final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
private static final String ENABLED_SERVICES_SEPARATOR = ":";
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index d6afe68..f2ac963 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -1351,11 +1351,11 @@
}
@Override
- public void setZenModeCondition(Uri conditionId) {
+ public void setZenModeCondition(Condition condition) {
enforceSystemOrSystemUI("INotificationManager.setZenModeCondition");
final long identity = Binder.clearCallingIdentity();
try {
- mConditionProviders.setZenModeCondition(conditionId, "binderCall");
+ mConditionProviders.setZenModeCondition(condition, "binderCall");
} finally {
Binder.restoreCallingIdentity(identity);
}
diff --git a/services/core/java/com/android/server/notification/ZenLog.java b/services/core/java/com/android/server/notification/ZenLog.java
index b22ed2d..525f5f8 100644
--- a/services/core/java/com/android/server/notification/ZenLog.java
+++ b/services/core/java/com/android/server/notification/ZenLog.java
@@ -22,8 +22,10 @@
import android.os.Build;
import android.os.RemoteException;
import android.provider.Settings.Global;
+import android.service.notification.Condition;
import android.service.notification.IConditionProvider;
import android.service.notification.ZenModeConfig;
+import android.util.ArraySet;
import android.util.Slog;
import java.io.PrintWriter;
@@ -52,13 +54,14 @@
private static final int TYPE_ALLOW_DISABLE = 2;
private static final int TYPE_SET_RINGER_MODE = 3;
private static final int TYPE_DOWNTIME = 4;
- private static final int TYPE_ZEN_MODE = 5;
- private static final int TYPE_EXIT_CONDITION = 6;
- private static final int TYPE_SUBSCRIBE = 7;
- private static final int TYPE_UNSUBSCRIBE = 8;
- private static final int TYPE_CONFIG = 9;
- private static final int TYPE_FOLLOW_RINGER_MODE = 10;
- private static final int TYPE_NOT_INTERCEPTED = 11;
+ private static final int TYPE_SET_ZEN_MODE = 5;
+ private static final int TYPE_UPDATE_ZEN_MODE = 6;
+ private static final int TYPE_EXIT_CONDITION = 7;
+ private static final int TYPE_SUBSCRIBE = 8;
+ private static final int TYPE_UNSUBSCRIBE = 9;
+ private static final int TYPE_CONFIG = 10;
+ private static final int TYPE_FOLLOW_RINGER_MODE = 11;
+ private static final int TYPE_NOT_INTERCEPTED = 12;
private static int sNext;
private static int sSize;
@@ -82,17 +85,20 @@
append(TYPE_SET_RINGER_MODE, ringerModeToString(ringerMode));
}
- public static void traceDowntime(boolean enter, int day, int[] days) {
- append(TYPE_DOWNTIME, enter + ",day=" + day + ",days=" + (days != null ? Arrays.asList(days)
- : null));
+ public static void traceDowntime(boolean inDowntime, int day, ArraySet<Integer> days) {
+ append(TYPE_DOWNTIME, inDowntime + ",day=" + day + ",days=" + days);
+ }
+
+ public static void traceSetZenMode(int mode, String reason) {
+ append(TYPE_SET_ZEN_MODE, zenModeToString(mode) + "," + reason);
}
public static void traceUpdateZenMode(int fromMode, int toMode) {
- append(TYPE_ZEN_MODE, zenModeToString(fromMode) + " -> " + zenModeToString(toMode));
+ append(TYPE_UPDATE_ZEN_MODE, zenModeToString(fromMode) + " -> " + zenModeToString(toMode));
}
- public static void traceExitCondition(Uri id, ComponentName component, String reason) {
- append(TYPE_EXIT_CONDITION, id + "," + componentToString(component) + "," + reason);
+ public static void traceExitCondition(Condition c, ComponentName component, String reason) {
+ append(TYPE_EXIT_CONDITION, c + "," + componentToString(component) + "," + reason);
}
public static void traceSubscribe(Uri uri, IConditionProvider provider, RemoteException e) {
@@ -122,7 +128,8 @@
case TYPE_ALLOW_DISABLE: return "allow_disable";
case TYPE_SET_RINGER_MODE: return "set_ringer_mode";
case TYPE_DOWNTIME: return "downtime";
- case TYPE_ZEN_MODE: return "zen_mode";
+ case TYPE_SET_ZEN_MODE: return "set_zen_mode";
+ case TYPE_UPDATE_ZEN_MODE: return "update_zen_mode";
case TYPE_EXIT_CONDITION: return "exit_condition";
case TYPE_SUBSCRIBE: return "subscribe";
case TYPE_UNSUBSCRIBE: return "unsubscribe";
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index 9282283..758f334 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -20,10 +20,8 @@
import static android.media.AudioAttributes.USAGE_NOTIFICATION_RINGTONE;
import static android.media.AudioAttributes.USAGE_UNKNOWN;
-import android.app.AlarmManager;
import android.app.AppOpsManager;
import android.app.Notification;
-import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.ContentResolver;
@@ -44,6 +42,7 @@
import android.service.notification.NotificationListenerService;
import android.service.notification.ZenModeConfig;
import android.telecomm.TelecommManager;
+import android.util.Log;
import android.util.Slog;
import com.android.internal.R;
@@ -57,8 +56,6 @@
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
-import java.util.Calendar;
-import java.util.Date;
import java.util.Objects;
/**
@@ -66,12 +63,7 @@
*/
public class ZenModeHelper {
private static final String TAG = "ZenModeHelper";
-
- private static final String ACTION_ENTER_ZEN = "enter_zen";
- private static final int REQUEST_CODE_ENTER = 100;
- private static final String ACTION_EXIT_ZEN = "exit_zen";
- private static final int REQUEST_CODE_EXIT = 101;
- private static final String EXTRA_TIME = "time";
+ private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
private final Context mContext;
private final Handler mHandler;
@@ -96,10 +88,8 @@
mSettingsObserver.observe();
final IntentFilter filter = new IntentFilter();
- filter.addAction(ACTION_ENTER_ZEN);
- filter.addAction(ACTION_EXIT_ZEN);
filter.addAction(AudioManager.RINGER_MODE_CHANGED_ACTION);
- mContext.registerReceiver(new ZenBroadcastReceiver(), filter);
+ mContext.registerReceiver(mReceiver, filter);
}
public static ZenModeConfig readDefaultConfig(Resources resources) {
@@ -156,7 +146,7 @@
public void requestFromListener(int hints) {
final int newZen = zenFromListenerHint(hints, -1);
if (newZen != -1) {
- setZenMode(newZen);
+ setZenMode(newZen, "listener");
}
}
@@ -179,24 +169,19 @@
return false;
}
}
- // audience has veto power over all following rules
- if (!audienceMatches(record)) {
- ZenLog.traceIntercepted(record, "!audienceMatches");
- return true;
- }
if (isCall(record)) {
if (!mConfig.allowCalls) {
ZenLog.traceIntercepted(record, "!allowCalls");
return true;
}
- return false;
+ return shouldInterceptAudience(record);
}
if (isMessage(record)) {
if (!mConfig.allowMessages) {
ZenLog.traceIntercepted(record, "!allowMessages");
return true;
}
- return false;
+ return shouldInterceptAudience(record);
}
ZenLog.traceIntercepted(record, "!allowed");
return true;
@@ -204,11 +189,20 @@
return false;
}
+ private boolean shouldInterceptAudience(NotificationRecord record) {
+ if (!audienceMatches(record)) {
+ ZenLog.traceIntercepted(record, "!audienceMatches");
+ return true;
+ }
+ return false;
+ }
+
public int getZenMode() {
return mZenMode;
}
- public void setZenMode(int zenModeValue) {
+ public void setZenMode(int zenModeValue, String reason) {
+ ZenLog.traceSetZenMode(zenModeValue, reason);
Global.putInt(mContext.getContentResolver(), Global.ZEN_MODE, zenModeValue);
}
@@ -216,9 +210,6 @@
final int mode = Global.getInt(mContext.getContentResolver(),
Global.ZEN_MODE, Global.ZEN_MODE_OFF);
if (mode != mZenMode) {
- Slog.d(TAG, String.format("updateZenMode: %s -> %s",
- Global.zenModeToString(mZenMode),
- Global.zenModeToString(mode)));
ZenLog.traceUpdateZenMode(mZenMode, mode);
}
mZenMode = mode;
@@ -255,12 +246,12 @@
if (mZenMode == Global.ZEN_MODE_NO_INTERRUPTIONS) {
if (ringerMode != AudioManager.RINGER_MODE_SILENT) {
mPreviousRingerMode = ringerMode;
- Slog.d(TAG, "Silencing ringer");
+ if (DEBUG) Slog.d(TAG, "Silencing ringer");
forcedRingerMode = AudioManager.RINGER_MODE_SILENT;
}
} else {
if (ringerMode == AudioManager.RINGER_MODE_SILENT) {
- Slog.d(TAG, "Unsilencing ringer");
+ if (DEBUG) Slog.d(TAG, "Unsilencing ringer");
forcedRingerMode = mPreviousRingerMode != -1 ? mPreviousRingerMode
: AudioManager.RINGER_MODE_NORMAL;
mPreviousRingerMode = -1;
@@ -318,7 +309,6 @@
dispatchOnConfigChanged();
final String val = Integer.toString(mConfig.hashCode());
Global.putString(mContext.getContentResolver(), Global.ZEN_MODE_CONFIG_ETAG, val);
- updateAlarms();
updateZenMode();
return true;
}
@@ -339,7 +329,7 @@
}
if (newZen != -1) {
ZenLog.traceFollowRingerMode(ringerMode, mZenMode, newZen);
- setZenMode(newZen);
+ setZenMode(newZen, "ringerMode");
}
}
}
@@ -377,7 +367,7 @@
final TelecommManager telecomm =
(TelecommManager) mContext.getSystemService(Context.TELECOMM_SERVICE);
mDefaultPhoneApp = telecomm != null ? telecomm.getDefaultPhoneApp() : null;
- Slog.d(TAG, "Default phone app: " + mDefaultPhoneApp);
+ if (DEBUG) Slog.d(TAG, "Default phone app: " + mDefaultPhoneApp);
}
return pkg != null && mDefaultPhoneApp != null
&& pkg.equals(mDefaultPhoneApp.getPackageName());
@@ -409,40 +399,6 @@
}
}
- private void updateAlarms() {
- updateAlarm(ACTION_ENTER_ZEN, REQUEST_CODE_ENTER,
- mConfig.sleepStartHour, mConfig.sleepStartMinute);
- updateAlarm(ACTION_EXIT_ZEN, REQUEST_CODE_EXIT,
- mConfig.sleepEndHour, mConfig.sleepEndMinute);
- }
-
- private void updateAlarm(String action, int requestCode, int hr, int min) {
- final AlarmManager alarms = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
- final long now = System.currentTimeMillis();
- final Calendar c = Calendar.getInstance();
- c.setTimeInMillis(now);
- c.set(Calendar.HOUR_OF_DAY, hr);
- c.set(Calendar.MINUTE, min);
- c.set(Calendar.SECOND, 0);
- c.set(Calendar.MILLISECOND, 0);
- if (c.getTimeInMillis() <= now) {
- c.add(Calendar.DATE, 1);
- }
- final long time = c.getTimeInMillis();
- final PendingIntent pendingIntent = PendingIntent.getBroadcast(mContext, requestCode,
- new Intent(action).putExtra(EXTRA_TIME, time), PendingIntent.FLAG_UPDATE_CURRENT);
- alarms.cancel(pendingIntent);
- if (mConfig.sleepMode != null) {
- Slog.d(TAG, String.format("Scheduling %s for %s, %s in the future, now=%s",
- action, ts(time), time - now, ts(now)));
- alarms.setExact(AlarmManager.RTC_WAKEUP, time, pendingIntent);
- }
- }
-
- private static String ts(long time) {
- return new Date(time) + " (" + time + ")";
- }
-
private final Runnable mRingerModeChanged = new Runnable() {
@Override
public void run() {
@@ -475,47 +431,12 @@
}
}
- private class ZenBroadcastReceiver extends BroadcastReceiver {
- private final Calendar mCalendar = Calendar.getInstance();
-
+ private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
- if (ACTION_ENTER_ZEN.equals(intent.getAction())) {
- setZenMode(intent, Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS);
- } else if (ACTION_EXIT_ZEN.equals(intent.getAction())) {
- setZenMode(intent, Global.ZEN_MODE_OFF);
- } else if (AudioManager.RINGER_MODE_CHANGED_ACTION.equals(intent.getAction())) {
- mHandler.post(mRingerModeChanged);
- }
+ mHandler.post(mRingerModeChanged);
}
-
- private void setZenMode(Intent intent, int zenModeValue) {
- final long schTime = intent.getLongExtra(EXTRA_TIME, 0);
- final long now = System.currentTimeMillis();
- Slog.d(TAG, String.format("%s scheduled for %s, fired at %s, delta=%s",
- intent.getAction(), ts(schTime), ts(now), now - schTime));
-
- final int[] days = ZenModeConfig.tryParseDays(mConfig.sleepMode);
- boolean enter = false;
- final int day = getDayOfWeek(schTime);
- if (days != null) {
- for (int i = 0; i < days.length; i++) {
- if (days[i] == day) {
- enter = true;
- ZenModeHelper.this.setZenMode(zenModeValue);
- break;
- }
- }
- }
- ZenLog.traceDowntime(enter, day, days);
- updateAlarms();
- }
-
- private int getDayOfWeek(long time) {
- mCalendar.setTimeInMillis(time);
- return mCalendar.get(Calendar.DAY_OF_WEEK);
- }
- }
+ };
public static class Callback {
void onConfigChanged() {}
diff --git a/services/core/java/com/android/server/pm/Installer.java b/services/core/java/com/android/server/pm/Installer.java
index b261ef5..d1e03ec 100644
--- a/services/core/java/com/android/server/pm/Installer.java
+++ b/services/core/java/com/android/server/pm/Installer.java
@@ -16,33 +16,23 @@
package com.android.server.pm;
-import com.android.server.SystemService;
-
import android.content.Context;
import android.content.pm.PackageStats;
-import android.net.LocalSocket;
-import android.net.LocalSocketAddress;
+import android.os.Build;
import android.util.Slog;
+import dalvik.system.VMRuntime;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.util.List;
+import com.android.internal.os.InstallerConnection;
+import com.android.server.SystemService;
public final class Installer extends SystemService {
private static final String TAG = "Installer";
- private static final boolean LOCAL_DEBUG = false;
-
- InputStream mIn;
- OutputStream mOut;
- LocalSocket mSocket;
-
- byte buf[] = new byte[1024];
- int buflen = 0;
+ private final InstallerConnection mInstaller;
public Installer(Context context) {
super(context);
+ mInstaller = new InstallerConnection();
}
@Override
@@ -51,154 +41,6 @@
ping();
}
- private boolean connect() {
- if (mSocket != null) {
- return true;
- }
- Slog.i(TAG, "connecting...");
- try {
- mSocket = new LocalSocket();
-
- LocalSocketAddress address = new LocalSocketAddress("installd",
- LocalSocketAddress.Namespace.RESERVED);
-
- mSocket.connect(address);
-
- mIn = mSocket.getInputStream();
- mOut = mSocket.getOutputStream();
- } catch (IOException ex) {
- disconnect();
- return false;
- }
- return true;
- }
-
- private void disconnect() {
- Slog.i(TAG, "disconnecting...");
- try {
- if (mSocket != null)
- mSocket.close();
- } catch (IOException ex) {
- }
- try {
- if (mIn != null)
- mIn.close();
- } catch (IOException ex) {
- }
- try {
- if (mOut != null)
- mOut.close();
- } catch (IOException ex) {
- }
- mSocket = null;
- mIn = null;
- mOut = null;
- }
-
- private boolean readBytes(byte buffer[], int len) {
- int off = 0, count;
- if (len < 0)
- return false;
- while (off != len) {
- try {
- count = mIn.read(buffer, off, len - off);
- if (count <= 0) {
- Slog.e(TAG, "read error " + count);
- break;
- }
- off += count;
- } catch (IOException ex) {
- Slog.e(TAG, "read exception");
- break;
- }
- }
- if (LOCAL_DEBUG) {
- Slog.i(TAG, "read " + len + " bytes");
- }
- if (off == len)
- return true;
- disconnect();
- return false;
- }
-
- private boolean readReply() {
- int len;
- buflen = 0;
- if (!readBytes(buf, 2))
- return false;
- len = (((int) buf[0]) & 0xff) | ((((int) buf[1]) & 0xff) << 8);
- if ((len < 1) || (len > 1024)) {
- Slog.e(TAG, "invalid reply length (" + len + ")");
- disconnect();
- return false;
- }
- if (!readBytes(buf, len))
- return false;
- buflen = len;
- return true;
- }
-
- private boolean writeCommand(String _cmd) {
- byte[] cmd = _cmd.getBytes();
- int len = cmd.length;
- if ((len < 1) || (len > 1024))
- return false;
- buf[0] = (byte) (len & 0xff);
- buf[1] = (byte) ((len >> 8) & 0xff);
- try {
- mOut.write(buf, 0, 2);
- mOut.write(cmd, 0, len);
- } catch (IOException ex) {
- Slog.e(TAG, "write error");
- disconnect();
- return false;
- }
- return true;
- }
-
- private synchronized String transaction(String cmd) {
- if (!connect()) {
- Slog.e(TAG, "connection failed");
- return "-1";
- }
-
- if (!writeCommand(cmd)) {
- /*
- * If installd died and restarted in the background (unlikely but
- * possible) we'll fail on the next write (this one). Try to
- * reconnect and write the command one more time before giving up.
- */
- Slog.e(TAG, "write command failed? reconnect!");
- if (!connect() || !writeCommand(cmd)) {
- return "-1";
- }
- }
- if (LOCAL_DEBUG) {
- Slog.i(TAG, "send: '" + cmd + "'");
- }
- if (readReply()) {
- String s = new String(buf, 0, buflen);
- if (LOCAL_DEBUG) {
- Slog.i(TAG, "recv: '" + s + "'");
- }
- return s;
- } else {
- if (LOCAL_DEBUG) {
- Slog.i(TAG, "fail");
- }
- return "-1";
- }
- }
-
- private int execute(String cmd) {
- String res = transaction(cmd);
- try {
- return Integer.parseInt(res);
- } catch (NumberFormatException ex) {
- return -1;
- }
- }
-
public int install(String name, int uid, int gid, String seinfo) {
StringBuilder builder = new StringBuilder("install");
builder.append(' ');
@@ -209,11 +51,16 @@
builder.append(gid);
builder.append(' ');
builder.append(seinfo != null ? seinfo : "!");
- return execute(builder.toString());
+ return mInstaller.execute(builder.toString());
}
public int patchoat(String apkPath, int uid, boolean isPublic, String pkgName,
String instructionSet) {
+ if (!isValidInstructionSet(instructionSet)) {
+ Slog.e(TAG, "Invalid instruction set: " + instructionSet);
+ return -1;
+ }
+
StringBuilder builder = new StringBuilder("patchoat");
builder.append(' ');
builder.append(apkPath);
@@ -224,37 +71,34 @@
builder.append(pkgName);
builder.append(' ');
builder.append(instructionSet);
- return execute(builder.toString());
+ return mInstaller.execute(builder.toString());
}
public int patchoat(String apkPath, int uid, boolean isPublic, String instructionSet) {
- StringBuilder builder = new StringBuilder("patchoat");
- builder.append(' ');
- builder.append(apkPath);
- builder.append(' ');
- builder.append(uid);
- builder.append(isPublic ? " 1" : " 0");
- builder.append(" *"); // No pkgName arg present
- builder.append(' ');
- builder.append(instructionSet);
- return execute(builder.toString());
+ if (!isValidInstructionSet(instructionSet)) {
+ Slog.e(TAG, "Invalid instruction set: " + instructionSet);
+ return -1;
+ }
+
+ return mInstaller.patchoat(apkPath, uid, isPublic, instructionSet);
}
public int dexopt(String apkPath, int uid, boolean isPublic, String instructionSet) {
- StringBuilder builder = new StringBuilder("dexopt");
- builder.append(' ');
- builder.append(apkPath);
- builder.append(' ');
- builder.append(uid);
- builder.append(isPublic ? " 1" : " 0");
- builder.append(" *"); // No pkgName arg present
- builder.append(' ');
- builder.append(instructionSet);
- return execute(builder.toString());
+ if (!isValidInstructionSet(instructionSet)) {
+ Slog.e(TAG, "Invalid instruction set: " + instructionSet);
+ return -1;
+ }
+
+ return mInstaller.dexopt(apkPath, uid, isPublic, instructionSet);
}
public int dexopt(String apkPath, int uid, boolean isPublic, String pkgName,
String instructionSet) {
+ if (!isValidInstructionSet(instructionSet)) {
+ Slog.e(TAG, "Invalid instruction set: " + instructionSet);
+ return -1;
+ }
+
StringBuilder builder = new StringBuilder("dexopt");
builder.append(' ');
builder.append(apkPath);
@@ -265,7 +109,7 @@
builder.append(pkgName);
builder.append(' ');
builder.append(instructionSet);
- return execute(builder.toString());
+ return mInstaller.execute(builder.toString());
}
public int idmap(String targetApkPath, String overlayApkPath, int uid) {
@@ -276,10 +120,15 @@
builder.append(overlayApkPath);
builder.append(' ');
builder.append(uid);
- return execute(builder.toString());
+ return mInstaller.execute(builder.toString());
}
public int movedex(String srcPath, String dstPath, String instructionSet) {
+ if (!isValidInstructionSet(instructionSet)) {
+ Slog.e(TAG, "Invalid instruction set: " + instructionSet);
+ return -1;
+ }
+
StringBuilder builder = new StringBuilder("movedex");
builder.append(' ');
builder.append(srcPath);
@@ -287,16 +136,21 @@
builder.append(dstPath);
builder.append(' ');
builder.append(instructionSet);
- return execute(builder.toString());
+ return mInstaller.execute(builder.toString());
}
public int rmdex(String codePath, String instructionSet) {
+ if (!isValidInstructionSet(instructionSet)) {
+ Slog.e(TAG, "Invalid instruction set: " + instructionSet);
+ return -1;
+ }
+
StringBuilder builder = new StringBuilder("rmdex");
builder.append(' ');
builder.append(codePath);
builder.append(' ');
builder.append(instructionSet);
- return execute(builder.toString());
+ return mInstaller.execute(builder.toString());
}
public int remove(String name, int userId) {
@@ -305,7 +159,7 @@
builder.append(name);
builder.append(' ');
builder.append(userId);
- return execute(builder.toString());
+ return mInstaller.execute(builder.toString());
}
public int rename(String oldname, String newname) {
@@ -314,7 +168,7 @@
builder.append(oldname);
builder.append(' ');
builder.append(newname);
- return execute(builder.toString());
+ return mInstaller.execute(builder.toString());
}
public int fixUid(String name, int uid, int gid) {
@@ -325,7 +179,7 @@
builder.append(uid);
builder.append(' ');
builder.append(gid);
- return execute(builder.toString());
+ return mInstaller.execute(builder.toString());
}
public int deleteCacheFiles(String name, int userId) {
@@ -334,7 +188,7 @@
builder.append(name);
builder.append(' ');
builder.append(userId);
- return execute(builder.toString());
+ return mInstaller.execute(builder.toString());
}
public int deleteCodeCacheFiles(String name, int userId) {
@@ -343,7 +197,7 @@
builder.append(name);
builder.append(' ');
builder.append(userId);
- return execute(builder.toString());
+ return mInstaller.execute(builder.toString());
}
public int createUserData(String name, int uid, int userId, String seinfo) {
@@ -356,21 +210,21 @@
builder.append(userId);
builder.append(' ');
builder.append(seinfo != null ? seinfo : "!");
- return execute(builder.toString());
+ return mInstaller.execute(builder.toString());
}
public int createUserConfig(int userId) {
StringBuilder builder = new StringBuilder("mkuserconfig");
builder.append(' ');
builder.append(userId);
- return execute(builder.toString());
+ return mInstaller.execute(builder.toString());
}
public int removeUserDataDirs(int userId) {
StringBuilder builder = new StringBuilder("rmuser");
builder.append(' ');
builder.append(userId);
- return execute(builder.toString());
+ return mInstaller.execute(builder.toString());
}
public int clearUserData(String name, int userId) {
@@ -379,11 +233,11 @@
builder.append(name);
builder.append(' ');
builder.append(userId);
- return execute(builder.toString());
+ return mInstaller.execute(builder.toString());
}
public boolean ping() {
- if (execute("ping") < 0) {
+ if (mInstaller.execute("ping") < 0) {
return false;
} else {
return true;
@@ -391,18 +245,25 @@
}
public int pruneDexCache(String cacheSubDir) {
- return execute("prunedexcache " + cacheSubDir);
+ return mInstaller.execute("prunedexcache " + cacheSubDir);
}
public int freeCache(long freeStorageSize) {
StringBuilder builder = new StringBuilder("freecache");
builder.append(' ');
builder.append(String.valueOf(freeStorageSize));
- return execute(builder.toString());
+ return mInstaller.execute(builder.toString());
}
public int getSizeInfo(String pkgName, int persona, String apkPath, String libDirPath,
String fwdLockApkPath, String asecPath, String[] instructionSets, PackageStats pStats) {
+ for (String instructionSet : instructionSets) {
+ if (!isValidInstructionSet(instructionSet)) {
+ Slog.e(TAG, "Invalid instruction set: " + instructionSet);
+ return -1;
+ }
+ }
+
StringBuilder builder = new StringBuilder("getsize");
builder.append(' ');
builder.append(pkgName);
@@ -423,7 +284,7 @@
// just the primary.
builder.append(instructionSets[0]);
- String s = transaction(builder.toString());
+ String s = mInstaller.transact(builder.toString());
String res[] = s.split(" ");
if ((res == null) || (res.length != 5)) {
@@ -441,7 +302,7 @@
}
public int moveFiles() {
- return execute("movefiles");
+ return mInstaller.execute("movefiles");
}
/**
@@ -467,7 +328,7 @@
builder.append(' ');
builder.append(userId);
- return execute(builder.toString());
+ return mInstaller.execute(builder.toString());
}
public boolean restoreconData(String pkgName, String seinfo, int uid) {
@@ -478,6 +339,23 @@
builder.append(seinfo != null ? seinfo : "!");
builder.append(' ');
builder.append(uid);
- return (execute(builder.toString()) == 0);
+ return (mInstaller.execute(builder.toString()) == 0);
+ }
+
+ /**
+ * Returns true iff. {@code instructionSet} is a valid instruction set.
+ */
+ private static boolean isValidInstructionSet(String instructionSet) {
+ if (instructionSet == null) {
+ return false;
+ }
+
+ for (String abi : Build.SUPPORTED_ABIS) {
+ if (instructionSet.equals(VMRuntime.getInstructionSet(abi))) {
+ return true;
+ }
+ }
+
+ return false;
}
}
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index c7e3fb7..dca8ad4 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -36,19 +36,23 @@
import android.app.ActivityManager;
import android.app.AppOpsManager;
+import android.app.PackageDeleteObserver;
+import android.app.PackageInstallObserver;
import android.content.Context;
import android.content.Intent;
-import android.content.pm.IPackageDeleteObserver2;
+import android.content.IntentSender;
+import android.content.IntentSender.SendIntentException;
import android.content.pm.IPackageInstaller;
import android.content.pm.IPackageInstallerCallback;
import android.content.pm.IPackageInstallerSession;
-import android.content.pm.InstallSessionInfo;
-import android.content.pm.InstallSessionParams;
import android.content.pm.PackageInstaller;
+import android.content.pm.PackageInstaller.SessionInfo;
+import android.content.pm.PackageInstaller.SessionParams;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.net.Uri;
import android.os.Binder;
+import android.os.Bundle;
import android.os.Environment;
import android.os.FileUtils;
import android.os.Handler;
@@ -63,6 +67,7 @@
import android.os.UserManager;
import android.system.ErrnoException;
import android.system.Os;
+import android.text.TextUtils;
import android.text.format.DateUtils;
import android.util.ArraySet;
import android.util.AtomicFile;
@@ -279,8 +284,8 @@
final File sessionStageDir = new File(readStringAttribute(in, ATTR_SESSION_STAGE_DIR));
final boolean sealed = readBooleanAttribute(in, ATTR_SEALED);
- final InstallSessionParams params = new InstallSessionParams(
- InstallSessionParams.MODE_INVALID);
+ final SessionParams params = new SessionParams(
+ SessionParams.MODE_INVALID);
params.mode = readIntAttribute(in, ATTR_MODE);
params.installFlags = readIntAttribute(in, ATTR_INSTALL_FLAGS);
params.installLocation = readIntAttribute(in, ATTR_INSTALL_LOCATION);
@@ -292,9 +297,9 @@
params.referrerUri = readUriAttribute(in, ATTR_REFERRER_URI);
params.abiOverride = readStringAttribute(in, ATTR_ABI_OVERRIDE);
- return new PackageInstallerSession(mInternalCallback, mPm, mInstallThread.getLooper(),
- sessionId, userId, installerPackageName, params, createdMillis, sessionStageDir,
- sealed);
+ return new PackageInstallerSession(mInternalCallback, mContext, mPm,
+ mInstallThread.getLooper(), sessionId, userId, installerPackageName, params,
+ createdMillis, sessionStageDir, sealed);
}
private void writeSessionsLocked() {
@@ -326,7 +331,7 @@
private void writeSessionLocked(XmlSerializer out, PackageInstallerSession session)
throws IOException {
- final InstallSessionParams params = session.params;
+ final SessionParams params = session.params;
final Snapshot snapshot = session.snapshot();
out.startTag(null, TAG_SESSION);
@@ -366,7 +371,7 @@
}
@Override
- public int createSession(InstallSessionParams params, String installerPackageName, int userId) {
+ public int createSession(SessionParams params, String installerPackageName, int userId) {
final int callingUid = Binder.getCallingUid();
mPm.enforceCrossUserPermission(callingUid, userId, true, "createSession");
@@ -389,8 +394,8 @@
}
switch (params.mode) {
- case InstallSessionParams.MODE_FULL_INSTALL:
- case InstallSessionParams.MODE_INHERIT_EXISTING:
+ case SessionParams.MODE_FULL_INSTALL:
+ case SessionParams.MODE_INHERIT_EXISTING:
break;
default:
throw new IllegalArgumentException("Params must have valid mode set");
@@ -437,7 +442,7 @@
final long createdMillis = System.currentTimeMillis();
final File sessionStageDir = prepareSessionStageDir(sessionId);
- session = new PackageInstallerSession(mInternalCallback, mPm,
+ session = new PackageInstallerSession(mInternalCallback, mContext, mPm,
mInstallThread.getLooper(), sessionId, userId, installerPackageName, params,
createdMillis, sessionStageDir, false);
mSessions.put(sessionId, session);
@@ -501,7 +506,7 @@
}
@Override
- public InstallSessionInfo getSessionInfo(int sessionId) {
+ public SessionInfo getSessionInfo(int sessionId) {
synchronized (mSessions) {
final PackageInstallerSession session = mSessions.get(sessionId);
if (!isCallingUidOwner(session)) {
@@ -512,11 +517,11 @@
}
@Override
- public List<InstallSessionInfo> getAllSessions(int userId) {
+ public List<SessionInfo> getAllSessions(int userId) {
mPm.enforceCrossUserPermission(Binder.getCallingUid(), userId, true, "getAllSessions");
enforceCallerCanReadSessions();
- final List<InstallSessionInfo> result = new ArrayList<>();
+ final List<SessionInfo> result = new ArrayList<>();
synchronized (mSessions) {
for (int i = 0; i < mSessions.size(); i++) {
final PackageInstallerSession session = mSessions.valueAt(i);
@@ -529,11 +534,11 @@
}
@Override
- public List<InstallSessionInfo> getMySessions(String installerPackageName, int userId) {
+ public List<SessionInfo> getMySessions(String installerPackageName, int userId) {
mPm.enforceCrossUserPermission(Binder.getCallingUid(), userId, true, "getMySessions");
mAppOps.checkPackage(Binder.getCallingUid(), installerPackageName);
- final List<InstallSessionInfo> result = new ArrayList<>();
+ final List<SessionInfo> result = new ArrayList<>();
synchronized (mSessions) {
for (int i = 0; i < mSessions.size(); i++) {
final PackageInstallerSession session = mSessions.valueAt(i);
@@ -547,37 +552,26 @@
}
@Override
- public void uninstall(String packageName, int flags, IPackageDeleteObserver2 observer,
- int userId) {
+ public void uninstall(String packageName, int flags, IntentSender statusReceiver, int userId) {
mPm.enforceCrossUserPermission(Binder.getCallingUid(), userId, true, "uninstall");
+ final PackageDeleteObserverAdapter adapter = new PackageDeleteObserverAdapter(mContext,
+ statusReceiver);
if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DELETE_PACKAGES)
== PackageManager.PERMISSION_GRANTED) {
// Sweet, call straight through!
- mPm.deletePackage(packageName, observer, userId, flags);
+ mPm.deletePackage(packageName, adapter.getBinder(), userId, flags);
} else {
// Take a short detour to confirm with user
final Intent intent = new Intent(Intent.ACTION_UNINSTALL_PACKAGE);
intent.setData(Uri.fromParts("package", packageName, null));
- intent.putExtra(PackageInstaller.EXTRA_CALLBACK, observer.asBinder());
- try {
- observer.onUserActionRequired(intent);
- } catch (RemoteException ignored) {
- }
+ intent.putExtra(PackageInstaller.EXTRA_CALLBACK, adapter.getBinder().asBinder());
+ adapter.onUserActionRequired(intent);
}
}
@Override
- public void uninstallSplit(String basePackageName, String overlayName, int flags,
- IPackageDeleteObserver2 observer, int userId) {
- mPm.enforceCrossUserPermission(Binder.getCallingUid(), userId, true, "uninstallSplit");
-
- // TODO: flesh out once PM has split support
- throw new UnsupportedOperationException();
- }
-
- @Override
public void setPermissionsResult(int sessionId, boolean accepted) {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.INSTALL_PACKAGES, TAG);
@@ -636,6 +630,87 @@
}
}
+ static class PackageDeleteObserverAdapter extends PackageDeleteObserver {
+ private final Context mContext;
+ private final IntentSender mTarget;
+
+ public PackageDeleteObserverAdapter(Context context, IntentSender target) {
+ mContext = context;
+ mTarget = target;
+ }
+
+ @Override
+ public void onUserActionRequired(Intent intent) {
+ final Intent fillIn = new Intent();
+ fillIn.putExtra(PackageInstaller.EXTRA_STATUS,
+ PackageInstaller.STATUS_USER_ACTION_REQUIRED);
+ fillIn.putExtra(Intent.EXTRA_INTENT, intent);
+ try {
+ mTarget.sendIntent(mContext, 0, fillIn, null, null);
+ } catch (SendIntentException ignored) {
+ }
+ }
+
+ @Override
+ public void onPackageDeleted(String basePackageName, int returnCode, String msg) {
+ final Intent fillIn = new Intent();
+ fillIn.putExtra(PackageInstaller.EXTRA_STATUS,
+ PackageManager.deleteStatusToPublicStatus(returnCode));
+ fillIn.putExtra(PackageInstaller.EXTRA_STATUS_MESSAGE,
+ PackageManager.deleteStatusToString(returnCode, msg));
+ fillIn.putExtra(PackageInstaller.EXTRA_LEGACY_STATUS, returnCode);
+ try {
+ mTarget.sendIntent(mContext, 0, fillIn, null, null);
+ } catch (SendIntentException ignored) {
+ }
+ }
+ }
+
+ static class PackageInstallObserverAdapter extends PackageInstallObserver {
+ private final Context mContext;
+ private final IntentSender mTarget;
+
+ public PackageInstallObserverAdapter(Context context, IntentSender target) {
+ mContext = context;
+ mTarget = target;
+ }
+
+ @Override
+ public void onUserActionRequired(Intent intent) {
+ final Intent fillIn = new Intent();
+ fillIn.putExtra(PackageInstaller.EXTRA_STATUS,
+ PackageInstaller.STATUS_USER_ACTION_REQUIRED);
+ fillIn.putExtra(Intent.EXTRA_INTENT, intent);
+ try {
+ mTarget.sendIntent(mContext, 0, fillIn, null, null);
+ } catch (SendIntentException ignored) {
+ }
+ }
+
+ @Override
+ public void onPackageInstalled(String basePackageName, int returnCode, String msg,
+ Bundle extras) {
+ final Intent fillIn = new Intent();
+ fillIn.putExtra(PackageInstaller.EXTRA_STATUS,
+ PackageManager.installStatusToPublicStatus(returnCode));
+ fillIn.putExtra(PackageInstaller.EXTRA_STATUS_MESSAGE,
+ PackageManager.installStatusToString(returnCode, msg));
+ fillIn.putExtra(PackageInstaller.EXTRA_LEGACY_STATUS, returnCode);
+ if (extras != null) {
+ final String existing = extras.getString(
+ PackageManager.EXTRA_FAILURE_EXISTING_PACKAGE);
+ if (!TextUtils.isEmpty(existing)) {
+ fillIn.putExtra(PackageInstaller.EXTRA_PACKAGE_NAMES, new String[] {
+ existing });
+ }
+ }
+ try {
+ mTarget.sendIntent(mContext, 0, fillIn, null, null);
+ } catch (SendIntentException ignored) {
+ }
+ }
+ }
+
private static class Callbacks extends Handler {
private static final int MSG_SESSION_CREATED = 1;
private static final int MSG_SESSION_OPENED = 2;
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index a3184f0..5ef24f2 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -25,13 +25,15 @@
import static android.system.OsConstants.O_RDONLY;
import static android.system.OsConstants.O_WRONLY;
+import android.content.Context;
import android.content.Intent;
+import android.content.IntentSender;
import android.content.pm.ApplicationInfo;
import android.content.pm.IPackageInstallObserver2;
import android.content.pm.IPackageInstallerSession;
-import android.content.pm.InstallSessionInfo;
-import android.content.pm.InstallSessionParams;
import android.content.pm.PackageInstaller;
+import android.content.pm.PackageInstaller.SessionInfo;
+import android.content.pm.PackageInstaller.SessionParams;
import android.content.pm.PackageManager;
import android.content.pm.PackageParser;
import android.content.pm.PackageParser.ApkLite;
@@ -59,6 +61,7 @@
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.Preconditions;
+import com.android.server.pm.PackageInstallerService.PackageInstallObserverAdapter;
import libcore.io.Libcore;
@@ -81,13 +84,14 @@
// TODO: treat INHERIT_EXISTING as installExistingPackage()
private final PackageInstallerService.InternalCallback mCallback;
+ private final Context mContext;
private final PackageManagerService mPm;
private final Handler mHandler;
final int sessionId;
final int userId;
final String installerPackageName;
- final InstallSessionParams params;
+ final SessionParams params;
final long createdMillis;
final File sessionStageDir;
@@ -159,10 +163,11 @@
};
public PackageInstallerSession(PackageInstallerService.InternalCallback callback,
- PackageManagerService pm, Looper looper, int sessionId, int userId,
- String installerPackageName, InstallSessionParams params, long createdMillis,
+ Context context, PackageManagerService pm, Looper looper, int sessionId, int userId,
+ String installerPackageName, SessionParams params, long createdMillis,
File sessionStageDir, boolean sealed) {
mCallback = callback;
+ mContext = context;
mPm = pm;
mHandler = new Handler(looper, mHandlerCallback);
@@ -188,8 +193,8 @@
computeProgressLocked();
}
- public InstallSessionInfo generateInfo() {
- final InstallSessionInfo info = new InstallSessionInfo();
+ public SessionInfo generateInfo() {
+ final SessionInfo info = new SessionInfo();
info.sessionId = sessionId;
info.installerPackageName = installerPackageName;
@@ -246,8 +251,8 @@
}
@Override
- public String[] list() {
- assertNotSealed("list");
+ public String[] getNames() {
+ assertNotSealed("getNames");
return sessionStageDir.list();
}
@@ -337,9 +342,12 @@
}
@Override
- public void commit(IPackageInstallObserver2 observer) {
- Preconditions.checkNotNull(observer);
- mHandler.obtainMessage(MSG_COMMIT, observer).sendToTarget();
+ public void commit(IntentSender statusReceiver) {
+ Preconditions.checkNotNull(statusReceiver);
+
+ final PackageInstallObserverAdapter adapter = new PackageInstallObserverAdapter(mContext,
+ statusReceiver);
+ mHandler.obtainMessage(MSG_COMMIT, adapter.getBinder()).sendToTarget();
}
private void commitLocked() throws PackageManagerException {
@@ -385,7 +393,7 @@
// Inherit any packages and native libraries from existing install that
// haven't been overridden.
- if (params.mode == InstallSessionParams.MODE_INHERIT_EXISTING) {
+ if (params.mode == SessionParams.MODE_INHERIT_EXISTING) {
spliceExistingFilesIntoStage();
}
@@ -396,7 +404,6 @@
// We've reached point of no return; call into PMS to install the stage.
// Regardless of success or failure we always destroy session.
- final IPackageInstallObserver2 remoteObserver = mRemoteObserver;
final IPackageInstallObserver2 localObserver = new IPackageInstallObserver2.Stub() {
@Override
public void onUserActionRequired(Intent intent) {
@@ -488,7 +495,7 @@
// currently relying on PMS to do this.
// TODO: teach about compatible upgrade keysets.
- if (params.mode == InstallSessionParams.MODE_FULL_INSTALL) {
+ if (params.mode == SessionParams.MODE_FULL_INSTALL) {
// Full installs must include a base package
if (!seenSplits.contains(null)) {
throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 89bd1d4..63f3c0f 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -107,12 +107,12 @@
import android.content.pm.IPackageManager;
import android.content.pm.IPackageMoveObserver;
import android.content.pm.IPackageStatsObserver;
-import android.content.pm.InstallSessionParams;
import android.content.pm.InstrumentationInfo;
import android.content.pm.ManifestDigest;
import android.content.pm.PackageCleanItem;
import android.content.pm.PackageInfo;
import android.content.pm.PackageInfoLite;
+import android.content.pm.PackageInstaller;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.LegacyPackageDeleteObserver;
import android.content.pm.PackageParser.ActivityIntentInfo;
@@ -1394,16 +1394,27 @@
* list of process files because dexopt will have been run
* if necessary during zygote startup.
*/
- String bootClassPath = System.getProperty("java.boot.class.path");
+ final String bootClassPath = System.getenv("BOOTCLASSPATH");
+ final String systemServerClassPath = System.getenv("SYSTEMSERVERCLASSPATH");
+
if (bootClassPath != null) {
- String[] paths = splitString(bootClassPath, ':');
- for (int i=0; i<paths.length; i++) {
- alreadyDexOpted.add(paths[i]);
+ String[] bootClassPathElements = splitString(bootClassPath, ':');
+ for (String element : bootClassPathElements) {
+ alreadyDexOpted.add(element);
}
} else {
Slog.w(TAG, "No BOOTCLASSPATH found!");
}
+ if (systemServerClassPath != null) {
+ String[] systemServerClassPathElements = splitString(systemServerClassPath, ':');
+ for (String element : systemServerClassPathElements) {
+ alreadyDexOpted.add(element);
+ }
+ } else {
+ Slog.w(TAG, "No SYSTEMSERVERCLASSPATH found!");
+ }
+
boolean didDexOptLibraryOrTool = false;
final List<String> allInstructionSets = getAllInstructionSets();
@@ -7835,7 +7846,7 @@
}
void installStage(String packageName, File stageDir, IPackageInstallObserver2 observer,
- InstallSessionParams params, String installerPackageName, int installerUid,
+ PackageInstaller.SessionParams params, String installerPackageName, int installerUid,
UserHandle user) {
final VerificationParams verifParams = new VerificationParams(null, params.originatingUri,
params.referrerUri, installerUid, null);
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index 39c6e0e..db19285 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -58,8 +58,6 @@
import android.os.SystemService;
import android.os.UserHandle;
import android.os.WorkSource;
-import android.os.Parcel;
-import android.os.ServiceManager;
import android.provider.Settings;
import android.service.dreams.DreamManagerInternal;
import android.util.EventLog;
@@ -709,7 +707,6 @@
if (mLowPowerModeEnabled != lowPowerModeEnabled) {
mLowPowerModeEnabled = lowPowerModeEnabled;
powerHintInternal(POWER_HINT_LOW_POWER_MODE, lowPowerModeEnabled ? 1 : 0);
- setSurfaceFlingerLowPowerMode(lowPowerModeEnabled ? 1 : 0);
mLowPowerModeEnabled = lowPowerModeEnabled;
BackgroundThread.getHandler().post(new Runnable() {
@Override
@@ -2198,21 +2195,6 @@
nativeSendPowerHint(hintId, data);
}
- private static void setSurfaceFlingerLowPowerMode(int enabled) {
- try {
- final IBinder flinger = ServiceManager.getService("SurfaceFlinger");
- if (flinger != null) {
- final Parcel data = Parcel.obtain();
- data.writeInterfaceToken("android.ui.ISurfaceComposer");
- data.writeInt(enabled);
- flinger.transact(1016, data, null, 0);
- data.recycle();
- }
- } catch (RemoteException ex) {
- Slog.e(TAG, "Failed to reduce refresh rate", ex);
- }
- }
-
/**
* Low-level function turn the device off immediately, without trying
* to be clean. Most people should use {@link ShutdownThread} for a clean shutdown.
diff --git a/services/core/java/com/android/server/tv/PersistentDataStore.java b/services/core/java/com/android/server/tv/PersistentDataStore.java
index 4af8f2c..05a2bde 100644
--- a/services/core/java/com/android/server/tv/PersistentDataStore.java
+++ b/services/core/java/com/android/server/tv/PersistentDataStore.java
@@ -20,6 +20,7 @@
import android.content.Intent;
import android.media.tv.TvContentRating;
import android.media.tv.TvInputManager;
+import android.os.Environment;
import android.os.Handler;
import android.os.UserHandle;
import android.text.TextUtils;
@@ -81,8 +82,13 @@
public PersistentDataStore(Context context, int userId) {
mContext = context;
- mAtomicFile = new AtomicFile(new File("/data/system/tv/" + userId
- + "/tv-input-manager-state.xml"));
+ File userDir = Environment.getUserSystemDirectory(userId);
+ if (!userDir.exists()) {
+ if (!userDir.mkdirs()) {
+ throw new IllegalStateException("User dir cannot be created: " + userDir);
+ }
+ }
+ mAtomicFile = new AtomicFile(new File(userDir, "tv-input-manager-state.xml"));
}
public boolean isParentalControlsEnabled() {
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index 3fcd067..b2575e6 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -252,6 +252,15 @@
return false;
}
+ void removeAllWindows() {
+ for (int winNdx = allAppWindows.size() - 1; winNdx >= 0; --winNdx) {
+ WindowState win = allAppWindows.get(winNdx);
+ if (WindowManagerService.DEBUG_WINDOW_MOVEMENT) Slog.w(WindowManagerService.TAG,
+ "removeAllWindows: removing win=" + win);
+ win.mService.removeWindowLocked(win.mSession, win);
+ }
+ }
+
@Override
void dump(PrintWriter pw, String prefix) {
super.dump(pw, prefix);
diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java
index dfb1200..238c77e 100644
--- a/services/core/java/com/android/server/wm/TaskStack.java
+++ b/services/core/java/com/android/server/wm/TaskStack.java
@@ -155,7 +155,7 @@
final ArrayList<WindowState> windows = activities.get(activityNdx).allAppWindows;
for (int winNdx = windows.size() - 1; winNdx >= 0; --winNdx) {
final WindowStateAnimator winAnimator = windows.get(winNdx).mWinAnimator;
- if (winAnimator.isAnimating() && !winAnimator.isDummyAnimation()) {
+ if (winAnimator.isAnimating() || winAnimator.mWin.mExiting) {
return true;
}
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 711cf9c..2295656 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -3592,7 +3592,7 @@
return;
}
final Task oldTask = mTaskIdToTask.get(atoken.groupId);
- removeAppFromTaskLocked(atoken);
+ oldTask.removeAppToken(atoken);
atoken.groupId = groupId;
Task newTask = mTaskIdToTask.get(groupId);
@@ -4634,6 +4634,8 @@
}
void removeAppFromTaskLocked(AppWindowToken wtoken) {
+ wtoken.removeAllWindows();
+
final Task task = mTaskIdToTask.get(wtoken.groupId);
if (task != null) {
if (!task.removeAppToken(wtoken)) {
diff --git a/services/core/jni/onload.cpp b/services/core/jni/onload.cpp
index ce2ca9b..39b70a8 100644
--- a/services/core/jni/onload.cpp
+++ b/services/core/jni/onload.cpp
@@ -41,6 +41,8 @@
int register_android_server_hdmi_HdmiMhlController(JNIEnv* env);
int register_android_server_tv_TvInputHal(JNIEnv* env);
int register_android_server_PersistentDataBlockService(JNIEnv* env);
+int register_android_server_fingerprint_FingerprintService(JNIEnv* env);
+int register_android_server_Watchdog(JNIEnv* env);
};
using namespace android;
@@ -77,6 +79,8 @@
register_android_server_hdmi_HdmiMhlController(env);
register_android_server_tv_TvInputHal(env);
register_android_server_PersistentDataBlockService(env);
+ register_android_server_fingerprint_FingerprintService(env);
+ register_android_server_Watchdog(env);
return JNI_VERSION_1_4;
}
diff --git a/services/devicepolicy/Android.mk b/services/devicepolicy/Android.mk
index a55d138..7020f17 100644
--- a/services/devicepolicy/Android.mk
+++ b/services/devicepolicy/Android.mk
@@ -7,6 +7,6 @@
LOCAL_SRC_FILES += \
$(call all-java-files-under,java)
-LOCAL_JAVA_LIBRARIES := conscrypt
+LOCAL_JAVA_LIBRARIES := conscrypt services.core
include $(BUILD_STATIC_JAVA_LIBRARY)
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index fc96991..d46ae42 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -19,6 +19,7 @@
import static android.Manifest.permission.MANAGE_CA_CERTIFICATES;
import android.app.admin.DevicePolicyManagerInternal;
+
import com.android.internal.R;
import com.android.internal.os.storage.ExternalStorageFormatter;
import com.android.internal.util.FastXmlSerializer;
@@ -58,6 +59,7 @@
import android.net.Uri;
import android.content.pm.PackageManager.NameNotFoundException;
import android.database.ContentObserver;
+import android.hardware.usb.UsbManager;
import android.net.ProxyInfo;
import android.os.Binder;
import android.os.Bundle;
@@ -3417,8 +3419,7 @@
synchronized (this) {
long ident = Binder.clearCallingIdentity();
try {
- mUserManager.setUserRestrictions(new Bundle(),
- new UserHandle(UserHandle.USER_OWNER));
+ clearUserRestrictions(new UserHandle(UserHandle.USER_OWNER));
if (mDeviceOwner != null) {
mDeviceOwner.clearDeviceOwner();
mDeviceOwner.writeOwnerFile();
@@ -3481,7 +3482,7 @@
synchronized (this) {
long ident = Binder.clearCallingIdentity();
try {
- mUserManager.setUserRestrictions(new Bundle(), callingUser);
+ clearUserRestrictions(callingUser);
if (mDeviceOwner != null) {
mDeviceOwner.removeProfileOwner(callingUser.getIdentifier());
mDeviceOwner.writeOwnerFile();
@@ -3492,6 +3493,19 @@
}
}
+ private void clearUserRestrictions(UserHandle userHandle) {
+ AudioManager audioManager =
+ (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
+ Bundle userRestrictions = mUserManager.getUserRestrictions();
+ mUserManager.setUserRestrictions(new Bundle(), userHandle);
+ if (userRestrictions.getBoolean(UserManager.DISALLOW_ADJUST_VOLUME)) {
+ audioManager.setMasterMute(false);
+ }
+ if (userRestrictions.getBoolean(UserManager.DISALLOW_UNMUTE_MICROPHONE)) {
+ audioManager.setMicrophoneMute(false);
+ }
+ }
+
@Override
public boolean hasUserSetupCompleted() {
if (!mHasFeature) {
@@ -4034,7 +4048,57 @@
long id = Binder.clearCallingIdentity();
try {
+ AudioManager audioManager =
+ (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
+ boolean alreadyRestricted = mUserManager.hasUserRestriction(key);
+
+ if (enabled && !alreadyRestricted) {
+ if (UserManager.DISALLOW_CONFIG_WIFI.equals(key)) {
+ Settings.Secure.putIntForUser(mContext.getContentResolver(),
+ Settings.Secure.WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON, 0,
+ userHandle.getIdentifier());
+ } else if (UserManager.DISALLOW_USB_FILE_TRANSFER.equals(key)) {
+ UsbManager manager =
+ (UsbManager) mContext.getSystemService(Context.USB_SERVICE);
+ manager.setCurrentFunction("none", false);
+ } else if (UserManager.DISALLOW_SHARE_LOCATION.equals(key)) {
+ Settings.Secure.putIntForUser(mContext.getContentResolver(),
+ Settings.Secure.LOCATION_MODE, Settings.Secure.LOCATION_MODE_OFF,
+ userHandle.getIdentifier());
+ Settings.Secure.putStringForUser(mContext.getContentResolver(),
+ Settings.Secure.LOCATION_PROVIDERS_ALLOWED, "",
+ userHandle.getIdentifier());
+ } else if (UserManager.DISALLOW_DEBUGGING_FEATURES.equals(key)) {
+ Settings.Global.putStringForUser(mContext.getContentResolver(),
+ Settings.Global.ADB_ENABLED, "0", userHandle.getIdentifier());
+ } else if (UserManager.ENSURE_VERIFY_APPS.equals(key)) {
+ Settings.Global.putStringForUser(mContext.getContentResolver(),
+ Settings.Global.PACKAGE_VERIFIER_ENABLE, "1",
+ userHandle.getIdentifier());
+ Settings.Global.putStringForUser(mContext.getContentResolver(),
+ Settings.Global.PACKAGE_VERIFIER_INCLUDE_ADB, "1",
+ userHandle.getIdentifier());
+ } else if (UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES.equals(key)) {
+ Settings.Secure.putIntForUser(mContext.getContentResolver(),
+ Settings.Secure.INSTALL_NON_MARKET_APPS, 0,
+ userHandle.getIdentifier());
+ } else if (UserManager.DISALLOW_UNMUTE_MICROPHONE.equals(key)) {
+ audioManager.setMicrophoneMute(true);
+ } else if (UserManager.DISALLOW_ADJUST_VOLUME.equals(key)) {
+ audioManager.setMasterMute(true);
+ }
+ }
+
mUserManager.setUserRestriction(key, enabled, userHandle);
+
+ if (!enabled && alreadyRestricted) {
+ if (UserManager.DISALLOW_UNMUTE_MICROPHONE.equals(key)) {
+ audioManager.setMicrophoneMute(false);
+ } else if (UserManager.DISALLOW_ADJUST_VOLUME.equals(key)) {
+ audioManager.setMasterMute(false);
+ }
+ }
+
} finally {
restoreCallingIdentity(id);
}
diff --git a/services/print/Android.mk b/services/print/Android.mk
index 33604b7..00eb2e4 100644
--- a/services/print/Android.mk
+++ b/services/print/Android.mk
@@ -7,4 +7,6 @@
LOCAL_SRC_FILES += \
$(call all-java-files-under,java)
+LOCAL_JAVA_LIBRARIES := services.core
+
include $(BUILD_STATIC_JAVA_LIBRARY)
diff --git a/services/restrictions/Android.mk b/services/restrictions/Android.mk
index fcf8626..57d1c46 100644
--- a/services/restrictions/Android.mk
+++ b/services/restrictions/Android.mk
@@ -7,4 +7,6 @@
LOCAL_SRC_FILES += \
$(call all-java-files-under,java)
+LOCAL_JAVA_LIBRARIES := services.core
+
include $(BUILD_STATIC_JAVA_LIBRARY)
diff --git a/services/usage/Android.mk b/services/usage/Android.mk
index d4b7fa8..f1cbe98 100644
--- a/services/usage/Android.mk
+++ b/services/usage/Android.mk
@@ -7,4 +7,6 @@
LOCAL_SRC_FILES += \
$(call all-java-files-under,java)
+LOCAL_JAVA_LIBRARIES := services.core
+
include $(BUILD_STATIC_JAVA_LIBRARY)
diff --git a/services/usb/java/com/android/server/usb/UsbHostManager.java b/services/usb/java/com/android/server/usb/UsbHostManager.java
index bcbae60..5860fc7 100644
--- a/services/usb/java/com/android/server/usb/UsbHostManager.java
+++ b/services/usb/java/com/android/server/usb/UsbHostManager.java
@@ -314,14 +314,22 @@
mConnectedUsbCard = cardsParser.getNumCardRecords() - 1;
mConnectedUsbDeviceNum = 0;
- if (!waitForAlsaFile(mConnectedUsbCard, mConnectedUsbDeviceNum, false)) {
- return;
- }
-
mConnectedHasPlayback = devicesParser.hasPlaybackDevices(mConnectedUsbCard);
mConnectedHasCapture = devicesParser.hasCaptureDevices(mConnectedUsbCard);
mConnectedHasMIDI = devicesParser.hasMIDIDevices(mConnectedUsbCard);
+ // Playback device file needed/present?
+ if (mConnectedHasPlayback &&
+ !waitForAlsaFile(mConnectedUsbCard, mConnectedUsbDeviceNum, false)) {
+ return;
+ }
+
+ // Capture device file needed/present?
+ if (mConnectedHasCapture &&
+ !waitForAlsaFile(mConnectedUsbCard, mConnectedUsbDeviceNum, true)) {
+ return;
+ }
+
if (DEBUG_AUDIO) {
Slog.d(TAG,
"usb: hasPlayback:" + mConnectedHasPlayback + " hasCapture:" + mConnectedHasCapture);
diff --git a/telecomm/java/android/telecomm/Call.java b/telecomm/java/android/telecomm/Call.java
index a292587..7223574 100644
--- a/telecomm/java/android/telecomm/Call.java
+++ b/telecomm/java/android/telecomm/Call.java
@@ -18,6 +18,7 @@
import android.app.PendingIntent;
import android.net.Uri;
+import android.os.Bundle;
import android.telephony.DisconnectCause;
import java.lang.String;
@@ -26,6 +27,7 @@
import java.util.List;
import java.util.Map;
import java.util.Objects;
+import java.util.concurrent.CopyOnWriteArrayList;
/**
* Represents an ongoing phone call that the in-call app should present to the user.
@@ -91,6 +93,7 @@
private final GatewayInfo mGatewayInfo;
private final int mVideoState;
private final StatusHints mStatusHints;
+ private final Bundle mExtras;
/**
* @return The handle (e.g., phone number) to which the {@code Call} is currently
@@ -186,6 +189,13 @@
return mStatusHints;
}
+ /**
+ * @return A bundle extras to pass with the call
+ */
+ public Bundle getExtras() {
+ return mExtras;
+ }
+
@Override
public boolean equals(Object o) {
if (o instanceof Details) {
@@ -203,7 +213,8 @@
Objects.equals(mConnectTimeMillis, d.mConnectTimeMillis) &&
Objects.equals(mGatewayInfo, d.mGatewayInfo) &&
Objects.equals(mVideoState, d.mVideoState) &&
- Objects.equals(mStatusHints, d.mStatusHints);
+ Objects.equals(mStatusHints, d.mStatusHints) &&
+ Objects.equals(mExtras, d.mExtras);
}
return false;
}
@@ -222,7 +233,8 @@
Objects.hashCode(mConnectTimeMillis) +
Objects.hashCode(mGatewayInfo) +
Objects.hashCode(mVideoState) +
- Objects.hashCode(mStatusHints);
+ Objects.hashCode(mStatusHints) +
+ Objects.hashCode(mExtras);
}
/** {@hide} */
@@ -238,7 +250,8 @@
long connectTimeMillis,
GatewayInfo gatewayInfo,
int videoState,
- StatusHints statusHints) {
+ StatusHints statusHints,
+ Bundle extras) {
mHandle = handle;
mHandlePresentation = handlePresentation;
mCallerDisplayName = callerDisplayName;
@@ -251,6 +264,7 @@
mGatewayInfo = gatewayInfo;
mVideoState = videoState;
mStatusHints = statusHints;
+ mExtras = extras;
}
}
@@ -351,7 +365,7 @@
private final InCallAdapter mInCallAdapter;
private final List<Call> mChildren = new ArrayList<>();
private final List<Call> mUnmodifiableChildren = Collections.unmodifiableList(mChildren);
- private final List<Listener> mListeners = new ArrayList<>();
+ private final List<Listener> mListeners = new CopyOnWriteArrayList<>();
private final List<Call> mConferenceableCalls = new ArrayList<>();
private final List<Call> mUnmodifiableConferenceableCalls =
Collections.unmodifiableList(mConferenceableCalls);
@@ -576,7 +590,9 @@
* @param listener A {@code Listener}.
*/
public void removeListener(Listener listener) {
- mListeners.remove(listener);
+ if (listener != null) {
+ mListeners.remove(listener);
+ }
}
/** {@hide} */
@@ -607,7 +623,8 @@
parcelableCall.getConnectTimeMillis(),
parcelableCall.getGatewayInfo(),
parcelableCall.getVideoState(),
- parcelableCall.getStatusHints());
+ parcelableCall.getStatusHints(),
+ parcelableCall.getExtras());
boolean detailsChanged = !Objects.equals(mDetails, details);
if (detailsChanged) {
mDetails = details;
@@ -695,72 +712,62 @@
}
private void fireStateChanged(int newState) {
- Listener[] listeners = mListeners.toArray(new Listener[mListeners.size()]);
- for (int i = 0; i < listeners.length; i++) {
- listeners[i].onStateChanged(this, newState);
+ for (Listener listener : mListeners) {
+ listener.onStateChanged(this, newState);
}
}
private void fireParentChanged(Call newParent) {
- Listener[] listeners = mListeners.toArray(new Listener[mListeners.size()]);
- for (int i = 0; i < listeners.length; i++) {
- listeners[i].onParentChanged(this, newParent);
+ for (Listener listener : mListeners) {
+ listener.onParentChanged(this, newParent);
}
}
private void fireChildrenChanged(List<Call> children) {
- Listener[] listeners = mListeners.toArray(new Listener[mListeners.size()]);
- for (int i = 0; i < listeners.length; i++) {
- listeners[i].onChildrenChanged(this, children);
+ for (Listener listener : mListeners) {
+ listener.onChildrenChanged(this, children);
}
}
private void fireDetailsChanged(Details details) {
- Listener[] listeners = mListeners.toArray(new Listener[mListeners.size()]);
- for (int i = 0; i < listeners.length; i++) {
- listeners[i].onDetailsChanged(this, details);
+ for (Listener listener : mListeners) {
+ listener.onDetailsChanged(this, details);
}
}
private void fireCannedTextResponsesLoaded(List<String> cannedTextResponses) {
- Listener[] listeners = mListeners.toArray(new Listener[mListeners.size()]);
- for (int i = 0; i < listeners.length; i++) {
- listeners[i].onCannedTextResponsesLoaded(this, cannedTextResponses);
+ for (Listener listener : mListeners) {
+ listener.onCannedTextResponsesLoaded(this, cannedTextResponses);
}
}
private void fireVideoCallChanged(InCallService.VideoCall videoCall) {
- Listener[] listeners = mListeners.toArray(new Listener[mListeners.size()]);
- for (int i = 0; i < listeners.length; i++) {
- listeners[i].onVideoCallChanged(this, videoCall);
+ for (Listener listener : mListeners) {
+ listener.onVideoCallChanged(this, videoCall);
}
}
private void firePostDialWait(String remainingPostDialSequence) {
- Listener[] listeners = mListeners.toArray(new Listener[mListeners.size()]);
- for (int i = 0; i < listeners.length; i++) {
- listeners[i].onPostDialWait(this, remainingPostDialSequence);
+ for (Listener listener : mListeners) {
+ listener.onPostDialWait(this, remainingPostDialSequence);
}
}
private void fireStartActivity(PendingIntent intent) {
- Listener[] listeners = mListeners.toArray(new Listener[mListeners.size()]);
- for (int i = 0; i < listeners.length; i++) {
- listeners[i].onStartActivity(this, intent);
+ for (Listener listener : mListeners) {
+ listener.onStartActivity(this, intent);
}
}
private void fireCallDestroyed() {
- Listener[] listeners = mListeners.toArray(new Listener[mListeners.size()]);
- for (int i = 0; i < listeners.length; i++) {
- listeners[i].onCallDestroyed(this);
+ for (Listener listener : mListeners) {
+ listener.onCallDestroyed(this);
}
}
private void fireConferenceableCallsChanged() {
- Listener[] listeners = mListeners.toArray(new Listener[mListeners.size()]);
- for (int i = 0; i < listeners.length; i++) {
- listeners[i].onConferenceableCallsChanged(this, mUnmodifiableConferenceableCalls);
+ for (Listener listener : mListeners) {
+ listener.onConferenceableCallsChanged(this, mUnmodifiableConferenceableCalls);
}
}
diff --git a/telecomm/java/android/telecomm/Connection.java b/telecomm/java/android/telecomm/Connection.java
index 3ecb4cb..78c34a1 100644
--- a/telecomm/java/android/telecomm/Connection.java
+++ b/telecomm/java/android/telecomm/Connection.java
@@ -32,7 +32,7 @@
import java.util.Collections;
import java.util.List;
import java.util.Set;
-import java.util.concurrent.CopyOnWriteArraySet;
+import java.util.concurrent.ConcurrentHashMap;
/**
* Represents a connection to a remote endpoint that carries voice traffic.
@@ -448,7 +448,13 @@
}
};
- private final Set<Listener> mListeners = new CopyOnWriteArraySet<>();
+ /**
+ * ConcurrentHashMap constructor params: 8 is initial table size, 0.9f is
+ * load factor before resizing, 1 means we only expect a single thread to
+ * access the map so make only a single shard
+ */
+ private final Set<Listener> mListeners = Collections.newSetFromMap(
+ new ConcurrentHashMap<Listener, Boolean>(8, 0.9f, 1));
private final List<Connection> mChildConnections = new ArrayList<>();
private final List<Connection> mUnmodifiableChildConnections =
Collections.unmodifiableList(mChildConnections);
@@ -587,7 +593,9 @@
* @hide
*/
public final Connection removeConnectionListener(Listener l) {
- mListeners.remove(l);
+ if (l != null) {
+ mListeners.remove(l);
+ }
return this;
}
@@ -874,13 +882,8 @@
* Tears down the Connection object.
*/
public final void destroy() {
- // It is possible that onDestroy() will trigger the listener to remove itself which will
- // result in a concurrent modification exception. To counteract this we make a copy of the
- // listeners and iterate on that.
- for (Listener l : new ArrayList<>(mListeners)) {
- if (mListeners.contains(l)) {
- l.onDestroyed(this);
- }
+ for (Listener l : mListeners) {
+ l.onDestroyed(this);
}
}
diff --git a/telecomm/java/android/telecomm/ConnectionService.java b/telecomm/java/android/telecomm/ConnectionService.java
index 5653f5e..8ab5e13 100644
--- a/telecomm/java/android/telecomm/ConnectionService.java
+++ b/telecomm/java/android/telecomm/ConnectionService.java
@@ -495,15 +495,12 @@
case Connection.STATE_INITIALIZING:
Log.d(this, "State changed to STATE_INITIALIZING; ignoring");
return; // Don't want to stop listening on this state transition.
- default:
- Log.d(this, "Connection created in state %s",
- Connection.stateToString(state));
- connectionCreated(callId, request, createdConnection);
- break;
}
c.removeConnectionListener(this);
}
});
+ Log.d(this, "Connection created in state INITIALIZING");
+ connectionCreated(callId, request, createdConnection);
} else if (createdConnection.getState() == Connection.STATE_CANCELED) {
// Tell telecomm not to attempt any more services.
mAdapter.handleCreateConnectionCancelled(callId, request);
@@ -553,7 +550,10 @@
connection.getCallerDisplayNamePresentation(),
connection.getVideoProvider() == null ?
null : connection.getVideoProvider().getInterface(),
- connection.getVideoState()));
+ connection.getVideoState(),
+ connection.isRequestingRingback(),
+ connection.getAudioModeIsVoip(),
+ connection.getStatusHints()));
}
private void abort(String callId) {
diff --git a/telecomm/java/android/telecomm/ConnectionServiceAdapter.java b/telecomm/java/android/telecomm/ConnectionServiceAdapter.java
index bfcb5c3..4144b81 100644
--- a/telecomm/java/android/telecomm/ConnectionServiceAdapter.java
+++ b/telecomm/java/android/telecomm/ConnectionServiceAdapter.java
@@ -36,8 +36,13 @@
* @hide
*/
final class ConnectionServiceAdapter implements DeathRecipient {
+ /**
+ * ConcurrentHashMap constructor params: 8 is initial table size, 0.9f is
+ * load factor before resizing, 1 means we only expect a single thread to
+ * access the map so make only a single shard
+ */
private final Set<IConnectionServiceAdapter> mAdapters = Collections.newSetFromMap(
- new ConcurrentHashMap<IConnectionServiceAdapter, Boolean>(2));
+ new ConcurrentHashMap<IConnectionServiceAdapter, Boolean>(8, 0.9f, 1));
ConnectionServiceAdapter() {
}
@@ -53,7 +58,7 @@
}
void removeAdapter(IConnectionServiceAdapter adapter) {
- if (mAdapters.remove(adapter)) {
+ if (adapter != null && mAdapters.remove(adapter)) {
adapter.asBinder().unlinkToDeath(this, 0);
}
}
diff --git a/telecomm/java/android/telecomm/ParcelableCall.java b/telecomm/java/android/telecomm/ParcelableCall.java
index 2a9a63a..8098b94 100644
--- a/telecomm/java/android/telecomm/ParcelableCall.java
+++ b/telecomm/java/android/telecomm/ParcelableCall.java
@@ -17,6 +17,7 @@
package android.telecomm;
import android.net.Uri;
+import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.RemoteException;
@@ -53,6 +54,7 @@
private final StatusHints mStatusHints;
private final int mVideoState;
private final List<String> mConferenceableCallIds;
+ private final Bundle mExtras;
public ParcelableCall(
String id,
@@ -73,7 +75,8 @@
List<String> childCallIds,
StatusHints statusHints,
int videoState,
- List<String> conferenceableCallIds) {
+ List<String> conferenceableCallIds,
+ Bundle extras) {
mId = id;
mState = state;
mDisconnectCauseCode = disconnectCauseCode;
@@ -93,6 +96,7 @@
mStatusHints = statusHints;
mVideoState = videoState;
mConferenceableCallIds = Collections.unmodifiableList(conferenceableCallIds);
+ mExtras = extras;
}
/** The unique ID of the call. */
@@ -220,6 +224,15 @@
return mVideoState;
}
+ /**
+ * Any extras to pass with the call
+ *
+ * @return a bundle of extras
+ */
+ public Bundle getExtras() {
+ return mExtras;
+ }
+
/** Responsible for creating ParcelableCall objects for deserialized Parcels. */
public static final Parcelable.Creator<ParcelableCall> CREATOR =
new Parcelable.Creator<ParcelableCall> () {
@@ -249,11 +262,12 @@
int videoState = source.readInt();
List<String> conferenceableCallIds = new ArrayList<>();
source.readList(conferenceableCallIds, classLoader);
+ Bundle extras = source.readParcelable(classLoader);
return new ParcelableCall(id, state, disconnectCauseCode, disconnectCauseMsg,
cannedSmsResponses, capabilities, connectTimeMillis, handle, handlePresentation,
callerDisplayName, callerDisplayNamePresentation, gatewayInfo,
accountHandle, videoCallProvider, parentCallId, childCallIds, statusHints,
- videoState, conferenceableCallIds);
+ videoState, conferenceableCallIds, extras);
}
@Override
@@ -291,6 +305,7 @@
destination.writeParcelable(mStatusHints, 0);
destination.writeInt(mVideoState);
destination.writeList(mConferenceableCallIds);
+ destination.writeParcelable(mExtras, 0);
}
@Override
diff --git a/telecomm/java/android/telecomm/ParcelableConnection.java b/telecomm/java/android/telecomm/ParcelableConnection.java
index 78dd64a..7a87b87 100644
--- a/telecomm/java/android/telecomm/ParcelableConnection.java
+++ b/telecomm/java/android/telecomm/ParcelableConnection.java
@@ -38,6 +38,9 @@
private int mCallerDisplayNamePresentation;
private IVideoProvider mVideoProvider;
private int mVideoState;
+ private boolean mRequestingRingback;
+ private boolean mAudioModeIsVoip;
+ private StatusHints mStatusHints;
/** @hide */
public ParcelableConnection(
@@ -49,7 +52,10 @@
String callerDisplayName,
int callerDisplayNamePresentation,
IVideoProvider videoProvider,
- int videoState) {
+ int videoState,
+ boolean requestingRingback,
+ boolean audioModeIsVoip,
+ StatusHints statusHints) {
mPhoneAccount = phoneAccount;
mState = state;
mCapabilities = capabilities;
@@ -59,6 +65,9 @@
mCallerDisplayNamePresentation = callerDisplayNamePresentation;
mVideoProvider = videoProvider;
mVideoState = videoState;
+ mRequestingRingback = requestingRingback;
+ mAudioModeIsVoip = audioModeIsVoip;
+ mStatusHints = statusHints;
}
public PhoneAccountHandle getPhoneAccount() {
@@ -98,6 +107,18 @@
return mVideoState;
}
+ public boolean isRequestingRingback() {
+ return mRequestingRingback;
+ }
+
+ public boolean getAudioModeIsVoip() {
+ return mAudioModeIsVoip;
+ }
+
+ public final StatusHints getStatusHints() {
+ return mStatusHints;
+ }
+
@Override
public String toString() {
return new StringBuilder()
@@ -126,6 +147,9 @@
IVideoProvider videoCallProvider =
IVideoProvider.Stub.asInterface(source.readStrongBinder());
int videoState = source.readInt();
+ boolean requestingRingback = source.readByte() == 1;
+ boolean audioModeIsVoip = source.readByte() == 1;
+ StatusHints statusHints = source.readParcelable(classLoader);
return new ParcelableConnection(
phoneAccount,
@@ -136,7 +160,10 @@
callerDisplayName,
callerDisplayNamePresentation,
videoCallProvider,
- videoState);
+ videoState,
+ requestingRingback,
+ audioModeIsVoip,
+ statusHints);
}
@Override
@@ -164,5 +191,8 @@
destination.writeStrongBinder(
mVideoProvider != null ? mVideoProvider.asBinder() : null);
destination.writeInt(mVideoState);
+ destination.writeByte((byte) (mRequestingRingback ? 1 : 0));
+ destination.writeByte((byte) (mAudioModeIsVoip ? 1 : 0));
+ destination.writeParcelable(mStatusHints, 0);
}
}
diff --git a/telecomm/java/android/telecomm/Phone.java b/telecomm/java/android/telecomm/Phone.java
index 79e777a..03a8676 100644
--- a/telecomm/java/android/telecomm/Phone.java
+++ b/telecomm/java/android/telecomm/Phone.java
@@ -24,6 +24,7 @@
import java.util.List;
import java.util.Map;
import java.util.Objects;
+import java.util.concurrent.CopyOnWriteArrayList;
/**
* A unified virtual device providing a means of voice (and other) communication on a device.
@@ -89,7 +90,7 @@
private AudioState mAudioState;
- private final List<Listener> mListeners = new ArrayList<>();
+ private final List<Listener> mListeners = new CopyOnWriteArrayList<>();
/** {@hide} */
Phone(InCallAdapter adapter) {
@@ -171,7 +172,9 @@
* @param listener A {@code Listener} object.
*/
public final void removeListener(Listener listener) {
- mListeners.remove(listener);
+ if (listener != null) {
+ mListeners.remove(listener);
+ }
}
/**
@@ -236,30 +239,26 @@
}
private void fireCallAdded(Call call) {
- Listener[] listeners = mListeners.toArray(new Listener[mListeners.size()]);
- for (int i = 0; i < listeners.length; i++) {
- listeners[i].onCallAdded(this, call);
+ for (Listener listener : mListeners) {
+ listener.onCallAdded(this, call);
}
}
private void fireCallRemoved(Call call) {
- Listener[] listeners = mListeners.toArray(new Listener[mListeners.size()]);
- for (int i = 0; i < listeners.length; i++) {
- listeners[i].onCallRemoved(this, call);
+ for (Listener listener : mListeners) {
+ listener.onCallRemoved(this, call);
}
}
private void fireAudioStateChanged(AudioState audioState) {
- Listener[] listeners = mListeners.toArray(new Listener[mListeners.size()]);
- for (int i = 0; i < listeners.length; i++) {
- listeners[i].onAudioStateChanged(this, audioState);
+ for (Listener listener : mListeners) {
+ listener.onAudioStateChanged(this, audioState);
}
}
private void fireBringToForeground(boolean showDialpad) {
- Listener[] listeners = mListeners.toArray(new Listener[mListeners.size()]);
- for (int i = 0; i < listeners.length; i++) {
- listeners[i].onBringToForeground(this, showDialpad);
+ for (Listener listener : mListeners) {
+ listener.onBringToForeground(this, showDialpad);
}
}
diff --git a/telecomm/java/android/telecomm/PhoneAccount.java b/telecomm/java/android/telecomm/PhoneAccount.java
index e3e942b..411f48c 100644
--- a/telecomm/java/android/telecomm/PhoneAccount.java
+++ b/telecomm/java/android/telecomm/PhoneAccount.java
@@ -254,7 +254,7 @@
return null;
}
try {
- return packageContext.getResources().getDrawable(resId);
+ return packageContext.getDrawable(resId);
} catch (NotFoundException|MissingResourceException e) {
Log.e(this, e, "Cannot find icon %d in package %s",
resId, mAccountHandle.getComponentName().getPackageName());
diff --git a/telecomm/java/android/telecomm/RemoteConnection.java b/telecomm/java/android/telecomm/RemoteConnection.java
index 13b0834..f1cee10 100644
--- a/telecomm/java/android/telecomm/RemoteConnection.java
+++ b/telecomm/java/android/telecomm/RemoteConnection.java
@@ -24,9 +24,11 @@
import android.telephony.DisconnectCause;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
/**
* A connection provided to a {@link ConnectionService} by another {@code ConnectionService}
@@ -182,7 +184,13 @@
private IConnectionService mConnectionService;
private final String mConnectionId;
- private final Set<Listener> mListeners = new HashSet<>();
+ /**
+ * ConcurrentHashMap constructor params: 8 is initial table size, 0.9f is
+ * load factor before resizing, 1 means we only expect a single thread to
+ * access the map so make only a single shard
+ */
+ private final Set<Listener> mListeners = Collections.newSetFromMap(
+ new ConcurrentHashMap<Listener, Boolean>(8, 0.9f, 1));
private final Set<RemoteConnection> mConferenceableConnections = new HashSet<>();
private int mState = Connection.STATE_NEW;
@@ -245,7 +253,9 @@
* @param listener A {@code Listener}.
*/
public void removeListener(Listener listener) {
- mListeners.remove(listener);
+ if (listener != null) {
+ mListeners.remove(listener);
+ }
}
/**
@@ -585,11 +595,10 @@
setDisconnected(DisconnectCause.ERROR_UNSPECIFIED, "Connection destroyed.");
}
- Set<Listener> listeners = new HashSet<Listener>(mListeners);
- mListeners.clear();
- for (Listener l : listeners) {
+ for (Listener l : mListeners) {
l.onDestroyed(this);
}
+ mListeners.clear();
mConnected = false;
}
diff --git a/telecomm/java/android/telecomm/StatusHints.java b/telecomm/java/android/telecomm/StatusHints.java
index 0cf1295..f7c4f2f 100644
--- a/telecomm/java/android/telecomm/StatusHints.java
+++ b/telecomm/java/android/telecomm/StatusHints.java
@@ -121,7 +121,7 @@
return null;
}
try {
- return packageContext.getResources().getDrawable(resId);
+ return packageContext.getDrawable(resId);
} catch (MissingResourceException e) {
Log.e(this, e, "Cannot find icon %d in package %s",
resId, mComponentName.getPackageName());
diff --git a/telecomm/java/android/telecomm/TelecommManager.java b/telecomm/java/android/telecomm/TelecommManager.java
index 6d14fa8..5192b0f 100644
--- a/telecomm/java/android/telecomm/TelecommManager.java
+++ b/telecomm/java/android/telecomm/TelecommManager.java
@@ -101,6 +101,17 @@
"android.intent.extra.INCOMING_CALL_EXTRAS";
/**
+ * Optional extra for {@link android.content.Intent#ACTION_CALL} and
+ * {@link android.content.Intent#ACTION_DIAL} {@code Intent} containing a {@link Bundle}
+ * which contains metadata about the call. This {@link Bundle} will be saved into
+ * {@code Call.Details}.
+ *
+ * @hide
+ */
+ public static final String EXTRA_OUTGOING_CALL_EXTRAS =
+ "android.intent.extra.OUTGOING_CALL_EXTRAS";
+
+ /**
* Optional extra for {@link android.telephony.TelephonyManager#ACTION_PHONE_STATE_CHANGED}
* containing the disconnect code.
*/
diff --git a/telephony/java/com/android/internal/telephony/PhoneConstants.java b/telephony/java/com/android/internal/telephony/PhoneConstants.java
index cf87bec..b4b1ea5 100644
--- a/telephony/java/com/android/internal/telephony/PhoneConstants.java
+++ b/telephony/java/com/android/internal/telephony/PhoneConstants.java
@@ -187,4 +187,7 @@
REASON_RADIO_UNAVAILABLE,
REASON_SIM_REFRESH_RESET
};
+
+ // Initial MTU value.
+ public static final int UNSET_MTU = 0;
}
diff --git a/telephony/java/com/android/internal/telephony/RILConstants.java b/telephony/java/com/android/internal/telephony/RILConstants.java
index 15298599..a8a9057 100644
--- a/telephony/java/com/android/internal/telephony/RILConstants.java
+++ b/telephony/java/com/android/internal/telephony/RILConstants.java
@@ -287,6 +287,7 @@
int RIL_REQUEST_ALLOW_DATA = 123;
int RIL_REQUEST_GET_HARDWARE_CONFIG = 124;
int RIL_REQUEST_SIM_AUTHENTICATION = 125;
+ int RIL_REQUEST_SET_DATA_PROFILE = 128;
int RIL_UNSOL_RESPONSE_BASE = 1000;
int RIL_UNSOL_RESPONSE_RADIO_STATE_CHANGED = 1000;
diff --git a/tests/MusicBrowserDemo/src/com/example/android/musicbrowserdemo/AppListFragment.java b/tests/MusicBrowserDemo/src/com/example/android/musicbrowserdemo/AppListFragment.java
index c0f3a7f..526ea5d 100644
--- a/tests/MusicBrowserDemo/src/com/example/android/musicbrowserdemo/AppListFragment.java
+++ b/tests/MusicBrowserDemo/src/com/example/android/musicbrowserdemo/AppListFragment.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2010 The Android Open Source Project
+ * Copyright (C) 2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/tests/MusicBrowserDemo/src/com/example/android/musicbrowserdemo/BrowserListFragment.java b/tests/MusicBrowserDemo/src/com/example/android/musicbrowserdemo/BrowserListFragment.java
index 3fc468d..2fc77dc 100644
--- a/tests/MusicBrowserDemo/src/com/example/android/musicbrowserdemo/BrowserListFragment.java
+++ b/tests/MusicBrowserDemo/src/com/example/android/musicbrowserdemo/BrowserListFragment.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2010 The Android Open Source Project
+ * Copyright (C) 2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/tests/MusicBrowserDemo/src/com/example/android/musicbrowserdemo/MainActivity.java b/tests/MusicBrowserDemo/src/com/example/android/musicbrowserdemo/MainActivity.java
index ed91aad..4c28234 100644
--- a/tests/MusicBrowserDemo/src/com/example/android/musicbrowserdemo/MainActivity.java
+++ b/tests/MusicBrowserDemo/src/com/example/android/musicbrowserdemo/MainActivity.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2010 The Android Open Source Project
+ * Copyright (C) 2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/tests/OneMedia/AndroidManifest.xml b/tests/OneMedia/AndroidManifest.xml
index 9d78ca5..95072a4 100644
--- a/tests/OneMedia/AndroidManifest.xml
+++ b/tests/OneMedia/AndroidManifest.xml
@@ -23,7 +23,7 @@
</activity>
<service
android:name="com.android.onemedia.OnePlayerService"
- android:exported="false"
+ android:exported="true"
android:process="com.android.onemedia.service" />
<service
android:name=".provider.OneMediaRouteProvider"
diff --git a/tests/OneMedia/res/drawable/ic_fast_forward.xml b/tests/OneMedia/res/drawable/ic_fast_forward.xml
new file mode 100644
index 0000000..8daf07d
--- /dev/null
+++ b/tests/OneMedia/res/drawable/ic_fast_forward.xml
@@ -0,0 +1,25 @@
+<!--
+Copyright (C) 2014 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="36dp"
+ android:height="36dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24" >
+
+ <path
+ android:fillColor="#FFFFFFFF"
+ android:pathData="M4.0,18.0l8.5,-6.0L4.0,6.0L4.0,18.0zM13.0,6.0l0.0,12.0l8.5,-6.0L13.0,6.0z"/>
+</vector>
diff --git a/tests/OneMedia/res/drawable/ic_fast_rewind.xml b/tests/OneMedia/res/drawable/ic_fast_rewind.xml
new file mode 100644
index 0000000..4ed1f54
--- /dev/null
+++ b/tests/OneMedia/res/drawable/ic_fast_rewind.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2014 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="36dp"
+ android:height="36dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24" >
+ <path
+ android:fillColor="#FFFFFFFF"
+ android:pathData="M11.0,18.0L11.0,6.0l-8.5,6.0L11.0,18.0zM11.5,12.0l8.5,6.0L20.0,6.0L11.5,12.0z"/>
+</vector>
diff --git a/tests/OneMedia/res/drawable/ic_pause.xml b/tests/OneMedia/res/drawable/ic_pause.xml
new file mode 100644
index 0000000..15d0756
--- /dev/null
+++ b/tests/OneMedia/res/drawable/ic_pause.xml
@@ -0,0 +1,25 @@
+<!--
+Copyright (C) 2014 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="36dp"
+ android:height="36dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24" >
+
+ <path
+ android:fillColor="#FFFFFFFF"
+ android:pathData="M6.0,19.0l4.0,0.0L10.0,5.0L6.0,5.0L6.0,19.0zM14.0,5.0l0.0,14.0l4.0,0.0L18.0,5.0L14.0,5.0z"/>
+</vector>
diff --git a/tests/OneMedia/res/drawable/ic_play_arrow.xml b/tests/OneMedia/res/drawable/ic_play_arrow.xml
new file mode 100644
index 0000000..49d766d
--- /dev/null
+++ b/tests/OneMedia/res/drawable/ic_play_arrow.xml
@@ -0,0 +1,25 @@
+<!--
+Copyright (C) 2014 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="36dp"
+ android:height="36dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24" >
+
+ <path
+ android:fillColor="#FFFFFFFF"
+ android:pathData="M8.0,5.0l0.0,14.0 11.0,-7.0z"/>
+</vector>
diff --git a/tests/OneMedia/res/drawable/ic_skip_next.xml b/tests/OneMedia/res/drawable/ic_skip_next.xml
new file mode 100644
index 0000000..8a6ceca
--- /dev/null
+++ b/tests/OneMedia/res/drawable/ic_skip_next.xml
@@ -0,0 +1,25 @@
+<!--
+Copyright (C) 2014 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="36dp"
+ android:height="36dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24" >
+
+ <path
+ android:fillColor="#FFFFFFFF"
+ android:pathData="M6.0,18.0l8.5,-6.0L6.0,6.0L6.0,18.0zM16.0,6.0l0.0,12.0l2.0,0.0L18.0,6.0L16.0,6.0z"/>
+</vector>
diff --git a/tests/OneMedia/res/drawable/ic_skip_previous.xml b/tests/OneMedia/res/drawable/ic_skip_previous.xml
new file mode 100644
index 0000000..c5d07db
--- /dev/null
+++ b/tests/OneMedia/res/drawable/ic_skip_previous.xml
@@ -0,0 +1,28 @@
+<!--
+Copyright (C) 2014 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="36dp"
+ android:height="36dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24" >
+
+ <path
+ android:fillColor="#FFFFFFFF"
+ android:pathData="M6.0,6.0l2.0,0.0l0.0,12.0l-2.0,0.0z"/>
+ <path
+ android:fillColor="#FFFFFFFF"
+ android:pathData="M9.5,12.0l8.5,6.0 0.0,-12.0z"/>
+</vector>
diff --git a/tests/OneMedia/res/drawable/ic_stop.xml b/tests/OneMedia/res/drawable/ic_stop.xml
new file mode 100644
index 0000000..6043fdb6
--- /dev/null
+++ b/tests/OneMedia/res/drawable/ic_stop.xml
@@ -0,0 +1,25 @@
+<!--
+Copyright (C) 2014 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="36dp"
+ android:height="36dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24" >
+
+ <path
+ android:fillColor="#FFFFFFFF"
+ android:pathData="M6.0,6.0l12.0,0.0l0.0,12.0l-12.0,0.0z"/>
+</vector>
diff --git a/tests/OneMedia/res/layout/activity_one_player.xml b/tests/OneMedia/res/layout/activity_one_player.xml
index 516562f..ce2d641 100644
--- a/tests/OneMedia/res/layout/activity_one_player.xml
+++ b/tests/OneMedia/res/layout/activity_one_player.xml
@@ -33,6 +33,19 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/has_video" />
+ <ImageView
+ android:id="@+id/art"
+ android:layout_width="match_parent"
+ android:layout_height="96dp"
+ android:scaleType="centerCrop"
+ android:visibility="gone"
+ />
+ <Button
+ android:id="@+id/art_picker"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/art_picker"
+ />
<LinearLayout
android:id="@+id/controls"
android:layout_width="match_parent"
diff --git a/tests/OneMedia/res/values/strings.xml b/tests/OneMedia/res/values/strings.xml
index 3735c8d..86657fd 100644
--- a/tests/OneMedia/res/values/strings.xml
+++ b/tests/OneMedia/res/values/strings.xml
@@ -12,5 +12,5 @@
<string name="media_next_hint">Next content</string>
<string name="has_video">Is video</string>
<string name="has_duration">Has duration</string>
-
+ <string name="art_picker">Choose artwork</string>
</resources>
diff --git a/tests/OneMedia/src/com/android/onemedia/IPlayerService.aidl b/tests/OneMedia/src/com/android/onemedia/IPlayerService.aidl
index d4df4c5..f53eac0 100644
--- a/tests/OneMedia/src/com/android/onemedia/IPlayerService.aidl
+++ b/tests/OneMedia/src/com/android/onemedia/IPlayerService.aidl
@@ -15,6 +15,7 @@
package com.android.onemedia;
+import android.graphics.Bitmap;
import android.media.session.MediaSession;
import android.os.Bundle;
@@ -26,4 +27,5 @@
void registerCallback(in IPlayerCallback cb);
void unregisterCallback(in IPlayerCallback cb);
void sendRequest(String action, in Bundle params, in IRequestCallback cb);
+ void setIcon(in Bitmap icon);
}
diff --git a/tests/OneMedia/src/com/android/onemedia/NotificationHelper.java b/tests/OneMedia/src/com/android/onemedia/NotificationHelper.java
new file mode 100644
index 0000000..a5bcda5
--- /dev/null
+++ b/tests/OneMedia/src/com/android/onemedia/NotificationHelper.java
@@ -0,0 +1,234 @@
+package com.android.onemedia;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.app.Service;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.graphics.Bitmap;
+import android.media.MediaMetadata;
+import android.media.session.MediaController;
+import android.media.session.MediaSession;
+import android.media.session.PlaybackState;
+import android.text.format.DateUtils;
+import android.util.Log;
+import android.util.SparseArray;
+
+import com.android.onemedia.playback.RequestUtils;
+
+/**
+ * Keeps track of a notification and updates it automatically for a given
+ * MediaSession.
+ */
+public class NotificationHelper extends BroadcastReceiver {
+ private static final String TAG = "NotificationHelper";
+
+ private static final int NOTIFICATION_ID = 433; // John Cage, 1952
+
+ private final Service mService;
+ private final MediaSession mSession;
+ private final MediaController mController;
+ private final MediaController.TransportControls mTransportControls;
+ private final SparseArray<PendingIntent> mIntents = new SparseArray<PendingIntent>();
+
+ private PlaybackState mPlaybackState;
+ private MediaMetadata mMetadata;
+
+ private boolean mStarted = false;
+
+ public NotificationHelper(Service service, MediaSession session) {
+ mService = service;
+ mSession = session;
+ mController = session.getController();
+ mTransportControls = mController.getTransportControls();
+ String pkg = mService.getPackageName();
+
+ mIntents.put(R.drawable.ic_pause, PendingIntent.getBroadcast(mService, 100, new Intent(
+ com.android.onemedia.playback.RequestUtils.ACTION_PAUSE).setPackage(pkg),
+ PendingIntent.FLAG_CANCEL_CURRENT));
+ mIntents.put(R.drawable.ic_play_arrow, PendingIntent.getBroadcast(mService, 100,
+ new Intent(com.android.onemedia.playback.RequestUtils.ACTION_PLAY).setPackage(pkg),
+ PendingIntent.FLAG_CANCEL_CURRENT));
+ mIntents.put(R.drawable.ic_skip_previous, PendingIntent.getBroadcast(mService, 100,
+ new Intent(com.android.onemedia.playback.RequestUtils.ACTION_PREV).setPackage(pkg),
+ PendingIntent.FLAG_CANCEL_CURRENT));
+ mIntents.put(R.drawable.ic_skip_next, PendingIntent.getBroadcast(mService, 100,
+ new Intent(com.android.onemedia.playback.RequestUtils.ACTION_NEXT).setPackage(pkg),
+ PendingIntent.FLAG_CANCEL_CURRENT));
+ mIntents.put(R.drawable.ic_fast_rewind, PendingIntent.getBroadcast(mService, 100,
+ new Intent(com.android.onemedia.playback.RequestUtils.ACTION_REW).setPackage(pkg),
+ PendingIntent.FLAG_CANCEL_CURRENT));
+ mIntents.put(R.drawable.ic_fast_forward, PendingIntent.getBroadcast(mService, 100,
+ new Intent(com.android.onemedia.playback.RequestUtils.ACTION_FFWD).setPackage(pkg),
+ PendingIntent.FLAG_CANCEL_CURRENT));
+ }
+
+ /**
+ * Posts the notification and starts tracking the session to keep it
+ * updated. The notification will automatically be removed if the session is
+ * destroyed before {@link #onStop} is called.
+ */
+ public void onStart() {
+ mController.addCallback(mCb);
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(RequestUtils.ACTION_FFWD);
+ filter.addAction(RequestUtils.ACTION_NEXT);
+ filter.addAction(RequestUtils.ACTION_PAUSE);
+ filter.addAction(RequestUtils.ACTION_PLAY);
+ filter.addAction(RequestUtils.ACTION_PREV);
+ filter.addAction(RequestUtils.ACTION_REW);
+ mService.registerReceiver(this, filter);
+
+ mMetadata = mController.getMetadata();
+ mPlaybackState = mController.getPlaybackState();
+
+ mStarted = true;
+ // The notification must be updated after setting started to true
+ updateNotification();
+ }
+
+ /**
+ * Removes the notification and stops tracking the session. If the session
+ * was destroyed this has no effect.
+ */
+ public void onStop() {
+ mStarted = false;
+ mController.removeCallback(mCb);
+ mService.unregisterReceiver(this);
+ updateNotification();
+ }
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ final String action = intent.getAction();
+ Log.d(TAG, "Received intent with action " + action);
+ if (RequestUtils.ACTION_PAUSE.equals(action)) {
+ mTransportControls.pause();
+ } else if (RequestUtils.ACTION_PLAY.equals(action)) {
+ mTransportControls.play();
+ } else if (RequestUtils.ACTION_NEXT.equals(action)) {
+ mTransportControls.skipToNext();
+ } else if (RequestUtils.ACTION_PREV.equals(action)) {
+ mTransportControls.skipToPrevious();
+ } else if (RequestUtils.ACTION_REW.equals(action)) {
+ mTransportControls.rewind();
+ } else if (RequestUtils.ACTION_FFWD.equals(action)) {
+ mTransportControls.fastForward();
+ }
+
+ }
+
+ private final MediaController.Callback mCb = new MediaController.Callback() {
+ @Override
+ public void onPlaybackStateChanged(PlaybackState state) {
+ mPlaybackState = state;
+ Log.d(TAG, "Received new playback state" + state);
+ updateNotification();
+ }
+
+ @Override
+ public void onMetadataChanged(MediaMetadata metadata) {
+ mMetadata = metadata;
+ Log.d(TAG, "Received new metadata " + metadata);
+ updateNotification();
+ }
+ };
+
+ NotificationManager mNoMan = null;
+
+ private void updateNotification() {
+ if (mNoMan == null) {
+ mNoMan = (NotificationManager) mService.getSystemService(Context.NOTIFICATION_SERVICE);
+ }
+ if (mPlaybackState == null) {
+ mNoMan.cancel(NOTIFICATION_ID);
+ return;
+ }
+ if (!mStarted) {
+ mNoMan.cancel(NOTIFICATION_ID);
+ return;
+ }
+
+ String status;
+ final int state = mPlaybackState.getState();
+ switch (state) {
+ case PlaybackState.STATE_PLAYING:
+ status = "PLAYING: ";
+ break;
+ case PlaybackState.STATE_PAUSED:
+ status = "PAUSED: ";
+ break;
+ case PlaybackState.STATE_STOPPED:
+ status = "STOPPED: ";
+ break;
+ case PlaybackState.STATE_ERROR:
+ status = "ERROR: ";
+ break;
+ case PlaybackState.STATE_BUFFERING:
+ status = "BUFFERING: ";
+ break;
+ case PlaybackState.STATE_NONE:
+ default:
+ status = "";
+ break;
+ }
+ CharSequence title, text;
+ Bitmap art;
+ if (mMetadata == null) {
+ title = status;
+ text = "Empty metadata!";
+ art = null;
+ } else {
+ MediaMetadata.Description description = mMetadata.getDescription();
+ title = description.getTitle();
+ text = description.getSubtitle();
+ art = description.getIcon();
+ }
+
+ String playPauseLabel = "";
+ int playPauseIcon;
+ if (state == PlaybackState.STATE_PLAYING) {
+ playPauseLabel = "Pause";
+ playPauseIcon = R.drawable.ic_pause;
+ } else {
+ playPauseLabel = "Play";
+ playPauseIcon = R.drawable.ic_play_arrow;
+ }
+
+ final long pos = mPlaybackState.getPosition();
+ final long end = mMetadata == null ? 0 : mMetadata
+ .getLong(MediaMetadata.METADATA_KEY_DURATION);
+ Notification notification = new Notification.Builder(mService)
+ .setSmallIcon(android.R.drawable.stat_notify_chat)
+ .setContentTitle(title)
+ .setContentText(text)
+ .setShowWhen(false)
+ .setContentInfo(DateUtils.formatElapsedTime(pos))
+ .setProgress((int) end, (int) pos, false)
+ .setLargeIcon(art)
+ .addAction(R.drawable.ic_skip_previous, "Previous",
+ mIntents.get(R.drawable.ic_skip_previous))
+ .addAction(R.drawable.ic_fast_rewind, "Rewind",
+ mIntents.get(R.drawable.ic_fast_rewind))
+ .addAction(playPauseIcon, playPauseLabel,
+ mIntents.get(playPauseIcon))
+ .addAction(R.drawable.ic_fast_forward, "Fast Forward",
+ mIntents.get(R.drawable.ic_fast_forward))
+ .addAction(R.drawable.ic_skip_next, "Next",
+ mIntents.get(R.drawable.ic_skip_next))
+ .setStyle(new Notification.MediaStyle()
+ .setShowActionsInCompactView(2)
+ .setMediaSession(mSession.getSessionToken()))
+ .setColor(0xFFDB4437)
+ .build();
+
+ mService.startForeground(NOTIFICATION_ID, notification);
+ }
+
+}
diff --git a/tests/OneMedia/src/com/android/onemedia/OnePlayerActivity.java b/tests/OneMedia/src/com/android/onemedia/OnePlayerActivity.java
index 894377b..2ff3e20 100644
--- a/tests/OneMedia/src/com/android/onemedia/OnePlayerActivity.java
+++ b/tests/OneMedia/src/com/android/onemedia/OnePlayerActivity.java
@@ -17,20 +17,35 @@
import android.app.Activity;
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
import android.media.MediaMetadata;
import android.media.session.PlaybackState;
+import android.net.Uri;
import android.os.Bundle;
+import android.provider.MediaStore;
+import android.text.format.DateUtils;
import android.util.Log;
import android.view.Menu;
import android.view.View;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.EditText;
+import android.widget.ImageView;
import android.widget.TextView;
+import java.io.IOException;
+
public class OnePlayerActivity extends Activity {
private static final String TAG = "OnePlayerActivity";
+ private static final int READ_REQUEST_CODE = 42;
+
protected PlayerController mPlayer;
private Button mStartButton;
@@ -41,8 +56,10 @@
private EditText mContentText;
private EditText mNextContentText;
private CheckBox mHasVideo;
+ private ImageView mArtView;
- private int mPlaybackState;
+ private PlaybackState mPlaybackState;
+ private Bitmap mAlbumArtBitmap;
@Override
protected void onCreate(Bundle savedInstanceState) {
@@ -58,6 +75,10 @@
mContentText = (EditText) findViewById(R.id.content);
mNextContentText = (EditText) findViewById(R.id.next_content);
mHasVideo = (CheckBox) findViewById(R.id.has_video);
+ mArtView = (ImageView) findViewById(R.id.art);
+
+ final Button artPicker = (Button) findViewById(R.id.art_picker);
+ artPicker.setOnClickListener(mButtonListener);
mStartButton.setOnClickListener(mButtonListener);
mPlayButton.setOnClickListener(mButtonListener);
@@ -86,6 +107,31 @@
super.onPause();
}
+ @Override
+ public void onActivityResult(int requestCode, int resultCode,
+ Intent resultData) {
+ if (requestCode == READ_REQUEST_CODE && resultCode == Activity.RESULT_OK) {
+ Uri uri = null;
+ if (resultData != null) {
+ uri = resultData.getData();
+ Log.i(TAG, "Uri: " + uri.toString());
+ mAlbumArtBitmap = null;
+ try {
+ mAlbumArtBitmap = MediaStore.Images.Media.getBitmap(this.getContentResolver(), uri);
+ } catch (IOException e) {
+ Log.v(TAG, "Couldn't load album art", e);
+ }
+ mArtView.setImageBitmap(mAlbumArtBitmap);
+ if (mAlbumArtBitmap != null) {
+ mArtView.setVisibility(View.VISIBLE);
+ } else {
+ mArtView.setVisibility(View.GONE);
+ }
+ mPlayer.setArt(mAlbumArtBitmap);
+ }
+ }
+ }
+
private void setControlsEnabled(boolean enabled) {
mStartButton.setEnabled(enabled);
mPlayButton.setEnabled(enabled);
@@ -94,36 +140,46 @@
private View.OnClickListener mButtonListener = new View.OnClickListener() {
@Override
public void onClick(View v) {
+ final int state = mPlaybackState.getState();
switch (v.getId()) {
case R.id.play_button:
- Log.d(TAG, "Play button pressed, in state " + mPlaybackState);
- if (mPlaybackState == PlaybackState.STATE_PAUSED
- || mPlaybackState == PlaybackState.STATE_STOPPED) {
+ Log.d(TAG, "Play button pressed, in state " + state);
+ if (state == PlaybackState.STATE_PAUSED
+ || state == PlaybackState.STATE_STOPPED) {
mPlayer.play();
- } else if (mPlaybackState == PlaybackState.STATE_PLAYING) {
+ } else if (state == PlaybackState.STATE_PLAYING) {
mPlayer.pause();
}
break;
case R.id.start_button:
- Log.d(TAG, "Start button pressed, in state " + mPlaybackState);
+ Log.d(TAG, "Start button pressed, in state " + state);
mPlayer.setContent(mContentText.getText().toString());
break;
case R.id.route_button:
mPlayer.showRoutePicker();
break;
+ case R.id.art_picker:
+ Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
+ intent.addCategory(Intent.CATEGORY_OPENABLE);
+ intent.setType("image/*");
+
+ startActivityForResult(intent, READ_REQUEST_CODE);
+ break;
}
}
};
private PlayerController.Listener mListener = new PlayerController.Listener() {
+ public MediaMetadata mMetadata;
+
@Override
public void onPlaybackStateChange(PlaybackState state) {
- mPlaybackState = state.getState();
+ mPlaybackState = state;
boolean enablePlay = false;
boolean enableControls = true;
StringBuilder statusBuilder = new StringBuilder();
- switch (mPlaybackState) {
+ switch (mPlaybackState.getState()) {
case PlaybackState.STATE_PLAYING:
statusBuilder.append("playing");
mPlayButton.setText("Pause");
@@ -172,7 +228,7 @@
@Override
public void onMetadataChange(MediaMetadata metadata) {
- Log.d(TAG, "Metadata update! Title: " + metadata);
+ mMetadata = metadata;
}
};
}
diff --git a/tests/OneMedia/src/com/android/onemedia/PlayerController.java b/tests/OneMedia/src/com/android/onemedia/PlayerController.java
index c0799fc..c8d72ca 100644
--- a/tests/OneMedia/src/com/android/onemedia/PlayerController.java
+++ b/tests/OneMedia/src/com/android/onemedia/PlayerController.java
@@ -30,6 +30,7 @@
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
+import android.graphics.Bitmap;
import android.util.Log;
import com.android.onemedia.playback.RequestUtils;
@@ -52,6 +53,7 @@
private Handler mHandler = new Handler();
private boolean mResumed;
+ private Bitmap mArt;
public PlayerController(Activity context, Intent serviceIntent) {
mContext = context;
@@ -89,6 +91,16 @@
unbindFromService();
}
+ public void setArt(Bitmap art) {
+ mArt = art;
+ if (mBinder != null) {
+ try {
+ mBinder.setIcon(art);
+ } catch (RemoteException e) {
+ }
+ }
+ }
+
public void play() {
if (mTransportControls != null) {
mTransportControls.play();
@@ -125,6 +137,16 @@
// TODO
}
+ public MediaSession.Token getSessionToken() {
+ if (mBinder != null) {
+ try {
+ return mBinder.getSessionToken();
+ } catch (RemoteException e) {
+ }
+ }
+ return null;
+ }
+
private void unbindFromService() {
mContext.unbindService(mServiceConnection);
}
@@ -165,6 +187,9 @@
mContext.setMediaController(mController);
mController.addCallback(mControllerCb, mHandler);
mTransportControls = mController.getTransportControls();
+ if (mArt != null) {
+ setArt(mArt);
+ }
Log.d(TAG, "Ready to use PlayerService");
if (mListener != null) {
@@ -194,6 +219,9 @@
return;
}
Log.d(TAG, "Received metadata change, " + metadata.getDescription());
+ if (mListener != null) {
+ mListener.onMetadataChange(metadata);
+ }
}
}
diff --git a/tests/OneMedia/src/com/android/onemedia/PlayerService.java b/tests/OneMedia/src/com/android/onemedia/PlayerService.java
index 58ee4a1..9967c99 100644
--- a/tests/OneMedia/src/com/android/onemedia/PlayerService.java
+++ b/tests/OneMedia/src/com/android/onemedia/PlayerService.java
@@ -17,6 +17,7 @@
import android.app.Service;
import android.content.Intent;
+import android.graphics.Bitmap;
import android.media.session.MediaSession;
import android.media.session.PlaybackState;
import android.os.Bundle;
@@ -34,6 +35,7 @@
private PlayerBinder mBinder;
private PlayerSession mSession;
+ private NotificationHelper mNotifyHelper;
private Intent mIntent;
private boolean mStarted = false;
@@ -47,6 +49,7 @@
mSession = onCreatePlayerController();
mSession.createSession();
mSession.setListener(mPlayerListener);
+ mNotifyHelper = new NotificationHelper(this, mSession.mSession);
}
}
@@ -75,6 +78,7 @@
if (!mStarted) {
Log.d(TAG, "Starting self");
startService(onCreateServiceIntent());
+ mNotifyHelper.onStart();
mStarted = true;
}
}
@@ -82,6 +86,7 @@
public void onPlaybackEnded() {
if (mStarted) {
Log.d(TAG, "Stopping self");
+ mNotifyHelper.onStop();
stopSelf();
mStarted = false;
}
@@ -150,8 +155,17 @@
@Override
public MediaSession.Token getSessionToken() throws RemoteException {
+ if (mSession == null) {
+ Log.e(TAG, "Error in PlayerService: mSession=null in getSessionToken()");
+ return null;
+ }
return mSession.getSessionToken();
}
+
+ @Override
+ public void setIcon(Bitmap icon) {
+ mSession.setIcon(icon);
+ }
}
}
diff --git a/tests/OneMedia/src/com/android/onemedia/PlayerSession.java b/tests/OneMedia/src/com/android/onemedia/PlayerSession.java
index 890d68d..9afcf24 100644
--- a/tests/OneMedia/src/com/android/onemedia/PlayerSession.java
+++ b/tests/OneMedia/src/com/android/onemedia/PlayerSession.java
@@ -17,6 +17,8 @@
import android.content.Context;
import android.content.Intent;
+import android.graphics.Bitmap;
+import android.media.MediaMetadata;
import android.media.routing.MediaRouteSelector;
import android.media.routing.MediaRouter;
import android.media.routing.MediaRouter.ConnectionRequest;
@@ -50,6 +52,7 @@
protected Renderer mRenderer;
protected MediaSession.Callback mCallback;
protected Renderer.Listener mRenderListener;
+ protected MediaMetadata.Builder mMetadataBuilder;
protected PlaybackState mPlaybackState;
protected Listener mListener;
@@ -66,6 +69,8 @@
mPlaybackState = psBob.build();
mRenderer.registerListener(mRenderListener);
+
+ initMetadata();
}
public void createSession() {
@@ -92,6 +97,7 @@
| MediaSession.FLAG_HANDLES_MEDIA_BUTTONS);
mSession.setMediaRouter(mRouter);
mSession.setActive(true);
+ updateMetadata();
}
public void onDestroy() {
@@ -130,6 +136,19 @@
mRenderer.setNextContent(request);
}
+ public void setIcon(Bitmap icon) {
+ mMetadataBuilder.putBitmap(MediaMetadata.METADATA_KEY_DISPLAY_ICON, icon);
+ updateMetadata();
+ }
+
+ private void updateMetadata() {
+ // This is a mild abuse of metadata and shouldn't be duplicated in real
+ // code
+ if (mSession != null && mSession.isActive()) {
+ mSession.setMetadata(mMetadataBuilder.build());
+ }
+ }
+
private void updateState(int newState) {
float rate = newState == PlaybackState.STATE_PLAYING ? 1 : 0;
long position = mRenderer == null ? -1 : mRenderer.getSeekPosition();
@@ -140,6 +159,14 @@
mSession.setPlaybackState(mPlaybackState);
}
+ private void initMetadata() {
+ mMetadataBuilder = new MediaMetadata.Builder();
+ mMetadataBuilder.putString(MediaMetadata.METADATA_KEY_DISPLAY_TITLE,
+ "OneMedia display title");
+ mMetadataBuilder.putString(MediaMetadata.METADATA_KEY_DISPLAY_SUBTITLE,
+ "OneMedia display subtitle");
+ }
+
public interface Listener {
public void onPlayStateChanged(PlaybackState state);
}
diff --git a/tests/OneMedia/src/com/android/onemedia/playback/RequestUtils.java b/tests/OneMedia/src/com/android/onemedia/playback/RequestUtils.java
index 3778c5f..1688395 100644
--- a/tests/OneMedia/src/com/android/onemedia/playback/RequestUtils.java
+++ b/tests/OneMedia/src/com/android/onemedia/playback/RequestUtils.java
@@ -26,6 +26,12 @@
public class RequestUtils {
public static final String ACTION_SET_CONTENT = "set_content";
public static final String ACTION_SET_NEXT_CONTENT = "set_next_content";
+ public static final String ACTION_PAUSE = "com.android.onemedia.pause";
+ public static final String ACTION_PLAY = "com.android.onemedia.play";
+ public static final String ACTION_REW = "com.android.onemedia.rew";
+ public static final String ACTION_FFWD = "com.android.onemedia.ffwd";
+ public static final String ACTION_PREV = "com.android.onemedia.prev";
+ public static final String ACTION_NEXT = "com.android.onemedia.next";
public static final String EXTRA_KEY_SOURCE = "source";
public static final String EXTRA_KEY_METADATA = "metadata";
diff --git a/tests/UsesFeature2Test/AndroidManifest.xml b/tests/UsesFeature2Test/AndroidManifest.xml
index 6b6c4da..8caf4a1 100644
--- a/tests/UsesFeature2Test/AndroidManifest.xml
+++ b/tests/UsesFeature2Test/AndroidManifest.xml
@@ -33,12 +33,5 @@
<uses-feature android:name="android.hardware.opengles.aep" />
</feature-group>
- <application android:label="@string/app_title">
- <activity android:name="ActivityMain">
- <intent-filter>
- <action android:name="android.intent.action.MAIN" />
- <category android:name="android.intent.category.LAUNCHER" />
- </intent-filter>
- </activity>
- </application>
+ <application android:label="@string/app_title" android:hasCode="false" />
</manifest>
diff --git a/tools/aapt/ApkBuilder.cpp b/tools/aapt/ApkBuilder.cpp
index 12f6040..01e02e2 100644
--- a/tools/aapt/ApkBuilder.cpp
+++ b/tools/aapt/ApkBuilder.cpp
@@ -85,11 +85,24 @@
if (mName.size() > 0) {
mName.append(",");
mDirName.append("_");
+ mPackageSafeName.append(".");
}
String8 configStr = iter->toString();
+ String8 packageConfigStr(configStr);
+ size_t len = packageConfigStr.length();
+ if (len > 0) {
+ char* buf = packageConfigStr.lockBuffer(len);
+ for (char* end = buf + len; buf < end; ++buf) {
+ if (*buf == '-') {
+ *buf = '_';
+ }
+ }
+ packageConfigStr.unlockBuffer(len);
+ }
mName.append(configStr);
mDirName.append(configStr);
+ mPackageSafeName.append(packageConfigStr);
}
}
diff --git a/tools/aapt/ApkBuilder.h b/tools/aapt/ApkBuilder.h
index db23c84..0d7f06b 100644
--- a/tools/aapt/ApkBuilder.h
+++ b/tools/aapt/ApkBuilder.h
@@ -95,6 +95,10 @@
return mDirName;
}
+ const android::String8& getPackageSafeName() const {
+ return mPackageSafeName;
+ }
+
bool isBase() const {
return mIsBase;
}
@@ -111,6 +115,7 @@
const bool mIsBase;
String8 mName;
String8 mDirName;
+ String8 mPackageSafeName;
std::set<OutputEntry> mFiles;
};
diff --git a/tools/aapt/Command.cpp b/tools/aapt/Command.cpp
index 41102fe..fe0a601 100644
--- a/tools/aapt/Command.cpp
+++ b/tools/aapt/Command.cpp
@@ -764,12 +764,9 @@
return 1;
}
+ // The dynamicRefTable can be null if there are no resources for this asset cookie.
+ // This fine.
const DynamicRefTable* dynamicRefTable = res.getDynamicRefTableForCookie(assetsCookie);
- if (dynamicRefTable == NULL) {
- fprintf(stderr, "ERROR: failed to find dynamic reference table for asset cookie %d\n",
- assetsCookie);
- return 1;
- }
Asset* asset = NULL;
@@ -1676,12 +1673,8 @@
String8 name = getResolvedAttribute(&res, tree, NAME_ATTR, &error);
if (name != "" && error == "") {
- int required = getIntegerAttribute(tree, REQUIRED_ATTR, NULL, 1);
- top.features.add(name, required);
- if (required) {
- addParentFeatures(&top, name);
- }
-
+ top.features.add(name, true);
+ addParentFeatures(&top, name);
} else {
int vers = getIntegerAttribute(tree, GL_ES_VERSION_ATTR, &error);
if (error == "") {
diff --git a/tools/aapt/Images.cpp b/tools/aapt/Images.cpp
index cf3dd0a..137c85c 100644
--- a/tools/aapt/Images.cpp
+++ b/tools/aapt/Images.cpp
@@ -485,9 +485,13 @@
find_max_opacity(image->rows, innerStartX, innerStartY, innerMidX, innerMidY, 1, 1,
&diagonalInset);
- // Determine source radius based upon inset
- // radius = 1 / (sqrt(2) - 1) * inset
- image->outlineRadius = 2.4142f * diagonalInset;
+ /* Determine source radius based upon inset:
+ * sqrt(r^2 + r^2) = sqrt(i^2 + i^2) + r
+ * sqrt(2) * r = sqrt(2) * i + r
+ * (sqrt(2) - 1) * r = sqrt(2) * i
+ * r = sqrt(2) / (sqrt(2) - 1) * i
+ */
+ image->outlineRadius = 3.4142f * diagonalInset;
NOISY(printf("outline insets %d %d %d %d, rad %f, alpha %x\n",
image->outlineInsetsLeft,
diff --git a/tools/aapt/Resource.cpp b/tools/aapt/Resource.cpp
index 2401b3a..0a80805 100644
--- a/tools/aapt/Resource.cpp
+++ b/tools/aapt/Resource.cpp
@@ -937,8 +937,8 @@
}
// Add the 'split' attribute which describes the configurations included.
- String8 splitName("config_");
- splitName.append(split->getDirectorySafeName());
+ String8 splitName("config.");
+ splitName.append(split->getPackageSafeName());
manifest->addAttribute(String16(), String16("split"), String16(splitName));
// Build an empty <application> tag (required).
@@ -1470,6 +1470,8 @@
String16 action16("action");
String16 category16("category");
String16 data16("scheme");
+ String16 feature_group16("feature-group");
+ String16 uses_feature16("uses-feature");
const char* packageIdentChars = "abcdefghijklmnopqrstuvwxyz"
"ABCDEFGHIJKLMNOPQRSTUVWXYZ._0123456789";
const char* packageIdentCharsWithTheStupid = "abcdefghijklmnopqrstuvwxyz"
@@ -1680,10 +1682,43 @@
schemeIdentChars, true) != ATTR_OKAY) {
hasErrors = true;
}
+ } else if (strcmp16(block.getElementName(&len), feature_group16.string()) == 0) {
+ int depth = 1;
+ while ((code=block.next()) != ResXMLTree::END_DOCUMENT
+ && code > ResXMLTree::BAD_DOCUMENT) {
+ if (code == ResXMLTree::START_TAG) {
+ depth++;
+ if (strcmp16(block.getElementName(&len), uses_feature16.string()) == 0) {
+ ssize_t idx = block.indexOfAttribute(
+ RESOURCES_ANDROID_NAMESPACE, "required");
+ if (idx < 0) {
+ continue;
+ }
+
+ int32_t data = block.getAttributeData(idx);
+ if (data == 0) {
+ fprintf(stderr, "%s:%d: Tag <uses-feature> can not have "
+ "android:required=\"false\" when inside a "
+ "<feature-group> tag.\n",
+ manifestPath.string(), block.getLineNumber());
+ hasErrors = true;
+ }
+ }
+ } else if (code == ResXMLTree::END_TAG) {
+ depth--;
+ if (depth == 0) {
+ break;
+ }
+ }
+ }
}
}
}
+ if (hasErrors) {
+ return UNKNOWN_ERROR;
+ }
+
if (resFile != NULL) {
// These resources are now considered to be a part of the included
// resources, for others to reference.