Merge "Fix glyph positions underflow issue" 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 584246b..5778c7e 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -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 {
@@ -23027,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 b13b009..b37f896 100644
--- a/cmds/media/src/com/android/commands/media/Media.java
+++ b/cmds/media/src/com/android/commands/media/Media.java
@@ -225,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!");
}
@@ -257,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/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/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/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/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/hardware/camera2/legacy/LegacyCameraDevice.java b/core/java/android/hardware/camera2/legacy/LegacyCameraDevice.java
index cbf4a3d..1cf7797 100644
--- a/core/java/android/hardware/camera2/legacy/LegacyCameraDevice.java
+++ b/core/java/android/hardware/camera2/legacy/LegacyCameraDevice.java
@@ -510,4 +510,6 @@
/*out*/int[/*2*/] dimens);
private static native int nativeSetNextTimestamp(Surface surface, long timestamp);
+
+ static native int nativeGetJpegFooterSize();
}
diff --git a/core/java/android/hardware/camera2/legacy/LegacyMetadataMapper.java b/core/java/android/hardware/camera2/legacy/LegacyMetadataMapper.java
index 0337c96..fbe26e5 100644
--- a/core/java/android/hardware/camera2/legacy/LegacyMetadataMapper.java
+++ b/core/java/android/hardware/camera2/legacy/LegacyMetadataMapper.java
@@ -33,6 +33,7 @@
import android.hardware.camera2.utils.ArrayUtils;
import android.hardware.camera2.utils.ListUtils;
import android.hardware.camera2.utils.ParamsUtils;
+import android.hardware.camera2.utils.SizeAreaComparator;
import android.util.Log;
import android.util.Range;
import android.util.Size;
@@ -40,6 +41,8 @@
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collections;
+import java.util.Comparator;
import java.util.List;
import static com.android.internal.util.Preconditions.*;
@@ -187,8 +190,10 @@
* flash.*
*/
mapFlash(m, p);
-
- // TODO: map other fields
+ /*
+ * jpeg.*
+ */
+ mapJpeg(m, p);
/*
* scaler.*
@@ -595,6 +600,16 @@
m.set(FLASH_INFO_AVAILABLE, flashAvailable);
}
+ private static void mapJpeg(CameraMetadataNative m, Camera.Parameters p) {
+ List<Camera.Size> thumbnailSizes = p.getSupportedJpegThumbnailSizes();
+
+ if (thumbnailSizes != null) {
+ Size[] sizes = convertSizeListToArray(thumbnailSizes);
+ Arrays.sort(sizes, new SizeAreaComparator());
+ m.set(JPEG_AVAILABLE_THUMBNAIL_SIZES, sizes);
+ }
+ }
+
private static void mapRequest(CameraMetadataNative m, Parameters p) {
/*
* request.availableCapabilities
diff --git a/core/java/android/hardware/camera2/legacy/LegacyRequestMapper.java b/core/java/android/hardware/camera2/legacy/LegacyRequestMapper.java
index 35646fe..4c4ad0d2 100644
--- a/core/java/android/hardware/camera2/legacy/LegacyRequestMapper.java
+++ b/core/java/android/hardware/camera2/legacy/LegacyRequestMapper.java
@@ -24,6 +24,7 @@
import android.hardware.camera2.params.MeteringRectangle;
import android.hardware.camera2.utils.ListUtils;
import android.hardware.camera2.utils.ParamsUtils;
+import android.location.Location;
import android.util.Log;
import android.util.Range;
import android.util.Size;
@@ -44,6 +45,8 @@
private static final String TAG = "LegacyRequestMapper";
private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
+ private static final byte DEFAULT_JPEG_QUALITY = 85;
+
/**
* Set the legacy parameters using the {@link LegacyRequest legacy request}.
*
@@ -350,6 +353,70 @@
+ testPatternMode + "; only OFF is supported");
}
}
+
+ /*
+ * jpeg.*
+ */
+
+ // jpeg.gpsLocation
+ {
+ Location location = request.get(JPEG_GPS_LOCATION);
+ if (location != null) {
+ if (checkForCompleteGpsData(location)) {
+ params.setGpsAltitude(location.getAltitude());
+ params.setGpsLatitude(location.getLatitude());
+ params.setGpsLongitude(location.getLongitude());
+ params.setGpsProcessingMethod(location.getProvider().toUpperCase());
+ params.setGpsTimestamp(location.getTime());
+ } else {
+ Log.w(TAG, "Incomplete GPS parameters provided in location " + location);
+ }
+ } else {
+ params.removeGpsData();
+ }
+ }
+
+ // jpeg.orientation
+ {
+ int orientation = characteristics.get(CameraCharacteristics.SENSOR_ORIENTATION);
+ params.setRotation(ParamsUtils.getOrDefault(request, JPEG_ORIENTATION, orientation));
+ }
+
+ // jpeg.quality
+ {
+ params.setJpegQuality(0xFF & ParamsUtils.getOrDefault(request, JPEG_QUALITY,
+ DEFAULT_JPEG_QUALITY));
+ }
+
+ // jpeg.thumbnailQuality
+ {
+ params.setJpegQuality(0xFF & ParamsUtils.getOrDefault(request, JPEG_THUMBNAIL_QUALITY,
+ DEFAULT_JPEG_QUALITY));
+ }
+
+ // jpeg.thumbnailSize
+ {
+ List<Camera.Size> sizes = params.getSupportedJpegThumbnailSizes();
+
+ if (sizes != null && sizes.size() > 0) {
+ Size s = request.get(JPEG_THUMBNAIL_SIZE);
+ boolean invalidSize = (s == null) ? false : !ParameterUtils.containsSize(sizes,
+ s.getWidth(), s.getHeight());
+ if (invalidSize) {
+ Log.w(TAG, "Invalid JPEG thumbnail size set " + s + ", skipping thumbnail...");
+ }
+ if (s == null || invalidSize) {
+ // (0,0) = "no thumbnail" in Camera API 1
+ params.setJpegThumbnailSize(/*width*/0, /*height*/0);
+ } else {
+ params.setJpegThumbnailSize(s.getWidth(), s.getHeight());
+ }
+ }
+ }
+ }
+
+ private static boolean checkForCompleteGpsData(Location location) {
+ return location != null && location.getProvider() != null && location.getTime() != 0;
}
static int filterSupportedCaptureIntent(int captureIntent) {
diff --git a/core/java/android/hardware/camera2/legacy/LegacyResultMapper.java b/core/java/android/hardware/camera2/legacy/LegacyResultMapper.java
index a2f9b4c..090a822 100644
--- a/core/java/android/hardware/camera2/legacy/LegacyResultMapper.java
+++ b/core/java/android/hardware/camera2/legacy/LegacyResultMapper.java
@@ -28,6 +28,7 @@
import android.hardware.camera2.params.MeteringRectangle;
import android.hardware.camera2.utils.ListUtils;
import android.hardware.camera2.utils.ParamsUtils;
+import android.location.Location;
import android.util.Log;
import android.util.Size;
@@ -250,6 +251,29 @@
result.set(SENSOR_TEST_PATTERN_MODE, SENSOR_TEST_PATTERN_MODE_OFF);
}
+ /*
+ * jpeg
+ */
+ // jpeg.gpsLocation
+ result.set(JPEG_GPS_LOCATION, request.get(CaptureRequest.JPEG_GPS_LOCATION));
+
+ // jpeg.orientation
+ result.set(JPEG_ORIENTATION, request.get(CaptureRequest.JPEG_ORIENTATION));
+
+ // jpeg.quality
+ result.set(JPEG_QUALITY, (byte) params.getJpegQuality());
+
+ // jpeg.thumbnailQuality
+ result.set(JPEG_THUMBNAIL_QUALITY, (byte) params.getJpegThumbnailQuality());
+
+ // jpeg.thumbnailSize
+ Camera.Size s = params.getJpegThumbnailSize();
+ if (s != null) {
+ result.set(JPEG_THUMBNAIL_SIZE, ParameterUtils.convertSize(s));
+ } else {
+ Log.w(TAG, "Null thumbnail size received from parameters.");
+ }
+
// TODO: Remaining result metadata tags conversions.
return result;
}
diff --git a/core/java/android/hardware/camera2/legacy/ParameterUtils.java b/core/java/android/hardware/camera2/legacy/ParameterUtils.java
index 385f844..98adcea 100644
--- a/core/java/android/hardware/camera2/legacy/ParameterUtils.java
+++ b/core/java/android/hardware/camera2/legacy/ParameterUtils.java
@@ -253,6 +253,33 @@
}
/**
+ * Convert a camera API1 list of sizes into an array of sizes
+ */
+ public static Size[] convertSizeListToArray(List<Camera.Size> sizeList) {
+ checkNotNull(sizeList, "sizeList must not be null");
+
+ Size[] array = new Size[sizeList.size()];
+ int ctr = 0;
+ for (Camera.Size s : sizeList) {
+ array[ctr++] = new Size(s.width, s.height);
+ }
+ return array;
+ }
+
+ /**
+ * Check if the camera API1 list of sizes contains a size with the given dimens.
+ */
+ public static boolean containsSize(List<Camera.Size> sizeList, int width, int height) {
+ checkNotNull(sizeList, "sizeList must not be null");
+ for (Camera.Size s : sizeList) {
+ if (s.height == height && s.width == width) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
* Returns the largest supported picture size, as compared by its area.
*/
public static Size getLargestSupportedJpegSizeByArea(Camera.Parameters params) {
diff --git a/core/java/android/hardware/camera2/legacy/RequestThreadManager.java b/core/java/android/hardware/camera2/legacy/RequestThreadManager.java
index 5c66753..eb8debb 100644
--- a/core/java/android/hardware/camera2/legacy/RequestThreadManager.java
+++ b/core/java/android/hardware/camera2/legacy/RequestThreadManager.java
@@ -190,7 +190,8 @@
try {
if (RequestHolder.jpegType(s)) {
Log.i(TAG, "Producing jpeg buffer...");
- LegacyCameraDevice.setSurfaceDimens(s, data.length, /*height*/1);
+ LegacyCameraDevice.setSurfaceDimens(s, data.length +
+ LegacyCameraDevice.nativeGetJpegFooterSize(), /*height*/1);
LegacyCameraDevice.setNextTimestamp(s, timestamp);
LegacyCameraDevice.produceFrame(s, data, data.length, /*height*/1,
CameraMetadataNative.NATIVE_JPEG_FORMAT);
@@ -665,10 +666,6 @@
}
mReceivedJpeg.close();
doJpegCapturePrepare(holder);
- if (!mReceivedJpeg.block(JPEG_FRAME_TIMEOUT)) {
- // TODO: report error to CameraDevice
- Log.e(TAG, "Hit timeout for jpeg callback!");
- }
}
/*
diff --git a/core/java/android/hardware/camera2/legacy/SurfaceTextureRenderer.java b/core/java/android/hardware/camera2/legacy/SurfaceTextureRenderer.java
index 3f24b2c..b1b0f9b 100644
--- a/core/java/android/hardware/camera2/legacy/SurfaceTextureRenderer.java
+++ b/core/java/android/hardware/camera2/legacy/SurfaceTextureRenderer.java
@@ -16,6 +16,7 @@
package android.hardware.camera2.legacy;
import android.graphics.ImageFormat;
+import android.graphics.RectF;
import android.graphics.SurfaceTexture;
import android.os.Environment;
import android.opengl.EGL14;
@@ -194,6 +195,9 @@
checkGlError("onDrawFrame start");
st.getTransformMatrix(mSTMatrix);
+ Matrix.setIdentityM(mMVPMatrix, /*smOffset*/0);
+
+ // Find intermediate buffer dimensions
Size dimens;
try {
dimens = LegacyCameraDevice.getTextureSize(st);
@@ -201,9 +205,6 @@
// Should never hit this.
throw new IllegalStateException("Surface abandoned, skipping drawFrame...", e);
}
-
- Matrix.setIdentityM(mMVPMatrix, /*smOffset*/0);
-
float texWidth = dimens.getWidth();
float texHeight = dimens.getHeight();
@@ -211,32 +212,30 @@
throw new IllegalStateException("Illegal intermediate texture with dimension of 0");
}
- // Find largest scaling factor from the intermediate texture dimension to the
- // output surface dimension. Scaling the intermediate texture by this allows
- // us to letterbox/pillerbox the output surface into the intermediate texture.
- float widthRatio = width / texWidth;
- float heightRatio = height / texHeight;
- float actual = (widthRatio < heightRatio) ? heightRatio : widthRatio;
+ // Letterbox or pillerbox output dimensions into intermediate dimensions.
+ RectF intermediate = new RectF(/*left*/0, /*top*/0, /*right*/texWidth, /*bottom*/texHeight);
+ RectF output = new RectF(/*left*/0, /*top*/0, /*right*/width, /*bottom*/height);
+ android.graphics.Matrix boxingXform = new android.graphics.Matrix();
+ boxingXform.setRectToRect(output, intermediate, android.graphics.Matrix.ScaleToFit.CENTER);
+ boxingXform.mapRect(output);
+
+ // Find scaling factor from pillerboxed/letterboxed output dimensions to intermediate
+ // buffer dimensions.
+ float scaleX = intermediate.width() / output.width();
+ float scaleY = intermediate.height() / output.height();
+
+ // Scale opposite dimension in clip coordinates so output is letterboxed/pillerboxed into
+ // the intermediate dimensions (rather than vice-versa).
+ Matrix.scaleM(mMVPMatrix, /*offset*/0, /*x*/scaleY, /*y*/scaleX, /*z*/1);
if (DEBUG) {
- Log.d(TAG, "Scaling factor " + actual + " used for " + width + "x" + height +
- " surface, intermediate buffer size is " + texWidth + "x" + texHeight);
+ Log.d(TAG, "Scaling factors (S_x = " + scaleX + ",S_y = " + scaleY + ") used for " +
+ width + "x" + height + " surface, intermediate buffer size is " + texWidth +
+ "x" + texHeight);
}
- // Set the viewport height and width to be the scaled intermediate texture dimensions.
- int viewportW = (int) (actual * texWidth);
- int viewportH = (int) (actual * texHeight);
-
- // Set the offset of the viewport so that the output surface is centered in the viewport.
- float dx = (width - viewportW) / 2f;
- float dy = (height - viewportH) / 2f;
-
- if (DEBUG) {
- Log.d(TAG, "Translation " + dx + "," + dy + " used for " + width + "x" + height +
- " surface");
- }
-
- GLES20.glViewport((int) dx, (int) dy, viewportW, viewportH);
+ // Set viewport to be output buffer dimensions
+ GLES20.glViewport(0, 0, width, height);
if (DEBUG) {
GLES20.glClearColor(1.0f, 0.0f, 0.0f, 1.0f);
@@ -251,7 +250,7 @@
mTriangleVertices.position(TRIANGLE_VERTICES_DATA_POS_OFFSET);
GLES20.glVertexAttribPointer(maPositionHandle, VERTEX_POS_SIZE, GLES20.GL_FLOAT,
- /*normalized*/ false,TRIANGLE_VERTICES_DATA_STRIDE_BYTES, mTriangleVertices);
+ /*normalized*/ false, TRIANGLE_VERTICES_DATA_STRIDE_BYTES, mTriangleVertices);
checkGlError("glVertexAttribPointer maPosition");
GLES20.glEnableVertexAttribArray(maPositionHandle);
checkGlError("glEnableVertexAttribArray maPositionHandle");
@@ -654,6 +653,8 @@
if (LegacyCameraDevice.containsSurfaceId(holder.surface, targetSurfaceIds)) {
makeCurrent(holder.eglSurface);
try {
+ LegacyCameraDevice.setSurfaceDimens(holder.surface, holder.width,
+ holder.height);
LegacyCameraDevice.setNextTimestamp(holder.surface, captureHolder.second);
drawFrame(mSurfaceTexture, holder.width, holder.height);
swapBuffers(holder.eglSurface);
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/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/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/jni/android_hardware_camera2_legacy_LegacyCameraDevice.cpp b/core/jni/android_hardware_camera2_legacy_LegacyCameraDevice.cpp
index 697cdc6..980ead0 100644
--- a/core/jni/android_hardware_camera2_legacy_LegacyCameraDevice.cpp
+++ b/core/jni/android_hardware_camera2_legacy_LegacyCameraDevice.cpp
@@ -541,6 +541,15 @@
ALOGE("%s: Error while setting surface dimens %s (%d).", __FUNCTION__, strerror(-err), err);
return err;
}
+
+ // WAR - Set user dimensions also to avoid incorrect scaling after TextureView orientation
+ // change.
+ err = native_window_set_buffers_user_dimensions(anw.get(), width, height);
+ if (err != NO_ERROR) {
+ ALOGE("%s: Error while setting surface user dimens %s (%d).", __FUNCTION__, strerror(-err),
+ err);
+ return err;
+ }
return NO_ERROR;
}
@@ -624,6 +633,11 @@
return NO_ERROR;
}
+static jint LegacyCameraDevice_nativeGetJpegFooterSize(JNIEnv* env, jobject thiz) {
+ ALOGV("nativeGetJpegFooterSize");
+ return static_cast<jint>(sizeof(struct camera3_jpeg_blob));
+}
+
} // extern "C"
static JNINativeMethod gCameraDeviceMethods[] = {
@@ -657,6 +671,9 @@
{ "nativeSetNextTimestamp",
"(Landroid/view/Surface;J)I",
(void *)LegacyCameraDevice_nativeSetNextTimestamp },
+ { "nativeGetJpegFooterSize",
+ "()I",
+ (void *)LegacyCameraDevice_nativeGetJpegFooterSize },
};
// Get all the required offsets in java class and register native functions
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 931d1c6..aee3090 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -1257,6 +1257,14 @@
android:description="@string/permdesc_bind_call_service"
android:label="@string/permlab_bind_call_service" />
+ <!-- @SystemApi Allows an application to bind to ConnectionService implementations.
+ @hide -->
+ <permission android:name="android.permission.BIND_CONNECTION_SERVICE"
+ android:permissionGroup="android.permission-group.PHONE_CALLS"
+ android:protectionLevel="system|signature"
+ android:description="@string/permdesc_bind_connection_service"
+ android:label="@string/permlab_bind_connection_service" />
+
<!-- ================================== -->
<!-- Permissions for sdcard interaction -->
<!-- ================================== -->
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/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/strings.xml b/core/res/res/values/strings.xml
index 74992ed..6262f13 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -2117,6 +2117,11 @@
<string name="permdesc_bind_call_service">Allows the app to control when and how the user sees the in-call screen.</string>
<!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+ <string name="permlab_bind_connection_service">interact with telephony services</string>
+ <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+ <string name="permdesc_bind_connection_service">Allows the app to interact with telephony services to make/receive calls.</string>
+
+ <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
<string name="permlab_readNetworkUsageHistory">read historical network usage</string>
<!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
<string name="permdesc_readNetworkUsageHistory">Allows the app to read historical network usage for specific networks and apps.</string>
@@ -5523,4 +5528,7 @@
<!-- [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/symbols.xml b/core/res/res/values/symbols.xml
index a4bec17..2cd0948 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1903,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" />
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/AnimatedStateListDrawable.java b/graphics/java/android/graphics/drawable/AnimatedStateListDrawable.java
index fc38e8a..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");
}
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/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/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/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/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 8c5151b..947d70d5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
@@ -1735,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 5b0bf03..127ff6c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java
@@ -255,7 +255,7 @@
public void setBelowSpeedBump(boolean below) {
}
- public void reset() {
+ 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/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/StatusBarWindowView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
index 04ee294..e1fd779 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
@@ -121,6 +121,7 @@
MotionEvent cancellation = MotionEvent.obtain(ev);
cancellation.setAction(MotionEvent.ACTION_CANCEL);
mStackScrollLayout.onInterceptTouchEvent(cancellation);
+ mNotificationPanel.onInterceptTouchEvent(cancellation);
cancellation.recycle();
}
return intercept;
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 8e5077c..1469d73 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
@@ -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 fc2be1a..fe855d9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java
@@ -758,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;
}
@@ -825,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/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/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/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/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/telecomm/java/android/telecomm/Call.java b/telecomm/java/android/telecomm/Call.java
index f988ac8..7223574 100644
--- a/telecomm/java/android/telecomm/Call.java
+++ b/telecomm/java/android/telecomm/Call.java
@@ -27,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.
@@ -364,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);
@@ -589,7 +590,9 @@
* @param listener A {@code Listener}.
*/
public void removeListener(Listener listener) {
- mListeners.remove(listener);
+ if (listener != null) {
+ mListeners.remove(listener);
+ }
}
/** {@hide} */
@@ -709,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 d5e4f1b..8ab5e13 100644
--- a/telecomm/java/android/telecomm/ConnectionService.java
+++ b/telecomm/java/android/telecomm/ConnectionService.java
@@ -16,6 +16,7 @@
package android.telecomm;
+import android.Manifest;
import android.annotation.SdkConstant;
import android.app.PendingIntent;
import android.app.Service;
@@ -26,6 +27,8 @@
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
+import android.os.Parcel;
+import android.os.RemoteException;
import android.telephony.DisconnectCause;
import com.android.internal.os.SomeArgs;
@@ -45,7 +48,6 @@
* Android device.
*/
public abstract class ConnectionService extends Service {
-
/**
* The {@link Intent} that must be declared as handled by the service.
*/
@@ -81,6 +83,18 @@
private final ConnectionServiceAdapter mAdapter = new ConnectionServiceAdapter();
private final IBinder mBinder = new IConnectionService.Stub() {
+ /**
+ * Enforces the requirement that all calls into the ConnectionService require the
+ * {@code BIND_CONNECTION_SERVICE} permission.
+ */
+ @Override
+ public boolean onTransact(int code, Parcel data, Parcel reply,
+ int flags) throws RemoteException
+ {
+ enforceBindConnectionServicePermission();
+ return super.onTransact(code, data, reply, flags);
+ }
+
@Override
public void addConnectionServiceAdapter(IConnectionServiceAdapter adapter) {
mHandler.obtainMessage(MSG_ADD_CONNECTION_SERVICE_ADAPTER, adapter).sendToTarget();
@@ -481,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);
@@ -539,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) {
@@ -617,7 +631,8 @@
public void onError(String request, int code, String reason) {
// no-op
}
- });
+ }
+ );
}
private void splitFromConference(String callId) {
@@ -830,4 +845,10 @@
return Connection.getNullConnection();
}
+ /**
+ * Enforces the {@code BIND_CONNECTION_SERVICE} permission for connection service calls.
+ */
+ private void enforceBindConnectionServicePermission() {
+ enforceCallingPermission(Manifest.permission.BIND_CONNECTION_SERVICE, null);
+ }
}
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/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/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/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/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/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 010d59b..0a80805 100644
--- a/tools/aapt/Resource.cpp
+++ b/tools/aapt/Resource.cpp
@@ -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.