Merge "Remove MR2 BLE Advertising hidden API from L codebase (1/2)." into lmp-dev
diff --git a/Android.mk b/Android.mk
index a823ba0..8a50ae84 100644
--- a/Android.mk
+++ b/Android.mk
@@ -336,6 +336,7 @@
media/java/android/media/tv/ITvInputHardware.aidl \
media/java/android/media/tv/ITvInputHardwareCallback.aidl \
media/java/android/media/tv/ITvInputManager.aidl \
+ media/java/android/media/tv/ITvInputManagerCallback.aidl \
media/java/android/media/tv/ITvInputService.aidl \
media/java/android/media/tv/ITvInputServiceCallback.aidl \
media/java/android/media/tv/ITvInputSession.aidl \
diff --git a/api/current.txt b/api/current.txt
index fcc02d0..8076d47 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -8358,6 +8358,36 @@
field public int reqGlEsVersion;
}
+ public class InstallSessionInfo implements android.os.Parcelable {
+ method public int describeContents();
+ method public android.graphics.Bitmap getIcon();
+ method public java.lang.String getInstallerPackageName();
+ method public java.lang.String getPackageName();
+ method public int getProgress();
+ method public int getSessionId();
+ method public java.lang.CharSequence getTitle();
+ 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();
+ method public int describeContents();
+ method public void setDeltaSize(long);
+ method public void setIcon(android.graphics.Bitmap);
+ method public void setInstallLocation(int);
+ method public void setModeFullInstall();
+ method public void setModeInheritExisting();
+ method public void setOriginatingUri(android.net.Uri);
+ method public void setPackageName(java.lang.String);
+ method public void setProgressMax(int);
+ method public void setReferrerUri(android.net.Uri);
+ method public void setSignatures(android.content.pm.Signature[]);
+ method public void setTitle(java.lang.CharSequence);
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator CREATOR;
+ }
+
public class InstrumentationInfo extends android.content.pm.PackageItemInfo implements android.os.Parcelable {
ctor public InstrumentationInfo();
ctor public InstrumentationInfo(android.content.pm.InstrumentationInfo);
@@ -8425,6 +8455,9 @@
method public int describeContents();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator CREATOR;
+ field public static final int INSTALL_LOCATION_AUTO = 0; // 0x0
+ field public static final int INSTALL_LOCATION_INTERNAL_ONLY = 1; // 0x1
+ field public static final int INSTALL_LOCATION_PREFER_EXTERNAL = 2; // 0x2
field public static final int REQUESTED_PERMISSION_GRANTED = 2; // 0x2
field public static final int REQUESTED_PERMISSION_REQUIRED = 1; // 0x1
field public android.content.pm.ActivityInfo[] activities;
@@ -8432,6 +8465,7 @@
field public android.content.pm.ConfigurationInfo[] configPreferences;
field public long firstInstallTime;
field public int[] gids;
+ field public int installLocation;
field public android.content.pm.InstrumentationInfo[] instrumentation;
field public long lastUpdateTime;
field public java.lang.String packageName;
@@ -8445,10 +8479,52 @@
field public java.lang.String sharedUserId;
field public int sharedUserLabel;
field public android.content.pm.Signature[] signatures;
+ field public java.lang.String[] splitNames;
field public int versionCode;
field public java.lang.String versionName;
}
+ public class PackageInstaller {
+ method public int createSession(android.content.pm.InstallSessionParams) throws java.io.IOException;
+ method public java.util.List<android.content.pm.InstallSessionInfo> getActiveSessions();
+ method public android.content.pm.PackageInstaller.Session openSession(int);
+ method public void registerSessionObserver(android.content.pm.PackageInstaller.SessionObserver);
+ method public void uninstall(java.lang.String, android.content.pm.PackageInstaller.UninstallResultCallback);
+ method public void unregisterSessionObserver(android.content.pm.PackageInstaller.SessionObserver);
+ }
+
+ public static abstract class PackageInstaller.CommitResultCallback {
+ ctor public PackageInstaller.CommitResultCallback();
+ method public abstract void onFailure(java.lang.String);
+ method public void onFailureConflict(java.lang.String, java.lang.String);
+ method public void onFailureIncompatible(java.lang.String);
+ method public void onFailureInvalid(java.lang.String);
+ method public void onFailureStorage(java.lang.String);
+ method public abstract void onSuccess();
+ }
+
+ public static class PackageInstaller.Session implements java.io.Closeable {
+ method public void close();
+ method public void commit(android.content.pm.PackageInstaller.CommitResultCallback);
+ method public void destroy();
+ method public void fsync(java.io.OutputStream) throws java.io.IOException;
+ method public java.io.OutputStream openWrite(java.lang.String, long, long) throws java.io.IOException;
+ method public void setProgress(int);
+ }
+
+ public static abstract class PackageInstaller.SessionObserver {
+ ctor public PackageInstaller.SessionObserver();
+ method public abstract void onCreated(android.content.pm.InstallSessionInfo);
+ method public abstract void onFinalized(int, boolean);
+ method public abstract void onProgress(int, int);
+ }
+
+ public static abstract class PackageInstaller.UninstallResultCallback {
+ ctor public PackageInstaller.UninstallResultCallback();
+ method public abstract void onFailure(java.lang.String);
+ method public abstract void onSuccess();
+ }
+
public class PackageItemInfo {
ctor public PackageItemInfo();
ctor public PackageItemInfo(android.content.pm.PackageItemInfo);
@@ -8511,6 +8587,7 @@
method public abstract android.graphics.drawable.Drawable getDrawable(java.lang.String, int, android.content.pm.ApplicationInfo);
method public abstract java.util.List<android.content.pm.ApplicationInfo> getInstalledApplications(int);
method public abstract java.util.List<android.content.pm.PackageInfo> getInstalledPackages(int);
+ method public abstract android.content.pm.PackageInstaller getInstaller();
method public abstract java.lang.String getInstallerPackageName(java.lang.String);
method public abstract android.content.pm.InstrumentationInfo getInstrumentationInfo(android.content.ComponentName, int) throws android.content.pm.PackageManager.NameNotFoundException;
method public abstract android.content.pm.KeySet getKeySetByAlias(java.lang.String, java.lang.String);
@@ -16425,8 +16502,13 @@
}
public final class TvInputManager {
- method public boolean getAvailability(java.lang.String);
+ method public int getInputState(java.lang.String);
method public java.util.List<android.media.tv.TvInputInfo> getTvInputList();
+ method public void registerListener(android.media.tv.TvInputManager.TvInputListener, android.os.Handler);
+ method public void unregisterListener(android.media.tv.TvInputManager.TvInputListener);
+ field public static final int INPUT_STATE_CONNECTED = 0; // 0x0
+ field public static final int INPUT_STATE_CONNECTED_STANDBY = 1; // 0x1
+ field public static final int INPUT_STATE_DISCONNECTED = 2; // 0x2
field public static final int VIDEO_UNAVAILABLE_REASON_BUFFERING = 3; // 0x3
field public static final int VIDEO_UNAVAILABLE_REASON_TUNE = 1; // 0x1
field public static final int VIDEO_UNAVAILABLE_REASON_UNKNOWN = 0; // 0x0
@@ -16435,7 +16517,7 @@
public static abstract class TvInputManager.TvInputListener {
ctor public TvInputManager.TvInputListener();
- method public void onAvailabilityChanged(java.lang.String, boolean);
+ method public void onInputStateChanged(java.lang.String, int);
}
public abstract class TvInputService extends android.app.Service {
@@ -27956,10 +28038,10 @@
public class CallPropertyPresentation {
ctor public CallPropertyPresentation();
- field public static final int ALLOWED = 0; // 0x0
- field public static final int PAYPHONE = 3; // 0x3
- field public static final int RESTRICTED = 1; // 0x1
- field public static final int UNKNOWN = 2; // 0x2
+ field public static final int ALLOWED = 1; // 0x1
+ field public static final int PAYPHONE = 4; // 0x4
+ field public static final int RESTRICTED = 2; // 0x2
+ field public static final int UNKNOWN = 3; // 0x3
}
public final class CallState extends java.lang.Enum {
@@ -28079,7 +28161,8 @@
public abstract class ConnectionService extends android.app.Service {
ctor public ConnectionService();
- method public final void createRemoteOutgoingConnection(android.telecomm.ConnectionRequest, android.telecomm.ConnectionService.OutgoingCallResponse<android.telecomm.RemoteConnection>);
+ method public final void createRemoteIncomingConnection(android.telecomm.ConnectionRequest, android.telecomm.ConnectionService.CreateConnectionResponse<android.telecomm.RemoteConnection>);
+ method public final void createRemoteOutgoingConnection(android.telecomm.ConnectionRequest, android.telecomm.ConnectionService.CreateConnectionResponse<android.telecomm.RemoteConnection>);
method public final java.util.Collection<android.telecomm.Connection> getAllConnections();
method public final void lookupRemoteAccounts(android.net.Uri, android.telecomm.SimpleResponse<android.net.Uri, java.util.List<android.telecomm.PhoneAccount>>);
method public final void maybeRespondToAccountLookup();
@@ -28087,11 +28170,11 @@
method protected void onConnectionAdded(android.telecomm.Connection);
method protected void onConnectionRemoved(android.telecomm.Connection);
method protected void onCreateConferenceConnection(java.lang.String, android.telecomm.Connection, android.telecomm.Response<java.lang.String, android.telecomm.Connection>);
- method protected void onCreateConnections(android.telecomm.ConnectionRequest, android.telecomm.ConnectionService.OutgoingCallResponse<android.telecomm.Connection>);
- method protected void onCreateIncomingConnection(android.telecomm.ConnectionRequest, android.telecomm.Response<android.telecomm.ConnectionRequest, android.telecomm.Connection>);
+ method protected void onCreateIncomingConnection(android.telecomm.ConnectionRequest, android.telecomm.ConnectionService.CreateConnectionResponse<android.telecomm.Connection>);
+ method protected void onCreateOutgoingConnection(android.telecomm.ConnectionRequest, android.telecomm.ConnectionService.CreateConnectionResponse<android.telecomm.Connection>);
}
- public static abstract interface ConnectionService.OutgoingCallResponse {
+ public static abstract interface ConnectionService.CreateConnectionResponse {
method public abstract void onCancel(android.telecomm.ConnectionRequest);
method public abstract void onFailure(android.telecomm.ConnectionRequest, int, java.lang.String);
method public abstract void onSuccess(android.telecomm.ConnectionRequest, CONNECTION);
@@ -29428,8 +29511,10 @@
method public android.graphics.drawable.Drawable getDrawable(java.lang.String, int, android.content.pm.ApplicationInfo);
method public java.util.List<android.content.pm.ApplicationInfo> getInstalledApplications(int);
method public java.util.List<android.content.pm.PackageInfo> getInstalledPackages(int);
+ method public android.content.pm.PackageInstaller getInstaller();
method public java.lang.String getInstallerPackageName(java.lang.String);
method public android.content.pm.InstrumentationInfo getInstrumentationInfo(android.content.ComponentName, int) throws android.content.pm.PackageManager.NameNotFoundException;
+ method public android.content.pm.KeySet getKeySetByAlias(java.lang.String, java.lang.String);
method public android.content.Intent getLaunchIntentForPackage(java.lang.String);
method public android.content.Intent getLeanbackLaunchIntentForPackage(java.lang.String);
method public java.lang.String getNameForUid(int);
@@ -29447,12 +29532,15 @@
method public android.content.res.Resources getResourcesForApplication(android.content.pm.ApplicationInfo);
method public android.content.res.Resources getResourcesForApplication(java.lang.String) throws android.content.pm.PackageManager.NameNotFoundException;
method public android.content.pm.ServiceInfo getServiceInfo(android.content.ComponentName, int) throws android.content.pm.PackageManager.NameNotFoundException;
+ method public android.content.pm.KeySet getSigningKeySet(java.lang.String);
method public android.content.pm.FeatureInfo[] getSystemAvailableFeatures();
method public java.lang.String[] getSystemSharedLibraryNames();
method public java.lang.CharSequence getText(java.lang.String, int, android.content.pm.ApplicationInfo);
method public android.content.res.XmlResourceParser getXml(java.lang.String, int, android.content.pm.ApplicationInfo);
method public boolean hasSystemFeature(java.lang.String);
method public boolean isSafeMode();
+ method public boolean isSignedBy(java.lang.String, android.content.pm.KeySet);
+ method public boolean isSignedByExactly(java.lang.String, android.content.pm.KeySet);
method public java.util.List<android.content.pm.ResolveInfo> queryBroadcastReceivers(android.content.Intent, int);
method public java.util.List<android.content.pm.ProviderInfo> queryContentProviders(java.lang.String, int, int);
method public java.util.List<android.content.pm.InstrumentationInfo> queryInstrumentation(java.lang.String, int);
diff --git a/cmds/pm/src/com/android/commands/pm/Pm.java b/cmds/pm/src/com/android/commands/pm/Pm.java
index 3a2ca30..b7f1ff9 100644
--- a/cmds/pm/src/com/android/commands/pm/Pm.java
+++ b/cmds/pm/src/com/android/commands/pm/Pm.java
@@ -998,7 +998,7 @@
final InstallSessionParams params = new InstallSessionParams();
params.installFlags = PackageManager.INSTALL_ALL_USERS;
- params.fullInstall = true;
+ params.mode = InstallSessionParams.MODE_FULL_INSTALL;
params.progressMax = -1;
String opt;
@@ -1021,7 +1021,7 @@
} else if (opt.equals("-d")) {
params.installFlags |= PackageManager.INSTALL_ALLOW_DOWNGRADE;
} else if (opt.equals("-p")) {
- params.fullInstall = false;
+ params.mode = InstallSessionParams.MODE_INHERIT_EXISTING;
} else if (opt.equals("-S")) {
params.deltaSize = Long.parseLong(nextOptionData());
params.progressMax = (int) params.deltaSize;
diff --git a/core/java/android/animation/PropertyValuesHolder.java b/core/java/android/animation/PropertyValuesHolder.java
index bf2924c..bdfbde1 100644
--- a/core/java/android/animation/PropertyValuesHolder.java
+++ b/core/java/android/animation/PropertyValuesHolder.java
@@ -631,6 +631,9 @@
public void setObjectValues(Object... values) {
mValueType = values[0].getClass();
mKeyframeSet = KeyframeSet.ofObject(values);
+ if (mEvaluator != null) {
+ mKeyframeSet.setEvaluator(mEvaluator);
+ }
}
/**
diff --git a/core/java/android/app/ActivityTransitionState.java b/core/java/android/app/ActivityTransitionState.java
index 0d2af8c..4f556a8 100644
--- a/core/java/android/app/ActivityTransitionState.java
+++ b/core/java/android/app/ActivityTransitionState.java
@@ -95,6 +95,8 @@
*/
private int mExitTransitionCoordinatorsKey = 1;
+ private boolean mIsEnterTriggered;
+
public ActivityTransitionState() {
}
@@ -142,8 +144,10 @@
public void setEnterActivityOptions(Activity activity, ActivityOptions options) {
if (activity.getWindow().hasFeature(Window.FEATURE_CONTENT_TRANSITIONS)
&& options != null && mEnterActivityOptions == null
+ && mEnterTransitionCoordinator == null
&& options.getAnimationType() == ActivityOptions.ANIM_SCENE_TRANSITION) {
mEnterActivityOptions = options;
+ mIsEnterTriggered = false;
if (mEnterActivityOptions.isReturning()) {
int result = mEnterActivityOptions.getResultCode();
if (result != 0) {
@@ -154,9 +158,10 @@
}
public void enterReady(Activity activity) {
- if (mEnterActivityOptions == null) {
+ if (mEnterActivityOptions == null || mIsEnterTriggered) {
return;
}
+ mIsEnterTriggered = true;
mHasExited = false;
ArrayList<String> sharedElementNames = mEnterActivityOptions.getSharedElementNames();
ResultReceiver resultReceiver = mEnterActivityOptions.getResultReceiver();
@@ -259,6 +264,7 @@
return;
}
ActivityOptions activityOptions = new ActivityOptions(options);
+ mEnterTransitionCoordinator = null;
if (activityOptions.getAnimationType() == ActivityOptions.ANIM_SCENE_TRANSITION) {
int key = activityOptions.getExitCoordinatorKey();
int index = mExitTransitionCoordinators.indexOfKey(key);
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 4730559..264553b 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -1520,7 +1520,7 @@
}
@Override
- public PackageInstaller getPackageInstaller() {
+ public PackageInstaller getInstaller() {
try {
return new PackageInstaller(this, mPM.getPackageInstaller(), mContext.getPackageName(),
mContext.getUserId());
@@ -1529,6 +1529,15 @@
}
}
+ @Override
+ public boolean isPackageAvailable(String packageName) {
+ try {
+ return mPM.isPackageAvailable(packageName, mContext.getUserId());
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+
/**
* @hide
*/
diff --git a/core/java/android/app/ExitTransitionCoordinator.java b/core/java/android/app/ExitTransitionCoordinator.java
index 3f3e00c..4f5a098 100644
--- a/core/java/android/app/ExitTransitionCoordinator.java
+++ b/core/java/android/app/ExitTransitionCoordinator.java
@@ -329,7 +329,7 @@
private Bundle captureExitSharedElementsState() {
Bundle bundle = new Bundle();
Rect bounds = new Rect();
- for (int i = 0; i < mSharedElementNames.size(); i++) {
+ for (int i = 0; i < mSharedElements.size(); i++) {
String name = mSharedElementNames.get(i);
Bundle sharedElementState = mExitSharedElementBundle.getBundle(name);
if (sharedElementState != null) {
diff --git a/core/java/android/content/pm/InstallSessionInfo.java b/core/java/android/content/pm/InstallSessionInfo.java
index 45606d1..3336727 100644
--- a/core/java/android/content/pm/InstallSessionInfo.java
+++ b/core/java/android/content/pm/InstallSessionInfo.java
@@ -20,15 +20,25 @@
import android.os.Parcel;
import android.os.Parcelable;
-/** {@hide} */
+/**
+ * Details for an active install session.
+ */
public class InstallSessionInfo implements Parcelable {
+
+ /** {@hide} */
public int sessionId;
+ /** {@hide} */
public String installerPackageName;
+ /** {@hide} */
public int progress;
- public boolean fullInstall;
+ /** {@hide} */
+ public int mode;
+ /** {@hide} */
public String packageName;
+ /** {@hide} */
public Bitmap icon;
+ /** {@hide} */
public CharSequence title;
/** {@hide} */
@@ -41,12 +51,62 @@
installerPackageName = source.readString();
progress = source.readInt();
- fullInstall = source.readInt() != 0;
+ mode = source.readInt();
packageName = source.readString();
icon = source.readParcelable(null);
title = 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 String getInstallerPackageName() {
+ return installerPackageName;
+ }
+
+ /**
+ * Return current overall progress of this session, between 0 and 100.
+ * <p>
+ * Note that this progress may not directly correspond to the value reported
+ * by {@link PackageInstaller.Session#setProgress(int)}, as the system may
+ * carve out a portion of the overall progress to represent its own internal
+ * installation work.
+ */
+ public int getProgress() {
+ return progress;
+ }
+
+ /**
+ * Return the package name this session is working with. May be {@code null}
+ * if unknown.
+ */
+ public String getPackageName() {
+ return packageName;
+ }
+
+ /**
+ * Return an icon representing the app being installed. May be {@code null}
+ * if unavailable.
+ */
+ public Bitmap getIcon() {
+ return icon;
+ }
+
+ /**
+ * Return a title representing the app being installed. May be {@code null}
+ * if unavailable.
+ */
+ public CharSequence getTitle() {
+ return title;
+ }
+
@Override
public int describeContents() {
return 0;
@@ -58,7 +118,7 @@
dest.writeString(installerPackageName);
dest.writeInt(progress);
- dest.writeInt(fullInstall ? 1 : 0);
+ dest.writeInt(mode);
dest.writeString(packageName);
dest.writeParcelable(icon, flags);
dest.writeString(title != null ? title.toString() : null);
diff --git a/core/java/android/content/pm/InstallSessionParams.java b/core/java/android/content/pm/InstallSessionParams.java
index 43e3314..e039699 100644
--- a/core/java/android/content/pm/InstallSessionParams.java
+++ b/core/java/android/content/pm/InstallSessionParams.java
@@ -16,6 +16,7 @@
package android.content.pm;
+import android.content.Intent;
import android.graphics.Bitmap;
import android.net.Uri;
import android.os.Parcel;
@@ -23,13 +24,22 @@
import com.android.internal.util.IndentingPrintWriter;
-/** {@hide} */
+/**
+ * Parameters for creating a new {@link PackageInstaller.Session}.
+ */
public class InstallSessionParams implements Parcelable {
// TODO: extend to support remaining VerificationParams
/** {@hide} */
- public boolean fullInstall;
+ public static final int MODE_INVALID = 0;
+ /** {@hide} */
+ public static final int MODE_FULL_INSTALL = 1;
+ /** {@hide} */
+ public static final int MODE_INHERIT_EXISTING = 2;
+
+ /** {@hide} */
+ public int mode = MODE_INVALID;
/** {@hide} */
public int installFlags;
/** {@hide} */
@@ -58,7 +68,7 @@
/** {@hide} */
public InstallSessionParams(Parcel source) {
- fullInstall = source.readInt() != 0;
+ mode = source.readInt();
installFlags = source.readInt();
installLocation = source.readInt();
signatures = (Signature[]) source.readParcelableArray(null);
@@ -72,53 +82,108 @@
abiOverride = source.readString();
}
- public void setFullInstall(boolean fullInstall) {
- this.fullInstall = fullInstall;
+ /**
+ * Set session mode indicating that it should fully replace any existing
+ * APKs for this application.
+ */
+ public void setModeFullInstall() {
+ this.mode = MODE_FULL_INSTALL;
}
- public void setInstallFlags(int installFlags) {
- this.installFlags = installFlags;
+ /**
+ * Set session mode indicating that it should inherit any existing APKs for
+ * this application, unless they are explicitly overridden (by split name)
+ * in the session.
+ */
+ public void setModeInheritExisting() {
+ this.mode = MODE_INHERIT_EXISTING;
}
+ /**
+ * 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 provide the required value of {@link PackageInfo#signatures}.
+ * This can be used to assert that all staged APKs have been signed with
+ * this set of specific certificates. Regardless of this value, all APKs in
+ * the application must have the same signing certificates.
+ */
public void setSignatures(Signature[] signatures) {
this.signatures = signatures;
}
+ /**
+ * Indicate the expected growth in disk usage resulting from this session.
+ * This may be used to ensure enough disk space exists before proceeding, or
+ * to estimate container size for installations living on external storage.
+ * <p>
+ * This value should only reflect APK sizes.
+ */
public void setDeltaSize(long deltaSize) {
this.deltaSize = deltaSize;
}
+ /**
+ * Set the maximum progress for this session, used for normalization
+ * purposes.
+ *
+ * @see PackageInstaller.Session#setProgress(int)
+ */
public void setProgressMax(int progressMax) {
this.progressMax = progressMax;
}
+ /**
+ * Optionally set the package name this session will be working with. It's
+ * strongly recommended that you provide this value when known.
+ */
public void setPackageName(String packageName) {
this.packageName = packageName;
}
+ /**
+ * Optionally set an icon representing the app being installed.
+ */
public void setIcon(Bitmap icon) {
this.icon = icon;
}
+ /**
+ * Optionally set a title representing the app being installed.
+ */
public void setTitle(CharSequence title) {
this.title = title;
}
+ /**
+ * Optionally set the URI where this package was downloaded from. Used for
+ * verification purposes.
+ *
+ * @see Intent#EXTRA_ORIGINATING_URI
+ */
public void setOriginatingUri(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(Uri referrerUri) {
this.referrerUri = referrerUri;
}
/** {@hide} */
public void dump(IndentingPrintWriter pw) {
- pw.printPair("fullInstall", fullInstall);
+ pw.printPair("mode", mode);
pw.printHexPair("installFlags", installFlags);
pw.printPair("installLocation", installLocation);
pw.printPair("signatures", (signatures != null));
@@ -140,7 +205,7 @@
@Override
public void writeToParcel(Parcel dest, int flags) {
- dest.writeInt(fullInstall ? 1 : 0);
+ dest.writeInt(mode);
dest.writeInt(installFlags);
dest.writeInt(installLocation);
dest.writeParcelableArray(signatures, flags);
diff --git a/core/java/android/content/pm/PackageInfo.java b/core/java/android/content/pm/PackageInfo.java
index ef0c4d5..8f0c249 100644
--- a/core/java/android/content/pm/PackageInfo.java
+++ b/core/java/android/content/pm/PackageInfo.java
@@ -31,6 +31,11 @@
public String packageName;
/**
+ * The names of any installed split APKs for this package.
+ */
+ public String[] splitNames;
+
+ /**
* The version number of this package, as specified by the <manifest>
* tag's {@link android.R.styleable#AndroidManifest_versionCode versionCode}
* attribute.
@@ -190,24 +195,25 @@
* @hide
*/
public static final int INSTALL_LOCATION_UNSPECIFIED = -1;
+
/**
- * Constant corresponding to <code>auto</code> in
- * the {@link android.R.attr#installLocation} attribute.
- * @hide
+ * Constant corresponding to <code>auto</code> in the
+ * {@link android.R.attr#installLocation} attribute.
*/
public static final int INSTALL_LOCATION_AUTO = 0;
+
/**
- * Constant corresponding to <code>internalOnly</code> in
- * the {@link android.R.attr#installLocation} attribute.
- * @hide
+ * Constant corresponding to <code>internalOnly</code> in the
+ * {@link android.R.attr#installLocation} attribute.
*/
public static final int INSTALL_LOCATION_INTERNAL_ONLY = 1;
+
/**
- * Constant corresponding to <code>preferExternal</code> in
- * the {@link android.R.attr#installLocation} attribute.
- * @hide
+ * Constant corresponding to <code>preferExternal</code> in the
+ * {@link android.R.attr#installLocation} attribute.
*/
public static final int INSTALL_LOCATION_PREFER_EXTERNAL = 2;
+
/**
* Flag for {@link #requiredForProfile}
* The application will always be installed for a restricted profile.
@@ -222,12 +228,10 @@
public static final int MANAGED_PROFILE = 2;
/**
- * The install location requested by the activity. From the
+ * The install location requested by the package. From the
* {@link android.R.attr#installLocation} attribute, one of
- * {@link #INSTALL_LOCATION_AUTO},
- * {@link #INSTALL_LOCATION_INTERNAL_ONLY},
+ * {@link #INSTALL_LOCATION_AUTO}, {@link #INSTALL_LOCATION_INTERNAL_ONLY},
* {@link #INSTALL_LOCATION_PREFER_EXTERNAL}
- * @hide
*/
public int installLocation = INSTALL_LOCATION_INTERNAL_ONLY;
@@ -269,6 +273,7 @@
public void writeToParcel(Parcel dest, int parcelableFlags) {
dest.writeString(packageName);
+ dest.writeStringArray(splitNames);
dest.writeInt(versionCode);
dest.writeString(versionName);
dest.writeString(sharedUserId);
@@ -314,6 +319,7 @@
private PackageInfo(Parcel source) {
packageName = source.readString();
+ splitNames = source.readStringArray();
versionCode = source.readInt();
versionName = source.readString();
sharedUserId = source.readString();
diff --git a/core/java/android/content/pm/PackageInfoLite.java b/core/java/android/content/pm/PackageInfoLite.java
index 50a0483..e336c5f 100644
--- a/core/java/android/content/pm/PackageInfoLite.java
+++ b/core/java/android/content/pm/PackageInfoLite.java
@@ -73,6 +73,7 @@
dest.writeInt(versionCode);
dest.writeInt(recommendedInstallLocation);
dest.writeInt(installLocation);
+ dest.writeInt(multiArch ? 1 : 0);
if (verifiers == null || verifiers.length == 0) {
dest.writeInt(0);
@@ -98,6 +99,7 @@
versionCode = source.readInt();
recommendedInstallLocation = source.readInt();
installLocation = source.readInt();
+ multiArch = (source.readInt() != 0);
final int verifiersLength = source.readInt();
if (verifiersLength == 0) {
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index 401be06..348a7ad 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -30,7 +30,31 @@
import java.io.OutputStream;
import java.util.List;
-/** {@hide} */
+/**
+ * Offers the ability to install, upgrade, and remove applications on the
+ * device. This includes support for apps packaged either as a single
+ * "monolithic" APK, or apps packaged as multiple "split" APKs.
+ * <p>
+ * An app is delivered for installation through a
+ * {@link PackageInstaller.Session}, which any app can create. Once the session
+ * is created, the installer can stream one or more APKs into place until it
+ * decides to either commit or destroy the session. Committing may require user
+ * intervention to complete the installation.
+ * <p>
+ * Sessions can install brand new apps, upgrade existing apps, or add new splits
+ * onto an existing app.
+ * <p>
+ * Apps packaged into multiple split APKs always consist of a single "base" APK
+ * (with a {@code null} split name) and zero or more "split" APKs (with unique
+ * split names). Any subset of these APKs can be installed together, as long as
+ * the following constraints are met:
+ * <ul>
+ * <li>All APKs must have the exact same package name, version code, and signing
+ * certificates.
+ * <li>All installations must contain a single base APK.
+ * <li>All APKs must have unique split names.
+ * </ul>
+ */
public class PackageInstaller {
private final PackageManager mPm;
private final IPackageInstaller mInstaller;
@@ -46,16 +70,18 @@
mUserId = userId;
}
+ /**
+ * Quickly test if the given package is already available on the device.
+ * This is typically used in multi-user scenarios where another user on the
+ * device has already installed the package.
+ *
+ * @hide
+ */
public boolean isPackageAvailable(String packageName) {
- try {
- final ApplicationInfo info = mPm.getApplicationInfo(packageName,
- PackageManager.GET_UNINSTALLED_PACKAGES);
- return ((info.flags & ApplicationInfo.FLAG_INSTALLED) != 0);
- } catch (NameNotFoundException e) {
- return false;
- }
+ return mPm.isPackageAvailable(packageName);
}
+ /** {@hide} */
public void installAvailablePackage(String packageName, PackageInstallObserver observer) {
int returnCode;
try {
@@ -66,6 +92,18 @@
observer.packageInstalled(packageName, null, returnCode);
}
+ /**
+ * Create a new session using the given parameters, returning a unique ID
+ * that represents the session. Once created, the session can be opened
+ * multiple times across multiple device boots.
+ * <p>
+ * The system may automatically destroy sessions that have not been
+ * finalized (either committed or abandoned) within a reasonable period of
+ * time, typically on the order of a day.
+ *
+ * @throws IOException if parameters were unsatisfiable, such as lack of
+ * disk space or unavailable media.
+ */
public int createSession(InstallSessionParams params) throws IOException {
try {
return mInstaller.createSession(mInstallerPackageName, params, mUserId);
@@ -77,6 +115,9 @@
}
}
+ /**
+ * Open an existing session to actively perform work.
+ */
public Session openSession(int sessionId) {
try {
return new Session(mInstaller.openSession(sessionId));
@@ -85,7 +126,10 @@
}
}
- public List<InstallSessionInfo> getSessions() {
+ /**
+ * Return list of all active install sessions on the device.
+ */
+ public List<InstallSessionInfo> getActiveSessions() {
// TODO: filter based on caller
// TODO: let launcher app see all active sessions
try {
@@ -95,6 +139,11 @@
}
}
+ /**
+ * Uninstall the given package, removing it completely from the device. This
+ * method is only available to the current "installer of record" for the
+ * package.
+ */
public void uninstall(String packageName, UninstallResultCallback callback) {
try {
mInstaller.uninstall(packageName, 0,
@@ -104,6 +153,11 @@
}
}
+ /**
+ * Uninstall only a specific split from the given package.
+ *
+ * @hide
+ */
public void uninstall(String packageName, String splitName, UninstallResultCallback callback) {
try {
mInstaller.uninstallSplit(packageName, splitName, 0,
@@ -113,6 +167,9 @@
}
}
+ /**
+ * Events for observing session lifecycle.
+ */
public static abstract class SessionObserver {
private final IPackageInstallerObserver.Stub mBinder = new IPackageInstallerObserver.Stub() {
@Override
@@ -127,7 +184,7 @@
@Override
public void onSessionFinished(int sessionId, boolean success) {
- SessionObserver.this.onFinished(sessionId, success);
+ SessionObserver.this.onFinalized(sessionId, success);
}
};
@@ -136,11 +193,30 @@
return mBinder;
}
+ /**
+ * New session has been created.
+ */
public abstract void onCreated(InstallSessionInfo info);
+
+ /**
+ * Progress for given session has been updated.
+ * <p>
+ * Note that this progress may not directly correspond to the value
+ * reported by {@link PackageInstaller.Session#setProgress(int)}, as the
+ * system may carve out a portion of the overall progress to represent
+ * its own internal installation work.
+ */
public abstract void onProgress(int sessionId, int progress);
- public abstract void onFinished(int sessionId, boolean success);
+
+ /**
+ * Session has been finalized, either with success or failure.
+ */
+ public abstract void onFinalized(int sessionId, boolean success);
}
+ /**
+ * Register to watch for session lifecycle events.
+ */
public void registerSessionObserver(SessionObserver observer) {
try {
mInstaller.registerObserver(observer.getBinder(), mUserId);
@@ -149,6 +225,9 @@
}
}
+ /**
+ * Unregister an existing observer.
+ */
public void unregisterSessionObserver(SessionObserver observer) {
try {
mInstaller.unregisterObserver(observer.getBinder(), mUserId);
@@ -177,6 +256,10 @@
mSession = session;
}
+ /**
+ * Set current progress. Valid values are anywhere between 0 and
+ * {@link InstallSessionParams#setProgressMax(int)}.
+ */
public void setProgress(int progress) {
try {
mSession.setClientProgress(progress);
@@ -197,7 +280,7 @@
/**
* Open an APK file for writing, starting at the given offset. You can
* then stream data into the file, periodically calling
- * {@link OutputStream#flush()} to ensure bytes have been written to
+ * {@link #fsync(OutputStream)} to ensure bytes have been written to
* disk.
*/
public OutputStream openWrite(String splitName, long offsetBytes, long lengthBytes)
@@ -214,6 +297,11 @@
}
}
+ /**
+ * Ensure that any outstanding data for given stream has been committed
+ * to disk. This is only valid for streams returned from
+ * {@link #openWrite(String, long, long)}.
+ */
public void fsync(OutputStream out) throws IOException {
if (out instanceof FileBridge.FileBridgeOutputStream) {
((FileBridge.FileBridgeOutputStream) out).fsync();
@@ -222,6 +310,15 @@
}
}
+ /**
+ * Attempt to commit everything staged in this session. This may require
+ * user intervention, and so it may not happen immediately. The final
+ * result of the commit will be reported through the given callback.
+ * <p>
+ * Once this method is called, no additional mutations may be performed
+ * on the session. If the device reboots before the session has been
+ * finalized, you may commit the session again.
+ */
public void commit(CommitResultCallback callback) {
try {
mSession.install(new CommitResultCallbackDelegate(callback).getBinder());
@@ -230,11 +327,18 @@
}
}
+ /**
+ * Release this session object. You can open the session again if it
+ * hasn't been finalized.
+ */
@Override
public void close() {
// No resources to release at the moment
}
+ /**
+ * Completely destroy this session, rendering it invalid.
+ */
public void destroy() {
try {
mSession.destroy();
@@ -244,11 +348,15 @@
}
}
+ /**
+ * Final result of an uninstall request.
+ */
public static abstract class UninstallResultCallback {
public abstract void onSuccess();
public abstract void onFailure(String msg);
}
+ /** {@hide} */
private static class UninstallResultCallbackDelegate extends PackageUninstallObserver {
private final UninstallResultCallback target;
@@ -271,8 +379,18 @@
}
}
+ /**
+ * Final result of a session commit request.
+ */
public static abstract class CommitResultCallback {
public abstract void onSuccess();
+
+ /**
+ * Generic failure occurred. You can override methods (such as
+ * {@link #onFailureInvalid(String)}) to handle more specific categories
+ * of failure. By default, those specific categories all flow into this
+ * generic failure.
+ */
public abstract void onFailure(String msg);
/**
@@ -286,12 +404,16 @@
}
/**
- * 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.
+ * 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.
+ *
+ * @param otherPackageName if one specific package was identified as the
+ * cause of the conflict, it's named here. If unknown, or
+ * multiple packages, this may be {@code null}.
*/
- public void onFailureConflict(String msg, String packageName) {
+ public void onFailureConflict(String msg, String otherPackageName) {
onFailure(msg);
}
@@ -317,6 +439,7 @@
}
}
+ /** {@hide} */
private static class CommitResultCallbackDelegate extends PackageInstallObserver {
private final CommitResultCallback target;
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 376369a..052c2ca 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -3672,8 +3672,11 @@
*/
public abstract VerifierDeviceIdentity getVerifierDeviceIdentity();
- /** {@hide} */
- public abstract PackageInstaller getPackageInstaller();
+ /**
+ * Return interface that offers the ability to install, upgrade, and remove
+ * applications on the device.
+ */
+ public abstract PackageInstaller getInstaller();
/**
* Returns the data directory for a particular user and package, given the uid of the package.
@@ -3719,4 +3722,7 @@
* @hide
*/
public abstract Drawable loadItemIcon(PackageItemInfo itemInfo, ApplicationInfo appInfo);
+
+ /** {@hide} */
+ public abstract boolean isPackageAvailable(String packageName);
}
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index d92a90d..7bfe55d 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -380,6 +380,7 @@
}
PackageInfo pi = new PackageInfo();
pi.packageName = p.packageName;
+ pi.splitNames = p.splitNames;
pi.versionCode = p.mVersionCode;
pi.versionName = p.mVersionName;
pi.sharedUserId = p.mSharedUserId;
@@ -1128,11 +1129,8 @@
} else if (attr.equals("versionCode")) {
versionCode = attrs.getAttributeIntValue(i, 0);
numFound++;
- } else if (attr.equals("multiArch")) {
- multiArch = attrs.getAttributeBooleanValue(i, false);
- numFound++;
}
- if (numFound >= 3) {
+ if (numFound >= 2) {
break;
}
}
@@ -1154,6 +1152,16 @@
verifiers.add(verifier);
}
}
+
+ if (parser.getDepth() == searchDepth && "application".equals(parser.getName())) {
+ for (int i = 0; i < attrs.getAttributeCount(); ++i) {
+ final String attr = attrs.getAttributeName(i);
+ if ("multiArch".equals(attr)) {
+ multiArch = attrs.getAttributeBooleanValue(i, false);
+ break;
+ }
+ }
+ }
}
return new ApkLite(codePath, packageSplit.first, packageSplit.second, versionCode,
diff --git a/core/java/com/android/internal/backup/LocalTransport.java b/core/java/com/android/internal/backup/LocalTransport.java
index 8b56ceb..7e58351 100644
--- a/core/java/com/android/internal/backup/LocalTransport.java
+++ b/core/java/com/android/internal/backup/LocalTransport.java
@@ -56,7 +56,7 @@
public class LocalTransport extends BackupTransport {
private static final String TAG = "LocalTransport";
- private static final boolean DEBUG = true;
+ private static final boolean DEBUG = false;
private static final String TRANSPORT_DIR_NAME
= "com.android.internal.backup.LocalTransport";
diff --git a/media/java/android/media/AudioDevice.java b/media/java/android/media/AudioDevice.java
index 1fd27fe..96d6196 100644
--- a/media/java/android/media/AudioDevice.java
+++ b/media/java/android/media/AudioDevice.java
@@ -66,8 +66,20 @@
return mConfig.port().address();
}
+ /** @hide */
+ public static int convertDeviceTypeToInternalDevice(int deviceType) {
+ return EXT_TO_INT_DEVICE_MAPPING.get(deviceType, AudioSystem.DEVICE_NONE);
+ }
+
+ /** @hide */
+ public static int convertInternalDeviceToDeviceType(int intDevice) {
+ return INT_TO_EXT_DEVICE_MAPPING.get(intDevice, DEVICE_TYPE_UNKNOWN);
+ }
+
private static final SparseIntArray INT_TO_EXT_DEVICE_MAPPING;
+ private static final SparseIntArray EXT_TO_INT_DEVICE_MAPPING;
+
static {
INT_TO_EXT_DEVICE_MAPPING = new SparseIntArray();
INT_TO_EXT_DEVICE_MAPPING.put(AudioSystem.DEVICE_OUT_EARPIECE, DEVICE_TYPE_BUILTIN_EARPIECE);
@@ -110,6 +122,27 @@
// not covered here, legacy
//AudioSystem.DEVICE_OUT_REMOTE_SUBMIX
//AudioSystem.DEVICE_IN_REMOTE_SUBMIX
+
+ // privileges mapping to output device
+ EXT_TO_INT_DEVICE_MAPPING = new SparseIntArray();
+ EXT_TO_INT_DEVICE_MAPPING.put(DEVICE_TYPE_BUILTIN_EARPIECE, AudioSystem.DEVICE_OUT_EARPIECE);
+ EXT_TO_INT_DEVICE_MAPPING.put(DEVICE_TYPE_BUILTIN_SPEAKER, AudioSystem.DEVICE_OUT_SPEAKER);
+ EXT_TO_INT_DEVICE_MAPPING.put(DEVICE_TYPE_WIRED_HEADSET, AudioSystem.DEVICE_OUT_WIRED_HEADSET);
+ EXT_TO_INT_DEVICE_MAPPING.put(DEVICE_TYPE_WIRED_HEADPHONES, AudioSystem.DEVICE_OUT_WIRED_HEADPHONE);
+ EXT_TO_INT_DEVICE_MAPPING.put(DEVICE_TYPE_LINE_ANALOG, AudioSystem.DEVICE_OUT_LINE);
+ EXT_TO_INT_DEVICE_MAPPING.put(DEVICE_TYPE_LINE_DIGITAL, AudioSystem.DEVICE_OUT_SPDIF);
+ EXT_TO_INT_DEVICE_MAPPING.put(DEVICE_TYPE_BLUETOOTH_SCO, AudioSystem.DEVICE_OUT_BLUETOOTH_SCO);
+ EXT_TO_INT_DEVICE_MAPPING.put(DEVICE_TYPE_BLUETOOTH_A2DP, AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP);
+ EXT_TO_INT_DEVICE_MAPPING.put(DEVICE_TYPE_HDMI, AudioSystem.DEVICE_OUT_HDMI);
+ EXT_TO_INT_DEVICE_MAPPING.put(DEVICE_TYPE_HDMI_ARC, AudioSystem.DEVICE_OUT_HDMI_ARC);
+ EXT_TO_INT_DEVICE_MAPPING.put(DEVICE_TYPE_USB_DEVICE, AudioSystem.DEVICE_OUT_USB_DEVICE);
+ EXT_TO_INT_DEVICE_MAPPING.put(DEVICE_TYPE_USB_ACCESSORY, AudioSystem.DEVICE_OUT_USB_ACCESSORY);
+ EXT_TO_INT_DEVICE_MAPPING.put(DEVICE_TYPE_DOCK, AudioSystem.DEVICE_OUT_ANLG_DOCK_HEADSET);
+ EXT_TO_INT_DEVICE_MAPPING.put(DEVICE_TYPE_FM, AudioSystem.DEVICE_OUT_FM);
+ EXT_TO_INT_DEVICE_MAPPING.put(DEVICE_TYPE_BUILTIN_MIC, AudioSystem.DEVICE_IN_BUILTIN_MIC);
+ EXT_TO_INT_DEVICE_MAPPING.put(DEVICE_TYPE_FM_TUNER, AudioSystem.DEVICE_IN_FM_TUNER);
+ EXT_TO_INT_DEVICE_MAPPING.put(DEVICE_TYPE_TV_TUNER, AudioSystem.DEVICE_IN_TV_TUNER);
+ EXT_TO_INT_DEVICE_MAPPING.put(DEVICE_TYPE_TELEPHONY, AudioSystem.DEVICE_OUT_TELEPHONY_TX);
}
}
diff --git a/media/java/android/media/AudioFormat.java b/media/java/android/media/AudioFormat.java
index 025d354..79be108 100644
--- a/media/java/android/media/AudioFormat.java
+++ b/media/java/android/media/AudioFormat.java
@@ -78,9 +78,9 @@
public static final int CHANNEL_OUT_FRONT_LEFT_OF_CENTER = 0x100;
public static final int CHANNEL_OUT_FRONT_RIGHT_OF_CENTER = 0x200;
public static final int CHANNEL_OUT_BACK_CENTER = 0x400;
- /** @hide */
+ /** @hide CANDIDATE FOR PUBLIC API */
public static final int CHANNEL_OUT_SIDE_LEFT = 0x800;
- /** @hide */
+ /** @hide CANDIDATE FOR PUBLIC API */
public static final int CHANNEL_OUT_SIDE_RIGHT = 0x1000;
/** @hide */
public static final int CHANNEL_OUT_TOP_CENTER = 0x2000;
@@ -128,6 +128,35 @@
CHANNEL_OUT_LOW_FREQUENCY);
// CHANNEL_OUT_ALL is not yet defined; if added then it should match AUDIO_CHANNEL_OUT_ALL
+ /**
+ * @hide
+ * Return the number of channels from an output channel mask
+ * @param mask a combination of the CHANNEL_OUT_* definitions, but not CHANNEL_OUT_DEFAULT
+ * @return number of channels for the mask
+ */
+ public static int channelCountFromOutChannelMask(int mask) {
+ return Integer.bitCount(mask);
+ }
+ /**
+ * @hide
+ * Return a channel mask ready to be used by native code
+ * @param mask a combination of the CHANNEL_OUT_* definitions, but not CHANNEL_OUT_DEFAULT
+ * @return a native channel mask
+ */
+ public static int convertChannelOutMaskToNativeMask(int javaMask) {
+ return (javaMask >> 2);
+ }
+
+ /**
+ * @hide
+ * Return a java output channel mask
+ * @param mask a native channel mask
+ * @return a combination of the CHANNEL_OUT_* definitions
+ */
+ public static int convertNativeChannelMaskToOutMask(int nativeMask) {
+ return (nativeMask << 2);
+ }
+
public static final int CHANNEL_IN_DEFAULT = 1;
// These directly match native
public static final int CHANNEL_IN_LEFT = 0x4;
diff --git a/media/java/android/media/audiofx/AudioEffect.java b/media/java/android/media/audiofx/AudioEffect.java
index 9b381cc..9fa3f50 100644
--- a/media/java/android/media/audiofx/AudioEffect.java
+++ b/media/java/android/media/audiofx/AudioEffect.java
@@ -1282,7 +1282,7 @@
/**
* @hide
*/
- public int byteArrayToInt(byte[] valueBuf) {
+ public static int byteArrayToInt(byte[] valueBuf) {
return byteArrayToInt(valueBuf, 0);
}
@@ -1290,7 +1290,7 @@
/**
* @hide
*/
- public int byteArrayToInt(byte[] valueBuf, int offset) {
+ public static int byteArrayToInt(byte[] valueBuf, int offset) {
ByteBuffer converter = ByteBuffer.wrap(valueBuf);
converter.order(ByteOrder.nativeOrder());
return converter.getInt(offset);
@@ -1300,7 +1300,7 @@
/**
* @hide
*/
- public byte[] intToByteArray(int value) {
+ public static byte[] intToByteArray(int value) {
ByteBuffer converter = ByteBuffer.allocate(4);
converter.order(ByteOrder.nativeOrder());
converter.putInt(value);
@@ -1310,14 +1310,14 @@
/**
* @hide
*/
- public short byteArrayToShort(byte[] valueBuf) {
+ public static short byteArrayToShort(byte[] valueBuf) {
return byteArrayToShort(valueBuf, 0);
}
/**
* @hide
*/
- public short byteArrayToShort(byte[] valueBuf, int offset) {
+ public static short byteArrayToShort(byte[] valueBuf, int offset) {
ByteBuffer converter = ByteBuffer.wrap(valueBuf);
converter.order(ByteOrder.nativeOrder());
return converter.getShort(offset);
@@ -1327,7 +1327,7 @@
/**
* @hide
*/
- public byte[] shortToByteArray(short value) {
+ public static byte[] shortToByteArray(short value) {
ByteBuffer converter = ByteBuffer.allocate(2);
converter.order(ByteOrder.nativeOrder());
short sValue = (short) value;
@@ -1338,7 +1338,7 @@
/**
* @hide
*/
- public byte[] concatArrays(byte[]... arrays) {
+ public static byte[] concatArrays(byte[]... arrays) {
int len = 0;
for (byte[] a : arrays) {
len += a.length;
diff --git a/media/java/android/media/audiofx/Virtualizer.java b/media/java/android/media/audiofx/Virtualizer.java
index 6b20006..136761b 100644
--- a/media/java/android/media/audiofx/Virtualizer.java
+++ b/media/java/android/media/audiofx/Virtualizer.java
@@ -16,9 +16,13 @@
package android.media.audiofx;
+import android.media.AudioDevice;
+import android.media.AudioFormat;
import android.media.audiofx.AudioEffect;
import android.util.Log;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
import java.util.StringTokenizer;
@@ -44,8 +48,10 @@
public class Virtualizer extends AudioEffect {
private final static String TAG = "Virtualizer";
+ private final static boolean DEBUG = false;
- // These constants must be synchronized with those in frameworks/base/include/media/EffectVirtualizerApi.h
+ // These constants must be synchronized with those in
+ // system/media/audio_effects/include/audio_effects/effect_virtualizer.h
/**
* Is strength parameter supported by virtualizer engine. Parameter ID for getParameter().
*/
@@ -55,6 +61,21 @@
* {@link android.media.audiofx.Virtualizer.OnParameterChangeListener}
*/
public static final int PARAM_STRENGTH = 1;
+ /**
+ * @hide
+ * Parameter ID to query the virtual speaker angles for a channel mask / device configuration.
+ */
+ public static final int PARAM_VIRTUAL_SPEAKER_ANGLES = 2;
+ /**
+ * @hide
+ * Parameter ID to force the virtualization mode to be that of a specific device
+ */
+ public static final int PARAM_FORCE_VIRTUALIZATION_MODE = 3;
+ /**
+ * @hide
+ * Parameter ID to query the current virtualization mode.
+ */
+ public static final int PARAM_VIRTUALIZATION_MODE = 4;
/**
* Indicates if strength parameter is supported by the virtualizer engine
@@ -145,6 +166,223 @@
}
/**
+ * Checks if a configuration is supported, and query the virtual speaker angles.
+ * @param inputChannelMask
+ * @param deviceType
+ * @param angles if non-null: array in which the angles will be written. If null, no angles
+ * are returned
+ * @return true if the combination of channel mask and output device type is supported, false
+ * otherwise
+ * @throws IllegalStateException
+ * @throws IllegalArgumentException
+ * @throws UnsupportedOperationException
+ */
+ private boolean getAnglesInt(int inputChannelMask, int deviceType, int[] angles)
+ throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException {
+ // parameter check
+ if (inputChannelMask == AudioFormat.CHANNEL_INVALID) {
+ throw (new IllegalArgumentException(
+ "Virtualizer: illegal CHANNEL_INVALID channel mask"));
+ }
+ int channelMask = inputChannelMask == AudioFormat.CHANNEL_OUT_DEFAULT ?
+ AudioFormat.CHANNEL_OUT_STEREO : inputChannelMask;
+ int nbChannels = AudioFormat.channelCountFromOutChannelMask(channelMask);
+ if ((angles != null) && (angles.length < (nbChannels * 3))) {
+ Log.e(TAG, "Size of array for angles cannot accomodate number of channels in mask ("
+ + nbChannels + ")");
+ throw (new IllegalArgumentException(
+ "Virtualizer: array for channel / angle pairs is too small: is " + angles.length
+ + ", should be " + (nbChannels * 3)));
+ }
+
+ ByteBuffer paramsConverter = ByteBuffer.allocate(3 /* param + mask + device*/ * 4);
+ paramsConverter.order(ByteOrder.nativeOrder());
+ paramsConverter.putInt(PARAM_VIRTUAL_SPEAKER_ANGLES);
+ // convert channel mask to internal native representation
+ paramsConverter.putInt(AudioFormat.convertChannelOutMaskToNativeMask(channelMask));
+ // convert Java device type to internal representation
+ paramsConverter.putInt(AudioDevice.convertDeviceTypeToInternalDevice(deviceType));
+ // allocate an array to store the results
+ byte[] result = new byte[nbChannels * 4/*int to byte*/ * 3/*for mask, azimuth, elevation*/];
+
+ // call into the effect framework
+ int status = getParameter(paramsConverter.array(), result);
+ if (DEBUG) {
+ Log.v(TAG, "getAngles(0x" + Integer.toHexString(inputChannelMask) + ", 0x"
+ + Integer.toHexString(deviceType) + ") returns " + status);
+ }
+
+ if (status >= 0) {
+ if (angles != null) {
+ // convert and copy the results
+ ByteBuffer resultConverter = ByteBuffer.wrap(result);
+ resultConverter.order(ByteOrder.nativeOrder());
+ for (int i = 0 ; i < nbChannels ; i++) {
+ // write the channel mask
+ angles[3 * i] = AudioFormat.convertNativeChannelMaskToOutMask(
+ resultConverter.getInt((i * 4 * 3)));
+ // write the azimuth
+ angles[3 * i + 1] = resultConverter.getInt(i * 4 * 3 + 4);
+ // write the elevation
+ angles[3 * i + 2] = resultConverter.getInt(i * 4 * 3 + 8);
+ if (DEBUG) {
+ Log.v(TAG, "channel 0x" + Integer.toHexString(angles[3*i]).toUpperCase()
+ + " at az=" + angles[3*i+1] + "deg"
+ + " elev=" + angles[3*i+2] + "deg");
+ }
+ }
+ }
+ return true;
+ } else if (status == AudioEffect.ERROR_BAD_VALUE) {
+ // a BAD_VALUE return from getParameter indicates the configuration is not supported
+ // don't throw an exception, just return false
+ return false;
+ } else {
+ // something wrong may have happened
+ checkStatus(status);
+ }
+ // unexpected virtualizer behavior
+ Log.e(TAG, "unexpected status code " + status
+ + " after getParameter(PARAM_VIRTUAL_SPEAKER_ANGLES)");
+ return false;
+ }
+
+ /**
+ * @hide
+ * CANDIDATE FOR PUBLIC API
+ * Checks if the combination of a channel mask and device type is supported by this virtualizer.
+ * Some virtualizer implementations may only support binaural processing (i.e. only support
+ * headphone output), some may support transaural processing (i.e. for speaker output) for the
+ * built-in speakers. Use this method to query the virtualizer implementation capabilities.
+ * @param inputChannelMask the channel mask of the content to virtualize.
+ * @param deviceType the device type for which virtualization processing is to be performed.
+ * Valid values are the device types defined in {@link AudioDevice}.
+ * @return true if the combination of channel mask and output device type is supported, false
+ * otherwise.
+ * <br>An indication that a certain channel mask is not supported doesn't necessarily mean
+ * you cannot play content with that channel mask, it more likely implies the content will
+ * be downmixed before being virtualized. For instance a virtualizer that only supports a
+ * mask such as {@link AudioFormat#CHANNEL_OUT_STEREO}
+ * will still be able to process content with a mask of
+ * {@link AudioFormat#CHANNEL_OUT_5POINT1}, but will downmix the content to stereo first, and
+ * then will virtualize, as opposed to virtualizing each channel individually.
+ * @throws IllegalStateException
+ * @throws IllegalArgumentException
+ * @throws UnsupportedOperationException
+ */
+ public boolean canVirtualize(int inputChannelMask, int deviceType)
+ throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException {
+ return getAnglesInt(inputChannelMask, deviceType, null);
+ }
+
+ /**
+ * @hide
+ * CANDIDATE FOR PUBLIC API
+ * Queries the virtual speaker angles (azimuth and elevation) for a combination of a channel
+ * mask and device type.
+ * If the virtualization configuration (mask and device) is supported (see
+ * {@link #canVirtualize(int, int)}, the array angles will contain upon return the
+ * definition of each virtual speaker and its azimuth and elevation angles relative to the
+ * listener.
+ * <br>Note that in some virtualizer implementations, the angles may be strength-dependent.
+ * @param inputChannelMask the channel mask of the content to virtualize.
+ * @param deviceType the device type for which virtualization processing is to be performed.
+ * Valid values are the device types defined in {@link AudioDevice}.
+ * @param angles a non-null array whose length is 3 times the number of channels in the channel
+ * mask.
+ * If the method indicates the configuration is supported, the array will contain upon return
+ * triplets of values: for each channel <code>i</code> among the channels of the mask:
+ * <ul>
+ * <li>the element at index <code>3*i</code> in the array contains the speaker
+ * identification (e.g. {@link AudioFormat#CHANNEL_OUT_FRONT_LEFT}),</li>
+ * <li>the element at index <code>3*i+1</code> contains its corresponding azimuth angle
+ * expressed in degrees, where 0 is the direction the listener faces, 180 is behind
+ * the listener, and -90 is to her/his left,</li>
+ * <li>the element at index <code>3*i+2</code> contains its corresponding elevation angle
+ * where +90 is directly above the listener, 0 is the horizontal plane, and -90 is
+ * directly below the listener.</li>
+ * @return true if the combination of channel mask and output device type is supported, false
+ * otherwise.
+ * @throws IllegalStateException
+ * @throws IllegalArgumentException
+ * @throws UnsupportedOperationException
+ */
+ public boolean getSpeakerAngles(int inputChannelMask, int deviceType, int[] angles)
+ throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException {
+ if (angles == null) {
+ throw (new IllegalArgumentException(
+ "Virtualizer: illegal null channel / angle array"));
+ }
+
+ return getAnglesInt(inputChannelMask, deviceType, angles);
+ }
+
+ /**
+ * @hide
+ * CANDIDATE FOR PUBLIC API
+ * Forces the virtualizer effect to use the processing mode used for the given device type.
+ * The effect must be enabled for the forced mode to be applied.
+ * @param deviceType one of the device types defined in {@link AudioDevice}.
+ * Use {@link AudioDevice#DEVICE_TYPE_UNKNOWN} to return to the non-forced mode.
+ * @return true if the processing mode for the device type is supported, and it is successfully
+ * set, or forcing was successfully disabled with {@link AudioDevice#DEVICE_TYPE_UNKNOWN},
+ * false otherwise.
+ * @throws IllegalStateException
+ * @throws IllegalArgumentException
+ * @throws UnsupportedOperationException
+ */
+ public boolean forceVirtualizationMode(int deviceType)
+ throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException {
+ // convert Java device type to internal representation
+ int internalDevice = AudioDevice.convertDeviceTypeToInternalDevice(deviceType);
+
+ int status = setParameter(PARAM_FORCE_VIRTUALIZATION_MODE, internalDevice);
+
+ if (status >= 0) {
+ return true;
+ } else if (status == AudioEffect.ERROR_BAD_VALUE) {
+ // a BAD_VALUE return from setParameter indicates the mode can't be forced to that
+ // of this device, don't throw an exception, just return false
+ return false;
+ } else {
+ // something wrong may have happened
+ checkStatus(status);
+ }
+ // unexpected virtualizer behavior
+ Log.e(TAG, "unexpected status code " + status
+ + " after setParameter(PARAM_FORCE_VIRTUALIZATION_MODE)");
+ return false;
+ }
+
+ /**
+ * @hide
+ * CANDIDATE FOR PUBLIC API
+ * Return the device type which reflects the virtualization mode being used, if any.
+ * @return a device type (as defined in {@link AudioDevice}) which reflects the virtualization
+ * mode being used.
+ * If virtualization is not active, the device type will be
+ * {@link AudioDevice#DEVICE_TYPE_UNKNOWN}. Virtualization may not be active either because
+ * the effect is not enabled or because the current output device is not compatible with
+ * this virtualization implementation.
+ */
+ public int getVirtualizationMode() {
+ int[] value = new int[1];
+ int status = getParameter(PARAM_VIRTUALIZATION_MODE, value);
+ if (status >= 0) {
+ return AudioDevice.convertInternalDeviceToDeviceType(value[0]);
+ } else if (status == AudioEffect.ERROR_BAD_VALUE) {
+ return AudioDevice.DEVICE_TYPE_UNKNOWN;
+ } else {
+ // something wrong may have happened
+ checkStatus(status);
+ }
+ // unexpected virtualizer behavior
+ Log.e(TAG, "unexpected status code " + status
+ + " after getParameter(PARAM_VIRTUALIZATION_MODE)");
+ return AudioDevice.DEVICE_TYPE_UNKNOWN;
+ }
+
+ /**
* The OnParameterChangeListener interface defines a method called by the Virtualizer when a
* parameter value has changed.
*/
diff --git a/media/java/android/media/tv/ITvInputClient.aidl b/media/java/android/media/tv/ITvInputClient.aidl
index cac8a14..423e317 100644
--- a/media/java/android/media/tv/ITvInputClient.aidl
+++ b/media/java/android/media/tv/ITvInputClient.aidl
@@ -30,7 +30,6 @@
*/
oneway interface ITvInputClient {
void onSessionCreated(in String inputId, IBinder token, in InputChannel channel, int seq);
- void onAvailabilityChanged(in String inputId, boolean isAvailable);
void onSessionReleased(int seq);
void onSessionEvent(in String name, in Bundle args, int seq);
void onChannelRetuned(in Uri channelUri, int seq);
diff --git a/media/java/android/media/tv/ITvInputManager.aidl b/media/java/android/media/tv/ITvInputManager.aidl
index 9a6a648..6a0c592 100644
--- a/media/java/android/media/tv/ITvInputManager.aidl
+++ b/media/java/android/media/tv/ITvInputManager.aidl
@@ -18,9 +18,10 @@
import android.content.ComponentName;
import android.graphics.Rect;
+import android.media.tv.ITvInputClient;
import android.media.tv.ITvInputHardware;
import android.media.tv.ITvInputHardwareCallback;
-import android.media.tv.ITvInputClient;
+import android.media.tv.ITvInputManagerCallback;
import android.media.tv.TvInputHardwareInfo;
import android.media.tv.TvInputInfo;
import android.media.tv.TvTrackInfo;
@@ -34,10 +35,8 @@
interface ITvInputManager {
List<TvInputInfo> getTvInputList(int userId);
- boolean getAvailability(in ITvInputClient client, in String inputId, int userId);
-
- void registerCallback(in ITvInputClient client, in String inputId, int userId);
- void unregisterCallback(in ITvInputClient client, in String inputId, int userId);
+ void registerCallback(in ITvInputManagerCallback callback, int userId);
+ void unregisterCallback(in ITvInputManagerCallback callback, int userId);
void createSession(in ITvInputClient client, in String inputId, int seq, int userId);
void releaseSession(in IBinder sessionToken, int userId);
@@ -56,7 +55,12 @@
// For TV input hardware binding
List<TvInputHardwareInfo> getHardwareList();
+ /*
+ * All TvInputServices which want to use hardware must call this method on
+ * BOOT_COMPLETE.
+ */
+ void registerTvInputInfo(in TvInputInfo info, int deviceId);
ITvInputHardware acquireTvInputHardware(int deviceId, in ITvInputHardwareCallback callback,
- int userId);
+ in TvInputInfo info, int userId);
void releaseTvInputHardware(int deviceId, in ITvInputHardware hardware, int userId);
}
diff --git a/media/java/android/media/tv/ITvInputManagerCallback.aidl b/media/java/android/media/tv/ITvInputManagerCallback.aidl
new file mode 100644
index 0000000..5c8a0a3
--- /dev/null
+++ b/media/java/android/media/tv/ITvInputManagerCallback.aidl
@@ -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.
+ */
+
+package android.media.tv;
+
+/**
+ * Interface to receive callbacks from ITvInputManager regardless of sessions.
+ * @hide
+ */
+oneway interface ITvInputManagerCallback {
+ void onInputStateChanged(in String inputId, int state);
+}
diff --git a/media/java/android/media/tv/ITvInputServiceCallback.aidl b/media/java/android/media/tv/ITvInputServiceCallback.aidl
index c9484dd..1fdb8c5 100644
--- a/media/java/android/media/tv/ITvInputServiceCallback.aidl
+++ b/media/java/android/media/tv/ITvInputServiceCallback.aidl
@@ -24,5 +24,5 @@
* @hide
*/
oneway interface ITvInputServiceCallback {
- void onAvailabilityChanged(in String inputId, boolean isAvailable);
+ void onInputStateChanged(int state);
}
diff --git a/media/java/android/media/tv/TvInputInfo.java b/media/java/android/media/tv/TvInputInfo.java
index 7b8f2ec..5624f3e 100644
--- a/media/java/android/media/tv/TvInputInfo.java
+++ b/media/java/android/media/tv/TvInputInfo.java
@@ -80,7 +80,7 @@
// Attributes from XML meta data.
private String mSetupActivity;
private String mSettingsActivity;
- private int mType;
+ private int mType = TYPE_VIRTUAL;
/**
* Create a new instance of the TvInputInfo class,
@@ -128,10 +128,13 @@
Log.d(TAG, "Settings activity loaded. [" + input.mSettingsActivity + "] for "
+ si.name);
}
- input.mType = sa.getInt(
- com.android.internal.R.styleable.TvInputService_tvInputType, TYPE_VIRTUAL);
- if (DEBUG) {
- Log.d(TAG, "Type loaded. [" + input.mType + "] for " + si.name);
+ if (pm.checkPermission(android.Manifest.permission.TV_INPUT_HARDWARE, si.packageName)
+ == PackageManager.PERMISSION_GRANTED) {
+ input.mType = sa.getInt(
+ com.android.internal.R.styleable.TvInputService_tvInputType, TYPE_VIRTUAL);
+ if (DEBUG) {
+ Log.d(TAG, "Type loaded. [" + input.mType + "] for " + si.name);
+ }
}
sa.recycle();
diff --git a/media/java/android/media/tv/TvInputManager.java b/media/java/android/media/tv/TvInputManager.java
index 867b0db..79a83b0 100644
--- a/media/java/android/media/tv/TvInputManager.java
+++ b/media/java/android/media/tv/TvInputManager.java
@@ -24,6 +24,7 @@
import android.os.Looper;
import android.os.Message;
import android.os.RemoteException;
+import android.util.ArrayMap;
import android.util.Log;
import android.util.Pools.Pool;
import android.util.Pools.SimplePool;
@@ -37,6 +38,7 @@
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
+import java.util.LinkedList;
import java.util.List;
import java.util.Map;
@@ -65,11 +67,43 @@
*/
public static final int VIDEO_UNAVAILABLE_REASON_BUFFERING = 3;
+ /**
+ * The TV input is connected.
+ * <p>
+ * State for {@link #getInputState} and {@link
+ * TvInputManager.TvInputListener#onInputStateChanged}.
+ * </p>
+ */
+ public static final int INPUT_STATE_CONNECTED = 0;
+ /**
+ * The TV input is connected but in standby mode. It would take a while until it becomes
+ * fully ready.
+ * <p>
+ * State for {@link #getInputState} and {@link
+ * TvInputManager.TvInputListener#onInputStateChanged}.
+ * </p>
+ */
+ public static final int INPUT_STATE_CONNECTED_STANDBY = 1;
+ /**
+ * The TV input is disconnected.
+ * <p>
+ * State for {@link #getInputState} and {@link
+ * TvInputManager.TvInputListener#onInputStateChanged}.
+ * </p>
+ */
+ public static final int INPUT_STATE_DISCONNECTED = 2;
+
private final ITvInputManager mService;
- // A mapping from an input to the list of its TvInputListenerRecords.
- private final Map<String, List<TvInputListenerRecord>> mTvInputListenerRecordsMap =
- new HashMap<String, List<TvInputListenerRecord>>();
+ private final Object mLock = new Object();
+
+ // @GuardedBy(mLock)
+ private final List<TvInputListenerRecord> mTvInputListenerRecordsList =
+ new LinkedList<TvInputListenerRecord>();
+
+ // A mapping from TV input ID to the state of corresponding input.
+ // @GuardedBy(mLock)
+ private final Map<String, Integer> mStateMap = new ArrayMap<String, Integer>();
// A mapping from the sequence number of a session to its SessionCallbackRecord.
private final SparseArray<SessionCallbackRecord> mSessionCallbackRecordMap =
@@ -81,6 +115,8 @@
private final ITvInputClient mClient;
+ private final ITvInputManagerCallback mCallback;
+
private final int mUserId;
/**
@@ -242,13 +278,17 @@
*/
public abstract static class TvInputListener {
/**
- * This is called when the availability status of a given TV input is changed.
+ * This is called when the state of a given TV input is changed.
*
* @param inputId the id of the TV input.
- * @param isAvailable {@code true} if the given TV input is available to show TV programs.
- * {@code false} otherwise.
+ * @param state state of the TV input. The value is one of the following:
+ * <ul>
+ * <li>{@link TvInputManager#INPUT_STATE_CONNECTED}
+ * <li>{@link TvInputManager#INPUT_STATE_CONNECTED_STANDBY}
+ * <li>{@link TvInputManager#INPUT_STATE_DISCONNECTED}
+ * </ul>
*/
- public void onAvailabilityChanged(String inputId, boolean isAvailable) {
+ public void onInputStateChanged(String inputId, int state) {
}
}
@@ -265,11 +305,11 @@
return mListener;
}
- public void postAvailabilityChanged(final String inputId, final boolean isAvailable) {
+ public void postStateChanged(final String inputId, final int state) {
mHandler.post(new Runnable() {
@Override
public void run() {
- mListener.onAvailabilityChanged(inputId, isAvailable);
+ mListener.onInputStateChanged(inputId, state);
}
});
}
@@ -373,22 +413,23 @@
record.postSessionEvent(eventType, eventArgs);
}
}
-
+ };
+ mCallback = new ITvInputManagerCallback.Stub() {
@Override
- public void onAvailabilityChanged(String inputId, boolean isAvailable) {
- synchronized (mTvInputListenerRecordsMap) {
- List<TvInputListenerRecord> records = mTvInputListenerRecordsMap.get(inputId);
- if (records == null) {
- // Silently ignore - no listener is registered yet.
- return;
- }
- int recordsCount = records.size();
- for (int i = 0; i < recordsCount; i++) {
- records.get(i).postAvailabilityChanged(inputId, isAvailable);
+ public void onInputStateChanged(String inputId, int state) {
+ synchronized (mLock) {
+ mStateMap.put(inputId, state);
+ for (TvInputListenerRecord record : mTvInputListenerRecordsList) {
+ record.postStateChanged(inputId, state);
}
}
}
};
+ try {
+ mService.registerCallback(mCallback, mUserId);
+ } catch (RemoteException e) {
+ Log.e(TAG, "mService.registerCallback failed: " + e);
+ }
}
/**
@@ -405,98 +446,66 @@
}
/**
- * Returns the availability of a given TV input.
+ * Returns the state of a given TV input. It retuns one of the following:
+ * <ul>
+ * <li>{@link #INPUT_STATE_CONNECTED}
+ * <li>{@link #INPUT_STATE_CONNECTED_STANDBY}
+ * <li>{@link #INPUT_STATE_DISCONNECTED}
+ * </ul>
*
* @param inputId the id of the TV input.
- * @throws IllegalArgumentException if the argument is {@code null}.
- * @throws IllegalStateException If there is no {@link TvInputListener} registered on the given
- * TV input.
+ * @throws IllegalArgumentException if the argument is {@code null} or if there is no
+ * {@link TvInputInfo} corresponding to {@code inputId}.
*/
- public boolean getAvailability(String inputId) {
+ public int getInputState(String inputId) {
if (inputId == null) {
throw new IllegalArgumentException("id cannot be null");
}
- synchronized (mTvInputListenerRecordsMap) {
- List<TvInputListenerRecord> records = mTvInputListenerRecordsMap.get(inputId);
- if (records == null || records.size() == 0) {
- throw new IllegalStateException("At least one listener should be registered.");
+ synchronized (mLock) {
+ Integer state = mStateMap.get(inputId);
+ if (state == null) {
+ throw new IllegalArgumentException("Unrecognized input ID: " + inputId);
}
- }
- try {
- return mService.getAvailability(mClient, inputId, mUserId);
- } catch (RemoteException e) {
- throw new RuntimeException(e);
+ return state.intValue();
}
}
/**
- * Registers a {@link TvInputListener} for a given TV input.
+ * Registers a {@link TvInputListener}.
*
- * @param inputId the id of the TV input.
- * @param listener a listener used to monitor status of the given TV input.
+ * @param listener a listener used to monitor status of the TV inputs.
* @param handler a {@link Handler} that the status change will be delivered to.
* @throws IllegalArgumentException if any of the arguments is {@code null}.
- * @hide
*/
- public void registerListener(String inputId, TvInputListener listener, Handler handler) {
- if (inputId == null) {
- throw new IllegalArgumentException("id cannot be null");
- }
+ public void registerListener(TvInputListener listener, Handler handler) {
if (listener == null) {
throw new IllegalArgumentException("listener cannot be null");
}
if (handler == null) {
throw new IllegalArgumentException("handler cannot be null");
}
- synchronized (mTvInputListenerRecordsMap) {
- List<TvInputListenerRecord> records = mTvInputListenerRecordsMap.get(inputId);
- if (records == null) {
- records = new ArrayList<TvInputListenerRecord>();
- mTvInputListenerRecordsMap.put(inputId, records);
- try {
- mService.registerCallback(mClient, inputId, mUserId);
- } catch (RemoteException e) {
- throw new RuntimeException(e);
- }
- }
- records.add(new TvInputListenerRecord(listener, handler));
+ synchronized (mLock) {
+ mTvInputListenerRecordsList.add(new TvInputListenerRecord(listener, handler));
}
}
/**
- * Unregisters the existing {@link TvInputListener} for a given TV input.
+ * Unregisters the existing {@link TvInputListener}.
*
- * @param inputId the id of the TV input.
- * @param listener the existing listener to remove for the given TV input.
+ * @param listener the existing listener to remove.
* @throws IllegalArgumentException if any of the arguments is {@code null}.
- * @hide
*/
- public void unregisterListener(String inputId, final TvInputListener listener) {
- if (inputId == null) {
- throw new IllegalArgumentException("id cannot be null");
- }
+ public void unregisterListener(final TvInputListener listener) {
if (listener == null) {
throw new IllegalArgumentException("listener cannot be null");
}
- synchronized (mTvInputListenerRecordsMap) {
- List<TvInputListenerRecord> records = mTvInputListenerRecordsMap.get(inputId);
- if (records == null) {
- Log.e(TAG, "No listener found for " + inputId);
- return;
- }
- for (Iterator<TvInputListenerRecord> it = records.iterator(); it.hasNext();) {
+ synchronized (mLock) {
+ for (Iterator<TvInputListenerRecord> it = mTvInputListenerRecordsList.iterator();
+ it.hasNext(); ) {
TvInputListenerRecord record = it.next();
if (record.getListener() == listener) {
it.remove();
- }
- }
- if (records.isEmpty()) {
- try {
- mService.unregisterCallback(mClient, inputId, mUserId);
- } catch (RemoteException e) {
- throw new RuntimeException(e);
- } finally {
- mTvInputListenerRecordsMap.remove(inputId);
+ break;
}
}
}
diff --git a/media/java/android/media/tv/TvInputService.java b/media/java/android/media/tv/TvInputService.java
index a994f54..3206320 100644
--- a/media/java/android/media/tv/TvInputService.java
+++ b/media/java/android/media/tv/TvInputService.java
@@ -82,20 +82,9 @@
*/
public static final String SERVICE_META_DATA = "android.media.tv.input";
- private String mId;
private final Handler mHandler = new ServiceHandler();
private final RemoteCallbackList<ITvInputServiceCallback> mCallbacks =
new RemoteCallbackList<ITvInputServiceCallback>();
- // STOPSHIP: Redesign the API around the availability change. For now, the service will be
- // always available.
- private final boolean mAvailable = true;
-
- @Override
- public void onCreate() {
- super.onCreate();
- mId = TvInputInfo.generateInputIdForComponentName(
- new ComponentName(getPackageName(), getClass().getName()));
- }
@Override
public final IBinder onBind(Intent intent) {
@@ -104,13 +93,6 @@
public void registerCallback(ITvInputServiceCallback cb) {
if (cb != null) {
mCallbacks.register(cb);
- // The first time a callback is registered, the service needs to report its
- // availability status so that the system can know its initial value.
- try {
- cb.onAvailabilityChanged(mId, mAvailable);
- } catch (RemoteException e) {
- Log.e(TAG, "error in onAvailabilityChanged", e);
- }
}
}
@@ -733,7 +715,6 @@
@SuppressLint("HandlerLeak")
private final class ServiceHandler extends Handler {
private static final int DO_CREATE_SESSION = 1;
- private static final int DO_BROADCAST_AVAILABILITY_CHANGE = 2;
@Override
public final void handleMessage(Message msg) {
@@ -759,20 +740,6 @@
args.recycle();
return;
}
- case DO_BROADCAST_AVAILABILITY_CHANGE: {
- boolean isAvailable = (Boolean) msg.obj;
- int n = mCallbacks.beginBroadcast();
- try {
- for (int i = 0; i < n; i++) {
- mCallbacks.getBroadcastItem(i).onAvailabilityChanged(mId, isAvailable);
- }
- } catch (RemoteException e) {
- Log.e(TAG, "Unexpected exception", e);
- } finally {
- mCallbacks.finishBroadcast();
- }
- return;
- }
default: {
Log.w(TAG, "Unhandled message code: " + msg.what);
return;
diff --git a/packages/SystemUI/res/layout/qs_user_detail.xml b/packages/SystemUI/res/layout/qs_user_detail.xml
new file mode 100644
index 0000000..eedae9f
--- /dev/null
+++ b/packages/SystemUI/res/layout/qs_user_detail.xml
@@ -0,0 +1,24 @@
+<?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
+ -->
+
+<com.android.systemui.qs.tiles.UserDetail
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+ <include layout="@layout/user_switcher_host" />
+</com.android.systemui.qs.tiles.UserDetail>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/recents_task_view.xml b/packages/SystemUI/res/layout/recents_task_view.xml
index 7e8bfd32..3e4c1f6 100644
--- a/packages/SystemUI/res/layout/recents_task_view.xml
+++ b/packages/SystemUI/res/layout/recents_task_view.xml
@@ -28,7 +28,7 @@
android:layout_height="@dimen/recents_task_bar_height"
android:layout_gravity="top|center_horizontal"
android:background="@color/recents_task_bar_default_background_color">
- <ImageView
+ <com.android.systemui.recents.views.FixedSizeImageView
android:id="@+id/application_icon"
android:layout_width="@dimen/recents_task_view_application_icon_size"
android:layout_height="@dimen/recents_task_view_application_icon_size"
@@ -51,7 +51,7 @@
android:maxLines="2"
android:ellipsize="marquee"
android:fadingEdge="horizontal" />
- <ImageView
+ <com.android.systemui.recents.views.FixedSizeImageView
android:id="@+id/dismiss_task"
android:layout_width="48dp"
android:layout_height="48dp"
diff --git a/packages/SystemUI/res/layout/user_switcher_host.xml b/packages/SystemUI/res/layout/user_switcher_host.xml
index 816af57..c1626c6 100644
--- a/packages/SystemUI/res/layout/user_switcher_host.xml
+++ b/packages/SystemUI/res/layout/user_switcher_host.xml
@@ -21,17 +21,11 @@
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:background="#dd000000"
- android:elevation="12dp">
- <FrameLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginTop="@dimen/volume_panel_top"
- android:background="@*android:drawable/dialog_full_holo_dark">
+ android:layout_height="match_parent">
+
<ListView android:id="@android:id/list"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:listitem="@layout/user_switcher_item"/>
- </FrameLayout>
+
</com.android.systemui.settings.UserSwitcherHostView>
\ No newline at end of file
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index 3d53f9c..adab243 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -45,7 +45,7 @@
<color name="data_usage_secondary">#99FFFFFF</color><!-- 60% white -->
<color name="data_usage_graph_track">#33FFFFFF</color><!-- 20% white -->
<color name="data_usage_graph_warning">#FFFFFFFF</color>
- <color name="status_bar_clock_color">#33FFFFFF</color>
+ <color name="status_bar_clock_color">#FFFFFFFF</color>
<!-- Tint color for the content on the notification overflow card. -->
<color name="keyguard_overflow_content_color">#ff686868</color>
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
index 72474b8..5f09cbd 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
@@ -34,6 +34,7 @@
import com.android.systemui.qs.QSTile.DetailAdapter;
import com.android.systemui.settings.BrightnessController;
import com.android.systemui.settings.ToggleSlider;
+import com.android.systemui.statusbar.phone.QSTileHost;
import java.util.ArrayList;
@@ -61,9 +62,10 @@
private boolean mExpanded;
private boolean mListening;
- private TileRecord mDetailRecord;
+ private Record mDetailRecord;
private Callback mCallback;
private BrightnessController mBrightnessController;
+ private QSTileHost mHost;
public QSPanel(Context context) {
this(context, null);
@@ -89,12 +91,24 @@
mBrightnessController = new BrightnessController(getContext(),
(ImageView) findViewById(R.id.brightness_icon),
(ToggleSlider) findViewById(R.id.brightness_slider));
+
+ mDetailDoneButton.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ showDetail(false, mDetailRecord);
+ }
+ });
}
public void setCallback(Callback callback) {
mCallback = callback;
}
+ public void setHost(QSTileHost host) {
+ mHost = host;
+ }
+
+
public void updateResources() {
final Resources res = mContext.getResources();
final int columns = Math.max(1, res.getInteger(R.integer.quick_settings_num_columns));
@@ -143,7 +157,13 @@
}
}
- private void showDetail(boolean show, TileRecord r) {
+ public void showDetailAdapter(boolean show, DetailAdapter adapter) {
+ Record r = new Record();
+ r.detailAdapter = adapter;
+ showDetail(show, r);
+ }
+
+ private void showDetail(boolean show, Record r) {
mHandler.obtainMessage(H.SHOW_DETAIL, show ? 1 : 0, 0, r).sendToTarget();
}
@@ -203,40 +223,52 @@
addView(r.tileView);
}
- private void handleShowDetail(TileRecord r, boolean show) {
- if (r == null) return;
- AnimatorListener listener = null;
+ private void handleShowDetail(Record r, boolean show) {
+ if (r instanceof TileRecord) {
+ handleShowDetailTile((TileRecord) r, show);
+ } else {
+ handleShowDetailImpl(r, show, getWidth() /* x */, 0/* y */);
+ }
+ }
+
+ private void handleShowDetailTile(TileRecord r, boolean show) {
+ if ((mDetailRecord != null) == show) return;
+
if (show) {
- if (mDetailRecord != null) return; // already showing something in detail
r.detailAdapter = r.tile.getDetailAdapter();
if (r.detailAdapter == null) return;
- mDetailRecord = r;
- r.detailView = r.detailAdapter.createDetailView(mContext, r.detailView, mDetailContent);
+ }
+ int x = r.tileView.getLeft() + r.tileView.getWidth() / 2;
+ int y = r.tileView.getTop() + r.tileView.getHeight() / 2;
+ handleShowDetailImpl(r, show, x, y);
+ }
+
+ private void handleShowDetailImpl(Record r, boolean show, int x, int y) {
+ if ((mDetailRecord != null) == show) return; // already in right state
+ DetailAdapter detailAdapter = null;
+ AnimatorListener listener = null;
+ if (show) {
+ detailAdapter = r.detailAdapter;
+ r.detailView = detailAdapter.createDetailView(mContext, r.detailView, mDetailContent);
if (r.detailView == null) throw new IllegalStateException("Must return detail view");
- mDetailDoneButton.setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View v) {
- showDetail(false, mDetailRecord);
- }
- });
- final Intent settingsIntent = r.detailAdapter.getSettingsIntent();
+
+ final Intent settingsIntent = detailAdapter.getSettingsIntent();
mDetailSettingsButton.setVisibility(settingsIntent != null ? VISIBLE : GONE);
mDetailSettingsButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
- mDetailRecord.tile.mHost.startSettingsActivity(settingsIntent);
+ mHost.startSettingsActivity(settingsIntent);
}
});
+
mDetailContent.removeAllViews();
mDetail.bringToFront();
mDetailContent.addView(r.detailView);
+ mDetailRecord = r;
} else {
- if (mDetailRecord == null) return;
listener = mTeardownDetailWhenDone;
}
- fireShowingDetail(show ? r.detailAdapter : null);
- int x = r.tileView.getLeft() + r.tileView.getWidth() / 2;
- int y = r.tileView.getTop() + r.tileView.getHeight() / 2;
+ fireShowingDetail(show ? detailAdapter : null);
mClipper.animateCircularClip(x, y, show, listener);
}
@@ -339,18 +371,21 @@
@Override
public void handleMessage(Message msg) {
if (msg.what == SHOW_DETAIL) {
- handleShowDetail((TileRecord)msg.obj, msg.arg1 != 0);
+ handleShowDetail((Record)msg.obj, msg.arg1 != 0);
} else if (msg.what == SET_TILE_VISIBILITY) {
handleSetTileVisibility((View)msg.obj, msg.arg1 != 0);
}
}
}
- private static final class TileRecord {
- QSTile<?> tile;
- QSTileView tileView;
+ private static class Record {
View detailView;
DetailAdapter detailAdapter;
+ }
+
+ private static final class TileRecord extends Record {
+ QSTile<?> tile;
+ QSTileView tileView;
int row;
int col;
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetail.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetail.java
new file mode 100644
index 0000000..a9a2724
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetail.java
@@ -0,0 +1,78 @@
+/*
+ * 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.systemui.qs.tiles;
+
+import com.android.systemui.R;
+import com.android.systemui.qs.QSTile;
+
+import android.content.Context;
+import android.content.Intent;
+import android.util.AttributeSet;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.FrameLayout;
+
+/**
+ * Quick settings detail view for user switching.
+ */
+public class UserDetail extends FrameLayout {
+
+ static final Intent USER_SETTINGS_INTENT = new Intent("android.settings.USER_SETTINGS");
+
+ public UserDetail(Context context) {
+ this(context, null);
+ }
+
+ public UserDetail(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public UserDetail(Context context, AttributeSet attrs, int defStyleAttr) {
+ this(context, attrs, defStyleAttr, 0);
+ }
+
+ public UserDetail(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+ }
+
+ public static QSTile.DetailAdapter USER_DETAIL_ADAPTER = new QSTile.DetailAdapter() {
+ @Override
+ public int getTitle() {
+ return R.string.quick_settings_user_title;
+ }
+
+ @Override
+ public Boolean getToggleState() {
+ return null;
+ }
+
+ @Override
+ public View createDetailView(Context context, View convertView, ViewGroup parent) {
+ return LayoutInflater.from(context).inflate(R.layout.qs_user_detail, parent, false);
+ }
+
+ @Override
+ public Intent getSettingsIntent() {
+ return USER_SETTINGS_INTENT;
+ }
+
+ @Override
+ public void setToggleState(boolean state) {
+ }
+ };
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsPackageMonitor.java b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsPackageMonitor.java
index 2f1c1c4..31011ae 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsPackageMonitor.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsPackageMonitor.java
@@ -25,7 +25,6 @@
import java.util.HashSet;
import java.util.List;
-import java.util.Set;
/**
* The package monitor listens for changes from PackageManager to update the contents of the Recents
@@ -33,7 +32,7 @@
*/
public class RecentsPackageMonitor extends PackageMonitor {
public interface PackageCallbacks {
- public void onComponentRemoved(Set<ComponentName> cns);
+ public void onComponentRemoved(HashSet<ComponentName> cns);
}
PackageCallbacks mCb;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java b/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java
index e3bcff0..13fbe64 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java
@@ -204,8 +204,8 @@
removeGroup(group);
}
// Update the lock-to-app state
- Task newFrontMostTask = getFrontMostTask();
t.canLockToTask = false;
+ Task newFrontMostTask = getFrontMostTask();
if (newFrontMostTask != null) {
newFrontMostTask.canLockToTask = true;
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/FixedSizeImageView.java b/packages/SystemUI/src/com/android/systemui/recents/views/FixedSizeImageView.java
new file mode 100644
index 0000000..3adee0ea
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/FixedSizeImageView.java
@@ -0,0 +1,82 @@
+/*
+ * 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.systemui.recents.views;
+
+import android.content.Context;
+import android.graphics.drawable.Drawable;
+import android.util.AttributeSet;
+import android.widget.ImageView;
+
+/**
+ * This is an optimized ImageView that does not trigger a requestLayout() or invalidate() when
+ * setting the image to Null.
+ */
+public class FixedSizeImageView extends ImageView {
+
+ int mFixedWidth;
+ int mFixedHeight;
+ boolean mAllowRelayout = true;
+ boolean mAllowInvalidate = true;
+
+ public FixedSizeImageView(Context context) {
+ this(context, null);
+ }
+
+ public FixedSizeImageView(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public FixedSizeImageView(Context context, AttributeSet attrs, int defStyleAttr) {
+ this(context, attrs, defStyleAttr, 0);
+ }
+
+ public FixedSizeImageView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ mFixedWidth = getMeasuredWidth();
+ mFixedHeight = getMeasuredHeight();
+ }
+
+ @Override
+ public void requestLayout() {
+ if (mAllowRelayout) {
+ super.requestLayout();
+ }
+ }
+
+ @Override
+ public void invalidate() {
+ if (mAllowInvalidate) {
+ super.invalidate();
+ }
+ }
+
+ @Override
+ public void setImageDrawable(Drawable drawable) {
+ if (drawable == null || (mFixedWidth > 0 && mFixedHeight > 0)) {
+ mAllowRelayout = false;
+ mAllowInvalidate = false;
+ }
+ super.setImageDrawable(drawable);
+ mAllowRelayout = true;
+ mAllowInvalidate = true;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
index 7bb6144..b32d3dd 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
@@ -45,7 +45,7 @@
import com.android.systemui.recents.model.TaskStack;
import java.util.ArrayList;
-import java.util.Set;
+import java.util.HashSet;
/**
* This view is the the top level layout that contains TaskStacks (which are laid out according
@@ -551,7 +551,7 @@
/**** RecentsPackageMonitor.PackageCallbacks Implementation ****/
@Override
- public void onComponentRemoved(Set<ComponentName> cns) {
+ public void onComponentRemoved(HashSet<ComponentName> cns) {
// Propagate this event down to each task stack view
int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
index 7b52163..0b35f59 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
@@ -23,9 +23,8 @@
import android.content.ComponentName;
import android.content.Context;
import android.graphics.Canvas;
+import android.graphics.Paint;
import android.graphics.Rect;
-import android.graphics.Region;
-import android.os.SystemClock;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
@@ -45,7 +44,7 @@
import java.util.ArrayList;
import java.util.HashMap;
-import java.util.Set;
+import java.util.HashSet;
/* The visual representation of a task stack view */
@@ -83,6 +82,7 @@
int mFocusedTaskIndex = -1;
OverScroller mScroller;
ObjectAnimator mScrollAnimator;
+ boolean mEnableStackClipping = true;
// Optimizations
ReferenceCountedTrigger mHwLayersTrigger;
@@ -169,6 +169,7 @@
void requestSynchronizeStackViewsWithModel(int duration) {
if (!mStackViewsDirty) {
invalidate(mStackAlgorithm.mStackRect);
+ mStackViewsDirty = true;
}
if (mAwaitingFirstLayout) {
// Skip the animation if we are awaiting first layout
@@ -176,7 +177,6 @@
} else {
mStackViewsAnimationDuration = Math.max(mStackViewsAnimationDuration, duration);
}
- mStackViewsDirty = true;
}
/** Returns a mapping of child view to Task. */
@@ -338,7 +338,7 @@
/** Updates the clip for each of the task views. */
void clipTaskViews() {
// Update the clip on each task child
- if (Constants.DebugFlags.App.EnableTaskStackClipping) {
+ if (Constants.DebugFlags.App.EnableTaskStackClipping && mEnableStackClipping) {
int childCount = getChildCount();
for (int i = 0; i < childCount - 1; i++) {
TaskView tv = (TaskView) getChildAt(i);
@@ -360,10 +360,12 @@
// stacked and we can make assumptions about the visibility of the this
// task relative to the ones in front of it.
if (nextTv != null) {
- // XXX: Can hash the visible rects for this run
+ // We calculate the bottom clip independent of the footer (since we animate
+ // that)
+ int scaledMaxFooterHeight = (int) (tv.getMaxFooterHeight() * tv.getScaleX());
tv.getHitRect(mTmpRect);
nextTv.getHitRect(mTmpRect2);
- clipBottom = (mTmpRect.bottom - mTmpRect2.top);
+ clipBottom = (mTmpRect.bottom - scaledMaxFooterHeight - mTmpRect2.top);
}
}
tv.setClipFromBottom(clipBottom);
@@ -376,6 +378,18 @@
}
}
+ /** Enables/Disables clipping of the tasks in the stack. */
+ void setStackClippingEnabled(boolean stackClippingEnabled) {
+ if (!stackClippingEnabled) {
+ int childCount = getChildCount();
+ for (int i = 0; i < childCount; i++) {
+ TaskView tv = (TaskView) getChildAt(i);
+ tv.setClipFromBottom(0);
+ }
+ }
+ mEnableStackClipping = stackClippingEnabled;
+ }
+
/** Sets the current stack scroll */
public void setStackScroll(int value) {
mStackScroll = value;
@@ -614,7 +628,6 @@
public void computeScroll() {
if (mScroller.computeScrollOffset()) {
setStackScroll(mScroller.getCurrY());
- invalidate(mStackAlgorithm.mStackRect);
// If we just finished scrolling, then disable the hw layers
if (mScroller.isFinished()) {
@@ -668,7 +681,7 @@
TaskView t = (TaskView) getChildAt(i);
t.measure(MeasureSpec.makeMeasureSpec(mStackAlgorithm.mTaskRect.width(), MeasureSpec.EXACTLY),
MeasureSpec.makeMeasureSpec(mStackAlgorithm.mTaskRect.height() +
- mConfig.taskViewLockToAppButtonHeight, MeasureSpec.EXACTLY));
+ t.getMaxFooterHeight(), MeasureSpec.EXACTLY));
}
setMeasuredDimension(width, height);
@@ -687,7 +700,7 @@
TaskView t = (TaskView) getChildAt(i);
t.layout(mStackAlgorithm.mTaskRect.left, mStackAlgorithm.mStackRectSansPeek.top,
mStackAlgorithm.mTaskRect.right, mStackAlgorithm.mStackRectSansPeek.top +
- mStackAlgorithm.mTaskRect.height() + mConfig.taskViewLockToAppButtonHeight);
+ mStackAlgorithm.mTaskRect.height() + t.getMaxFooterHeight());
}
if (mAwaitingFirstLayout) {
@@ -1056,7 +1069,7 @@
/**** RecentsPackageMonitor.PackageCallbacks Implementation ****/
@Override
- public void onComponentRemoved(Set<ComponentName> cns) {
+ public void onComponentRemoved(HashSet<ComponentName> cns) {
// For other tasks, just remove them directly if they no longer exist
ArrayList<Task> tasks = mStack.getTasks();
for (int i = tasks.size() - 1; i >= 0; i--) {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewLayoutAlgorithm.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewLayoutAlgorithm.java
index 9c48896..65407a6 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewLayoutAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewLayoutAlgorithm.java
@@ -146,7 +146,8 @@
transformOut.translationZ = (int) Math.max(minZ, minZ + ((boundedT + numPeekCards) * incZ));
// Set the alphas
- transformOut.dismissAlpha = Math.max(-1f, Math.min(0f, t + 1)) + 1f;
+ // transformOut.dismissAlpha = Math.max(-1f, Math.min(0f, t + 1)) + 1f;
+ transformOut.dismissAlpha = 1f;
// Update the rect and visibility
transformOut.rect.set(mTaskRect);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskThumbnailView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskThumbnailView.java
index 636746d..08a25f1 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskThumbnailView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskThumbnailView.java
@@ -21,12 +21,11 @@
import android.graphics.Rect;
import android.util.AttributeSet;
import android.view.View;
-import android.widget.ImageView;
import com.android.systemui.recents.model.Task;
/** The task thumbnail view */
-public class TaskThumbnailView extends ImageView {
+public class TaskThumbnailView extends FixedSizeImageView {
Task mTask;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
index 7e30047..199d3f3 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
@@ -495,7 +495,7 @@
mLockToAppButtonView.setLayerType(View.LAYER_TYPE_NONE, mLayerPaint);
}
- /** Sets the stubbed state of this task view.
+ /** Sets the stubbed state of this task view. */
void setStubState(boolean isStub) {
if (!mIsStub && isStub) {
// This is now a stub task view, so clip to the bar height, hide the thumbnail
@@ -508,7 +508,7 @@
mThumbnailView.setVisibility(View.VISIBLE);
}
mIsStub = isStub;
- } */
+ }
/**
* Returns whether this view should be clipped, or any views below should clip against this
@@ -549,9 +549,19 @@
return mFooterHeight;
}
+ /** Gets the max footer height. */
+ public int getMaxFooterHeight() {
+ return mMaxFooterHeight;
+ }
+
/** Animates the footer into and out of view. */
public void animateFooterVisibility(boolean visible, int duration, int delay) {
- if (!mTask.canLockToTask) return;
+ if (!mTask.canLockToTask) {
+ if (mLockToAppButtonView.getVisibility() == View.VISIBLE) {
+ mLockToAppButtonView.setVisibility(View.INVISIBLE);
+ }
+ return;
+ }
if (mMaxFooterHeight <= 0) return;
if (mFooterAnimator != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/settings/UserSwitcherHostView.java b/packages/SystemUI/src/com/android/systemui/settings/UserSwitcherHostView.java
index a3b10f2..a5c5862 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/UserSwitcherHostView.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/UserSwitcherHostView.java
@@ -90,6 +90,7 @@
mListView = (ListView) findViewById(android.R.id.list);
mListView.setAdapter(mAdapter);
mListView.setOnItemClickListener(this);
+ refreshUsers();
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/MultiUserSwitch.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/MultiUserSwitch.java
index 0f12274..d32ad50 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/MultiUserSwitch.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/MultiUserSwitch.java
@@ -18,29 +18,22 @@
import android.content.Context;
import android.content.Intent;
-import android.graphics.Canvas;
-import android.graphics.Path;
-import android.graphics.drawable.Drawable;
import android.os.UserHandle;
import android.os.UserManager;
import android.provider.ContactsContract;
import android.util.AttributeSet;
-import android.view.LayoutInflater;
import android.view.View;
-import android.view.ViewGroup;
import android.widget.FrameLayout;
-import android.widget.ImageButton;
-import com.android.systemui.R;
-import com.android.systemui.settings.UserSwitcherHostView;
-import com.android.systemui.statusbar.policy.UserInfoController;
+import com.android.systemui.qs.QSPanel;
+import com.android.systemui.qs.tiles.UserDetail;
/**
* Container for image of the multi user switcher (tappable).
*/
public class MultiUserSwitch extends FrameLayout implements View.OnClickListener {
- private ViewGroup mOverlayParent;
+ private QSPanel mQsPanel;
public MultiUserSwitch(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -52,25 +45,15 @@
setOnClickListener(this);
}
- public void setOverlayParent(ViewGroup parent) {
- mOverlayParent = parent;
+ public void setQsPanel(QSPanel qsPanel) {
+ mQsPanel = qsPanel;
}
@Override
public void onClick(View v) {
final UserManager um = UserManager.get(getContext());
if (um.isUserSwitcherEnabled()) {
- final UserSwitcherHostView switcher =
- (UserSwitcherHostView) LayoutInflater.from(getContext()).inflate(
- R.layout.user_switcher_host, mOverlayParent, false);
- switcher.setFinishRunnable(new Runnable() {
- @Override
- public void run() {
- mOverlayParent.removeView(switcher);
- }
- });
- switcher.refreshUsers();
- mOverlayParent.addView(switcher);
+ mQsPanel.showDetailAdapter(true, UserDetail.USER_DETAIL_ADAPTER);
} else {
Intent intent = ContactsContract.QuickContact.composeQuickContactsIntent(
getContext(), v, ContactsContract.Profile.CONTENT_URI,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
index 55b3088..fc0f2d5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -150,7 +150,6 @@
super.onFinishInflate();
mHeader = (StatusBarHeaderView) findViewById(R.id.header);
mHeader.setOnClickListener(this);
- mHeader.setOverlayParent(this);
mKeyguardStatusView = findViewById(R.id.keyguard_status_view);
mQsContainer = findViewById(R.id.quick_settings_container);
mQsPanel = (QSPanel) findViewById(R.id.quick_settings_panel);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
index 505af44..2c43161 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -699,6 +699,7 @@
mBluetoothController, mLocationController, mRotationLockController,
mNetworkController, mZenModeController, null /*tethering*/,
mCastController, mVolumeComponent, mFlashlightController);
+ mQSPanel.setHost(qsh);
for (QSTile<?> tile : qsh.getTiles()) {
mQSPanel.addTile(tile);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java
index 33d1b15..dc06ec2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java
@@ -475,10 +475,6 @@
});
}
- public void setOverlayParent(ViewGroup parent) {
- mMultiUserSwitch.setOverlayParent(parent);
- }
-
@Override
public void onClick(View v) {
if (v == mSettingsButton) {
@@ -501,6 +497,7 @@
if (mQSPanel != null) {
mQSPanel.setCallback(mQsPanelCallback);
}
+ mMultiUserSwitch.setQsPanel(qsp);
}
@Override
@@ -589,7 +586,7 @@
mQsDetailHeader.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
- detail.setToggleState(!toggleState);
+ detail.setToggleState(!mQsDetailHeaderSwitch.isChecked());
}
});
}
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index 260a5b5..7e5cac7 100755
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -1081,6 +1081,7 @@
if (next == mLastScreenshotActivity) {
invalidateLastScreenshot();
}
+ mReturningActivityOptions = null;
}
private void setVisibile(ActivityRecord r, boolean visible) {
@@ -1217,13 +1218,10 @@
if (DEBUG_VISBILITY) Slog.v(TAG, "Skipping: already visible at " + r);
r.stopFreezingScreenLocked(false);
try {
- if (mReturningActivityOptions != null) {
- if (activityNdx > 0) {
- ActivityRecord under = activities.get(activityNdx - 1);
- under.app.thread.scheduleOnNewActivityOptions(under.appToken,
- mReturningActivityOptions);
- }
- mReturningActivityOptions = null;
+ if (mReturningActivityOptions != null && r == top && activityNdx > 0) {
+ ActivityRecord under = activities.get(activityNdx - 1);
+ under.app.thread.scheduleOnNewActivityOptions(under.appToken,
+ mReturningActivityOptions);
}
} catch(RemoteException e) {
}
@@ -1842,8 +1840,11 @@
ActivityStack lastStack = mStackSupervisor.getLastStack();
final boolean fromHome = lastStack.isHomeStack();
if (!isHomeStack() && (fromHome || topTask() != task)) {
- task.setTaskToReturnTo(fromHome ?
- lastStack.topTask().taskType : APPLICATION_ACTIVITY_TYPE);
+ task.setTaskToReturnTo(fromHome
+ ? lastStack.topTask() == null
+ ? HOME_ACTIVITY_TYPE
+ : lastStack.topTask().taskType
+ : APPLICATION_ACTIVITY_TYPE);
}
} else {
task.setTaskToReturnTo(APPLICATION_ACTIVITY_TYPE);
diff --git a/services/core/java/com/android/server/connectivity/NetworkMonitor.java b/services/core/java/com/android/server/connectivity/NetworkMonitor.java
index 6fb8570..8289f5a 100644
--- a/services/core/java/com/android/server/connectivity/NetworkMonitor.java
+++ b/services/core/java/com/android/server/connectivity/NetworkMonitor.java
@@ -648,6 +648,7 @@
new InputStreamReader(socket.getInputStream()));
OutputStreamWriter writer = new OutputStreamWriter(socket.getOutputStream());
writer.write("GET " + url.getFile() + " HTTP/1.1\r\nHost: " + url.getHost() +
+ "\r\nUser-Agent: " + System.getProperty("http.agent") +
"\r\nConnection: close\r\n\r\n");
writer.flush();
String response = reader.readLine();
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index 0eb922d..190e87c 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -60,6 +60,7 @@
// TODO: destroy sessions with old timestamps
// TODO: remove outstanding sessions when installer package goes away
+ // TODO: notify listeners in other users when package has been installed there
private final Context mContext;
private final PackageManagerService mPm;
@@ -167,6 +168,10 @@
params.installFlags |= INSTALL_REPLACE_EXISTING;
}
+ if (params.mode == InstallSessionParams.MODE_INVALID) {
+ throw new IllegalArgumentException("Params must have valid mode set");
+ }
+
// Sanity check that install could fit
if (params.deltaSize > 0) {
try {
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 41ab66a..31d9704 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -166,7 +166,7 @@
info.installerPackageName = installerPackageName;
info.progress = mProgress;
- info.fullInstall = params.fullInstall;
+ info.mode = params.mode;
info.packageName = params.packageName;
info.icon = params.icon;
info.title = params.title;
@@ -289,7 +289,7 @@
// Inherit any packages and native libraries from existing install that
// haven't been overridden.
- if (!params.fullInstall) {
+ if (params.mode == InstallSessionParams.MODE_INHERIT_EXISTING) {
spliceExistingFilesIntoStage();
}
@@ -383,7 +383,7 @@
// currently relying on PMS to do this.
// TODO: teach about compatible upgrade keysets.
- if (params.fullInstall) {
+ if (params.mode == InstallSessionParams.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 cfba19c..74a1945 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -1713,7 +1713,7 @@
// NOTE: We ignore potential failures here during a system scan (like
// the rest of the commands above) because there's precious little we
// can do about it. A settings error is reported, though.
- adjustCpuAbisForSharedUserLPw(setting.packages, null,
+ adjustCpuAbisForSharedUserLPw(setting.packages, null /* scanned package */,
false /* force dexopt */, false /* defer dexopt */);
}
@@ -5428,11 +5428,8 @@
}
}
- if (abi32 < 0 && abi32 != PackageManager.NO_NATIVE_LIBRARIES) {
- throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
- "Error unpackaging 32 bit native libs for multiarch app, errorCode="
- + abi32);
- }
+ maybeThrowExceptionForMultiArchCopy(
+ "Error unpackaging 32 bit native libs for multiarch app.", abi32);
if (Build.SUPPORTED_64_BIT_ABIS.length > 0) {
if (isAsec) {
@@ -5443,11 +5440,8 @@
}
}
- if (abi64 < 0 && abi64 != PackageManager.NO_NATIVE_LIBRARIES) {
- throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
- "Error unpackaging 64 bit native libs for multiarch app, errorCode="
- + abi32);
- }
+ maybeThrowExceptionForMultiArchCopy(
+ "Error unpackaging 64 bit native libs for multiarch app.", abi64);
if (abi64 >= 0) {
pkg.applicationInfo.primaryCpuAbi = Build.SUPPORTED_64_BIT_ABIS[abi64];
@@ -5544,11 +5538,8 @@
// We also do this *before* we perform dexopt on this package, so that
// we can avoid redundant dexopts, and also to make sure we've got the
// code and package path correct.
- if (!adjustCpuAbisForSharedUserLPw(pkgSetting.sharedUser.packages,
- pkg, forceDex, (scanMode & SCAN_DEFER_DEX) != 0)) {
- throw new PackageManagerException(INSTALL_FAILED_CPU_ABI_INCOMPATIBLE,
- "scanPackageLI");
- }
+ adjustCpuAbisForSharedUserLPw(pkgSetting.sharedUser.packages,
+ pkg, forceDex, (scanMode & SCAN_DEFER_DEX) != 0);
}
if ((scanMode&SCAN_NO_DEX) == 0) {
@@ -6047,7 +6038,7 @@
* NOTE: We currently only match for the primary CPU abi string. Matching the secondary
* adds unnecessary complexity.
*/
- private boolean adjustCpuAbisForSharedUserLPw(Set<PackageSetting> packagesForUser,
+ private void adjustCpuAbisForSharedUserLPw(Set<PackageSetting> packagesForUser,
PackageParser.Package scannedPackage, boolean forceDexOpt, boolean deferDexOpt) {
String requiredInstructionSet = null;
if (scannedPackage != null && scannedPackage.applicationInfo.primaryCpuAbi != null) {
@@ -6061,27 +6052,23 @@
// when scannedPackage is an update of an existing package. Without this check,
// we will never be able to change the ABI of any package belonging to a shared
// user, even if it's compatible with other packages.
- if (scannedPackage == null || ! scannedPackage.packageName.equals(ps.name)) {
+ if (scannedPackage == null || !scannedPackage.packageName.equals(ps.name)) {
if (ps.primaryCpuAbiString == null) {
continue;
}
final String instructionSet = VMRuntime.getInstructionSet(ps.primaryCpuAbiString);
- if (requiredInstructionSet != null) {
- if (!instructionSet.equals(requiredInstructionSet)) {
- // We have a mismatch between instruction sets (say arm vs arm64).
- // bail out.
- String errorMessage = "Instruction set mismatch, "
- + ((requirer == null) ? "[caller]" : requirer)
- + " requires " + requiredInstructionSet + " whereas " + ps
- + " requires " + instructionSet;
- Slog.e(TAG, errorMessage);
+ if (requiredInstructionSet != null && !instructionSet.equals(requiredInstructionSet)) {
+ // We have a mismatch between instruction sets (say arm vs arm64) warn about
+ // this but there's not much we can do.
+ String errorMessage = "Instruction set mismatch, "
+ + ((requirer == null) ? "[caller]" : requirer)
+ + " requires " + requiredInstructionSet + " whereas " + ps
+ + " requires " + instructionSet;
+ Slog.w(TAG, errorMessage);
+ }
- reportSettingsProblem(Log.WARN, errorMessage);
- // Give up, don't bother making any other changes to the package settings.
- return false;
- }
- } else {
+ if (requiredInstructionSet == null) {
requiredInstructionSet = instructionSet;
requirer = ps;
}
@@ -6118,7 +6105,7 @@
if (performDexOptLI(ps.pkg, forceDexOpt, deferDexOpt, true) == DEX_OPT_FAILED) {
ps.primaryCpuAbiString = null;
ps.pkg.applicationInfo.primaryCpuAbi = null;
- return false;
+ return;
} else {
mInstaller.rmdex(ps.codePathString, getPreferredInstructionSet());
}
@@ -6126,8 +6113,6 @@
}
}
}
-
- return true;
}
private void setUpCustomResolverActivity(PackageParser.Package pkg) {
@@ -6240,9 +6225,6 @@
if (info.primaryCpuAbi != null) {
info.nativeLibraryDir = new File(info.nativeLibraryRootDir,
VMRuntime.getInstructionSet(info.primaryCpuAbi)).getAbsolutePath();
- } else {
- Slog.w(TAG, "Package " + info.packageName
- + " missing ABI; unable to derive nativeLibraryDir");
}
} else {
info.nativeLibraryDir = info.nativeLibraryRootDir;
@@ -9264,29 +9246,13 @@
if (Build.SUPPORTED_32_BIT_ABIS.length > 0) {
copyRet = copyNativeLibrariesForInternalApp(handle, libraryRoot,
Build.SUPPORTED_32_BIT_ABIS, true /* use isa specific subdirs */);
- if (copyRet < 0 && copyRet != PackageManager.NO_NATIVE_LIBRARIES) {
- Slog.w(TAG, "Failure copying 32 bit native libraries [errorCode=" + copyRet + "]");
- return copyRet;
- }
- }
-
- if (DEBUG_ABI_SELECTION && copyRet >= 0) {
- Log.d(TAG, "Installed 32 bit libraries under: " + codeFile + " abi=" +
- Build.SUPPORTED_32_BIT_ABIS[copyRet]);
+ maybeThrowExceptionForMultiArchCopy("Failure copying 32 bit native libraries", copyRet);
}
if (Build.SUPPORTED_64_BIT_ABIS.length > 0) {
copyRet = copyNativeLibrariesForInternalApp(handle, libraryRoot,
Build.SUPPORTED_64_BIT_ABIS, true /* use isa specific subdirs */);
- if (copyRet < 0 && copyRet != PackageManager.NO_NATIVE_LIBRARIES) {
- Slog.w(TAG, "Failure copying 64 bit native libraries [errorCode=" + copyRet + "]");
- return copyRet;
- }
- }
-
- if (DEBUG_ABI_SELECTION && copyRet >= 0) {
- Log.d(TAG, "Installed 64 bit libraries under: " + codeFile + " abi=" +
- Build.SUPPORTED_64_BIT_ABIS[copyRet]);
+ maybeThrowExceptionForMultiArchCopy("Failure copying 64 bit native libraries", copyRet);
}
} else {
String[] abiList = (abiOverride != null) ?
@@ -9303,14 +9269,13 @@
Slog.w(TAG, "Failure copying native libraries [errorCode=" + copyRet + "]");
return copyRet;
}
-
- if (DEBUG_ABI_SELECTION && copyRet >= 0) {
- Log.d(TAG, "Installed libraries under: " + codeFile + " abi=" + abiList[copyRet]);
- }
}
} catch (IOException e) {
Slog.e(TAG, "Copying native libraries failed", e);
ret = PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
+ } catch (PackageManagerException pme) {
+ Slog.e(TAG, "Copying native libraries failed", pme);
+ ret = pme.error;
} finally {
IoUtils.closeQuietly(handle);
}
@@ -9459,6 +9424,16 @@
return !asecPath.startsWith(mAsecInternalPath);
}
+ private static void maybeThrowExceptionForMultiArchCopy(String message, int copyRet) throws
+ PackageManagerException {
+ if (copyRet < 0) {
+ if (copyRet != PackageManager.NO_NATIVE_LIBRARIES &&
+ copyRet != PackageManager.INSTALL_FAILED_NO_MATCHING_ABIS) {
+ throw new PackageManagerException(copyRet, message);
+ }
+ }
+ }
+
/**
* Extract the MountService "container ID" from the full code path of an
* .apk.
diff --git a/services/core/java/com/android/server/tv/TvInputHardwareManager.java b/services/core/java/com/android/server/tv/TvInputHardwareManager.java
index efe543b..8f237db 100644
--- a/services/core/java/com/android/server/tv/TvInputHardwareManager.java
+++ b/services/core/java/com/android/server/tv/TvInputHardwareManager.java
@@ -16,7 +16,12 @@
package com.android.server.tv;
+import static android.media.tv.TvInputManager.INPUT_STATE_CONNECTED;
+import static android.media.tv.TvInputManager.INPUT_STATE_DISCONNECTED;
+
import android.content.Context;
+import android.hardware.hdmi.HdmiControlManager;
+import android.hardware.hdmi.HdmiHotplugEvent;
import android.media.AudioDevicePort;
import android.media.AudioManager;
import android.media.AudioPatch;
@@ -25,14 +30,22 @@
import android.media.tv.ITvInputHardware;
import android.media.tv.ITvInputHardwareCallback;
import android.media.tv.TvInputHardwareInfo;
+import android.media.tv.TvInputInfo;
import android.media.tv.TvStreamConfig;
+import android.os.Handler;
+import android.os.HandlerThread;
import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
import android.os.RemoteException;
import android.util.Slog;
import android.util.SparseArray;
+import android.util.SparseBooleanArray;
import android.view.KeyEvent;
import android.view.Surface;
+import com.android.server.SystemService;
+
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
@@ -46,23 +59,42 @@
*
* @hide
*/
-class TvInputHardwareManager implements TvInputHal.Callback {
+class TvInputHardwareManager
+ implements TvInputHal.Callback, HdmiControlManager.HotplugEventListener {
private static final String TAG = TvInputHardwareManager.class.getSimpleName();
private final TvInputHal mHal = new TvInputHal(this);
private final SparseArray<Connection> mConnections = new SparseArray<Connection>();
private final List<TvInputHardwareInfo> mInfoList = new ArrayList<TvInputHardwareInfo>();
private final Context mContext;
+ private final TvInputManagerService.Client mClient;
private final Set<Integer> mActiveHdmiSources = new HashSet<Integer>();
private final AudioManager mAudioManager;
+ private final SparseBooleanArray mHdmiStateMap = new SparseBooleanArray();
+ // TODO: Should handle INACTIVE case.
+ private final SparseArray<TvInputInfo> mTvInputInfoMap = new SparseArray<TvInputInfo>();
+
+ // Calls to mClient should happen here.
+ private final HandlerThread mHandlerThread = new HandlerThread(TAG);
+ private final Handler mHandler;
private final Object mLock = new Object();
- public TvInputHardwareManager(Context context) {
+ public TvInputHardwareManager(Context context, TvInputManagerService.Client client) {
mContext = context;
+ mClient = client;
mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
- // TODO(hdmi): mHdmiManager = mContext.getSystemService(...);
- // TODO(hdmi): mHdmiClient = mHdmiManager.getTvClient();
mHal.init();
+
+ mHandlerThread.start();
+ mHandler = new ClientHandler(mHandlerThread.getLooper());
+ }
+
+ public void onBootPhase(int phase) {
+ if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {
+ HdmiControlManager hdmiControlManager =
+ (HdmiControlManager) mContext.getSystemService(Context.HDMI_CONTROL_SERVICE);
+ hdmiControlManager.addHotplugEventListener(this);
+ }
}
@Override
@@ -80,7 +112,7 @@
private void buildInfoListLocked() {
mInfoList.clear();
for (int i = 0; i < mConnections.size(); ++i) {
- mInfoList.add(mConnections.valueAt(i).getInfoLocked());
+ mInfoList.add(mConnections.valueAt(i).getHardwareInfoLocked());
}
}
@@ -92,7 +124,7 @@
Slog.e(TAG, "onDeviceUnavailable: Cannot find a connection with " + deviceId);
return;
}
- connection.resetLocked(null, null, null, null);
+ connection.resetLocked(null, null, null, null, null);
mConnections.remove(deviceId);
buildInfoListLocked();
// TODO: notify if necessary
@@ -136,6 +168,37 @@
return false;
}
+ private int convertConnectedToState(boolean connected) {
+ if (connected) {
+ return INPUT_STATE_CONNECTED;
+ } else {
+ return INPUT_STATE_DISCONNECTED;
+ }
+ }
+
+ public void registerTvInputInfo(TvInputInfo info, int deviceId) {
+ if (info.getType() == TvInputInfo.TYPE_VIRTUAL) {
+ throw new IllegalArgumentException("info (" + info + ") has virtual type.");
+ }
+ synchronized (mLock) {
+ if (mTvInputInfoMap.indexOfKey(deviceId) >= 0) {
+ Slog.w(TAG, "Trying to override previous registration: old = "
+ + mTvInputInfoMap.get(deviceId) + ":" + deviceId + ", new = "
+ + info + ":" + deviceId);
+ }
+ mTvInputInfoMap.put(deviceId, info);
+
+ for (int i = 0; i < mHdmiStateMap.size(); ++i) {
+ String inputId = findInputIdForHdmiPortLocked(mHdmiStateMap.keyAt(i));
+ if (inputId != null && inputId.equals(info.getId())) {
+ mHandler.obtainMessage(ClientHandler.DO_SET_AVAILABLE,
+ convertConnectedToState(mHdmiStateMap.valueAt(i)), 0,
+ inputId).sendToTarget();
+ }
+ }
+ }
+ }
+
/**
* Create a TvInputHardware object with a specific deviceId. One service at a time can access
* the object, and if more than one process attempts to create hardware with the same deviceId,
@@ -143,7 +206,7 @@
* release is notified via ITvInputHardwareCallback.onReleased().
*/
public ITvInputHardware acquireHardware(int deviceId, ITvInputHardwareCallback callback,
- int callingUid, int resolvedUserId) {
+ TvInputInfo info, int callingUid, int resolvedUserId) {
if (callback == null) {
throw new NullPointerException();
}
@@ -154,14 +217,15 @@
return null;
}
if (checkUidChangedLocked(connection, callingUid, resolvedUserId)) {
- TvInputHardwareImpl hardware = new TvInputHardwareImpl(connection.getInfoLocked());
+ TvInputHardwareImpl hardware =
+ new TvInputHardwareImpl(connection.getHardwareInfoLocked());
try {
callback.asBinder().linkToDeath(connection, 0);
} catch (RemoteException e) {
hardware.release();
return null;
}
- connection.resetLocked(hardware, callback, callingUid, resolvedUserId);
+ connection.resetLocked(hardware, callback, info, callingUid, resolvedUserId);
}
return connection.getHardwareLocked();
}
@@ -182,26 +246,55 @@
|| checkUidChangedLocked(connection, callingUid, resolvedUserId)) {
return;
}
- connection.resetLocked(null, null, null, null);
+ connection.resetLocked(null, null, null, null, null);
+ }
+ }
+
+ private String findInputIdForHdmiPortLocked(int port) {
+ for (TvInputHardwareInfo hardwareInfo : mInfoList) {
+ if (hardwareInfo.getType() == TvInputHardwareInfo.TV_INPUT_TYPE_HDMI
+ && hardwareInfo.getHdmiPortId() == port) {
+ TvInputInfo info = mTvInputInfoMap.get(hardwareInfo.getDeviceId());
+ return (info == null) ? null : info.getId();
+ }
+ }
+ return null;
+ }
+
+ // HdmiControlManager.HotplugEventListener implementation.
+
+ @Override
+ public void onReceived(HdmiHotplugEvent event) {
+ String inputId = null;
+
+ synchronized (mLock) {
+ mHdmiStateMap.put(event.getPort(), event.isConnected());
+ inputId = findInputIdForHdmiPortLocked(event.getPort());
+ if (inputId == null) {
+ return;
+ }
+ mHandler.obtainMessage(ClientHandler.DO_SET_AVAILABLE,
+ convertConnectedToState(event.isConnected()), 0, inputId).sendToTarget();
}
}
private class Connection implements IBinder.DeathRecipient {
- private final TvInputHardwareInfo mInfo;
+ private final TvInputHardwareInfo mHardwareInfo;
+ private TvInputInfo mInfo;
private TvInputHardwareImpl mHardware = null;
private ITvInputHardwareCallback mCallback;
private TvStreamConfig[] mConfigs = null;
private Integer mCallingUid = null;
private Integer mResolvedUserId = null;
- public Connection(TvInputHardwareInfo info) {
- mInfo = info;
+ public Connection(TvInputHardwareInfo hardwareInfo) {
+ mHardwareInfo = hardwareInfo;
}
// *Locked methods assume TvInputHardwareManager.mLock is held.
- public void resetLocked(TvInputHardwareImpl hardware,
- ITvInputHardwareCallback callback, Integer callingUid, Integer resolvedUserId) {
+ public void resetLocked(TvInputHardwareImpl hardware, ITvInputHardwareCallback callback,
+ TvInputInfo info, Integer callingUid, Integer resolvedUserId) {
if (mHardware != null) {
try {
mCallback.onReleased();
@@ -212,6 +305,7 @@
}
mHardware = hardware;
mCallback = callback;
+ mInfo = info;
mCallingUid = callingUid;
mResolvedUserId = resolvedUserId;
@@ -228,7 +322,11 @@
mConfigs = configs;
}
- public TvInputHardwareInfo getInfoLocked() {
+ public TvInputHardwareInfo getHardwareInfoLocked() {
+ return mHardwareInfo;
+ }
+
+ public TvInputInfo getInfoLocked() {
return mInfo;
}
@@ -255,7 +353,7 @@
@Override
public void binderDied() {
synchronized (mLock) {
- resetLocked(null, null, null, null);
+ resetLocked(null, null, null, null, null);
}
}
}
@@ -403,4 +501,28 @@
return false;
}
}
+
+ private class ClientHandler extends Handler {
+ private static final int DO_SET_AVAILABLE = 1;
+
+ ClientHandler(Looper looper) {
+ super(looper);
+ }
+
+ @Override
+ public final void handleMessage(Message msg) {
+ switch (msg.what) {
+ case DO_SET_AVAILABLE: {
+ String inputId = (String) msg.obj;
+ int state = msg.arg1;
+ mClient.setState(inputId, state);
+ break;
+ }
+ default: {
+ Slog.w(TAG, "Unhandled message: " + msg);
+ break;
+ }
+ }
+ }
+ }
}
diff --git a/services/core/java/com/android/server/tv/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java
index 5e95af4..20fdefa 100644
--- a/services/core/java/com/android/server/tv/TvInputManagerService.java
+++ b/services/core/java/com/android/server/tv/TvInputManagerService.java
@@ -16,6 +16,9 @@
package com.android.server.tv;
+import static android.media.tv.TvInputManager.INPUT_STATE_CONNECTED;
+import static android.media.tv.TvInputManager.INPUT_STATE_DISCONNECTED;
+
import android.app.ActivityManager;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
@@ -38,6 +41,7 @@
import android.media.tv.ITvInputHardware;
import android.media.tv.ITvInputHardwareCallback;
import android.media.tv.ITvInputManager;
+import android.media.tv.ITvInputManagerCallback;
import android.media.tv.ITvInputService;
import android.media.tv.ITvInputServiceCallback;
import android.media.tv.ITvInputSession;
@@ -109,7 +113,7 @@
mContentResolver = context.getContentResolver();
mLogHandler = new LogHandler(IoThread.get().getLooper());
- mTvInputHardwareManager = new TvInputHardwareManager(context);
+ mTvInputHardwareManager = new TvInputHardwareManager(context, new Client());
synchronized (mLock) {
mUserStates.put(mCurrentUserId, new UserState());
@@ -129,6 +133,7 @@
buildTvInputListLocked(mCurrentUserId);
}
}
+ mTvInputHardwareManager.onBootPhase(phase);
}
private void registerBroadcastReceivers() {
@@ -144,7 +149,7 @@
public void onPackageRemoved(String packageName, int uid) {
synchronized (mLock) {
UserState userState = getUserStateLocked(mCurrentUserId);
- if (!userState.packageList.contains(packageName)) {
+ if (!userState.packageSet.contains(packageName)) {
// Not a TV input package.
return;
}
@@ -198,8 +203,11 @@
private void buildTvInputListLocked(int userId) {
UserState userState = getUserStateLocked(userId);
- userState.inputMap.clear();
- userState.packageList.clear();
+
+ Map<String, TvInputState> oldInputMap = userState.inputMap;
+ userState.inputMap = new HashMap<String, TvInputState>();
+
+ userState.packageSet.clear();
if (DEBUG) Slog.d(TAG, "buildTvInputList");
PackageManager pm = mContext.getPackageManager();
@@ -216,8 +224,13 @@
try {
TvInputInfo info = TvInputInfo.createTvInputInfo(mContext, ri);
if (DEBUG) Slog.d(TAG, "add " + info.getId());
- userState.inputMap.put(info.getId(), info);
- userState.packageList.add(si.packageName);
+ TvInputState state = oldInputMap.get(info.getId());
+ if (state == null) {
+ state = new TvInputState();
+ }
+ userState.inputMap.put(info.getId(), state);
+ state.mInfo = info;
+ userState.packageSet.add(si.packageName);
// Reconnect the service if existing input is updated.
updateServiceConnectionLocked(info.getId(), userId);
@@ -225,6 +238,7 @@
Slog.e(TAG, "Can't load TV input " + si.name, e);
}
}
+ oldInputMap.clear();
}
private void switchUser(int userId) {
@@ -359,7 +373,7 @@
}
Intent i = new Intent(TvInputService.SERVICE_INTERFACE).setComponent(
- userState.inputMap.get(inputId).getComponent());
+ userState.inputMap.get(inputId).mInfo.getComponent());
// Binding service may fail if the service is updating.
// In that case, the connection will be revived in buildTvInputListLocked called by
// onSomePackagesChanged.
@@ -588,7 +602,7 @@
updateServiceConnectionLocked(sessionState.mInputId, userId);
}
- private void unregisterCallbackInternalLocked(IBinder clientToken, String inputId,
+ private void unregisterClientInternalLocked(IBinder clientToken, String inputId,
int userId) {
UserState userState = getUserStateLocked(userId);
ClientState clientState = userState.clientStateMap.get(clientToken);
@@ -623,14 +637,43 @@
}
}
- private void broadcastServiceAvailabilityChangedLocked(ServiceState serviceState) {
- for (IBinder clientToken : serviceState.mClientTokens) {
- try {
- ITvInputClient.Stub.asInterface(clientToken).onAvailabilityChanged(
- serviceState.mTvInputInfo.getId(), serviceState.mAvailable);
- } catch (RemoteException e) {
- Slog.e(TAG, "error in onAvailabilityChanged", e);
+ private void notifyStateChangedLocked(UserState userState, String inputId,
+ int state, ITvInputManagerCallback targetCallback) {
+ if (DEBUG) {
+ Slog.d(TAG, "notifyStateChangedLocked: inputId = " + inputId
+ + "; state = " + state);
+ }
+ if (targetCallback == null) {
+ for (ITvInputManagerCallback callback : userState.callbackSet) {
+ try {
+ callback.onInputStateChanged(inputId, state);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to report state change to callback.");
+ }
}
+ } else {
+ try {
+ targetCallback.onInputStateChanged(inputId, state);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to report state change to callback.");
+ }
+ }
+ }
+
+ private void setStateLocked(String inputId, int state, int userId) {
+ UserState userState = getUserStateLocked(userId);
+ TvInputState inputState = userState.inputMap.get(inputId);
+ ServiceState serviceState = userState.serviceStateMap.get(inputId);
+ int oldState = inputState.mState;
+ inputState.mState = state;
+ boolean isStateEmpty = serviceState.mClientTokens.isEmpty()
+ && serviceState.mSessionTokens.isEmpty();
+ if (serviceState != null && serviceState.mService == null && !isStateEmpty) {
+ // We don't notify state change while reconnecting. It should remain disconnected.
+ return;
+ }
+ if (oldState != state) {
+ notifyStateChangedLocked(userState, inputId, state, null);
}
}
@@ -643,80 +686,29 @@
try {
synchronized (mLock) {
UserState userState = getUserStateLocked(resolvedUserId);
- return new ArrayList<TvInputInfo>(userState.inputMap.values());
- }
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
- }
-
- @Override
- public boolean getAvailability(final ITvInputClient client, final String inputId,
- int userId) {
- final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(),
- Binder.getCallingUid(), userId, "getAvailability");
- final long identity = Binder.clearCallingIdentity();
- try {
- synchronized (mLock) {
- UserState userState = getUserStateLocked(resolvedUserId);
- ServiceState serviceState = userState.serviceStateMap.get(inputId);
- if (serviceState != null) {
- // We already know the status of this input service. Return the cached
- // status.
- return serviceState.mAvailable;
+ List<TvInputInfo> inputList = new ArrayList<TvInputInfo>();
+ for (TvInputState state : userState.inputMap.values()) {
+ inputList.add(state.mInfo);
}
+ return inputList;
}
} finally {
Binder.restoreCallingIdentity(identity);
}
- // STOPSHIP: Redesign the API around the availability change. For now, the service
- // will be always available.
- return true;
}
@Override
- public void registerCallback(final ITvInputClient client, final String inputId,
- int userId) {
+ public void registerCallback(final ITvInputManagerCallback callback, int userId) {
final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(),
Binder.getCallingUid(), userId, "registerCallback");
final long identity = Binder.clearCallingIdentity();
try {
synchronized (mLock) {
- // Create a new service callback and add it to the callback map of the current
- // service.
UserState userState = getUserStateLocked(resolvedUserId);
- ServiceState serviceState = userState.serviceStateMap.get(inputId);
- if (serviceState == null) {
- serviceState = new ServiceState(
- userState.inputMap.get(inputId), resolvedUserId);
- userState.serviceStateMap.put(inputId, serviceState);
- }
- IBinder clientToken = client.asBinder();
- if (!serviceState.mClientTokens.contains(clientToken)) {
- serviceState.mClientTokens.add(clientToken);
- }
-
- ClientState clientState = userState.clientStateMap.get(clientToken);
- if (clientState == null) {
- clientState = createClientStateLocked(clientToken, resolvedUserId);
- }
- if (!clientState.mInputIds.contains(inputId)) {
- clientState.mInputIds.add(inputId);
- }
-
- if (serviceState.mService != null) {
- if (serviceState.mCallback != null) {
- // We already handled.
- return;
- }
- serviceState.mCallback = new ServiceCallback(resolvedUserId);
- try {
- serviceState.mService.registerCallback(serviceState.mCallback);
- } catch (RemoteException e) {
- Slog.e(TAG, "error in registerCallback", e);
- }
- } else {
- updateServiceConnectionLocked(inputId, resolvedUserId);
+ userState.callbackSet.add(callback);
+ for (TvInputState state : userState.inputMap.values()) {
+ notifyStateChangedLocked(userState, state.mInfo.getId(),
+ state.mState, callback);
}
}
} finally {
@@ -725,13 +717,14 @@
}
@Override
- public void unregisterCallback(ITvInputClient client, String inputId, int userId) {
+ public void unregisterCallback(ITvInputManagerCallback callback, int userId) {
final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(),
Binder.getCallingUid(), userId, "unregisterCallback");
final long identity = Binder.clearCallingIdentity();
try {
synchronized (mLock) {
- unregisterCallbackInternalLocked(client.asBinder(), inputId, resolvedUserId);
+ UserState userState = getUserStateLocked(resolvedUserId);
+ userState.callbackSet.remove(callback);
}
} finally {
Binder.restoreCallingIdentity(identity);
@@ -751,7 +744,7 @@
ServiceState serviceState = userState.serviceStateMap.get(inputId);
if (serviceState == null) {
serviceState = new ServiceState(
- userState.inputMap.get(inputId), resolvedUserId);
+ userState.inputMap.get(inputId).mInfo, resolvedUserId);
userState.serviceStateMap.put(inputId, serviceState);
}
// Send a null token immediately while reconnecting.
@@ -868,7 +861,7 @@
}
// Create a log entry and fill it later.
- String packageName = userState.inputMap.get(sessionState.mInputId)
+ String packageName = userState.inputMap.get(sessionState.mInputId).mInfo
.getServiceInfo().packageName;
ContentValues values = new ContentValues();
values.put(TvContract.WatchedPrograms.COLUMN_PACKAGE_NAME, packageName);
@@ -1017,8 +1010,7 @@
@Override
public List<TvInputHardwareInfo> getHardwareList() throws RemoteException {
- if (mContext.checkCallingPermission(
- android.Manifest.permission.TV_INPUT_HARDWARE)
+ if (mContext.checkCallingPermission(android.Manifest.permission.TV_INPUT_HARDWARE)
!= PackageManager.PERMISSION_GRANTED) {
return null;
}
@@ -1032,10 +1024,25 @@
}
@Override
+ public void registerTvInputInfo(TvInputInfo info, int deviceId) {
+ if (mContext.checkCallingPermission(android.Manifest.permission.TV_INPUT_HARDWARE)
+ != PackageManager.PERMISSION_GRANTED) {
+ return;
+ }
+
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ mTvInputHardwareManager.registerTvInputInfo(info, deviceId);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ @Override
public ITvInputHardware acquireTvInputHardware(int deviceId,
- ITvInputHardwareCallback callback, int userId) throws RemoteException {
- if (mContext.checkCallingPermission(
- android.Manifest.permission.TV_INPUT_HARDWARE)
+ ITvInputHardwareCallback callback, TvInputInfo info, int userId)
+ throws RemoteException {
+ if (mContext.checkCallingPermission(android.Manifest.permission.TV_INPUT_HARDWARE)
!= PackageManager.PERMISSION_GRANTED) {
return null;
}
@@ -1046,7 +1053,7 @@
userId, "acquireTvInputHardware");
try {
return mTvInputHardwareManager.acquireHardware(
- deviceId, callback, callingUid, resolvedUserId);
+ deviceId, callback, info, callingUid, resolvedUserId);
} finally {
Binder.restoreCallingIdentity(identity);
}
@@ -1055,8 +1062,7 @@
@Override
public void releaseTvInputHardware(int deviceId, ITvInputHardware hardware, int userId)
throws RemoteException {
- if (mContext.checkCallingPermission(
- android.Manifest.permission.TV_INPUT_HARDWARE)
+ if (mContext.checkCallingPermission(android.Manifest.permission.TV_INPUT_HARDWARE)
!= PackageManager.PERMISSION_GRANTED) {
return;
}
@@ -1099,16 +1105,16 @@
pw.println("UserState (" + userId + "):");
pw.increaseIndent();
- pw.println("inputMap: inputId -> TvInputInfo");
+ pw.println("inputMap: inputId -> TvInputState");
pw.increaseIndent();
- for (TvInputInfo info : userState.inputMap.values()) {
- pw.println(info.toString());
+ for (TvInputState state : userState.inputMap.values()) {
+ pw.println(state.toString());
}
pw.decreaseIndent();
- pw.println("packageList:");
+ pw.println("packageSet:");
pw.increaseIndent();
- for (String packageName : userState.packageList) {
+ for (String packageName : userState.packageSet) {
pw.println(packageName);
}
pw.decreaseIndent();
@@ -1169,7 +1175,6 @@
pw.println("mService: " + service.mService);
pw.println("mCallback: " + service.mCallback);
pw.println("mBound: " + service.mBound);
- pw.println("mAvailable: " + service.mAvailable);
pw.println("mReconnecting: " + service.mReconnecting);
pw.decreaseIndent();
@@ -1196,18 +1201,38 @@
}
pw.decreaseIndent();
+ pw.println("callbackSet:");
+ pw.increaseIndent();
+ for (ITvInputManagerCallback callback : userState.callbackSet) {
+ pw.println(callback.toString());
+ }
+ pw.decreaseIndent();
+
pw.decreaseIndent();
}
}
}
}
- private static final class UserState {
- // A mapping from the TV input id to its TvInputInfo.
- private final Map<String, TvInputInfo> inputMap = new HashMap<String,TvInputInfo>();
+ private static final class TvInputState {
+ // A TvInputInfo object which represents the TV input.
+ private TvInputInfo mInfo;
- // A list of all TV input packages.
- private final Set<String> packageList = new HashSet<String>();
+ // The state of TV input. Connected by default.
+ private int mState = INPUT_STATE_CONNECTED;
+
+ @Override
+ public String toString() {
+ return "mInfo: " + mInfo + "; mState: " + mState;
+ }
+ }
+
+ private static final class UserState {
+ // A mapping from the TV input id to its TvInputState.
+ private Map<String, TvInputState> inputMap = new HashMap<String, TvInputState>();
+
+ // A set of all TV input packages.
+ private final Set<String> packageSet = new HashSet<String>();
// A mapping from the token of a client to its state.
private final Map<IBinder, ClientState> clientStateMap =
@@ -1220,6 +1245,10 @@
// A mapping from the token of a TV input session to its state.
private final Map<IBinder, SessionState> sessionStateMap =
new HashMap<IBinder, SessionState>();
+
+ // A set of callbacks.
+ private final Set<ITvInputManagerCallback> callbackSet =
+ new HashSet<ITvInputManagerCallback>();
}
private final class ClientState implements IBinder.DeathRecipient {
@@ -1243,7 +1272,7 @@
synchronized (mLock) {
UserState userState = getUserStateLocked(mUserId);
// DO NOT remove the client state of clientStateMap in this method. It will be
- // removed in releaseSessionLocked() or unregisterCallbackInternalLocked().
+ // removed in releaseSessionLocked() or unregisterClientInternalLocked().
ClientState clientState = userState.clientStateMap.get(mClientToken);
if (clientState != null) {
while (clientState.mSessionTokens.size() > 0) {
@@ -1251,7 +1280,7 @@
clientState.mSessionTokens.get(0), Process.SYSTEM_UID, mUserId);
}
while (clientState.mInputIds.size() > 0) {
- unregisterCallbackInternalLocked(
+ unregisterClientInternalLocked(
mClientToken, clientState.mInputIds.get(0), mUserId);
}
}
@@ -1269,7 +1298,6 @@
private ITvInputService mService;
private ServiceCallback mCallback;
private boolean mBound;
- private boolean mAvailable;
private boolean mReconnecting;
private ServiceState(TvInputInfo inputInfo, int userId) {
@@ -1325,16 +1353,18 @@
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
+ String inputId = mTvInputInfo.getId();
if (DEBUG) {
- Slog.d(TAG, "onServiceConnected(inputId=" + mTvInputInfo.getId() + ")");
+ Slog.d(TAG, "onServiceConnected(inputId=" + inputId + ")");
}
synchronized (mLock) {
- ServiceState serviceState = getServiceStateLocked(mTvInputInfo.getId(), mUserId);
+ UserState userState = getUserStateLocked(mUserId);
+ ServiceState serviceState = userState.serviceStateMap.get(inputId);
serviceState.mService = ITvInputService.Stub.asInterface(service);
// Register a callback, if we need to.
if (!serviceState.mClientTokens.isEmpty() && serviceState.mCallback == null) {
- serviceState.mCallback = new ServiceCallback(mUserId);
+ serviceState.mCallback = new ServiceCallback(mTvInputInfo.getId(), mUserId);
try {
serviceState.mService.registerCallback(serviceState.mCallback);
} catch (RemoteException e) {
@@ -1346,6 +1376,12 @@
for (IBinder sessionToken : serviceState.mSessionTokens) {
createSessionInternalLocked(serviceState.mService, sessionToken, mUserId);
}
+
+ TvInputState inputState = userState.inputMap.get(inputId);
+ if (inputState != null && inputState.mState != INPUT_STATE_DISCONNECTED) {
+ notifyStateChangedLocked(userState, mTvInputInfo.getId(),
+ inputState.mState, null);
+ }
}
}
@@ -1377,10 +1413,8 @@
}
}
- if (serviceState.mAvailable) {
- serviceState.mAvailable = false;
- broadcastServiceAvailabilityChangedLocked(serviceState);
- }
+ notifyStateChangedLocked(userState, mTvInputInfo.getId(),
+ INPUT_STATE_DISCONNECTED, null);
updateServiceConnectionLocked(mTvInputInfo.getId(), mUserId);
}
}
@@ -1388,24 +1422,22 @@
}
private final class ServiceCallback extends ITvInputServiceCallback.Stub {
+ private final String mInputId;
private final int mUserId;
- ServiceCallback(int userId) {
+ ServiceCallback(String inputId, int userId) {
+ mInputId = inputId;
mUserId = userId;
}
@Override
- public void onAvailabilityChanged(String inputId, boolean isAvailable) {
+ public void onInputStateChanged(int state) {
if (DEBUG) {
- Slog.d(TAG, "onAvailabilityChanged(inputId=" + inputId + ", isAvailable="
- + isAvailable + ")");
+ Slog.d(TAG, "onInputStateChanged(inputId=" + mInputId + ", state="
+ + state + ")");
}
synchronized (mLock) {
- ServiceState serviceState = getServiceStateLocked(inputId, mUserId);
- if (serviceState.mAvailable != isAvailable) {
- serviceState.mAvailable = isAvailable;
- broadcastServiceAvailabilityChangedLocked(serviceState);
- }
+ setStateLocked(mInputId, state, mUserId);
}
}
}
@@ -1553,4 +1585,12 @@
mContentResolver.update(uri, values, null, null);
}
}
+
+ final class Client {
+ public void setState(String inputId, int state) {
+ synchronized (mLock) {
+ setStateLocked(inputId, state, mCurrentUserId);
+ }
+ }
+ }
}
diff --git a/telecomm/java/android/telecomm/CallPropertyPresentation.java b/telecomm/java/android/telecomm/CallPropertyPresentation.java
index 350980c..319e565 100644
--- a/telecomm/java/android/telecomm/CallPropertyPresentation.java
+++ b/telecomm/java/android/telecomm/CallPropertyPresentation.java
@@ -19,14 +19,14 @@
/** Defines how numbers and names are displayed in caller id. */
public class CallPropertyPresentation {
/** Property is displayed normally. */
- public static final int ALLOWED = 0;
+ public static final int ALLOWED = 1;
/** Property was blocked. */
- public static final int RESTRICTED = 1;
+ public static final int RESTRICTED = 2;
/** Presentation was not specified or is unknown. */
- public static final int UNKNOWN = 2;
+ public static final int UNKNOWN = 3;
/** Property should be displayed as a pay phone. */
- public static final int PAYPHONE = 3;
+ public static final int PAYPHONE = 4;
}
diff --git a/telecomm/java/android/telecomm/ConnectionRequest.java b/telecomm/java/android/telecomm/ConnectionRequest.java
index 0db9e29..5888d6a 100644
--- a/telecomm/java/android/telecomm/ConnectionRequest.java
+++ b/telecomm/java/android/telecomm/ConnectionRequest.java
@@ -30,11 +30,11 @@
// TODO: Token to limit recursive invocations
// TODO: Consider upgrading "mHandle" to ordered list of handles, indicating a set of phone
// numbers that would satisfy the client's needs, in order of preference
+ private final PhoneAccount mAccount;
private final String mCallId;
private final Uri mHandle;
private final int mHandlePresentation;
private final Bundle mExtras;
- private final PhoneAccount mAccount;
private final int mVideoState;
/**
@@ -61,6 +61,15 @@
mVideoState = videoState;
}
+ private ConnectionRequest(Parcel in) {
+ mAccount = in.readParcelable(getClass().getClassLoader());
+ mCallId = in.readString();
+ mHandle = in.readParcelable(getClass().getClassLoader());
+ mHandlePresentation = in.readInt();
+ mExtras = in.readParcelable(getClass().getClassLoader());
+ mVideoState = in.readInt();
+ }
+
/**
* The account which should be used to place the call.
*/
@@ -109,26 +118,17 @@
mExtras == null ? "" : mExtras);
}
- public static final Parcelable.Creator<ConnectionRequest> CREATOR =
- new Parcelable.Creator<ConnectionRequest> () {
- @Override
- public ConnectionRequest createFromParcel(Parcel source) {
- PhoneAccount account = (PhoneAccount) source.readParcelable(
- getClass().getClassLoader());
- String callId = source.readString();
- Uri handle = (Uri) source.readParcelable(getClass().getClassLoader());
- int presentation = source.readInt();
- Bundle extras = (Bundle) source.readParcelable(getClass().getClassLoader());
- int videoState = source.readInt();
- return new ConnectionRequest(
- account, callId, handle, presentation, extras, videoState);
- }
+ public static final Creator<ConnectionRequest> CREATOR = new Creator<ConnectionRequest> () {
+ @Override
+ public ConnectionRequest createFromParcel(Parcel source) {
+ return new ConnectionRequest(source);
+ }
- @Override
- public ConnectionRequest[] newArray(int size) {
- return new ConnectionRequest[size];
- }
- };
+ @Override
+ public ConnectionRequest[] newArray(int size) {
+ return new ConnectionRequest[size];
+ }
+ };
/**
* {@inheritDoc}
diff --git a/telecomm/java/android/telecomm/ConnectionService.java b/telecomm/java/android/telecomm/ConnectionService.java
index 1966081..178cee8 100644
--- a/telecomm/java/android/telecomm/ConnectionService.java
+++ b/telecomm/java/android/telecomm/ConnectionService.java
@@ -47,22 +47,21 @@
private static final Connection NULL_CONNECTION = new Connection() {};
private static final int MSG_ADD_CALL_SERVICE_ADAPTER = 1;
- private static final int MSG_CALL = 2;
+ private static final int MSG_CREATE_CONNECTION = 2;
private static final int MSG_ABORT = 3;
- private static final int MSG_CREATE_INCOMING_CALL = 4;
- private static final int MSG_ANSWER = 5;
- private static final int MSG_REJECT = 6;
- private static final int MSG_DISCONNECT = 7;
- private static final int MSG_HOLD = 8;
- private static final int MSG_UNHOLD = 9;
- private static final int MSG_ON_AUDIO_STATE_CHANGED = 10;
- private static final int MSG_PLAY_DTMF_TONE = 11;
- private static final int MSG_STOP_DTMF_TONE = 12;
- private static final int MSG_CONFERENCE = 13;
- private static final int MSG_SPLIT_FROM_CONFERENCE = 14;
- private static final int MSG_SWAP_WITH_BACKGROUND_CALL = 15;
- private static final int MSG_ON_POST_DIAL_CONTINUE = 16;
- private static final int MSG_ON_PHONE_ACCOUNT_CLICKED = 17;
+ private static final int MSG_ANSWER = 4;
+ private static final int MSG_REJECT = 5;
+ private static final int MSG_DISCONNECT = 6;
+ private static final int MSG_HOLD = 7;
+ private static final int MSG_UNHOLD = 8;
+ private static final int MSG_ON_AUDIO_STATE_CHANGED = 9;
+ private static final int MSG_PLAY_DTMF_TONE = 10;
+ private static final int MSG_STOP_DTMF_TONE = 11;
+ private static final int MSG_CONFERENCE = 12;
+ private static final int MSG_SPLIT_FROM_CONFERENCE = 13;
+ private static final int MSG_SWAP_WITH_BACKGROUND_CALL = 14;
+ private static final int MSG_ON_POST_DIAL_CONTINUE = 15;
+ private static final int MSG_ON_PHONE_ACCOUNT_CLICKED = 16;
private final Map<String, Connection> mConnectionById = new HashMap<>();
private final Map<Connection, String> mIdByConnection = new HashMap<>();
@@ -74,11 +73,11 @@
private final ConnectionServiceAdapter mAdapter = new ConnectionServiceAdapter();
/**
- * A callback for providing the resuilt of creating a connection.
+ * A callback for providing the result of creating a connection.
*/
- public interface OutgoingCallResponse<CONNECTION> {
+ public interface CreateConnectionResponse<CONNECTION> {
/**
- * Tells Telecomm that an attempt to place the specified outgoing call succeeded.
+ * Tells Telecomm that an attempt to create the connection succeeded.
*
* @param request The original request.
* @param connection The connection.
@@ -86,7 +85,8 @@
void onSuccess(ConnectionRequest request, CONNECTION connection);
/**
- * Tells Telecomm that an attempt to place the specified outgoing call failed.
+ * Tells Telecomm that an attempt to create the connection failed. Telecomm will try a
+ * different service until a service cancels the process or completes it successfully.
*
* @param request The original request.
* @param code An integer code indicating the reason for failure.
@@ -95,7 +95,8 @@
void onFailure(ConnectionRequest request, int code, String msg);
/**
- * Tells Telecomm to cancel the call.
+ * Tells Telecomm to cancel creating the connection. Telecomm will stop trying to create
+ * the connection an no more services will be tried.
*
* @param request The original request.
*/
@@ -109,8 +110,9 @@
}
@Override
- public void call(ConnectionRequest request) {
- mHandler.obtainMessage(MSG_CALL, request).sendToTarget();
+ public void createConnection(ConnectionRequest request, boolean isIncoming) {
+ mHandler.obtainMessage(
+ MSG_CREATE_CONNECTION, isIncoming ? 1 : 0, 0, request).sendToTarget();
}
@Override
@@ -119,11 +121,6 @@
}
@Override
- public void createIncomingCall(ConnectionRequest request) {
- mHandler.obtainMessage(MSG_CREATE_INCOMING_CALL, request).sendToTarget();
- }
-
- @Override
public void answer(String callId) {
mHandler.obtainMessage(MSG_ANSWER, callId).sendToTarget();
}
@@ -206,15 +203,12 @@
mAdapter.addAdapter((IConnectionServiceAdapter) msg.obj);
onAdapterAttached();
break;
- case MSG_CALL:
- call((ConnectionRequest) msg.obj);
+ case MSG_CREATE_CONNECTION:
+ createConnection((ConnectionRequest) msg.obj, msg.arg1 == 1);
break;
case MSG_ABORT:
abort((String) msg.obj);
break;
- case MSG_CREATE_INCOMING_CALL:
- createIncomingCall((ConnectionRequest) msg.obj);
- break;
case MSG_ANSWER:
answer((String) msg.obj);
break;
@@ -394,29 +388,39 @@
return mBinder;
}
- private void call(final ConnectionRequest originalRequest) {
+ /**
+ * This can be used by telecomm to either create a new outgoing call or attach to an existing
+ * incoming call. In either case, telecomm will cycle through a set of services and call
+ * createConnection util a connection service cancels the process or completes it successfully.
+ */
+ private void createConnection(ConnectionRequest originalRequest, boolean isIncoming) {
Log.d(this, "call %s", originalRequest);
- onCreateConnections(
- originalRequest,
- new OutgoingCallResponse<Connection>() {
- @Override
- public void onSuccess(ConnectionRequest request, Connection connection) {
- Log.d(this, "adapter handleSuccessfulOutgoingCall %s", request.getCallId());
- mAdapter.handleSuccessfulOutgoingCall(request);
- addConnection(request.getCallId(), connection);
- }
+ CreateConnectionResponse response = new CreateConnectionResponse<Connection>() {
+ @Override
+ public void onSuccess(ConnectionRequest request, Connection connection) {
+ Log.d(this, "adapter handleCreateConnectionSuccessful %s",
+ request.getCallId());
+ mAdapter.handleCreateConnectionSuccessful(request);
+ addConnection(request.getCallId(), connection);
+ }
- @Override
- public void onFailure(ConnectionRequest request, int code, String msg) {
- mAdapter.handleFailedOutgoingCall(request, code, msg);
- }
+ @Override
+ public void onFailure(ConnectionRequest request, int code, String msg) {
+ // Tell telecomm to try a different service.
+ mAdapter.handleCreateConnectionFailed(request, code, msg);
+ }
- @Override
- public void onCancel(ConnectionRequest request) {
- mAdapter.cancelOutgoingCall(request);
- }
- }
- );
+ @Override
+ public void onCancel(ConnectionRequest request) {
+ // Tell telecomm not to attempt any more services.
+ mAdapter.handleCreateConnectionCancelled(request);
+ }
+ };
+ if (isIncoming) {
+ onCreateIncomingConnection(originalRequest, response);
+ } else {
+ onCreateOutgoingConnection(originalRequest, response);
+ }
}
private void abort(String callId) {
@@ -424,33 +428,6 @@
findConnectionForAction(callId, "abort").onAbort();
}
- private void createIncomingCall(ConnectionRequest originalRequest) {
- Log.d(this, "createIncomingCall %s", originalRequest);
- onCreateIncomingConnection(
- originalRequest,
- new Response<ConnectionRequest, Connection>() {
- @Override
- public void onResult(ConnectionRequest request, Connection... result) {
- if (result != null && result.length != 1) {
- for (Connection c : result) {
- c.onAbort();
- }
- } else {
- addConnection(request.getCallId(), result[0]);
- Log.d(this, "adapter notifyIncomingCall %s", request);
- mAdapter.notifyIncomingCall(request);
- }
- }
-
- @Override
- public void onError(ConnectionRequest request, int code, String msg) {
- Log.d(this, "adapter failed createIncomingCall %s %d %s",
- request, code, msg);
- }
- }
- );
- }
-
private void answer(String callId) {
Log.d(this, "answer %s", callId);
findConnectionForAction(callId, "answer").onAnswer();
@@ -570,7 +547,7 @@
IConnectionService.Stub.asInterface(services.get(i)));
}
mAreAccountsInitialized = true;
- Log.d(this, "remote call services found: " + services);
+ Log.d(this, "remote connection services found: " + services);
maybeRespondToAccountLookup();
}
});
@@ -606,10 +583,16 @@
}
}
+ public final void createRemoteIncomingConnection(
+ ConnectionRequest request,
+ CreateConnectionResponse<RemoteConnection> response) {
+ mRemoteConnectionManager.createRemoteConnection(request, response, true);
+ }
+
public final void createRemoteOutgoingConnection(
ConnectionRequest request,
- OutgoingCallResponse<RemoteConnection> response) {
- mRemoteConnectionManager.createOutgoingConnection(request, response);
+ CreateConnectionResponse<RemoteConnection> response) {
+ mRemoteConnectionManager.createRemoteConnection(request, response, false);
}
/**
@@ -620,14 +603,25 @@
}
/**
- * Create a Connection given a request.
+ * Create a Connection given an incoming request. This is used to attach to existing incoming
+ * calls.
*
- * @param request Data encapsulating details of the desired Connection.
+ * @param request Details about the incoming call.
* @param callback A callback for providing the result.
*/
- protected void onCreateConnections(
+ protected void onCreateIncomingConnection(
ConnectionRequest request,
- OutgoingCallResponse<Connection> callback) {}
+ CreateConnectionResponse<Connection> callback) {}
+
+ /**
+ * Create a Connection given an outgoing request. This is used to initiate new outgoing calls.
+ *
+ * @param request Details about the outgoing call.
+ * @param callback A callback for providing the result.
+ */
+ protected void onCreateOutgoingConnection(
+ ConnectionRequest request,
+ CreateConnectionResponse<Connection> callback) {}
/**
* Returns a new or existing conference connection when the the user elects to convert the
@@ -645,21 +639,6 @@
Response<String, Connection> callback) {}
/**
- * Create a Connection to match an incoming connection notification.
- *
- * IMPORTANT: If the incoming connection has a phone number (or other handle) that the user
- * is not supposed to be able to see (e.g. it is PRESENTATION_RESTRICTED), then a compliant
- * ConnectionService implementation MUST NOT reveal this phone number as part of the Intent
- * it sends to notify Telecomm of an incoming connection.
- *
- * @param request Data encapsulating details of the desired Connection.
- * @param callback A callback for providing the result.
- */
- protected void onCreateIncomingConnection(
- ConnectionRequest request,
- Response<ConnectionRequest, Connection> callback) {}
-
- /**
* Notifies that a connection has been added to this connection service and sent to Telecomm.
*
* @param connection The connection which was added.
diff --git a/telecomm/java/android/telecomm/ConnectionServiceAdapter.java b/telecomm/java/android/telecomm/ConnectionServiceAdapter.java
index b90dec3..a812fa4 100644
--- a/telecomm/java/android/telecomm/ConnectionServiceAdapter.java
+++ b/telecomm/java/android/telecomm/ConnectionServiceAdapter.java
@@ -73,65 +73,28 @@
}
}
- /**
- * Provides Telecomm with the details of an incoming call. An invocation of this method must
- * follow {@link ConnectionService#setIncomingCallId} and use the call ID specified therein.
- * Upon the invocation of this method, Telecomm will bring up the incoming-call interface where
- * the user can elect to answer or reject a call.
- *
- * @param request The connection request.
- */
- void notifyIncomingCall(ConnectionRequest request) {
+ void handleCreateConnectionSuccessful(ConnectionRequest request) {
for (IConnectionServiceAdapter adapter : mAdapters) {
try {
- adapter.notifyIncomingCall(request);
+ adapter.handleCreateConnectionSuccessful(request);
} catch (RemoteException e) {
}
}
}
- /**
- * Tells Telecomm that an attempt to place the specified outgoing call succeeded.
- *
- * @param request The originating request for a connection.
- */
- void handleSuccessfulOutgoingCall(ConnectionRequest request) {
+ void handleCreateConnectionFailed(ConnectionRequest request, int errorCode, String errorMsg) {
for (IConnectionServiceAdapter adapter : mAdapters) {
try {
- adapter.handleSuccessfulOutgoingCall(request);
+ adapter.handleCreateConnectionFailed(request, errorCode, errorMsg);
} catch (RemoteException e) {
}
}
}
- /**
- * Tells Telecomm that an attempt to place the specified outgoing call failed.
- *
- * @param request The originating request for a connection.
- * @param errorCode The error code associated with the failed call attempt.
- * @param errorMsg The error message associated with the failed call attempt.
- */
- void handleFailedOutgoingCall(
- ConnectionRequest request,
- int errorCode,
- String errorMsg) {
+ void handleCreateConnectionCancelled(ConnectionRequest request) {
for (IConnectionServiceAdapter adapter : mAdapters) {
try {
- adapter.handleFailedOutgoingCall(request, errorCode, errorMsg);
- } catch (RemoteException e) {
- }
- }
- }
-
- /**
- * Tells Telecomm to cancel the call.
- *
- * @param request The originating request for a connection.
- */
- void cancelOutgoingCall(ConnectionRequest request) {
- for (IConnectionServiceAdapter adapter : mAdapters) {
- try {
- adapter.cancelOutgoingCall(request);
+ adapter.handleCreateConnectionCancelled(request);
} catch (RemoteException e) {
}
}
diff --git a/telecomm/java/android/telecomm/RemoteConnectionManager.java b/telecomm/java/android/telecomm/RemoteConnectionManager.java
index 9cffdcc..0a0b245 100644
--- a/telecomm/java/android/telecomm/RemoteConnectionManager.java
+++ b/telecomm/java/android/telecomm/RemoteConnectionManager.java
@@ -54,9 +54,10 @@
return accounts;
}
- public void createOutgoingConnection(
+ public void createRemoteConnection(
ConnectionRequest request,
- final ConnectionService.OutgoingCallResponse response) {
+ ConnectionService.CreateConnectionResponse response,
+ boolean isIncoming) {
PhoneAccount account = request.getAccount();
if (account == null) {
throw new IllegalArgumentException("account must be specified.");
@@ -67,7 +68,7 @@
throw new UnsupportedOperationException("account not supported: " + componentName);
} else {
RemoteConnectionService remoteService = mRemoteConnectionServices.get(componentName);
- remoteService.createOutgoingConnection(request, response);
+ remoteService.createRemoteConnection(request, response, isIncoming);
}
}
}
diff --git a/telecomm/java/android/telecomm/RemoteConnectionService.java b/telecomm/java/android/telecomm/RemoteConnectionService.java
index 430133c..7fd8f93 100644
--- a/telecomm/java/android/telecomm/RemoteConnectionService.java
+++ b/telecomm/java/android/telecomm/RemoteConnectionService.java
@@ -44,40 +44,34 @@
private String mConnectionId;
private ConnectionRequest mPendingRequest;
- private ConnectionService.OutgoingCallResponse<RemoteConnection> mPendingOutgoingCallResponse;
+ private ConnectionService.CreateConnectionResponse<RemoteConnection> mPendingResponse;
// Remote connection services only support a single connection.
private RemoteConnection mConnection;
private final IConnectionServiceAdapter mAdapter = new IConnectionServiceAdapter.Stub() {
-
@Override
- public void notifyIncomingCall(ConnectionRequest request) {
- Log.w(this, "notifyIncomingCall not implemented in Remote connection");
- }
-
- @Override
- public void handleSuccessfulOutgoingCall(ConnectionRequest request) {
+ public void handleCreateConnectionSuccessful(ConnectionRequest request) {
if (isPendingConnection(request.getCallId())) {
mConnection = new RemoteConnection(mConnectionService, request.getCallId());
- mPendingOutgoingCallResponse.onSuccess(request, mConnection);
+ mPendingResponse.onSuccess(request, mConnection);
clearPendingInformation();
}
}
@Override
- public void handleFailedOutgoingCall(
+ public void handleCreateConnectionFailed(
ConnectionRequest request, int errorCode, String errorMessage) {
if (isPendingConnection(request.getCallId())) {
- mPendingOutgoingCallResponse.onFailure(request, errorCode, errorMessage);
+ mPendingResponse.onFailure(request, errorCode, errorMessage);
mConnectionId = null;
clearPendingInformation();
}
}
@Override
- public void cancelOutgoingCall(ConnectionRequest request) {
+ public void handleCreateConnectionCancelled(ConnectionRequest request) {
if (isPendingConnection(request.getCallId())) {
- mPendingOutgoingCallResponse.onCancel(request);
+ mPendingResponse.onCancel(request);
mConnectionId = null;
clearPendingInformation();
}
@@ -226,12 +220,10 @@
release();
}
- /**
- * Places an outgoing call.
- */
- final void createOutgoingConnection(
+ final void createRemoteConnection(
ConnectionRequest request,
- ConnectionService.OutgoingCallResponse<RemoteConnection> response) {
+ ConnectionService.CreateConnectionResponse<RemoteConnection> response,
+ boolean isIncoming) {
if (mConnectionId == null) {
String id = UUID.randomUUID().toString();
@@ -243,9 +235,9 @@
request.getExtras(),
request.getVideoState());
try {
- mConnectionService.call(newRequest);
+ mConnectionService.createConnection(newRequest, isIncoming);
mConnectionId = id;
- mPendingOutgoingCallResponse = response;
+ mPendingResponse = response;
mPendingRequest = request;
} catch (RemoteException e) {
response.onFailure(request, DisconnectCause.ERROR_UNSPECIFIED, e.toString());
@@ -255,9 +247,6 @@
}
}
- // TODO(santoscordon): Handle incoming connections
- // public final void handleIncomingConnection() {}
-
final List<PhoneAccount> lookupAccounts(Uri handle) {
// TODO(santoscordon): Update this so that is actually calls into the RemoteConnection
// each time.
@@ -279,7 +268,7 @@
}
private boolean isPendingConnection(String id) {
- return TextUtils.equals(mConnectionId, id) && mPendingOutgoingCallResponse != null;
+ return TextUtils.equals(mConnectionId, id) && mPendingResponse != null;
}
private boolean isCurrentConnection(String id) {
@@ -288,7 +277,7 @@
private void clearPendingInformation() {
mPendingRequest = null;
- mPendingOutgoingCallResponse = null;
+ mPendingResponse = null;
}
private void destroyConnection() {
diff --git a/telecomm/java/com/android/internal/telecomm/IConnectionService.aidl b/telecomm/java/com/android/internal/telecomm/IConnectionService.aidl
index 16d2edf..9360219 100644
--- a/telecomm/java/com/android/internal/telecomm/IConnectionService.aidl
+++ b/telecomm/java/com/android/internal/telecomm/IConnectionService.aidl
@@ -32,12 +32,10 @@
oneway interface IConnectionService {
void addConnectionServiceAdapter(in IConnectionServiceAdapter adapter);
- void call(in ConnectionRequest request);
+ void createConnection(in ConnectionRequest request, boolean isIncoming);
void abort(String callId);
- void createIncomingCall(in ConnectionRequest request);
-
void answer(String callId);
void reject(String callId);
diff --git a/telecomm/java/com/android/internal/telecomm/IConnectionServiceAdapter.aidl b/telecomm/java/com/android/internal/telecomm/IConnectionServiceAdapter.aidl
index bc67eab..b36f72c 100644
--- a/telecomm/java/com/android/internal/telecomm/IConnectionServiceAdapter.aidl
+++ b/telecomm/java/com/android/internal/telecomm/IConnectionServiceAdapter.aidl
@@ -31,13 +31,12 @@
* {@hide}
*/
oneway interface IConnectionServiceAdapter {
- void notifyIncomingCall(in ConnectionRequest request);
+ void handleCreateConnectionSuccessful(in ConnectionRequest request);
- void handleSuccessfulOutgoingCall(in ConnectionRequest request);
+ void handleCreateConnectionFailed(
+ in ConnectionRequest request, int errorCode, String errorMessage);
- void handleFailedOutgoingCall(in ConnectionRequest request, int errorCode, String errorMessage);
-
- void cancelOutgoingCall(in ConnectionRequest request);
+ void handleCreateConnectionCancelled(in ConnectionRequest request);
void setActive(String callId);
diff --git a/test-runner/src/android/test/mock/MockPackageManager.java b/test-runner/src/android/test/mock/MockPackageManager.java
index 648c418..e388480 100644
--- a/test-runner/src/android/test/mock/MockPackageManager.java
+++ b/test-runner/src/android/test/mock/MockPackageManager.java
@@ -750,7 +750,13 @@
}
/** {@hide} */
- public PackageInstaller getPackageInstaller() {
+ public PackageInstaller getInstaller() {
+ throw new UnsupportedOperationException();
+ }
+
+ /** {@hide} */
+ @Override
+ public boolean isPackageAvailable(String packageName) {
throw new UnsupportedOperationException();
}