Merge "Remove CallServiceProvider and CallServiceDescriptor do not merge" into lmp-dev
diff --git a/api/current.txt b/api/current.txt
index 29b1fc6..39d2eb7 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -1368,6 +1368,7 @@
field public static final int windowContentTransitions = 16843794; // 0x1010412
field public static final int windowDisablePreview = 16843298; // 0x1010222
field public static final int windowDrawsSystemBarBackgrounds = 16843858; // 0x1010452
+ field public static final int windowElevation = 16843922; // 0x1010492
field public static final int windowEnableSplitTouch = 16843543; // 0x1010317
field public static final int windowEnterAnimation = 16842932; // 0x10100b4
field public static final int windowEnterTransition = 16843833; // 0x1010439
@@ -7061,6 +7062,7 @@
method public abstract java.io.File getFileStreamPath(java.lang.String);
method public abstract java.io.File getFilesDir();
method public abstract android.os.Looper getMainLooper();
+ method public abstract java.io.File getNoBackupFilesDir();
method public abstract java.io.File getObbDir();
method public abstract java.io.File[] getObbDirs();
method public abstract java.lang.String getPackageCodePath();
@@ -7230,6 +7232,7 @@
method public java.io.File getFileStreamPath(java.lang.String);
method public java.io.File getFilesDir();
method public android.os.Looper getMainLooper();
+ method public java.io.File getNoBackupFilesDir();
method public java.io.File getObbDir();
method public java.io.File[] getObbDirs();
method public java.lang.String getPackageCodePath();
@@ -27851,6 +27854,62 @@
package android.telecomm {
+ public final class Call {
+ method public void addListener(android.telecomm.Call.Listener);
+ method public void answer();
+ method public void conference();
+ method public void disconnect();
+ method public android.telecomm.RemoteCallVideoProvider getCallVideoProvider();
+ method public java.util.List<java.lang.String> getCannedTextResponses();
+ method public java.util.List<android.telecomm.Call> getChildren();
+ method public android.telecomm.Call.Details getDetails();
+ method public android.telecomm.Call getParent();
+ method public java.lang.String getRemainingPostDialSequence();
+ method public int getState();
+ method public void hold();
+ method public void phoneAccountClicked();
+ method public void playDtmfTone(char);
+ method public void postDialContinue(boolean);
+ method public void reject(boolean, java.lang.String);
+ method public void removeListener(android.telecomm.Call.Listener);
+ method public void splitFromConference();
+ method public void stopDtmfTone();
+ method public void swapWithBackgroundCall();
+ method public void unhold();
+ field public static final int STATE_ACTIVE = 4; // 0x4
+ field public static final int STATE_DIALING = 1; // 0x1
+ field public static final int STATE_DISCONNECTED = 7; // 0x7
+ field public static final int STATE_HOLDING = 3; // 0x3
+ field public static final int STATE_NEW = 0; // 0x0
+ field public static final int STATE_RINGING = 2; // 0x2
+ }
+
+ public static class Call.Details {
+ method public android.telecomm.PhoneAccount getAccount();
+ method public java.lang.String getCallerDisplayName();
+ method public int getCallerDisplayNamePresentation();
+ method public int getCapabilities();
+ method public long getConnectTimeMillis();
+ method public int getDisconnectCauseCode();
+ method public java.lang.String getDisconnectCauseMsg();
+ method public android.telecomm.GatewayInfo getGatewayInfo();
+ method public android.net.Uri getHandle();
+ method public int getHandlePresentation();
+ }
+
+ public static abstract class Call.Listener {
+ ctor public Call.Listener();
+ method public void onCallDestroyed(android.telecomm.Call);
+ method public void onCallVideoProviderChanged(android.telecomm.Call, android.telecomm.RemoteCallVideoProvider);
+ method public void onCannedTextResponsesLoaded(android.telecomm.Call, java.util.List<java.lang.String>);
+ method public void onChildrenChanged(android.telecomm.Call, java.util.List<android.telecomm.Call>);
+ method public void onDetailsChanged(android.telecomm.Call, android.telecomm.Call.Details);
+ method public void onParentChanged(android.telecomm.Call, android.telecomm.Call);
+ method public void onPostDial(android.telecomm.Call, java.lang.String);
+ method public void onPostDialWait(android.telecomm.Call, java.lang.String);
+ method public void onStateChanged(android.telecomm.Call, int);
+ }
+
public final class CallAudioState implements android.os.Parcelable {
method public int describeContents();
method public void writeToParcel(android.os.Parcel, int);
@@ -27908,8 +27967,6 @@
enum_constant public static final android.telecomm.CallState DISCONNECTED;
enum_constant public static final android.telecomm.CallState NEW;
enum_constant public static final android.telecomm.CallState ON_HOLD;
- enum_constant public static final android.telecomm.CallState POST_DIAL;
- enum_constant public static final android.telecomm.CallState POST_DIAL_WAIT;
enum_constant public static final android.telecomm.CallState RINGING;
}
@@ -28083,31 +28140,52 @@
field public static final android.os.Parcelable.Creator CREATOR;
}
- public abstract class InCallService extends android.app.Service {
+ public abstract class InCallService {
ctor protected InCallService();
- method protected abstract void addCall(android.telecomm.InCallCall);
- method protected abstract void bringToForeground(boolean);
- method protected final android.telecomm.InCallAdapter getAdapter();
- method protected void onAdapterAttached(android.telecomm.InCallAdapter);
- method protected abstract void onAudioStateChanged(android.telecomm.CallAudioState);
- method public final android.os.IBinder onBind(android.content.Intent);
- method protected abstract void setPostDial(java.lang.String, java.lang.String);
- method protected abstract void setPostDialWait(java.lang.String, java.lang.String);
- method protected abstract void updateCall(android.telecomm.InCallCall);
+ method public final android.os.IBinder getBinder();
+ method public android.telecomm.Phone getPhone();
+ method public void onPhoneCreated(android.telecomm.Phone);
+ method public void onPhoneDestroyed(android.telecomm.Phone);
}
- public final class PhoneAccount implements android.os.Parcelable {
- ctor public PhoneAccount(android.content.ComponentName, java.lang.String, android.net.Uri, java.lang.String, java.lang.String, boolean, boolean);
+ public final class Phone {
+ method public final void addListener(android.telecomm.Phone.Listener);
+ method public final android.telecomm.CallAudioState getAudioState();
+ method public final java.util.List<android.telecomm.Call> getCalls();
+ method public final void removeListener(android.telecomm.Phone.Listener);
+ method public final void setAudioRoute(int);
+ method public final void setMuted(boolean);
+ }
+
+ public static abstract class Phone.Listener {
+ ctor public Phone.Listener();
+ method public void onAudioStateChanged(android.telecomm.Phone, android.telecomm.CallAudioState);
+ method public void onBringToForeground(android.telecomm.Phone, boolean);
+ method public void onCallAdded(android.telecomm.Phone, android.telecomm.Call);
+ method public void onCallRemoved(android.telecomm.Phone, android.telecomm.Call);
+ }
+
+ public class PhoneAccount implements android.os.Parcelable {
+ ctor public PhoneAccount(android.content.ComponentName, java.lang.String, android.net.Uri, int);
method public int describeContents();
+ method public int getCapabilities();
method public android.content.ComponentName getComponentName();
method public android.net.Uri getHandle();
- method public android.graphics.drawable.Drawable getIcon(android.content.Context);
- method public android.graphics.drawable.Drawable getIcon(android.content.Context, int);
method public java.lang.String getId();
- method public java.lang.String getLabel(android.content.Context);
- method public java.lang.String getShortDescription(android.content.Context);
- method public boolean isEnabled();
- method public boolean isSystemDefault();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final int CAPABILITY_CALL_PROVIDER = 2; // 0x2
+ field public static final int CAPABILITY_SIM_CALL_MANAGER = 1; // 0x1
+ field public static final int CAPABILITY_SIM_SUBSCRIPTION = 4; // 0x4
+ field public static final android.os.Parcelable.Creator CREATOR;
+ }
+
+ public class PhoneAccountMetadata implements android.os.Parcelable {
+ ctor public PhoneAccountMetadata(android.telecomm.PhoneAccount, int, java.lang.String, java.lang.String);
+ method public int describeContents();
+ method public android.telecomm.PhoneAccount getAccount();
+ method public android.graphics.drawable.Drawable getIcon(android.content.Context);
+ method public java.lang.String getLabel();
+ method public java.lang.String getShortDescription();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator CREATOR;
}
@@ -28122,18 +28200,17 @@
method public void updatePeerDimensions(int, int) throws android.os.RemoteException;
}
- public class RemoteCallVideoProvider implements android.os.IBinder.DeathRecipient {
- method public void binderDied();
- method public void requestCallDataUsage() throws android.os.RemoteException;
- method public void requestCameraCapabilities() throws android.os.RemoteException;
- method public void sendSessionModifyRequest(android.telecomm.VideoCallProfile) throws android.os.RemoteException;
- method public void sendSessionModifyResponse(android.telecomm.VideoCallProfile) throws android.os.RemoteException;
- method public void setCallVideoClient(android.telecomm.CallVideoClient) throws android.os.RemoteException;
+ public class RemoteCallVideoProvider {
+ method public void requestCallDataUsage();
+ method public void requestCameraCapabilities();
+ method public void sendSessionModifyRequest(android.telecomm.VideoCallProfile);
+ method public void sendSessionModifyResponse(android.telecomm.VideoCallProfile);
+ method public void setCallVideoClient(android.telecomm.CallVideoClient);
method public void setCamera(java.lang.String) throws android.os.RemoteException;
- method public void setDeviceOrientation(int) throws android.os.RemoteException;
- method public void setDisplaySurface(android.view.Surface) throws android.os.RemoteException;
- method public void setPauseImage(java.lang.String) throws android.os.RemoteException;
- method public void setPreviewSurface(android.view.Surface) throws android.os.RemoteException;
+ method public void setDeviceOrientation(int);
+ method public void setDisplaySurface(android.view.Surface);
+ method public void setPauseImage(java.lang.String);
+ method public void setPreviewSurface(android.view.Surface);
method public void setZoom(float) throws android.os.RemoteException;
}
@@ -28648,7 +28725,6 @@
}
public class TelephonyManager {
- method public java.util.List<android.telecomm.PhoneAccount> getAccounts();
method public java.util.List<android.telephony.CellInfo> getAllCellInfo();
method public int getCallState();
method public android.telephony.CellLocation getCellLocation();
@@ -28697,7 +28773,6 @@
field public static final int DATA_CONNECTING = 1; // 0x1
field public static final int DATA_DISCONNECTED = 0; // 0x0
field public static final int DATA_SUSPENDED = 3; // 0x3
- field public static final java.lang.String EXTRA_ACCOUNT = "account";
field public static final java.lang.String EXTRA_INCOMING_NUMBER = "incoming_number";
field public static final java.lang.String EXTRA_STATE = "state";
field public static final java.lang.String EXTRA_STATE_IDLE;
@@ -29213,6 +29288,7 @@
method public java.io.File getFileStreamPath(java.lang.String);
method public java.io.File getFilesDir();
method public android.os.Looper getMainLooper();
+ method public java.io.File getNoBackupFilesDir();
method public java.io.File getObbDir();
method public java.io.File[] getObbDirs();
method public java.lang.String getPackageCodePath();
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index bbfb05e..b9de220 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -247,6 +247,8 @@
@GuardedBy("mSync")
private File mFilesDir;
@GuardedBy("mSync")
+ private File mNoBackupFilesDir;
+ @GuardedBy("mSync")
private File mCacheDir;
@GuardedBy("mSync")
@@ -558,9 +560,7 @@
registerService(TELECOMM_SERVICE, new ServiceFetcher() {
public Object createService(ContextImpl ctx) {
- IBinder b = ServiceManager.getService(TELECOMM_SERVICE);
- return new TelecommManager(ctx.getOuterContext(),
- ITelecommService.Stub.asInterface(b));
+ return new TelecommManager(ctx.getOuterContext());
}});
registerService(PHONE_SERVICE, new ServiceFetcher() {
@@ -963,27 +963,42 @@
return f.delete();
}
+ // Common-path handling of app data dir creation
+ private static File createFilesDirLocked(File file) {
+ if (!file.exists()) {
+ if (!file.mkdirs()) {
+ if (file.exists()) {
+ // spurious failure; probably racing with another process for this app
+ return file;
+ }
+ Log.w(TAG, "Unable to create files subdir " + file.getPath());
+ return null;
+ }
+ FileUtils.setPermissions(
+ file.getPath(),
+ FileUtils.S_IRWXU|FileUtils.S_IRWXG|FileUtils.S_IXOTH,
+ -1, -1);
+ }
+ return file;
+ }
+
@Override
public File getFilesDir() {
synchronized (mSync) {
if (mFilesDir == null) {
mFilesDir = new File(getDataDirFile(), "files");
}
- if (!mFilesDir.exists()) {
- if(!mFilesDir.mkdirs()) {
- if (mFilesDir.exists()) {
- // spurious failure; probably racing with another process for this app
- return mFilesDir;
- }
- Log.w(TAG, "Unable to create files directory " + mFilesDir.getPath());
- return null;
- }
- FileUtils.setPermissions(
- mFilesDir.getPath(),
- FileUtils.S_IRWXU|FileUtils.S_IRWXG|FileUtils.S_IXOTH,
- -1, -1);
+ return createFilesDirLocked(mFilesDir);
+ }
+ }
+
+ @Override
+ public File getNoBackupFilesDir() {
+ synchronized (mSync) {
+ if (mNoBackupFilesDir == null) {
+ mNoBackupFilesDir = new File(getDataDirFile(), "no_backup");
}
- return mFilesDir;
+ return createFilesDirLocked(mNoBackupFilesDir);
}
}
@@ -1035,22 +1050,8 @@
if (mCacheDir == null) {
mCacheDir = new File(getDataDirFile(), "cache");
}
- if (!mCacheDir.exists()) {
- if(!mCacheDir.mkdirs()) {
- if (mCacheDir.exists()) {
- // spurious failure; probably racing with another process for this app
- return mCacheDir;
- }
- Log.w(TAG, "Unable to create cache directory " + mCacheDir.getAbsolutePath());
- return null;
- }
- FileUtils.setPermissions(
- mCacheDir.getPath(),
- FileUtils.S_IRWXU|FileUtils.S_IRWXG|FileUtils.S_IXOTH,
- -1, -1);
- }
+ return createFilesDirLocked(mCacheDir);
}
- return mCacheDir;
}
@Override
diff --git a/core/java/android/app/backup/BackupAgent.java b/core/java/android/app/backup/BackupAgent.java
index 886f1a6..e2a86e8 100644
--- a/core/java/android/app/backup/BackupAgent.java
+++ b/core/java/android/app/backup/BackupAgent.java
@@ -247,12 +247,40 @@
throws IOException;
/**
- * The default implementation backs up the entirety of the application's "owned"
- * file system trees to the output.
+ * The application is having its entire file system contents backed up. {@code data}
+ * points to the backup destination, and the app has the opportunity to choose which
+ * files are to be stored. To commit a file as part of the backup, call the
+ * {@link #fullBackupFile(File, FullBackupDataOutput)} helper method. After all file
+ * data is written to the output, the agent returns from this method and the backup
+ * operation concludes.
+ *
+ * <p>Certain parts of the app's data are never backed up even if the app explicitly
+ * sends them to the output:
+ *
+ * <ul>
+ * <li>The contents of the {@link #getCacheDir()} directory</li>
+ * <li>The contents of the {@link #getNoBackupFilesDir()} directory</li>
+ * <li>The contents of the app's shared library directory</li>
+ * </ul>
+ *
+ * <p>The default implementation of this method backs up the entirety of the
+ * application's "owned" file system trees to the output other than the few exceptions
+ * listed above. Apps only need to override this method if they need to impose special
+ * limitations on which files are being stored beyond the control that
+ * {@link #getNoBackupFilesDir()} offers.
+ *
+ * @param data A structured wrapper pointing to the backup destination.
+ * @throws IOException
+ *
+ * @see Context#getNoBackupFilesDir()
+ * @see #fullBackupFile(File, FullBackupDataOutput)
+ * @see #onRestoreFile(ParcelFileDescriptor, long, File, int, long, long)
*/
public void onFullBackup(FullBackupDataOutput data) throws IOException {
ApplicationInfo appInfo = getApplicationInfo();
+ // Note that we don't need to think about the no_backup dir because it's outside
+ // all of the ones we will be traversing
String rootDir = new File(appInfo.dataDir).getCanonicalPath();
String filesDir = getFilesDir().getCanonicalPath();
String databaseDir = getDatabasePath("foo").getParentFile().getCanonicalPath();
@@ -311,6 +339,10 @@
* to place it with the proper location and permissions on the device where the
* data is restored.
*
+ * <p class="note">It is safe to explicitly back up files underneath your application's
+ * {@link #getNoBackupFilesDir()} directory, and they will be restored to that
+ * location correctly.
+ *
* @param file The file to be backed up. The file must exist and be readable by
* the caller.
* @param output The destination to which the backed-up file data will be sent.
@@ -319,6 +351,7 @@
// Look up where all of our various well-defined dir trees live on this device
String mainDir;
String filesDir;
+ String nbFilesDir;
String dbDir;
String spDir;
String cacheDir;
@@ -331,6 +364,7 @@
try {
mainDir = new File(appInfo.dataDir).getCanonicalPath();
filesDir = getFilesDir().getCanonicalPath();
+ nbFilesDir = getNoBackupFilesDir().getCanonicalPath();
dbDir = getDatabasePath("foo").getParentFile().getCanonicalPath();
spDir = getSharedPrefsFile("foo").getParentFile().getCanonicalPath();
cacheDir = getCacheDir().getCanonicalPath();
@@ -354,8 +388,10 @@
return;
}
- if (filePath.startsWith(cacheDir) || filePath.startsWith(libDir)) {
- Log.w(TAG, "lib and cache files are not backed up");
+ if (filePath.startsWith(cacheDir)
+ || filePath.startsWith(libDir)
+ || filePath.startsWith(nbFilesDir)) {
+ Log.w(TAG, "lib, cache, and no_backup files are not backed up");
return;
}
@@ -508,6 +544,8 @@
mode = -1; // < 0 is a token to skip attempting a chmod()
}
}
+ } else if (domain.equals(FullBackup.NO_BACKUP_TREE_TOKEN)) {
+ basePath = getNoBackupFilesDir().getCanonicalPath();
} else {
// Not a supported location
Log.i(TAG, "Unrecognized domain " + domain);
diff --git a/core/java/android/app/backup/FullBackup.java b/core/java/android/app/backup/FullBackup.java
index 6ebb6c4..e5b47c6 100644
--- a/core/java/android/app/backup/FullBackup.java
+++ b/core/java/android/app/backup/FullBackup.java
@@ -40,6 +40,7 @@
public static final String OBB_TREE_TOKEN = "obb";
public static final String ROOT_TREE_TOKEN = "r";
public static final String DATA_TREE_TOKEN = "f";
+ public static final String NO_BACKUP_TREE_TOKEN = "nb";
public static final String DATABASE_TREE_TOKEN = "db";
public static final String SHAREDPREFS_TREE_TOKEN = "sp";
public static final String MANAGED_EXTERNAL_TREE_TOKEN = "ef";
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 535aaa1..a52cbdd 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -647,6 +647,26 @@
public abstract File getFilesDir();
/**
+ * Returns the absolute path to the directory on the filesystem similar to
+ * {@link #getFilesDir()}. The difference is that files placed under this
+ * directory will be excluded from automatic backup to remote storage. See
+ * {@link android.app.backup.BackupAgent BackupAgent} for a full discussion
+ * of the automatic backup mechanism in Android.
+ *
+ * <p>No permissions are required to read or write to the returned path, since this
+ * path is internal storage.
+ *
+ * @return The path of the directory holding application files that will not be
+ * automatically backed up to remote storage.
+ *
+ * @see #openFileOutput
+ * @see #getFileStreamPath
+ * @see #getDir
+ * @see android.app.backup.BackupAgent
+ */
+ public abstract File getNoBackupFilesDir();
+
+ /**
* Returns the absolute path to the directory on the primary external filesystem
* (that is somewhere on {@link android.os.Environment#getExternalStorageDirectory()
* Environment.getExternalStorageDirectory()}) where the application can
diff --git a/core/java/android/content/ContextWrapper.java b/core/java/android/content/ContextWrapper.java
index dbf9122..13eed07 100644
--- a/core/java/android/content/ContextWrapper.java
+++ b/core/java/android/content/ContextWrapper.java
@@ -200,7 +200,12 @@
public File getFilesDir() {
return mBase.getFilesDir();
}
-
+
+ @Override
+ public File getNoBackupFilesDir() {
+ return mBase.getNoBackupFilesDir();
+ }
+
@Override
public File getExternalFilesDir(String type) {
return mBase.getExternalFilesDir(type);
diff --git a/core/java/android/hardware/camera2/legacy/LegacyCameraDevice.java b/core/java/android/hardware/camera2/legacy/LegacyCameraDevice.java
index e9793c4..5f29e5c 100644
--- a/core/java/android/hardware/camera2/legacy/LegacyCameraDevice.java
+++ b/core/java/android/hardware/camera2/legacy/LegacyCameraDevice.java
@@ -17,6 +17,7 @@
package android.hardware.camera2.legacy;
import android.graphics.ImageFormat;
+import android.graphics.SurfaceTexture;
import android.hardware.Camera;
import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CaptureRequest;
@@ -463,6 +464,24 @@
return ids.contains(id);
}
+ static void setSurfaceOrientation(Surface surface, int facing, int sensorOrientation)
+ throws BufferQueueAbandonedException {
+ checkNotNull(surface);
+ LegacyExceptionUtils.throwOnError(nativeSetSurfaceOrientation(surface, facing,
+ sensorOrientation));
+ }
+
+ static Size getTextureSize(SurfaceTexture surfaceTexture)
+ throws BufferQueueAbandonedException {
+ checkNotNull(surfaceTexture);
+
+ int[] dimens = new int[2];
+ LegacyExceptionUtils.throwOnError(nativeDetectTextureDimens(surfaceTexture,
+ /*out*/dimens));
+
+ return new Size(dimens[0], dimens[1]);
+ }
+
private static native int nativeDetectSurfaceType(Surface surface);
private static native int nativeDetectSurfaceDimens(Surface surface,
@@ -479,4 +498,11 @@
private static native int nativeSetSurfaceDimens(Surface surface, int width, int height);
private static native long nativeGetSurfaceId(Surface surface);
+
+ private static native int nativeSetSurfaceOrientation(Surface surface, int facing,
+ int sensorOrientation);
+
+ private static native int nativeDetectTextureDimens(SurfaceTexture surfaceTexture,
+ /*out*/int[/*2*/] dimens);
+
}
diff --git a/core/java/android/hardware/camera2/legacy/RequestThreadManager.java b/core/java/android/hardware/camera2/legacy/RequestThreadManager.java
index e6d84c5..7d1be3b 100644
--- a/core/java/android/hardware/camera2/legacy/RequestThreadManager.java
+++ b/core/java/android/hardware/camera2/legacy/RequestThreadManager.java
@@ -292,10 +292,13 @@
mInFlightPreview = null;
mInFlightJpeg = null;
+ int facing = mCharacteristics.get(CameraCharacteristics.LENS_FACING);
+ int orientation = mCharacteristics.get(CameraCharacteristics.SENSOR_ORIENTATION);
if (outputs != null) {
for (Surface s : outputs) {
try {
int format = LegacyCameraDevice.detectSurfaceType(s);
+ LegacyCameraDevice.setSurfaceOrientation(s, facing, orientation);
switch (format) {
case CameraMetadataNative.NATIVE_JPEG_FORMAT:
mCallbackOutputs.add(s);
diff --git a/core/java/android/hardware/camera2/legacy/SurfaceTextureRenderer.java b/core/java/android/hardware/camera2/legacy/SurfaceTextureRenderer.java
index daa64c0..fdf9ba0 100644
--- a/core/java/android/hardware/camera2/legacy/SurfaceTextureRenderer.java
+++ b/core/java/android/hardware/camera2/legacy/SurfaceTextureRenderer.java
@@ -1,18 +1,18 @@
/*
-* 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.
-*/
+ * 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.hardware.camera2.legacy;
import android.graphics.ImageFormat;
@@ -98,14 +98,14 @@
*/
private static final String VERTEX_SHADER =
"uniform mat4 uMVPMatrix;\n" +
- "uniform mat4 uSTMatrix;\n" +
- "attribute vec4 aPosition;\n" +
- "attribute vec4 aTextureCoord;\n" +
- "varying vec2 vTextureCoord;\n" +
- "void main() {\n" +
- " gl_Position = uMVPMatrix * aPosition;\n" +
- " vTextureCoord = (uSTMatrix * aTextureCoord).xy;\n" +
- "}\n";
+ "uniform mat4 uSTMatrix;\n" +
+ "attribute vec4 aPosition;\n" +
+ "attribute vec4 aTextureCoord;\n" +
+ "varying vec2 vTextureCoord;\n" +
+ "void main() {\n" +
+ " gl_Position = uMVPMatrix * aPosition;\n" +
+ " vTextureCoord = (uSTMatrix * aTextureCoord).xy;\n" +
+ "}\n";
/**
* This fragment shader simply draws the color in the 2D texture at
@@ -113,12 +113,12 @@
*/
private static final String FRAGMENT_SHADER =
"#extension GL_OES_EGL_image_external : require\n" +
- "precision mediump float;\n" +
- "varying vec2 vTextureCoord;\n" +
- "uniform samplerExternalOES sTexture;\n" +
- "void main() {\n" +
- " gl_FragColor = texture2D(sTexture, vTextureCoord);\n" +
- "}\n";
+ "precision mediump float;\n" +
+ "varying vec2 vTextureCoord;\n" +
+ "uniform samplerExternalOES sTexture;\n" +
+ "void main() {\n" +
+ " gl_FragColor = texture2D(sTexture, vTextureCoord);\n" +
+ "}\n";
private float[] mMVPMatrix = new float[GL_MATRIX_SIZE];
private float[] mSTMatrix = new float[GL_MATRIX_SIZE];
@@ -189,12 +189,56 @@
return program;
}
- private void drawFrame(SurfaceTexture st) {
+ private void drawFrame(SurfaceTexture st, int width, int height) {
checkGlError("onDrawFrame start");
st.getTransformMatrix(mSTMatrix);
+ Size dimens;
+ try {
+ dimens = LegacyCameraDevice.getTextureSize(st);
+ } catch (LegacyExceptionUtils.BufferQueueAbandonedException e) {
+ // Should never hit this.
+ throw new IllegalStateException("Surface abandoned, skipping drawFrame...", e);
+ }
+
+ Matrix.setIdentityM(mMVPMatrix, /*smOffset*/0);
+
+ float texWidth = dimens.getWidth();
+ float texHeight = dimens.getHeight();
+
+ if (texWidth <= 0 || texHeight <= 0) {
+ throw new IllegalStateException("Illegal intermediate texture with dimension of 0");
+ }
+
+ // Find largest scaling factor from the intermediate texture dimension to the
+ // output surface dimension. Scaling the intermediate texture by this allows
+ // us to letterbox/pillerbox the output surface into the intermediate texture.
+ float widthRatio = width / texWidth;
+ float heightRatio = height / texHeight;
+ float actual = (widthRatio < heightRatio) ? heightRatio : widthRatio;
+
if (DEBUG) {
- GLES20.glClearColor(0.0f, 1.0f, 0.0f, 1.0f);
+ Log.d(TAG, "Scaling factor " + actual + " used for " + width + "x" + height +
+ " surface, intermediate buffer size is " + texWidth + "x" + texHeight);
+ }
+
+ // Set the viewport height and width to be the scaled intermediate texture dimensions.
+ int viewportW = (int) (actual * texWidth);
+ int viewportH = (int) (actual * texHeight);
+
+ // Set the offset of the viewport so that the output surface is centered in the viewport.
+ float dx = (width - viewportW) / 2f;
+ float dy = (height - viewportH) / 2f;
+
+ if (DEBUG) {
+ Log.d(TAG, "Translation " + dx + "," + dy + " used for " + width + "x" + height +
+ " surface");
+ }
+
+ GLES20.glViewport((int) dx, (int) dy, viewportW, viewportH);
+
+ if (DEBUG) {
+ GLES20.glClearColor(1.0f, 0.0f, 0.0f, 1.0f);
GLES20.glClear(GLES20.GL_DEPTH_BUFFER_BIT | GLES20.GL_COLOR_BUFFER_BIT);
}
@@ -218,7 +262,6 @@
GLES20.glEnableVertexAttribArray(maTextureHandle);
checkGlError("glEnableVertexAttribArray maTextureHandle");
- Matrix.setIdentityM(mMVPMatrix, 0);
GLES20.glUniformMatrix4fv(muMVPMatrixHandle, /*count*/ 1, /*transpose*/ false, mMVPMatrix,
/*offset*/ 0);
GLES20.glUniformMatrix4fv(muSTMatrixHandle, /*count*/ 1, /*transpose*/ false, mSTMatrix,
@@ -589,18 +632,19 @@
for (EGLSurfaceHolder holder : mSurfaces) {
if (LegacyCameraDevice.containsSurfaceId(holder.surface, targetSurfaceIds)) {
makeCurrent(holder.eglSurface);
- drawFrame(mSurfaceTexture);
+ drawFrame(mSurfaceTexture, holder.width, holder.height);
swapBuffers(holder.eglSurface);
}
}
for (EGLSurfaceHolder holder : mConversionSurfaces) {
if (LegacyCameraDevice.containsSurfaceId(holder.surface, targetSurfaceIds)) {
makeCurrent(holder.eglSurface);
- drawFrame(mSurfaceTexture);
+ drawFrame(mSurfaceTexture, holder.width, holder.height);
mPBufferPixels.clear();
- GLES20.glReadPixels(/*x*/ 0, /*y*/ 0, holder.width, holder.height, GLES20.GL_RGBA,
- GLES20.GL_UNSIGNED_BYTE, mPBufferPixels);
+ GLES20.glReadPixels(/*x*/ 0, /*y*/ 0, holder.width, holder.height,
+ GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, mPBufferPixels);
checkGlError("glReadPixels");
+
try {
int format = LegacyCameraDevice.detectSurfaceType(holder.surface);
LegacyCameraDevice.produceFrame(holder.surface, mPBufferPixels.array(),
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 7fab808..b554548 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -461,6 +461,11 @@
}
}
+ // Compute surface insets required to draw at specified Z value.
+ // TODO: Use real shadow insets for a constant max Z.
+ final int surfaceInset = (int) Math.ceil(view.getZ() * 2);
+ attrs.surfaceInsets.set(surfaceInset, surfaceInset, surfaceInset, surfaceInset);
+
CompatibilityInfo compatibilityInfo = mDisplayAdjustments.getCompatibilityInfo();
mTranslator = compatibilityInfo.getTranslator();
mDisplayAdjustments.setActivityToken(attrs.token);
@@ -1713,8 +1718,8 @@
if (hwInitialized ||
mWidth != mAttachInfo.mHardwareRenderer.getWidth() ||
mHeight != mAttachInfo.mHardwareRenderer.getHeight()) {
- final Rect shadowInsets = params != null ? params.shadowInsets : null;
- mAttachInfo.mHardwareRenderer.setup(mWidth, mHeight, shadowInsets);
+ final Rect surfaceInsets = params != null ? params.surfaceInsets : null;
+ mAttachInfo.mHardwareRenderer.setup(mWidth, mHeight, surfaceInsets);
if (!hwInitialized) {
mAttachInfo.mHardwareRenderer.invalidate(mSurface);
mFullRedrawNeeded = true;
@@ -2371,7 +2376,7 @@
}
final WindowManager.LayoutParams params = mWindowAttributes;
- final Rect surfaceInsets = params != null ? params.shadowInsets : null;
+ final Rect surfaceInsets = params != null ? params.surfaceInsets : null;
boolean animating = mScroller != null && mScroller.computeScrollOffset();
final int curScrollY;
if (animating) {
@@ -3155,7 +3160,7 @@
mFullRedrawNeeded = true;
try {
final WindowManager.LayoutParams lp = mWindowAttributes;
- final Rect surfaceInsets = lp != null ? lp.shadowInsets : null;
+ final Rect surfaceInsets = lp != null ? lp.surfaceInsets : null;
mAttachInfo.mHardwareRenderer.initializeIfNeeded(
mWidth, mHeight, mSurface, surfaceInsets);
} catch (OutOfResourcesException e) {
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index c06b5d8..034778f 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -1298,7 +1298,7 @@
*
* @hide
*/
- public Rect shadowInsets = new Rect();
+ public Rect surfaceInsets = new Rect();
/**
* The desired bitmap format. May be one of the constants in
@@ -1580,10 +1580,10 @@
out.writeInt(hasSystemUiListeners ? 1 : 0);
out.writeInt(inputFeatures);
out.writeLong(userActivityTimeout);
- out.writeInt(shadowInsets.left);
- out.writeInt(shadowInsets.top);
- out.writeInt(shadowInsets.right);
- out.writeInt(shadowInsets.bottom);
+ out.writeInt(surfaceInsets.left);
+ out.writeInt(surfaceInsets.top);
+ out.writeInt(surfaceInsets.right);
+ out.writeInt(surfaceInsets.bottom);
}
public static final Parcelable.Creator<LayoutParams> CREATOR
@@ -1626,7 +1626,10 @@
hasSystemUiListeners = in.readInt() != 0;
inputFeatures = in.readInt();
userActivityTimeout = in.readLong();
- shadowInsets.set(in.readInt(), in.readInt(), in.readInt(), in.readInt());
+ surfaceInsets.left = in.readInt();
+ surfaceInsets.top = in.readInt();
+ surfaceInsets.right = in.readInt();
+ surfaceInsets.bottom = in.readInt();
}
@SuppressWarnings({"PointlessBitwiseExpression"})
@@ -1658,7 +1661,7 @@
/** {@hide} */
public static final int TRANSLUCENT_FLAGS_CHANGED = 1<<19;
/** {@hide} */
- public static final int SHADOW_INSETS_CHANGED = 1<<20;
+ public static final int SURFACE_INSETS_CHANGED = 1<<20;
/** {@hide} */
public static final int EVERYTHING_CHANGED = 0xffffffff;
@@ -1794,9 +1797,9 @@
changes |= USER_ACTIVITY_TIMEOUT_CHANGED;
}
- if (!shadowInsets.equals(o.shadowInsets)) {
- shadowInsets.set(o.shadowInsets);
- changes |= SHADOW_INSETS_CHANGED;
+ if (!surfaceInsets.equals(o.surfaceInsets)) {
+ surfaceInsets.set(o.surfaceInsets);
+ changes |= SURFACE_INSETS_CHANGED;
}
return changes;
@@ -1898,8 +1901,8 @@
if (userActivityTimeout >= 0) {
sb.append(" userActivityTimeout=").append(userActivityTimeout);
}
- if (!shadowInsets.equals(Insets.NONE)) {
- sb.append(" shadowInsets=").append(shadowInsets);
+ if (!surfaceInsets.equals(Insets.NONE)) {
+ sb.append(" surfaceInsets=").append(surfaceInsets);
}
sb.append('}');
return sb.toString();
diff --git a/core/java/android/widget/PopupWindow.java b/core/java/android/widget/PopupWindow.java
index a35d447..41d3e320 100644
--- a/core/java/android/widget/PopupWindow.java
+++ b/core/java/android/widget/PopupWindow.java
@@ -1096,10 +1096,6 @@
p.softInputMode = mSoftInputMode;
p.setTitle("PopupWindow:" + Integer.toHexString(hashCode()));
- // TODO: Use real shadow insets once that algorithm is finalized.
- final int shadowInset = (int) Math.ceil(mElevation * 2);
- p.shadowInsets.set(shadowInset, shadowInset, shadowInset, shadowInset);
-
return p;
}
diff --git a/core/jni/android_hardware_camera2_legacy_LegacyCameraDevice.cpp b/core/jni/android_hardware_camera2_legacy_LegacyCameraDevice.cpp
index 9621bb2..d2f5b5d 100644
--- a/core/jni/android_hardware_camera2_legacy_LegacyCameraDevice.cpp
+++ b/core/jni/android_hardware_camera2_legacy_LegacyCameraDevice.cpp
@@ -19,17 +19,20 @@
#include <utils/Log.h>
#include <utils/Errors.h>
#include <utils/Trace.h>
+#include <camera/CameraUtils.h>
#include "jni.h"
#include "JNIHelp.h"
#include "android_runtime/AndroidRuntime.h"
#include "android_runtime/android_view_Surface.h"
+#include "android_runtime/android_graphics_SurfaceTexture.h"
#include <gui/Surface.h>
#include <gui/IGraphicBufferProducer.h>
#include <ui/GraphicBuffer.h>
#include <system/window.h>
#include <hardware/camera3.h>
+#include <system/camera_metadata.h>
#include <stdint.h>
#include <inttypes.h>
@@ -335,6 +338,25 @@
return anw;
}
+static sp<ANativeWindow> getNativeWindowFromTexture(JNIEnv* env, jobject surfaceTexture) {
+ sp<ANativeWindow> anw;
+ if (surfaceTexture) {
+ anw = android_SurfaceTexture_getNativeWindow(env, surfaceTexture);
+ if (env->ExceptionCheck()) {
+ return NULL;
+ }
+ } else {
+ jniThrowNullPointerException(env, "surfaceTexture");
+ return NULL;
+ }
+ if (anw == NULL) {
+ jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
+ "SurfaceTexture had no valid native window.");
+ return NULL;
+ }
+ return anw;
+}
+
static sp<Surface> getSurface(JNIEnv* env, jobject surface) {
sp<Surface> s;
if (surface) {
@@ -376,6 +398,17 @@
static jint LegacyCameraDevice_nativeDetectSurfaceDimens(JNIEnv* env, jobject thiz,
jobject surface, jintArray dimens) {
ALOGV("nativeGetSurfaceDimens");
+
+ if (dimens == NULL) {
+ ALOGE("%s: Null dimens argument passed to nativeDetectSurfaceDimens", __FUNCTION__);
+ return BAD_VALUE;
+ }
+
+ if (env->GetArrayLength(dimens) < 2) {
+ ALOGE("%s: Invalid length of dimens argument in nativeDetectSurfaceDimens", __FUNCTION__);
+ return BAD_VALUE;
+ }
+
sp<ANativeWindow> anw;
if ((anw = getNativeWindow(env, surface)) == NULL) {
ALOGE("%s: Could not retrieve native window from surface.", __FUNCTION__);
@@ -398,6 +431,37 @@
return NO_ERROR;
}
+static jint LegacyCameraDevice_nativeDetectTextureDimens(JNIEnv* env, jobject thiz,
+ jobject surfaceTexture, jintArray dimens) {
+ ALOGV("nativeDetectTextureDimens");
+ sp<ANativeWindow> anw;
+ if ((anw = getNativeWindowFromTexture(env, surfaceTexture)) == NULL) {
+ ALOGE("%s: Could not retrieve native window from SurfaceTexture.", __FUNCTION__);
+ return BAD_VALUE;
+ }
+
+ int32_t dimenBuf[2];
+ status_t err = anw->query(anw.get(), NATIVE_WINDOW_WIDTH, dimenBuf);
+ if(err != NO_ERROR) {
+ ALOGE("%s: Error while querying SurfaceTexture width %s (%d)", __FUNCTION__,
+ strerror(-err), err);
+ return err;
+ }
+
+ err = anw->query(anw.get(), NATIVE_WINDOW_HEIGHT, dimenBuf + 1);
+ if(err != NO_ERROR) {
+ ALOGE("%s: Error while querying SurfaceTexture height %s (%d)", __FUNCTION__,
+ strerror(-err), err);
+ return err;
+ }
+
+ env->SetIntArrayRegion(dimens, /*start*/0, /*length*/ARRAY_SIZE(dimenBuf), dimenBuf);
+ if (env->ExceptionCheck()) {
+ return BAD_VALUE;
+ }
+ return NO_ERROR;
+}
+
static jint LegacyCameraDevice_nativeConfigureSurface(JNIEnv* env, jobject thiz, jobject surface,
jint width, jint height, jint pixelFormat) {
ALOGV("nativeConfigureSurface");
@@ -504,6 +568,42 @@
return reinterpret_cast<jlong>(b.get());
}
+static jint LegacyCameraDevice_nativeSetSurfaceOrientation(JNIEnv* env, jobject thiz,
+ jobject surface, jint facing, jint orientation) {
+ ALOGV("nativeSetSurfaceOrientation");
+ sp<ANativeWindow> anw;
+ if ((anw = getNativeWindow(env, surface)) == NULL) {
+ ALOGE("%s: Could not retrieve native window from surface.", __FUNCTION__);
+ return BAD_VALUE;
+ }
+
+ status_t err = NO_ERROR;
+ CameraMetadata staticMetadata;
+
+ int32_t orientVal = static_cast<int32_t>(orientation);
+ uint8_t facingVal = static_cast<uint8_t>(facing);
+ staticMetadata.update(ANDROID_SENSOR_ORIENTATION, &orientVal, 1);
+ staticMetadata.update(ANDROID_LENS_FACING, &facingVal, 1);
+
+ int32_t transform = 0;
+
+ if ((err = CameraUtils::getRotationTransform(staticMetadata, /*out*/&transform)) != OK) {
+ ALOGE("%s: Invalid rotation transform %s (%d)", __FUNCTION__, strerror(-err),
+ err);
+ return err;
+ }
+
+ ALOGV("%s: Setting buffer sticky transform to %d", __FUNCTION__, transform);
+
+ if ((err = native_window_set_buffers_sticky_transform(anw.get(), transform)) != OK) {
+ ALOGE("%s: Unable to configure surface transform, error %s (%d)", __FUNCTION__,
+ strerror(-err), err);
+ return err;
+ }
+
+ return NO_ERROR;
+}
+
} // extern "C"
static JNINativeMethod gCameraDeviceMethods[] = {
@@ -528,6 +628,12 @@
{ "nativeGetSurfaceId",
"(Landroid/view/Surface;)J",
(void *)LegacyCameraDevice_nativeGetSurfaceId },
+ { "nativeDetectTextureDimens",
+ "(Landroid/graphics/SurfaceTexture;[I)I",
+ (void *)LegacyCameraDevice_nativeDetectTextureDimens },
+ { "nativeSetSurfaceOrientation",
+ "(Landroid/view/Surface;II)I",
+ (void *)LegacyCameraDevice_nativeSetSurfaceOrientation },
};
// Get all the required offsets in java class and register native functions
diff --git a/core/res/res/layout/alert_dialog_material.xml b/core/res/res/layout/alert_dialog_material.xml
index 93acc3f..6a435b2 100644
--- a/core/res/res/layout/alert_dialog_material.xml
+++ b/core/res/res/layout/alert_dialog_material.xml
@@ -20,13 +20,7 @@
android:id="@+id/parentPanel"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:orientation="vertical"
- android:background="@drawable/dialog_background_material"
- android:translationZ="@dimen/floating_window_z"
- android:layout_marginLeft="@dimen/floating_window_margin_left"
- android:layout_marginTop="@dimen/floating_window_margin_top"
- android:layout_marginRight="@dimen/floating_window_margin_right"
- android:layout_marginBottom="@dimen/floating_window_margin_bottom">
+ android:orientation="vertical">
<LinearLayout android:id="@+id/topPanel"
android:layout_width="match_parent"
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 5fdadb7..ed228e4 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -1826,6 +1826,9 @@
Activity Transition. Corresponds to
{@link android.view.Window#setTransitionBackgroundFadeDuration(long)}. -->
<attr name="windowTransitionBackgroundFadeDuration" />
+
+ <!-- Elevation to use for the window. -->
+ <attr name="windowElevation" format="dimension" />
</declare-styleable>
<!-- The set of attributes that describe a AlertDialog's theme. -->
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index a0ca06e..fee5c43 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2233,6 +2233,9 @@
<public type="attr" name="buttonBarNegativeButtonStyle" />
<public type="attr" name="popupElevation" />
<public type="attr" name="actionBarPopupTheme" />
+ <public type="attr" name="multiArch" />
+ <public type="attr" name="touchscreenBlocksFocus" />
+ <public type="attr" name="windowElevation" />
<public-padding type="dimen" name="l_resource_pad" end="0x01050010" />
@@ -2509,6 +2512,4 @@
<!-- A transition that moves views in or out of the scene to or from the left edge when
a view visibility changes. -->
<public type="transition" name="slide_left"/>
- <public type="attr" name="multiArch" />
- <public type="attr" name="touchscreenBlocksFocus" />
</resources>
diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml
index 0eceae6..f5d7f5f 100644
--- a/core/res/res/values/styles.xml
+++ b/core/res/res/values/styles.xml
@@ -824,15 +824,9 @@
<item name="textColorLink">?textColorLinkInverse</item>
</style>
- <style name="TextAppearance.Theme.Dialog" parent="TextAppearance.Theme">
- </style>
+ <style name="TextAppearance.Theme.Dialog" parent="TextAppearance.Theme" />
- <style name="TextAppearance.Theme.Dialog.AppError">
- <item name="textColor">#ffffc0c0</item>
- </style>
-
- <style name="TextAppearance.Widget">
- </style>
+ <style name="TextAppearance.Widget" />
<style name="TextAppearance.Widget.Button" parent="TextAppearance.Small.Inverse">
<item name="textColor">@color/primary_text_light_nodisable</item>
diff --git a/core/res/res/values/themes.xml b/core/res/res/values/themes.xml
index 6ada975..a519c37 100644
--- a/core/res/res/values/themes.xml
+++ b/core/res/res/values/themes.xml
@@ -820,21 +820,14 @@
<eat-comment />
<!-- Theme for the dialog shown when an app crashes or ANRs. -->
- <style name="Theme.Dialog.AppError" parent="Theme.DeviceDefault.Light.Dialog">
- <item name="windowFrame">@null</item>
- <item name="windowTitleStyle">@style/DialogWindowTitle</item>
- <item name="windowBackground">@color/transparent</item>
- <item name="windowIsFloating">true</item>
- <item name="windowContentOverlay">@null</item>
+ <style name="Theme.Dialog.AppError" parent="Theme.DeviceDefault.Light.Dialog.Alert">
<item name="windowContentTransitions">false</item>
- <item name="textAppearance">@style/TextAppearance.Theme.Dialog.AppError</item>
<item name="windowCloseOnTouchOutside">false</item>
</style>
<!-- Special theme for the recent apps dialog, to allow customization
with overlays. -->
<style name="Theme.Dialog.RecentApplications" parent="Theme.DeviceDefault.Light.Dialog">
- <item name="windowFrame">@null</item>
<item name="windowBackground">@color/transparent</item>
<item name="windowAnimationStyle">@style/Animation.RecentApplications</item>
<item name="textColor">@color/secondary_text_nofocus</item>
diff --git a/core/res/res/values/themes_material.xml b/core/res/res/values/themes_material.xml
index 47f3778..efc92d9 100644
--- a/core/res/res/values/themes_material.xml
+++ b/core/res/res/values/themes_material.xml
@@ -1011,7 +1011,8 @@
<style name="Theme.Material.Dialog">
<item name="windowFrame">@null</item>
<item name="windowTitleStyle">@style/DialogWindowTitle.Material</item>
- <item name="windowBackground">@drawable/dialog_background_shadow_material</item>
+ <item name="windowBackground">@drawable/dialog_background_material</item>
+ <item name="windowElevation">@dimen/floating_window_z</item>
<item name="windowIsFloating">true</item>
<item name="windowContentOverlay">@null</item>
<item name="windowAnimationStyle">@style/Animation.Material.Dialog</item>
@@ -1077,6 +1078,7 @@
its pixels. -->
<style name="Theme.Material.Dialog.NoFrame">
<item name="windowBackground">@color/transparent</item>
+ <item name="windowElevation">0dp</item>
<item name="windowAnimationStyle">@null</item>
<item name="backgroundDimEnabled">false</item>
<item name="windowIsTranslucent">true</item>
@@ -1085,7 +1087,6 @@
</style>
<style name="Theme.Material.Dialog.BaseAlert">
- <item name="windowBackground">@color/transparent</item>
<item name="windowTitleStyle">@style/DialogWindowTitle.Material</item>
<item name="windowMinWidthMajor">@dimen/dialog_min_width_major</item>
<item name="windowMinWidthMinor">@dimen/dialog_min_width_minor</item>
@@ -1100,6 +1101,7 @@
<style name="Theme.Material.Dialog.BaseTimePicker">
<item name="windowBackground">@color/transparent</item>
+ <item name="windowElevation">0dp</item>
<item name="windowTitleStyle">@style/DialogWindowTitle.Material</item>
<item name="windowContentOverlay">@null</item>
</style>
@@ -1131,7 +1133,8 @@
<style name="Theme.Material.Light.Dialog">
<item name="windowFrame">@null</item>
<item name="windowTitleStyle">@style/DialogWindowTitle.Material.Light</item>
- <item name="windowBackground">@drawable/dialog_background_shadow_material</item>
+ <item name="windowBackground">@drawable/dialog_background_material</item>
+ <item name="windowElevation">@dimen/floating_window_z</item>
<item name="windowIsFloating">true</item>
<item name="windowContentOverlay">@null</item>
<item name="windowAnimationStyle">@style/Animation.Material.Dialog</item>
@@ -1203,7 +1206,6 @@
<style name="Theme.Material.Light.DialogWhenLarge.NoActionBar" parent="@style/Theme.Material.Light.NoActionBar" />
<style name="Theme.Material.Light.Dialog.BaseAlert">
- <item name="windowBackground">@color/transparent</item>
<item name="windowTitleStyle">@style/DialogWindowTitle.Material.Light</item>
<item name="windowMinWidthMajor">@dimen/dialog_min_width_major</item>
<item name="windowMinWidthMinor">@dimen/dialog_min_width_minor</item>
@@ -1218,6 +1220,7 @@
<style name="Theme.Material.Light.Dialog.BaseTimePicker">
<item name="windowBackground">@color/transparent</item>
+ <item name="windowElevation">0dp</item>
<item name="windowTitleStyle">@style/DialogWindowTitle.Material.Light</item>
</style>
diff --git a/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java b/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java
index 1cecef3..b5fc628 100644
--- a/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java
+++ b/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java
@@ -209,6 +209,7 @@
if (drawableRes != 0) {
mAnimatedVectorState.mVectorDrawable = (VectorDrawable) res.getDrawable(
drawableRes, theme).mutate();
+ mAnimatedVectorState.mVectorDrawable.setAllowCaching(false);
}
a.recycle();
} else if (TARGET.equals(tagName)) {
@@ -258,6 +259,7 @@
mChangingConfigurations = copy.mChangingConfigurations;
// TODO: Make sure the constant state are handled correctly.
mVectorDrawable = new VectorDrawable();
+ mVectorDrawable.setAllowCaching(false);
mAnimators = new ArrayList<Animator>();
}
}
diff --git a/graphics/java/android/graphics/drawable/VectorDrawable.java b/graphics/java/android/graphics/drawable/VectorDrawable.java
index 8783994..8c907b2 100644
--- a/graphics/java/android/graphics/drawable/VectorDrawable.java
+++ b/graphics/java/android/graphics/drawable/VectorDrawable.java
@@ -18,6 +18,7 @@
import android.content.res.Resources;
import android.content.res.Resources.Theme;
import android.content.res.TypedArray;
+import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.ColorFilter;
@@ -142,6 +143,10 @@
private boolean mMutated;
+ // AnimatedVectorDrawable needs to turn off the cache all the time, otherwise,
+ // caching the bitmap by default is allowed.
+ private boolean mAllowCaching = true;
+
public VectorDrawable() {
mVectorState = new VectorDrawableState();
}
@@ -183,7 +188,23 @@
final int saveCount = canvas.save();
final Rect bounds = getBounds();
canvas.translate(bounds.left, bounds.top);
- mVectorState.mVPathRenderer.draw(canvas, bounds.width(), bounds.height());
+
+ if (!mAllowCaching) {
+ mVectorState.mVPathRenderer.draw(canvas, bounds.width(), bounds.height());
+ } else {
+ Bitmap bitmap = mVectorState.mCachedBitmap;
+ if (bitmap == null || !mVectorState.canReuseCache(bounds.width(),
+ bounds.height())) {
+ bitmap = Bitmap.createBitmap(bounds.width(), bounds.height(),
+ Bitmap.Config.ARGB_8888);
+ Canvas tmpCanvas = new Canvas(bitmap);
+ mVectorState.mVPathRenderer.draw(tmpCanvas, bounds.width(), bounds.height());
+ mVectorState.mCachedBitmap = bitmap;
+
+ mVectorState.updateCacheStates();
+ }
+ canvas.drawBitmap(bitmap, null, bounds, null);
+ }
canvas.restoreToCount(saveCount);
}
@@ -444,6 +465,10 @@
return super.getChangingConfigurations() | mVectorState.mChangingConfigurations;
}
+ void setAllowCaching(boolean allowCaching) {
+ mAllowCaching = allowCaching;
+ }
+
private static class VectorDrawableState extends ConstantState {
int[] mThemeAttrs;
int mChangingConfigurations;
@@ -451,6 +476,12 @@
ColorStateList mTint;
Mode mTintMode;
+ Bitmap mCachedBitmap;
+ int[] mCachedThemeAttrs;
+ ColorStateList mCachedTint;
+ Mode mCachedTintMode;
+ int mCachedRootAlpha;
+
// Deep copy for mutate() or implicitly mutate.
public VectorDrawableState(VectorDrawableState copy) {
if (copy != null) {
@@ -462,6 +493,27 @@
}
}
+ public boolean canReuseCache(int width, int height) {
+ if (mCachedThemeAttrs == mThemeAttrs
+ && mCachedTint == mTint
+ && mCachedTintMode == mTintMode
+ && width == mCachedBitmap.getWidth()
+ && height == mCachedBitmap.getHeight()
+ && mCachedRootAlpha == mVPathRenderer.getRootAlpha()) {
+ return true;
+ }
+ return false;
+ }
+
+ public void updateCacheStates() {
+ // Use shallow copy here and shallow comparison in canReuseCache(),
+ // likely hit cache miss more, but practically not much difference.
+ mCachedThemeAttrs = mThemeAttrs;
+ mCachedTint = mTint;
+ mCachedTintMode = mTintMode;
+ mCachedRootAlpha = mVPathRenderer.getRootAlpha();
+ }
+
public VectorDrawableState() {
mVPathRenderer = new VPathRenderer();
}
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindow.java b/policy/src/com/android/internal/policy/impl/PhoneWindow.java
index 2e7d331..f431fdb 100644
--- a/policy/src/com/android/internal/policy/impl/PhoneWindow.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindow.java
@@ -208,6 +208,8 @@
private Drawable mBackgroundDrawable;
+ private float mElevation;
+
private int mFrameResource = 0;
private int mTextColor = 0;
@@ -3251,6 +3253,7 @@
+ Integer.toHexString(mFrameResource));
}
}
+ mElevation = a.getDimension(com.android.internal.R.styleable.Window_windowElevation, 0);
mTextColor = a.getColor(com.android.internal.R.styleable.Window_textColor, 0xFF000000);
}
@@ -3340,28 +3343,31 @@
// Remaining setup -- of background and title -- that only applies
// to top-level windows.
if (getContainer() == null) {
- Drawable drawable = mBackgroundDrawable;
+ final Drawable background;
if (mBackgroundResource != 0) {
- drawable = getContext().getDrawable(mBackgroundResource);
+ background = getContext().getDrawable(mBackgroundResource);
+ } else {
+ background = mBackgroundDrawable;
}
- mDecor.setWindowBackground(drawable);
- drawable = null;
+ mDecor.setWindowBackground(background);
+
+ final Drawable frame;
if (mFrameResource != 0) {
- drawable = getContext().getDrawable(mFrameResource);
+ frame = getContext().getDrawable(mFrameResource);
+ } else {
+ frame = null;
}
- mDecor.setWindowFrame(drawable);
+ mDecor.setWindowFrame(frame);
- // System.out.println("Text=" + Integer.toHexString(mTextColor) +
- // " Sel=" + Integer.toHexString(mTextSelectedColor) +
- // " Title=" + Integer.toHexString(mTitleColor));
-
- if (mTitleColor == 0) {
- mTitleColor = mTextColor;
- }
+ mDecor.setElevation(mElevation);
if (mTitle != null) {
setTitle(mTitle);
}
+
+ if (mTitleColor == 0) {
+ mTitleColor = mTextColor;
+ }
setTitleColor(mTitleColor);
}
diff --git a/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java
index 5bfde4d..c3a9dbe 100644
--- a/services/backup/java/com/android/server/backup/BackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/BackupManagerService.java
@@ -3922,6 +3922,11 @@
break;
}
+ // Is it a *file* we need to drop?
+ if (!isRestorableFile(info)) {
+ okay = false;
+ }
+
// If the policy is satisfied, go ahead and set up to pipe the
// data to the agent.
if (DEBUG && okay && mAgent != null) {
@@ -4082,9 +4087,9 @@
}
}
- // Problems setting up the agent communication, or an already-
- // ignored package: skip to the next tar stream entry by
- // reading and discarding this file.
+ // Problems setting up the agent communication, an explicitly
+ // dropped file, or an already-ignored package: skip to the
+ // next stream entry by reading and discarding this file.
if (!okay) {
if (DEBUG) Slog.d(TAG, "[discarding file content]");
long bytesToConsume = (info.size + 511) & ~511;
@@ -4691,6 +4696,31 @@
return info;
}
+ private boolean isRestorableFile(FileMetadata info) {
+ if (FullBackup.CACHE_TREE_TOKEN.equals(info.domain)) {
+ if (MORE_DEBUG) {
+ Slog.i(TAG, "Dropping cache file path " + info.path);
+ }
+ return false;
+ }
+
+ if (FullBackup.ROOT_TREE_TOKEN.equals(info.domain)) {
+ // It's possible this is "no-backup" dir contents in an archive stream
+ // produced on a device running a version of the OS that predates that
+ // API. Respect the no-backup intention and don't let the data get to
+ // the app.
+ if (info.path.startsWith("no_backup/")) {
+ if (MORE_DEBUG) {
+ Slog.i(TAG, "Dropping no_backup file path " + info.path);
+ }
+ return false;
+ }
+ }
+
+ // Otherwise we think this file is good to go
+ return true;
+ }
+
private void HEXLOG(byte[] block) {
int offset = 0;
int todo = block.length;
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index 8387b65..b24072f 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -717,10 +717,10 @@
float top = w.mFrame.top + w.mYOffset;
// Adjust for surface insets.
- width += attrs.shadowInsets.left + attrs.shadowInsets.right;
- height += attrs.shadowInsets.top + attrs.shadowInsets.bottom;
- left -= attrs.shadowInsets.left;
- top -= attrs.shadowInsets.top;
+ width += attrs.surfaceInsets.left + attrs.surfaceInsets.right;
+ height += attrs.surfaceInsets.top + attrs.surfaceInsets.bottom;
+ left -= attrs.surfaceInsets.left;
+ top -= attrs.surfaceInsets.top;
if (DEBUG_VISIBILITY) {
Slog.v(TAG, "Creating surface in session "
@@ -1140,19 +1140,12 @@
void applyDecorRect(final Rect decorRect) {
final WindowState w = mWin;
- int width = w.mFrame.width();
- int height = w.mFrame.height();
+ final int width = w.mFrame.width();
+ final int height = w.mFrame.height();
// Compute the offset of the window in relation to the decor rect.
- int left = w.mXOffset + w.mFrame.left;
- int top = w.mYOffset + w.mFrame.top;
-
- // Adjust for surface insets.
- final WindowManager.LayoutParams attrs = w.mAttrs;
- width += attrs.shadowInsets.left + attrs.shadowInsets.right;
- height += attrs.shadowInsets.top + attrs.shadowInsets.bottom;
- left -= attrs.shadowInsets.left;
- top -= attrs.shadowInsets.top;
+ final int left = w.mXOffset + w.mFrame.left;
+ final int top = w.mYOffset + w.mFrame.top;
// Initialize the decor rect to the entire frame.
w.mSystemDecorRect.set(0, 0, width, height);
@@ -1182,7 +1175,6 @@
if (displayContent == null) {
return;
}
- DisplayInfo displayInfo = displayContent.getDisplayInfo();
// Need to recompute a new system decor rect each time.
if ((w.mAttrs.flags & LayoutParams.FLAG_SCALED) != 0) {
@@ -1192,6 +1184,7 @@
} else if (!w.isDefaultDisplay()) {
// On a different display there is no system decor. Crop the window
// by the screen boundaries.
+ final DisplayInfo displayInfo = displayContent.getDisplayInfo();
w.mSystemDecorRect.set(0, 0, w.mCompatFrame.width(), w.mCompatFrame.height());
w.mSystemDecorRect.intersect(-w.mCompatFrame.left, -w.mCompatFrame.top,
displayInfo.logicalWidth - w.mCompatFrame.left,
@@ -1202,44 +1195,52 @@
// windows need to be cropped by the screen, so they don't cover
// the universe background.
if (mAnimator.mUniverseBackground == null) {
- w.mSystemDecorRect.set(0, 0, w.mCompatFrame.width(),
- w.mCompatFrame.height());
+ w.mSystemDecorRect.set(0, 0, w.mCompatFrame.width(), w.mCompatFrame.height());
} else {
applyDecorRect(mService.mScreenRect);
}
} else if (w.mAttrs.type == WindowManager.LayoutParams.TYPE_UNIVERSE_BACKGROUND
|| w.mDecorFrame.isEmpty()) {
// The universe background isn't cropped, nor windows without policy decor.
- w.mSystemDecorRect.set(0, 0, w.mCompatFrame.width(),
- w.mCompatFrame.height());
+ w.mSystemDecorRect.set(0, 0, w.mCompatFrame.width(), w.mCompatFrame.height());
} else {
// Crop to the system decor specified by policy.
applyDecorRect(w.mDecorFrame);
}
- // By default, the clip rect is the system decor rect
- Rect clipRect = w.mSystemDecorRect;
- if (mHasClipRect) {
+ // By default, the clip rect is the system decor.
+ final Rect clipRect = mTmpClipRect;
+ clipRect.set(w.mSystemDecorRect);
- // If we have an animated clip rect, intersect it with the system decor rect
- // NOTE: We are adding a temporary workaround due to the status bar not always reporting
- // the correct system decor rect. In such cases, we take into account the specified
- // content insets as well.
- int offsetTop = Math.max(w.mSystemDecorRect.top, w.mContentInsets.top);
- mTmpClipRect.set(w.mSystemDecorRect);
- // Don't apply the workaround to apps explicitly requesting fullscreen layout.
+ // Expand the clip rect for surface insets.
+ final WindowManager.LayoutParams attrs = w.mAttrs;
+ clipRect.left -= attrs.surfaceInsets.left;
+ clipRect.top -= attrs.surfaceInsets.top;
+ clipRect.right += attrs.surfaceInsets.right;
+ clipRect.bottom += attrs.surfaceInsets.bottom;
+
+ // If we have an animated clip rect, intersect it with the clip rect.
+ if (mHasClipRect) {
+ // NOTE: We are adding a temporary workaround due to the status bar
+ // not always reporting the correct system decor rect. In such
+ // cases, we take into account the specified content insets as well.
if ((w.mSystemUiVisibility & SYSTEM_UI_FLAGS_LAYOUT_STABLE_FULLSCREEN)
== SYSTEM_UI_FLAGS_LAYOUT_STABLE_FULLSCREEN) {
- mTmpClipRect.intersect(mClipRect);
+ // Don't apply the workaround to apps explicitly requesting
+ // fullscreen layout.
+ clipRect.intersect(mClipRect);
} else {
- mTmpClipRect.offset(0, -offsetTop);
- mTmpClipRect.intersect(mClipRect);
- mTmpClipRect.offset(0, offsetTop);
+ final int offsetTop = Math.max(clipRect.top, w.mContentInsets.top);
+ clipRect.offset(0, -offsetTop);
+ clipRect.intersect(mClipRect);
+ clipRect.offset(0, offsetTop);
}
- clipRect = mTmpClipRect;
-
}
+ // The clip rect was generated assuming (0,0) as the window origin,
+ // so we need to translate to match the actual surface coordinates.
+ clipRect.offset(attrs.surfaceInsets.left, attrs.surfaceInsets.top);
+
if (!clipRect.equals(mLastClipRect)) {
mLastClipRect.set(clipRect);
try {
@@ -1285,10 +1286,10 @@
// Adjust for surface insets.
final LayoutParams attrs = w.getAttrs();
- width += attrs.shadowInsets.left + attrs.shadowInsets.right;
- height += attrs.shadowInsets.top + attrs.shadowInsets.bottom;
- left -= attrs.shadowInsets.left;
- top -= attrs.shadowInsets.top;
+ width += attrs.surfaceInsets.left + attrs.surfaceInsets.right;
+ height += attrs.surfaceInsets.top + attrs.surfaceInsets.bottom;
+ left -= attrs.surfaceInsets.left;
+ top -= attrs.surfaceInsets.top;
final boolean surfaceMoved = mSurfaceX != left || mSurfaceY != top;
if (surfaceMoved) {
diff --git a/telecomm/java/android/telecomm/Call.java b/telecomm/java/android/telecomm/Call.java
new file mode 100644
index 0000000..ba7e253
--- /dev/null
+++ b/telecomm/java/android/telecomm/Call.java
@@ -0,0 +1,710 @@
+/*
+ * 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.telecomm;
+
+import android.net.Uri;
+import android.os.RemoteException;
+import android.telephony.DisconnectCause;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * Represents an ongoing phone call that the in-call app should present to the user.
+ */
+public final class Call {
+ /**
+ * The state of a {@code Call} when newly created.
+ */
+ public static final int STATE_NEW = 0;
+
+ /**
+ * The state of an outgoing {@code Call} when dialing the remote number, but not yet connected.
+ */
+ public static final int STATE_DIALING = 1;
+
+ /**
+ * The state of an incoming {@code Call} when ringing locally, but not yet connected.
+ */
+ public static final int STATE_RINGING = 2;
+
+ /**
+ * The state of a {@code Call} when in a holding state.
+ */
+ public static final int STATE_HOLDING = 3;
+
+ /**
+ * The state of a {@code Call} when actively supporting conversation.
+ */
+ public static final int STATE_ACTIVE = 4;
+
+ /**
+ * The state of a {@code Call} when no further voice or other communication is being
+ * transmitted, the remote side has been or will inevitably be informed that the {@code Call}
+ * is no longer active, and the local data transport has or inevitably will release resources
+ * associated with this {@code Call}.
+ */
+ public static final int STATE_DISCONNECTED = 7;
+
+ public static class Details {
+ private final Uri mHandle;
+ private final int mHandlePresentation;
+ private final String mCallerDisplayName;
+ private final int mCallerDisplayNamePresentation;
+ private final PhoneAccount mAccount;
+ private final int mCapabilities;
+ private final int mDisconnectCauseCode;
+ private final String mDisconnectCauseMsg;
+ private final long mConnectTimeMillis;
+ private final GatewayInfo mGatewayInfo;
+
+ /**
+ * @return The handle (e.g., phone number) to which the {@code Call} is currently
+ * connected.
+ */
+ public Uri getHandle() {
+ return mHandle;
+ }
+
+ /**
+ * @return The presentation requirements for the handle. See
+ * {@link android.telecomm.CallPropertyPresentation} for valid values.
+ */
+ public int getHandlePresentation() {
+ return mHandlePresentation;
+ }
+
+ /**
+ * @return The display name for the caller.
+ */
+ public String getCallerDisplayName() {
+ return mCallerDisplayName;
+ }
+
+ /**
+ * @return The presentation requirements for the caller display name. See
+ * {@link android.telecomm.CallPropertyPresentation} for valid values.
+ */
+ public int getCallerDisplayNamePresentation() {
+ return mCallerDisplayNamePresentation;
+ }
+
+ /**
+ * @return The {@code PhoneAccount} whereby the {@code Call} is currently being routed.
+ */
+ public PhoneAccount getAccount() {
+ return mAccount;
+ }
+
+ /**
+ * @return A bitmask of the capabilities of the {@code Call}, as defined in
+ * {@link CallCapabilities}.
+ */
+ public int getCapabilities() {
+ return mCapabilities;
+ }
+
+ /**
+ * @return For a {@link #STATE_DISCONNECTED} {@code Call}, the disconnect cause expressed
+ * as a code chosen from among those declared in {@link DisconnectCause}.
+ */
+ public int getDisconnectCauseCode() {
+ return mDisconnectCauseCode;
+ }
+
+ /**
+ * @return For a {@link #STATE_DISCONNECTED} {@code Call}, an optional reason for
+ * disconnection expressed as a free text message.
+ */
+ public String getDisconnectCauseMsg() {
+ return mDisconnectCauseMsg;
+ }
+
+ /**
+ * @return The time the {@code Call} has been connected. This information is updated
+ * periodically, but user interfaces should not rely on this to display any "call time
+ * clock".
+ */
+ public long getConnectTimeMillis() {
+ return mConnectTimeMillis;
+ }
+
+ /**
+ * @return Information about any calling gateway the {@code Call} may be using.
+ */
+ public GatewayInfo getGatewayInfo() {
+ return mGatewayInfo;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o instanceof Details) {
+ Details d = (Details) o;
+ return
+ Objects.equals(mHandle, d.mHandle) &&
+ Objects.equals(mHandlePresentation, d.mHandlePresentation) &&
+ Objects.equals(mCallerDisplayName, d.mCallerDisplayName) &&
+ Objects.equals(mCallerDisplayNamePresentation,
+ d.mCallerDisplayNamePresentation) &&
+ Objects.equals(mAccount, d.mAccount) &&
+ Objects.equals(mCapabilities, d.mCapabilities) &&
+ Objects.equals(mDisconnectCauseCode, d.mDisconnectCauseCode) &&
+ Objects.equals(mDisconnectCauseMsg, d.mDisconnectCauseMsg) &&
+ Objects.equals(mConnectTimeMillis, d.mConnectTimeMillis) &&
+ Objects.equals(mGatewayInfo, d.mGatewayInfo);
+ }
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ return
+ Objects.hashCode(mHandle) +
+ Objects.hashCode(mHandlePresentation) +
+ Objects.hashCode(mCallerDisplayName) +
+ Objects.hashCode(mCallerDisplayNamePresentation) +
+ Objects.hashCode(mAccount) +
+ Objects.hashCode(mCapabilities) +
+ Objects.hashCode(mDisconnectCauseCode) +
+ Objects.hashCode(mDisconnectCauseMsg) +
+ Objects.hashCode(mConnectTimeMillis) +
+ Objects.hashCode(mGatewayInfo);
+ }
+
+ /** {@hide} */
+ public Details(
+ Uri handle,
+ int handlePresentation,
+ String callerDisplayName,
+ int callerDisplayNamePresentation,
+ PhoneAccount account,
+ int capabilities,
+ int disconnectCauseCode,
+ String disconnectCauseMsg,
+ long connectTimeMillis,
+ GatewayInfo gatewayInfo) {
+ mHandle = handle;
+ mHandlePresentation = handlePresentation;
+ mCallerDisplayName = callerDisplayName;
+ mCallerDisplayNamePresentation = callerDisplayNamePresentation;
+ mAccount = account;
+ mCapabilities = capabilities;
+ mDisconnectCauseCode = disconnectCauseCode;
+ mDisconnectCauseMsg = disconnectCauseMsg;
+ mConnectTimeMillis = connectTimeMillis;
+ mGatewayInfo = gatewayInfo;
+ }
+ }
+
+ public static abstract class Listener {
+ /**
+ * Invoked when the state of this {@code Call} has changed. See {@link #getState()}.
+ *
+ * TODO(ihab): Provide previous state also?
+ *
+ * @param call The {@code Call} invoking this method.
+ * @param state The new state of the {@code Call}.
+ */
+ public void onStateChanged(Call call, int state) {}
+
+ /**
+ * Invoked when the parent of this {@code Call} has changed. See {@link #getParent()}.
+ *
+ * @param call The {@code Call} invoking this method.
+ * @param parent The new parent of the {@code Call}.
+ */
+ public void onParentChanged(Call call, Call parent) {}
+
+ /**
+ * Invoked when the children of this {@code Call} have changed. See {@link #getChildren()}.
+ *
+ * @param call The {@code Call} invoking this method.
+ * @param children The new children of the {@code Call}.
+ */
+ public void onChildrenChanged(Call call, List<Call> children) {}
+
+ /**
+ * Invoked when the details of this {@code Call} have changed. See {@link #getDetails()}.
+ *
+ * @param call The {@code Call} invoking this method.
+ * @param details A {@code Details} object describing the {@code Call}.
+ */
+ public void onDetailsChanged(Call call, Details details) {}
+
+ /**
+ * Invoked when the text messages that can be used as responses to the incoming
+ * {@code Call} are loaded from the relevant database.
+ * See {@link #getCannedTextResponses()}.
+ *
+ * @param call The {@code Call} invoking this method.
+ * @param cannedTextResponses The text messages useable as responses.
+ */
+ public void onCannedTextResponsesLoaded(Call call, List<String> cannedTextResponses) {}
+
+ /**
+ * Invoked when the outgoing {@code Call} has finished dialing but is sending DTMF signals
+ * that were embedded into the outgoing number.
+ *
+ * @param call The {@code Call} invoking this method.
+ * @param remainingPostDialSequence The post-dial characters that remain to be sent.
+ */
+ public void onPostDial(Call call, String remainingPostDialSequence) {}
+
+ /**
+ * Invoked when the post-dial sequence in the outgoing {@code Call} has reached a pause
+ * character. This causes the post-dial signals to stop pending user confirmation. An
+ * implementation should present this choice to the user and invoke
+ * {@link #postDialContinue(boolean)} when the user makes the choice.
+ *
+ * @param call The {@code Call} invoking this method.
+ * @param remainingPostDialSequence The post-dial characters that remain to be sent.
+ */
+ public void onPostDialWait(Call call, String remainingPostDialSequence) {}
+
+ /**
+ * Invoked when the {@code RemoteCallVideoProvider} of the {@code Call} has changed.
+ *
+ * @param call The {@code Call} invoking this method.
+ * @param callVideoProvider The {@code RemoteCallVideoProvider} associated with the
+ * {@code Call}.
+ */
+
+ public void onCallVideoProviderChanged(Call call,
+ RemoteCallVideoProvider callVideoProvider) {}
+
+ /**
+ * Invoked when the {@code Call} is destroyed. Clients should refrain from cleaning
+ * up their UI for the {@code Call} in response to state transitions. Specifically,
+ * clients should not assume that a {@link #onStateChanged(Call, int)} with a state of
+ * {@link #STATE_DISCONNECTED} is the final notification the {@code Call} will send. Rather,
+ * clients should wait for this method to be invoked.
+ *
+ * @param call The {@code Call} being destroyed.
+ */
+ public void onCallDestroyed(Call call) {}
+ }
+
+ private final Phone mPhone;
+ private final String mTelecommCallId;
+ private final InCallAdapter mInCallAdapter;
+ private Call mParent = null;
+ private int mState;
+ private final List<Call> mChildren = new ArrayList<>();
+ private final List<Call> mUnmodifiableChildren = Collections.unmodifiableList(mChildren);
+ private List<String> mCannedTextResponses = null;
+ private String mRemainingPostDialSequence;
+ private RemoteCallVideoProvider mCallVideoProvider;
+ private Details mDetails;
+ private final List<Listener> mListeners = new ArrayList<>();
+
+ /**
+ * Obtains the post-dial sequence remaining to be emitted by this {@code Call}, if any.
+ *
+ * @return The remaining post-dial sequence, or {@code null} if there is no post-dial sequence
+ * remaining or this {@code Call} is not in a post-dial state.
+ */
+ public String getRemainingPostDialSequence() {
+ return mRemainingPostDialSequence;
+ }
+
+ /**
+ * Instructs this {@link #STATE_RINGING} {@code Call} to answer.
+ */
+ public void answer() {
+ mInCallAdapter.answerCall(mTelecommCallId);
+ }
+
+ /**
+ * Instructs this {@link #STATE_RINGING} {@code Call} to reject.
+ *
+ * @param rejectWithMessage Whether to reject with a text message.
+ * @param textMessage An optional text message with which to respond.
+ */
+ public void reject(boolean rejectWithMessage, String textMessage) {
+ mInCallAdapter.rejectCall(mTelecommCallId, rejectWithMessage, textMessage);
+ }
+
+ /**
+ * Instructs this {@code Call} to disconnect.
+ */
+ public void disconnect() {
+ mInCallAdapter.disconnectCall(mTelecommCallId);
+ }
+
+ /**
+ * Instructs this {@code Call} to go on hold.
+ */
+ public void hold() {
+ mInCallAdapter.holdCall(mTelecommCallId);
+ }
+
+ /**
+ * Instructs this {@link #STATE_HOLDING} call to release from hold.
+ */
+ public void unhold() {
+ mInCallAdapter.unholdCall(mTelecommCallId);
+ }
+
+ /**
+ * Instructs this {@code Call} to play a dual-tone multi-frequency signaling (DTMF) tone.
+ *
+ * Any other currently playing DTMF tone in the specified call is immediately stopped.
+ *
+ * @param digit A character representing the DTMF digit for which to play the tone. This
+ * value must be one of {@code '0'} through {@code '9'}, {@code '*'} or {@code '#'}.
+ */
+ public void playDtmfTone(char digit) {
+ mInCallAdapter.playDtmfTone(mTelecommCallId, digit);
+ }
+
+ /**
+ * Instructs this {@code Call} to stop any dual-tone multi-frequency signaling (DTMF) tone
+ * currently playing.
+ *
+ * DTMF tones are played by calling {@link #playDtmfTone(char)}. If no DTMF tone is
+ * currently playing, this method will do nothing.
+ */
+ public void stopDtmfTone() {
+ mInCallAdapter.stopDtmfTone(mTelecommCallId);
+ }
+
+ /**
+ * Instructs this {@code Call} to continue playing a post-dial DTMF string.
+ *
+ * A post-dial DTMF string is a string of digits entered after a phone number, when dialed,
+ * that are immediately sent as DTMF tones to the recipient as soon as the connection is made.
+ * While these tones are playing, this {@code Call} will notify listeners via
+ * {@link Listener#onPostDial(Call, String)}.
+ *
+ * If the DTMF string contains a {@link TelecommConstants#DTMF_CHARACTER_PAUSE} symbol, this
+ * {@code Call} will temporarily pause playing the tones for a pre-defined period of time.
+ *
+ * If the DTMF string contains a {@link TelecommConstants#DTMF_CHARACTER_WAIT} symbol, this
+ * {@code Call} will pause playing the tones and notify listeners via
+ * {@link Listener#onPostDialWait(Call, String)}. At this point, the in-call app
+ * should display to the user an indication of this state and an affordance to continue
+ * the postdial sequence. When the user decides to continue the postdial sequence, the in-call
+ * app should invoke the {@link #postDialContinue(boolean)} method.
+ *
+ * @param proceed Whether or not to continue with the post-dial sequence.
+ */
+ public void postDialContinue(boolean proceed) {
+ mInCallAdapter.postDialContinue(mTelecommCallId, proceed);
+ }
+
+ /**
+ * Notifies this {@code Call} that the phone account user interface element was touched.
+ *
+ * TODO(ihab): Figure out if and how we can generalize this
+ */
+ public void phoneAccountClicked() {
+ mInCallAdapter.phoneAccountClicked(mTelecommCallId);
+ }
+
+ /**
+ * Instructs this {@code Call} to enter a conference.
+ */
+ public void conference() {
+ mInCallAdapter.conference(mTelecommCallId);
+ }
+
+ /**
+ * Instructs this {@code Call} to split from any conference call with which it may be
+ * connected.
+ */
+ public void splitFromConference() {
+ mInCallAdapter.splitFromConference(mTelecommCallId);
+ }
+
+ /**
+ * Instructs this {@code Call} to swap itself with an existing background call, if one
+ * such call exists.
+ */
+ public void swapWithBackgroundCall() {
+ mInCallAdapter.swapWithBackgroundCall(mTelecommCallId);
+ }
+
+ /**
+ * Obtains the parent of this {@code Call} in a conference, if any.
+ *
+ * @return The parent {@code Call}, or {@code null} if this {@code Call} is not a
+ * child of any conference {@code Call}s.
+ */
+ public Call getParent() {
+ return mParent;
+ }
+
+ /**
+ * Obtains the children of this conference {@code Call}, if any.
+ *
+ * @return The children of this {@code Call} if this {@code Call} is a conference, or an empty
+ * {@code List} otherwise.
+ */
+ public List<Call> getChildren() {
+ return mUnmodifiableChildren;
+ }
+
+ /**
+ * Obtains the state of this {@code Call}.
+ *
+ * @return A state value, chosen from the {@code STATE_*} constants.
+ */
+ public int getState() {
+ return mState;
+ }
+
+ /**
+ * Obtains a list of canned, pre-configured message responses to present to the user as
+ * ways of rejecting this {@code Call} using via a text message.
+ *
+ * @see #reject(boolean, String)
+ *
+ * @return A list of canned text message responses.
+ */
+ public List<String> getCannedTextResponses() {
+ return mCannedTextResponses;
+ }
+
+ /**
+ * Obtains an object that can be used to display video from this {@code Call}.
+ *
+ * @return An {@code ICallVideoProvider}.
+ */
+ public RemoteCallVideoProvider getCallVideoProvider() {
+ return mCallVideoProvider;
+ }
+
+ /**
+ * Obtains an object containing call details.
+ *
+ * @return A {@link Details} object. Depending on the state of the {@code Call}, the
+ * result may be {@code null}.
+ */
+ public Details getDetails() {
+ return mDetails;
+ }
+
+ /**
+ * Adds a listener to this {@code Call}.
+ *
+ * @param listener A {@code Listener}.
+ */
+ public void addListener(Listener listener) {
+ mListeners.add(listener);
+ }
+
+ /**
+ * Removes a listener from this {@code Call}.
+ *
+ * @param listener A {@code Listener}.
+ */
+ public void removeListener(Listener listener) {
+ mListeners.remove(listener);
+ }
+
+ /** {@hide} */
+ Call(Phone phone, String telecommCallId, InCallAdapter inCallAdapter) {
+ mPhone = phone;
+ mTelecommCallId = telecommCallId;
+ mInCallAdapter = inCallAdapter;
+ mState = STATE_NEW;
+ }
+
+ /** {@hide} */
+ final String internalGetCallId() {
+ return mTelecommCallId;
+ }
+
+ /** {@hide} */
+ final void internalUpdate(InCallCall inCallCall) {
+ // First, we update the internal state as far as possible before firing any updates.
+
+ Details details = new Details(
+ inCallCall.getHandle(),
+ inCallCall.getHandlePresentation(),
+ inCallCall.getCallerDisplayName(),
+ inCallCall.getCallerDisplayNamePresentation(),
+ inCallCall.getAccount(),
+ inCallCall.getCapabilities(),
+ inCallCall.getDisconnectCauseCode(),
+ inCallCall.getDisconnectCauseMsg(),
+ inCallCall.getConnectTimeMillis(),
+ inCallCall.getGatewayInfo());
+ boolean detailsChanged = !Objects.equals(mDetails, details);
+ if (detailsChanged) {
+ mDetails = details;
+ }
+
+ boolean cannedTextResponsesChanged = false;
+ if (mCannedTextResponses == null && inCallCall.getCannedSmsResponses() != null
+ && !inCallCall.getCannedSmsResponses().isEmpty()) {
+ mCannedTextResponses = Collections.unmodifiableList(inCallCall.getCannedSmsResponses());
+ }
+
+ boolean callVideoProviderChanged = false;
+ try {
+ callVideoProviderChanged =
+ !Objects.equals(mCallVideoProvider, inCallCall.getCallVideoProvider());
+ if (callVideoProviderChanged) {
+ mCallVideoProvider = inCallCall.getCallVideoProvider();
+ }
+ } catch (RemoteException e) {
+ }
+
+ int state = stateFromInCallCallState(inCallCall.getState());
+ boolean stateChanged = mState != state;
+ if (stateChanged) {
+ mState = state;
+ }
+
+ if (inCallCall.getParentCallId() != null) {
+ mParent = mPhone.internalGetCallByTelecommId(inCallCall.getParentCallId());
+ }
+
+ mChildren.clear();
+ if (inCallCall.getChildCallIds() != null) {
+ for (int i = 0; i < inCallCall.getChildCallIds().size(); i++) {
+ mChildren.add(mPhone.internalGetCallByTelecommId(
+ inCallCall.getChildCallIds().get(i)));
+ }
+ }
+
+ // Now we fire updates, ensuring that any client who listens to any of these notifications
+ // gets the most up-to-date state.
+
+ if (stateChanged) {
+ fireStateChanged(mState);
+ }
+ if (detailsChanged) {
+ fireDetailsChanged(mDetails);
+ }
+ if (cannedTextResponsesChanged) {
+ fireCannedTextResponsesLoaded(mCannedTextResponses);
+ }
+ if (callVideoProviderChanged) {
+ fireCallVideoProviderChanged(mCallVideoProvider);
+ }
+
+ // If we have transitioned to DISCONNECTED, that means we need to notify clients and
+ // remove ourselves from the Phone. Note that we do this after completing all state updates
+ // so a client can cleanly transition all their UI to the state appropriate for a
+ // DISCONNECTED Call while still relying on the existence of that Call in the Phone's list.
+ if (mState == STATE_DISCONNECTED) {
+ fireCallDestroyed();
+ mPhone.internalRemoveCall(this);
+ }
+ }
+
+ /** {@hide} */
+ final void internalSetPostDial(String remaining) {
+ mRemainingPostDialSequence = remaining;
+ firePostDial(mRemainingPostDialSequence);
+ }
+
+ /** {@hide} */
+ final void internalSetPostDialWait(String remaining) {
+ mRemainingPostDialSequence = remaining;
+ firePostDialWait(mRemainingPostDialSequence);
+ }
+
+ private void fireStateChanged(int newState) {
+ Listener[] listeners = mListeners.toArray(new Listener[mListeners.size()]);
+ for (int i = 0; i < listeners.length; i++) {
+ listeners[i].onStateChanged(this, newState);
+ }
+ }
+
+ private void fireParentChanged(Call newParent) {
+ Listener[] listeners = mListeners.toArray(new Listener[mListeners.size()]);
+ for (int i = 0; i < listeners.length; i++) {
+ listeners[i].onParentChanged(this, newParent);
+ }
+ }
+
+ private void fireChildrenChanged(List<Call> children) {
+ Listener[] listeners = mListeners.toArray(new Listener[mListeners.size()]);
+ for (int i = 0; i < listeners.length; i++) {
+ listeners[i].onChildrenChanged(this, children);
+ }
+ }
+
+ private void fireDetailsChanged(Details details) {
+ Listener[] listeners = mListeners.toArray(new Listener[mListeners.size()]);
+ for (int i = 0; i < listeners.length; i++) {
+ listeners[i].onDetailsChanged(this, details);
+ }
+ }
+
+ private void fireCannedTextResponsesLoaded(List<String> cannedTextResponses) {
+ Listener[] listeners = mListeners.toArray(new Listener[mListeners.size()]);
+ for (int i = 0; i < listeners.length; i++) {
+ listeners[i].onCannedTextResponsesLoaded(this, cannedTextResponses);
+ }
+ }
+
+ private void fireCallVideoProviderChanged(RemoteCallVideoProvider callVideoProvider) {
+ Listener[] listeners = mListeners.toArray(new Listener[mListeners.size()]);
+ for (int i = 0; i < listeners.length; i++) {
+ listeners[i].onCallVideoProviderChanged(this, callVideoProvider);
+ }
+ }
+
+ private void firePostDial(String remainingPostDialSequence) {
+ Listener[] listeners = mListeners.toArray(new Listener[mListeners.size()]);
+ for (int i = 0; i < listeners.length; i++) {
+ listeners[i].onPostDial(this, remainingPostDialSequence);
+ }
+ }
+
+ private void firePostDialWait(String remainingPostDialSequence) {
+ Listener[] listeners = mListeners.toArray(new Listener[mListeners.size()]);
+ for (int i = 0; i < listeners.length; i++) {
+ listeners[i].onPostDialWait(this, remainingPostDialSequence);
+ }
+ }
+
+ private void fireCallDestroyed() {
+ Listener[] listeners = mListeners.toArray(new Listener[mListeners.size()]);
+ for (int i = 0; i < listeners.length; i++) {
+ listeners[i].onCallDestroyed(this);
+ }
+ }
+
+ private int stateFromInCallCallState(CallState inCallCallState) {
+ switch (inCallCallState) {
+ case NEW:
+ return STATE_NEW;
+ case DIALING:
+ return STATE_DIALING;
+ case RINGING:
+ return STATE_RINGING;
+ case ACTIVE:
+ return STATE_ACTIVE;
+ case ON_HOLD:
+ return STATE_HOLDING;
+ case DISCONNECTED:
+ return STATE_DISCONNECTED;
+ case ABORTED:
+ return STATE_DISCONNECTED;
+ default:
+ Log.wtf(this, "Unrecognized CallState %s", inCallCallState);
+ return STATE_NEW;
+ }
+ }
+}
diff --git a/telecomm/java/android/telecomm/CallState.java b/telecomm/java/android/telecomm/CallState.java
index 152c2023..a464da5 100644
--- a/telecomm/java/android/telecomm/CallState.java
+++ b/telecomm/java/android/telecomm/CallState.java
@@ -48,22 +48,6 @@
RINGING,
/**
- * Indicates that the call is active but in a "post-dial" state where Telecomm is now sending
- * some dual-tone multi-frequency signaling (DTMF) tones appended to the dialed number. Normal
- * transitions are to {@link #POST_DIAL_WAIT} when the post-dial string requires user
- * confirmation to proceed, {@link #ACTIVE} when the post-dial tones are completed, or
- * {@link #DISCONNECTED}.
- */
- POST_DIAL,
-
- /**
- * Indicates that the call was in the {@link #POST_DIAL} state but is now waiting for user
- * confirmation before the remaining digits can be sent. Normal transitions are to
- * {@link #POST_DIAL} when the user asks Telecomm to proceed with the post-dial sequence.
- */
- POST_DIAL_WAIT,
-
- /**
* Indicates that a call is currently connected to another party and a communication channel is
* open between them. The normal transition to this state is by the user answering a
* {@link #DIALING} call or a {@link #RINGING} call being answered by the other party.
diff --git a/telecomm/java/android/telecomm/CallVideoClient.java b/telecomm/java/android/telecomm/CallVideoClient.java
index 76b28fa..fb970dc 100644
--- a/telecomm/java/android/telecomm/CallVideoClient.java
+++ b/telecomm/java/android/telecomm/CallVideoClient.java
@@ -241,6 +241,7 @@
*
* @param callCameraCapabilities The changed camera capabilities.
*/
- public abstract void onHandleCameraCapabilitiesChange(CallCameraCapabilities callCameraCapabilities);
+ public abstract void onHandleCameraCapabilitiesChange(
+ CallCameraCapabilities callCameraCapabilities);
}
diff --git a/telecomm/java/android/telecomm/InCallAdapter.java b/telecomm/java/android/telecomm/InCallAdapter.java
index d8293a5..66cf1df 100644
--- a/telecomm/java/android/telecomm/InCallAdapter.java
+++ b/telecomm/java/android/telecomm/InCallAdapter.java
@@ -24,7 +24,7 @@
* Receives commands from {@link InCallService} implementations which should be executed by
* Telecomm. When Telecomm binds to a {@link InCallService}, an instance of this class is given to
* the in-call service through which it can manipulate live (active, dialing, ringing) calls. When
- * the in-call service is notified of new calls ({@link InCallService#addCall}), it can use the
+ * the in-call service is notified of new calls, it can use the
* given call IDs to execute commands such as {@link #answerCall} for incoming calls or
* {@link #disconnectCall} for active calls the user would like to end. Some commands are only
* appropriate for calls in certain states; please consult each method for such limitations.
@@ -167,16 +167,15 @@
* A post-dial DTMF string is a string of digits entered after a phone number, when dialed,
* that are immediately sent as DTMF tones to the recipient as soon as the connection is made.
* While these tones are playing, Telecomm will notify the {@link InCallService} that the call
- * is in the {@link InCallService#setPostDial(String,String)} state.
+ * is in the post dial state.
*
* If the DTMF string contains a {@link TelecommConstants#DTMF_CHARACTER_PAUSE} symbol, Telecomm
* will temporarily pause playing the tones for a pre-defined period of time.
*
* If the DTMF string contains a {@link TelecommConstants#DTMF_CHARACTER_WAIT} symbol, Telecomm
* will pause playing the tones and notify the {@link InCallService} that the call is in the
- * {@link InCallService#setPostDialWait(String,String)} state. When the user decides to continue
- * the postdial sequence, the {@link InCallService} should invoke the
- * {@link #postDialContinue(String,boolean)} method.
+ * post dial wait state. When the user decides to continue the postdial sequence, the
+ * {@link InCallService} should invoke the {@link #postDialContinue(String,boolean)} method.
*
* @param callId The unique ID of the call for which postdial string playing should continue.
* @param proceed Whether or not to continue with the post-dial sequence.
diff --git a/telecomm/java/android/telecomm/InCallService.java b/telecomm/java/android/telecomm/InCallService.java
index 31291fb..028b6e4 100644
--- a/telecomm/java/android/telecomm/InCallService.java
+++ b/telecomm/java/android/telecomm/InCallService.java
@@ -16,8 +16,6 @@
package android.telecomm;
-import android.app.Service;
-import android.content.Intent;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
@@ -31,11 +29,10 @@
* This service is implemented by any app that wishes to provide the user-interface for managing
* phone calls. Telecomm binds to this service while there exists a live (active or incoming)
* call, and uses it to notify the in-call app of any live and and recently disconnected calls.
- * TODO(santoscordon): Needs more/better description of lifecycle once the interface is better
- * defined.
+ *
* TODO(santoscordon): What happens if two or more apps on a given device implement this interface?
*/
-public abstract class InCallService extends Service {
+public abstract class InCallService {
private static final int MSG_SET_IN_CALL_ADAPTER = 1;
private static final int MSG_ADD_CALL = 2;
private static final int MSG_UPDATE_CALL = 3;
@@ -50,21 +47,21 @@
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_SET_IN_CALL_ADAPTER:
- mAdapter = new InCallAdapter((IInCallAdapter) msg.obj);
- onAdapterAttached(mAdapter);
+ mPhone = new Phone(new InCallAdapter((IInCallAdapter) msg.obj));
+ onPhoneCreated(mPhone);
break;
case MSG_ADD_CALL:
- addCall((InCallCall) msg.obj);
+ mPhone.internalAddCall((InCallCall) msg.obj);
break;
case MSG_UPDATE_CALL:
- updateCall((InCallCall) msg.obj);
+ mPhone.internalUpdateCall((InCallCall) msg.obj);
break;
- case MSG_SET_POST_DIAL: {
+ case MSG_SET_POST_DIAL: {
SomeArgs args = (SomeArgs) msg.obj;
try {
String callId = (String) args.arg1;
String remaining = (String) args.arg2;
- setPostDial(callId, remaining);
+ mPhone.internalSetPostDial(callId, remaining);
} finally {
args.recycle();
}
@@ -75,17 +72,17 @@
try {
String callId = (String) args.arg1;
String remaining = (String) args.arg2;
- setPostDialWait(callId, remaining);
+ mPhone.internalSetPostDialWait(callId, remaining);
} finally {
args.recycle();
}
break;
}
case MSG_ON_AUDIO_STATE_CHANGED:
- onAudioStateChanged((CallAudioState) msg.obj);
+ mPhone.internalAudioStateChanged((CallAudioState) msg.obj);
break;
case MSG_BRING_TO_FOREGROUND:
- bringToForeground(msg.arg1 == 1);
+ mPhone.internalBringToForeground(msg.arg1 == 1);
break;
default:
break;
@@ -142,85 +139,41 @@
}
}
- private final InCallServiceBinder mBinder;
+ private Phone mPhone;
- private InCallAdapter mAdapter;
+ protected InCallService() {}
- protected InCallService() {
- mBinder = new InCallServiceBinder();
- }
-
- @Override
- public final IBinder onBind(Intent intent) {
- return mBinder;
+ public final IBinder getBinder() {
+ return new InCallServiceBinder();
}
/**
- * @return The attached {@link InCallAdapter} if attached, or null otherwise.
+ * Obtain the {@code Phone} associated with this {@code InCallService}.
+ *
+ * @return The {@code Phone} object associated with this {@code InCallService}, or {@code null}
+ * if the {@code InCallService} is not in a state where it has an associated {@code Phone}.
*/
- protected final InCallAdapter getAdapter() {
- return mAdapter;
+ public Phone getPhone() {
+ return mPhone;
}
/**
- * Lifecycle callback which is called when this {@link InCallService} has been attached
- * to a {@link InCallAdapter}, indicating {@link #getAdapter()} is now safe to use.
+ * Invoked when the {@code Phone} has been created. This is a signal to the in-call experience
+ * to start displaying in-call information to the user. Each instance of {@code InCallService}
+ * will have only one {@code Phone}, and this method will be called exactly once in the
+ * lifetime of the {@code InCallService}.
*
- * @param adapter The adapter now attached to this in-call service.
+ * @param phone The {@code Phone} object associated with this {@code InCallService}.
*/
- protected void onAdapterAttached(InCallAdapter adapter) {
- }
+ public void onPhoneCreated(Phone phone) { }
/**
- * Indicates to the in-call app that a new call has been created and an appropriate
- * user-interface should be built and shown to notify the user.
+ * Invoked when a {@code Phone} has been destroyed. This is a signal to the in-call experience
+ * to stop displaying in-call information to the user. This method will be called exactly once
+ * in the lifetime of the {@code InCallService}, and it will always be called after a previous
+ * call to {@link #onPhoneCreated(Phone)}.
*
- * @param call Information about the new call.
+ * @param phone The {@code Phone} object associated with this {@code InCallService}.
*/
- protected abstract void addCall(InCallCall call);
-
- /**
- * Call when information about a call has changed.
- *
- * @param call Information about the new call.
- */
- protected abstract void updateCall(InCallCall call);
-
- /**
- * Indicates to the in-call app that the specified call is active but in a "post-dial" state
- * where Telecomm is now sending some dual-tone multi-frequency signaling (DTMF) tones appended
- * to the dialed number. Normal transitions are to {@link #setPostDialWait(String,String)} when
- * the post-dial string requires user confirmation to proceed, and {@link CallState#ACTIVE} when
- * the post-dial tones are completed.
- *
- * @param callId The identifier of the call changing state.
- * @param remaining The remaining postdial string to be dialed.
- */
- protected abstract void setPostDial(String callId, String remaining);
-
- /**
- * Indicates to the in-call app that the specified call was in the
- * {@link #setPostDial(String,String)} state but is now waiting for user confirmation before the
- * remaining digits can be sent. Normal transitions are to {@link #setPostDial(String,String)}
- * when the user asks Telecomm to proceed with the post-dial sequence and the in-call app
- * informs Telecomm of this by invoking {@link InCallAdapter#postDialContinue(String,boolean)}.
- *
- * @param callId The identifier of the call changing state.
- * @param remaining The remaining postdial string to be dialed.
- */
- protected abstract void setPostDialWait(String callId, String remaining);
-
- /**
- * Called when the audio state changes.
- *
- * @param audioState The new {@link CallAudioState}.
- */
- protected abstract void onAudioStateChanged(CallAudioState audioState);
-
- /**
- * Brings the in-call screen to the foreground.
- *
- * @param showDialpad If true, put up the dialpad when the screen is shown.
- */
- protected abstract void bringToForeground(boolean showDialpad);
+ public void onPhoneDestroyed(Phone phone) { }
}
diff --git a/telecomm/java/android/telecomm/Phone.java b/telecomm/java/android/telecomm/Phone.java
new file mode 100644
index 0000000..9c97b45
--- /dev/null
+++ b/telecomm/java/android/telecomm/Phone.java
@@ -0,0 +1,255 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telecomm;
+
+import android.util.ArrayMap;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+
+/**
+ * A unified virtual device providing a means of voice (and other) communication on a device.
+ */
+public final class Phone {
+
+ public abstract static class Listener {
+ /**
+ * Called when the audio state changes.
+ *
+ * @param phone The {@code Phone} calling this method.
+ * @param audioState The new {@link CallAudioState}.
+ */
+ public void onAudioStateChanged(Phone phone, CallAudioState audioState) { }
+
+ /**
+ * Called to bring the in-call screen to the foreground. The in-call experience should
+ * respond immediately by coming to the foreground to inform the user of the state of
+ * ongoing {@code Call}s.
+ *
+ * @param phone The {@code Phone} calling this method.
+ * @param showDialpad If true, put up the dialpad when the screen is shown.
+ */
+ public void onBringToForeground(Phone phone, boolean showDialpad) { }
+
+ /**
+ * Called when a {@code Call} has been added to this in-call session. The in-call user
+ * experience should add necessary state listeners to the specified {@code Call} and
+ * immediately start to show the user information about the existence
+ * and nature of this {@code Call}. Subsequent invocations of {@link #getCalls()} will
+ * include this {@code Call}.
+ *
+ * @param phone The {@code Phone} calling this method.
+ * @param call A newly added {@code Call}.
+ */
+ public void onCallAdded(Phone phone, Call call) { }
+
+ /**
+ * Called when a {@code Call} has been removed from this in-call session. The in-call user
+ * experience should remove any state listeners from the specified {@code Call} and
+ * immediately stop displaying any information about this {@code Call}.
+ * Subsequent invocations of {@link #getCalls()} will no longer include this {@code Call}.
+ *
+ * @param phone The {@code Phone} calling this method.
+ * @param call A newly removed {@code Call}.
+ */
+ public void onCallRemoved(Phone phone, Call call) { }
+ }
+
+ // A Map allows us to track each Call by its Telecomm-specified call ID
+ private final Map<String, Call> mCallByTelecommCallId = new ArrayMap<>();
+
+ // A List allows us to keep the Calls in a stable iteration order so that casually developed
+ // user interface components do not incur any spurious jank
+ private final List<Call> mCalls = new ArrayList<>();
+
+ // An unmodifiable view of the above List can be safely shared with subclass implementations
+ private final List<Call> mUnmodifiableCalls = Collections.unmodifiableList(mCalls);
+
+ private final InCallAdapter mInCallAdapter;
+
+ private CallAudioState mAudioState;
+
+ private final List<Listener> mListeners = new ArrayList<>();
+
+ /** {@hide} */
+ Phone(InCallAdapter adapter) {
+ mInCallAdapter = adapter;
+ }
+
+ /** {@hide} */
+ final void internalAddCall(InCallCall inCallCall) {
+ Call call = new Call(this, inCallCall.getId(), mInCallAdapter);
+ mCallByTelecommCallId.put(inCallCall.getId(), call);
+ mCalls.add(call);
+ checkCallTree(inCallCall);
+ call.internalUpdate(inCallCall);
+ fireCallAdded(call);
+ }
+
+ /** {@hide} */
+ final void internalRemoveCall(Call call) {
+ mCallByTelecommCallId.remove(call.internalGetCallId());
+ mCalls.remove(call);
+ fireCallRemoved(call);
+ }
+
+ /** {@hide} */
+ final void internalUpdateCall(InCallCall inCallCall) {
+ Call call = mCallByTelecommCallId.get(inCallCall.getId());
+ if (call != null) {
+ checkCallTree(inCallCall);
+ call.internalUpdate(inCallCall);
+ }
+ }
+
+ /** {@hide} */
+ final void internalSetPostDial(String callId, String remaining) {
+ Call call = mCallByTelecommCallId.get(callId);
+ if (call != null) {
+ call.internalSetPostDial(remaining);
+ }
+ }
+
+ /** {@hide} */
+ final void internalSetPostDialWait(String callId, String remaining) {
+ Call call = mCallByTelecommCallId.get(callId);
+ if (call != null) {
+ call.internalSetPostDialWait(remaining);
+ }
+ }
+
+ /** {@hide} */
+ final void internalAudioStateChanged(CallAudioState callAudioState) {
+ if (!Objects.equals(mAudioState, callAudioState)) {
+ mAudioState = callAudioState;
+ fireAudioStateChanged(callAudioState);
+ }
+ }
+
+ /** {@hide} */
+ final Call internalGetCallByTelecommId(String telecommId) {
+ return mCallByTelecommCallId.get(telecommId);
+ }
+
+ /** {@hide} */
+ final void internalBringToForeground(boolean showDialpad) {
+ fireBringToForeground(showDialpad);
+ }
+
+ /**
+ * Adds a listener to this {@code Phone}.
+ *
+ * @param listener A {@code Listener} object.
+ */
+ public final void addListener(Listener listener) {
+ mListeners.add(listener);
+ }
+
+ /**
+ * Removes a listener from this {@code Phone}.
+ *
+ * @param listener A {@code Listener} object.
+ */
+ public final void removeListener(Listener listener) {
+ mListeners.remove(listener);
+ }
+
+ /**
+ * Obtains the current list of {@code Call}s to be displayed by this in-call experience.
+ *
+ * @return A list of the relevant {@code Call}s.
+ */
+ public final List<Call> getCalls() {
+ return mUnmodifiableCalls;
+ }
+
+ /**
+ * Sets the microphone mute state. When this request is honored, there will be change to
+ * the {@link #getAudioState()}.
+ *
+ * @param state {@code true} if the microphone should be muted; {@code false} otherwise.
+ */
+ public final void setMuted(boolean state) {
+ mInCallAdapter.mute(state);
+ }
+
+ /**
+ * Sets the audio route (speaker, bluetooth, etc...). When this request is honored, there will
+ * be change to the {@link #getAudioState()}.
+ *
+ * @param route The audio route to use.
+ */
+ public final void setAudioRoute(int route) {
+ mInCallAdapter.setAudioRoute(route);
+ }
+
+ /**
+ * Obtains the current phone call audio state of the {@code Phone}.
+ *
+ * @return An object encapsulating the audio state.
+ */
+ public final CallAudioState getAudioState() {
+ return mAudioState;
+ }
+
+ private void fireCallAdded(Call call) {
+ Listener[] listeners = mListeners.toArray(new Listener[mListeners.size()]);
+ for (int i = 0; i < listeners.length; i++) {
+ listeners[i].onCallAdded(this, call);
+ }
+ }
+
+ private void fireCallRemoved(Call call) {
+ Listener[] listeners = mListeners.toArray(new Listener[mListeners.size()]);
+ for (int i = 0; i < listeners.length; i++) {
+ listeners[i].onCallRemoved(this, call);
+ }
+ }
+
+ private void fireAudioStateChanged(CallAudioState audioState) {
+ Listener[] listeners = mListeners.toArray(new Listener[mListeners.size()]);
+ for (int i = 0; i < listeners.length; i++) {
+ listeners[i].onAudioStateChanged(this, audioState);
+ }
+ }
+
+ private void fireBringToForeground(boolean showDialpad) {
+ Listener[] listeners = mListeners.toArray(new Listener[mListeners.size()]);
+ for (int i = 0; i < listeners.length; i++) {
+ listeners[i].onBringToForeground(this, showDialpad);
+ }
+ }
+
+ private void checkCallTree(InCallCall inCallCall) {
+ if (inCallCall.getParentCallId() != null &&
+ !mCallByTelecommCallId.containsKey(inCallCall.getParentCallId())) {
+ Log.wtf(this, "InCallCall %s has nonexistent parent %s",
+ inCallCall.getId(), inCallCall.getParentCallId());
+ }
+ if (inCallCall.getChildCallIds() != null) {
+ for (int i = 0; i < inCallCall.getChildCallIds().size(); i++) {
+ if (!mCallByTelecommCallId.containsKey(inCallCall.getChildCallIds().get(i))) {
+ Log.wtf(this, "InCallCall %s has nonexistent child %s",
+ inCallCall.getId(), inCallCall.getChildCallIds().get(i));
+ }
+ }
+ }
+ }
+}
diff --git a/telecomm/java/android/telecomm/PhoneAccount.java b/telecomm/java/android/telecomm/PhoneAccount.java
index 4e440d8..c1eec83 100644
--- a/telecomm/java/android/telecomm/PhoneAccount.java
+++ b/telecomm/java/android/telecomm/PhoneAccount.java
@@ -17,52 +17,64 @@
package android.telecomm;
import android.content.ComponentName;
-import android.content.Context;
-import android.content.pm.PackageManager;
-import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Parcel;
import android.os.Parcelable;
-import android.telephony.Rlog;
-import android.util.DisplayMetrics;
-import android.util.Log;
-import java.util.MissingResourceException;
import java.util.Objects;
/**
* Represents a distinct account, line of service or call placement method that
* the system can use to place phone calls.
*/
-public final class PhoneAccount implements Parcelable {
+public class PhoneAccount implements Parcelable {
- private static final int NO_DENSITY = -1;
- private static final String LOG_TAG = "Account";
+ /**
+ * Flag indicating that this {@code PhoneAccount} can act as a call manager for traditional
+ * SIM-based telephony calls. The {@link ConnectionService} associated with this phone-account
+ * will be allowed to manage SIM-based phone calls including using its own proprietary
+ * phone-call implementation (like VoIP calling) to make calls instead of the telephony stack.
+ * When a user opts to place a call using the SIM-based telephony stack, the connection-service
+ * associated with this phone-account will be attempted first if the user has explicitly
+ * selected it to be used as the default call-manager.
+ * <p>
+ * See {@link #getCapabilities}
+ */
+ public static final int CAPABILITY_SIM_CALL_MANAGER = 0x1;
- private final ComponentName mComponentName;
- private final String mId;
- private final Uri mHandle;
- private final String mLabel;
- private final String mShortDescription;
- private final boolean mIsEnabled;
- private final boolean mIsSystemDefault;
+ /**
+ * Flag indicating that this {@code PhoneAccount} can make phone calls in place of traditional
+ * SIM-based telephony calls. This account will be treated as a distinct method for placing
+ * calls alongside the traditional SIM-based telephony stack. This flag is distinct from
+ * {@link #CAPABILITY_SIM_CALL_MANAGER} in that it is not allowed to manage calls from or use
+ * the built-in telephony stack to place its calls.
+ * <p>
+ * See {@link #getCapabilities}
+ */
+ public static final int CAPABILITY_CALL_PROVIDER = 0x2;
+
+ /**
+ * Flag indicating that this {@code PhoneAccount} represents a built-in PSTN SIM subscription.
+ * <p>
+ * Only the android framework can set this capability on a phone-account.
+ */
+ public static final int CAPABILITY_SIM_SUBSCRIPTION = 0x4;
+
+ private ComponentName mComponentName;
+ private String mId;
+ private Uri mHandle;
+ private int mCapabilities;
public PhoneAccount(
ComponentName componentName,
String id,
Uri handle,
- String label,
- String shortDescription,
- boolean isEnabled,
- boolean isSystemDefault) {
+ int capabilities) {
mComponentName = componentName;
mId = id;
mHandle = handle;
- mLabel = label;
- mShortDescription = shortDescription;
- mIsSystemDefault = isSystemDefault;
- mIsEnabled = isEnabled;
+ mCapabilities = capabilities;
}
/**
@@ -87,8 +99,8 @@
/**
* The handle (e.g., a phone number) associated with this {@code PhoneAccount}. This represents
- * the destination from which outgoing calls using this {@code PhoneAccount} will appear to come
- * from, if applicable, and the destination to which incoming calls using this
+ * the destination from which outgoing calls using this {@code PhoneAccount} will appear to
+ * come, if applicable, and the destination to which incoming calls using this
* {@code PhoneAccount} may be addressed.
*
* @return A handle expressed as a {@code Uri}, for example, a phone number.
@@ -98,76 +110,23 @@
}
/**
- * A short string label describing this {@code PhoneAccount}.
+ * The capabilities of this {@code PhoneAccount}.
*
- * @param context The invoking {@code Context}, used for retrieving resources.
- *
- * TODO(ihab): If don't need context, remove param
- *
- * @return A label for this {@code PhoneAccount}.
+ * @return A bit field of flags describing this {@code PhoneAccount}'s capabilities.
*/
- public String getLabel(Context context) {
- return mLabel;
+ public int getCapabilities() {
+ return mCapabilities;
}
- /**
- * A short paragraph describing this {@code PhoneAccount}.
- *
- * @param context The invoking {@code Context}, used for retrieving resources.
- *
- * TODO(ihab): If don't need context, remove param
- *
- * @return A description for this {@code PhoneAccount}.
- */
- public String getShortDescription(Context context) {
- return mShortDescription;
+ @Override
+ public int hashCode() {
+ return Objects.hashCode(mComponentName) + Objects.hashCode(mId) +
+ Objects.hashCode(mHandle) + mCapabilities;
}
- // TODO(ihab): Representation of the icons
//
- // Refactor to pass a Bitmap (scale it at runtime), but if they don't pass one, fall
- // back to the android:icon attr in the manifest (<service /> first, <application /> second)
-
- /**
- * An icon to represent this {@code PhoneAccount} in a user interface.
- *
- * @param context The invoking {@code Context}, used for retrieving resources.
- *
- * @return An icon for this {@code PhoneAccount}.
- */
- public Drawable getIcon(Context context) {
- return null; // TODO(ihab): See above
- }
-
- /**
- * An icon to represent this {@code PhoneAccount} in a user interface.
- *
- * @param context The invoking {@code Context}, used for retrieving resources.
- * @param density A display density from {@link DisplayMetrics}.
- *
- * @return An icon for this {@code PhoneAccount}.
- */
- public Drawable getIcon(Context context, int density) {
- return null; // TODO(ihab): See above
- }
-
- /**
- * Whether this {@code PhoneAccount} is enabled for use.
- *
- * @return {@code true} if this {@code PhoneAccount} is enabled.
- */
- public boolean isEnabled() {
- return mIsEnabled;
- }
-
- /**
- * Whether this {@code PhoneAccount} is the system default.
- *
- * @return {@code true} if this {@code PhoneAccount} is the system default.
- */
- public boolean isSystemDefault() {
- return mIsSystemDefault;
- }
+ // Parcelable implementation.
+ //
@Override
public int describeContents() {
@@ -179,18 +138,16 @@
out.writeParcelable(mComponentName, flags);
out.writeString(mId);
out.writeString(mHandle != null ? mHandle.toString() : "");
- out.writeString(mLabel);
- out.writeString(mShortDescription);
- out.writeInt(mIsEnabled ? 1 : 0);
- out.writeInt(mIsSystemDefault ? 1 : 0);
+ out.writeInt(mCapabilities);
}
- public static final Creator<PhoneAccount> CREATOR
- = new Creator<PhoneAccount>() {
+ public static final Creator<PhoneAccount> CREATOR = new Creator<PhoneAccount>() {
+ @Override
public PhoneAccount createFromParcel(Parcel in) {
return new PhoneAccount(in);
}
+ @Override
public PhoneAccount[] newArray(int size) {
return new PhoneAccount[size];
}
@@ -201,22 +158,6 @@
mId = in.readString();
String uriString = in.readString();
mHandle = uriString.length() > 0 ? Uri.parse(uriString) : null;
- mLabel = in.readString();
- mShortDescription = in.readString();
- mIsEnabled = in.readInt() == 1;
- mIsSystemDefault = in.readInt() == 1;
- }
-
- @Override
- public boolean equals(Object other) {
- return
- other instanceof PhoneAccount &&
- Objects.equals(mComponentName, ((PhoneAccount) other).mComponentName) &&
- Objects.equals(mId, ((PhoneAccount) other).mId);
- }
-
- @Override
- public int hashCode() {
- return Objects.hashCode(mComponentName) + Objects.hashCode(mId);
+ mCapabilities = in.readInt();
}
}
diff --git a/telecomm/java/android/telecomm/PhoneAccountMetadata.aidl b/telecomm/java/android/telecomm/PhoneAccountMetadata.aidl
new file mode 100644
index 0000000..55b8900
--- /dev/null
+++ b/telecomm/java/android/telecomm/PhoneAccountMetadata.aidl
@@ -0,0 +1,22 @@
+/*
+ * 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.telecomm;
+
+/**
+ * {@hide}
+ */
+parcelable PhoneAccountMetadata;
diff --git a/telecomm/java/android/telecomm/PhoneAccountMetadata.java b/telecomm/java/android/telecomm/PhoneAccountMetadata.java
new file mode 100644
index 0000000..20a4d47
--- /dev/null
+++ b/telecomm/java/android/telecomm/PhoneAccountMetadata.java
@@ -0,0 +1,140 @@
+/*
+ * 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.telecomm;
+
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.graphics.drawable.Drawable;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.io.IOException;
+import java.io.ObjectStreamException;
+import java.io.Serializable;
+import java.util.MissingResourceException;
+
+/**
+ * Provides user interface description information for a {@code PhoneAccount}.
+ */
+public class PhoneAccountMetadata implements Parcelable {
+ private PhoneAccount mAccount;
+ private int mIconResId;
+ private String mLabel;
+ private String mShortDescription;
+
+ public PhoneAccountMetadata(
+ PhoneAccount account,
+ int iconResId,
+ String label,
+ String shortDescription) {
+ mAccount = account;
+ mIconResId = iconResId;
+ mLabel = label;
+ mShortDescription = shortDescription;
+ }
+
+ /**
+ * The {@code PhoneAccount} to which this metadata pertains.
+ *
+ * @return A {@code PhoneAccount}.
+ */
+ public PhoneAccount getAccount() {
+ return mAccount;
+ }
+
+ /**
+ * A short string label describing a {@code PhoneAccount}.
+ *
+ * @return A label for this {@code PhoneAccount}.
+ */
+ public String getLabel() {
+ return mLabel;
+ }
+
+ /**
+ * A short paragraph describing a {@code PhoneAccount}.
+ *
+ * @return A description for this {@code PhoneAccount}.
+ */
+ public String getShortDescription() {
+ return mShortDescription;
+ }
+
+ /**
+ * An icon to represent this {@code PhoneAccount} in a user interface.
+ *
+ * @return An icon for this {@code PhoneAccount}.
+ */
+ public Drawable getIcon(Context context) {
+ return getIcon(context, mIconResId);
+ }
+
+ private Drawable getIcon(Context context, int resId) {
+ Context packageContext;
+ try {
+ packageContext = context.createPackageContext(
+ mAccount.getComponentName().getPackageName(), 0);
+ } catch (PackageManager.NameNotFoundException e) {
+ Log.w(this, "Cannot find package %s", mAccount.getComponentName().getPackageName());
+ return null;
+ }
+ try {
+ return packageContext.getResources().getDrawable(resId);
+ } catch (MissingResourceException e) {
+ Log.e(this, e, "Cannot find icon %d in package %s",
+ resId, mAccount.getComponentName().getPackageName());
+ return null;
+ }
+ }
+
+ //
+ // Parcelable implementation
+ //
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeParcelable(mAccount, 0);
+ out.writeInt(mIconResId);
+ out.writeString(mLabel);
+ out.writeString(mShortDescription);
+ }
+
+ public static final Creator<PhoneAccountMetadata> CREATOR
+ = new Creator<PhoneAccountMetadata>() {
+ @Override
+ public PhoneAccountMetadata createFromParcel(Parcel in) {
+ return new PhoneAccountMetadata(in);
+ }
+
+ @Override
+ public PhoneAccountMetadata[] newArray(int size) {
+ return new PhoneAccountMetadata[size];
+ }
+ };
+
+ private PhoneAccountMetadata(Parcel in) {
+ mAccount = in.readParcelable(getClass().getClassLoader());
+ mIconResId = in.readInt();
+ mLabel = in.readString();
+ mShortDescription = in.readString();
+ }
+}
diff --git a/telecomm/java/android/telecomm/RemoteCallVideoProvider.java b/telecomm/java/android/telecomm/RemoteCallVideoProvider.java
index 856d321..a49076a 100644
--- a/telecomm/java/android/telecomm/RemoteCallVideoProvider.java
+++ b/telecomm/java/android/telecomm/RemoteCallVideoProvider.java
@@ -20,63 +20,92 @@
import android.os.RemoteException;
import android.view.Surface;
-import com.android.internal.telecomm.ICallVideoClient;
import com.android.internal.telecomm.ICallVideoProvider;
-public class RemoteCallVideoProvider implements IBinder.DeathRecipient {
+public class RemoteCallVideoProvider {
private final ICallVideoProvider mCallVideoProvider;
+ private IBinder.DeathRecipient mDeathRecipient = new IBinder.DeathRecipient() {
+ @Override
+ public void binderDied() {
+ mCallVideoProvider.asBinder().unlinkToDeath(this, 0);
+ }
+ };
+
+ /** {@hide} */
RemoteCallVideoProvider(ICallVideoProvider callVideoProvider) throws RemoteException {
mCallVideoProvider = callVideoProvider;
- mCallVideoProvider.asBinder().linkToDeath(this, 0);
+ mCallVideoProvider.asBinder().linkToDeath(mDeathRecipient, 0);
}
- @Override
- public void binderDied() {
- mCallVideoProvider.asBinder().unlinkToDeath(this, 0);
- }
-
- public void setCallVideoClient(CallVideoClient callVideoClient) throws RemoteException {
- mCallVideoProvider.setCallVideoClient(callVideoClient.getBinder());
+ public void setCallVideoClient(CallVideoClient callVideoClient) {
+ try {
+ mCallVideoProvider.setCallVideoClient(callVideoClient.getBinder());
+ } catch (RemoteException e) {
+ }
}
public void setCamera(String cameraId) throws RemoteException {
mCallVideoProvider.setCamera(cameraId);
}
- public void setPreviewSurface(Surface surface) throws RemoteException {
- mCallVideoProvider.setPreviewSurface(surface);
+ public void setPreviewSurface(Surface surface) {
+ try {
+ mCallVideoProvider.setPreviewSurface(surface);
+ } catch (RemoteException e) {
+ }
}
- public void setDisplaySurface(Surface surface) throws RemoteException {
- mCallVideoProvider.setDisplaySurface(surface);
+ public void setDisplaySurface(Surface surface) {
+ try {
+ mCallVideoProvider.setDisplaySurface(surface);
+ } catch (RemoteException e) {
+ }
}
- public void setDeviceOrientation(int rotation) throws RemoteException {
- mCallVideoProvider.setDeviceOrientation(rotation);
+ public void setDeviceOrientation(int rotation) {
+ try {
+ mCallVideoProvider.setDeviceOrientation(rotation);
+ } catch (RemoteException e) {
+ }
}
public void setZoom(float value) throws RemoteException {
mCallVideoProvider.setZoom(value);
}
- public void sendSessionModifyRequest(VideoCallProfile requestProfile) throws RemoteException {
- mCallVideoProvider.sendSessionModifyRequest(requestProfile);
+ public void sendSessionModifyRequest(VideoCallProfile requestProfile) {
+ try {
+ mCallVideoProvider.sendSessionModifyRequest(requestProfile);
+ } catch (RemoteException e) {
+ }
}
- public void sendSessionModifyResponse(VideoCallProfile responseProfile) throws RemoteException {
- mCallVideoProvider.sendSessionModifyResponse(responseProfile);
+ public void sendSessionModifyResponse(VideoCallProfile responseProfile) {
+ try {
+ mCallVideoProvider.sendSessionModifyResponse(responseProfile);
+ } catch (RemoteException e) {
+ }
}
- public void requestCameraCapabilities() throws RemoteException {
- mCallVideoProvider.requestCameraCapabilities();
+ public void requestCameraCapabilities() {
+ try {
+ mCallVideoProvider.requestCameraCapabilities();
+ } catch (RemoteException e) {
+ }
}
- public void requestCallDataUsage() throws RemoteException {
- mCallVideoProvider.requestCallDataUsage();
+ public void requestCallDataUsage() {
+ try {
+ mCallVideoProvider.requestCallDataUsage();
+ } catch (RemoteException e) {
+ }
}
- public void setPauseImage(String uri) throws RemoteException {
- mCallVideoProvider.setPauseImage(uri);
+ public void setPauseImage(String uri) {
+ try {
+ mCallVideoProvider.setPauseImage(uri);
+ } catch (RemoteException e) {
+ }
}
}
\ No newline at end of file
diff --git a/telecomm/java/android/telecomm/RemoteConnectionService.java b/telecomm/java/android/telecomm/RemoteConnectionService.java
index a436af2..430133c 100644
--- a/telecomm/java/android/telecomm/RemoteConnectionService.java
+++ b/telecomm/java/android/telecomm/RemoteConnectionService.java
@@ -266,10 +266,7 @@
mComponentName,
null /* id */,
null /* handle */,
- "" /* label */,
- "" /* shortDescription */,
- true /* isEnabled */,
- false /* isSystemDefault */));
+ 0 /* capabilities */));
return accounts;
}
diff --git a/telecomm/java/android/telecomm/TelecommManager.java b/telecomm/java/android/telecomm/TelecommManager.java
index 1bb18f2..fcd2eba 100644
--- a/telecomm/java/android/telecomm/TelecommManager.java
+++ b/telecomm/java/android/telecomm/TelecommManager.java
@@ -23,31 +23,30 @@
import com.android.internal.telecomm.ITelecommService;
+import java.util.List;
+
/**
* Provides access to Telecomm-related functionality.
* TODO(santoscordon): Move this all into PhoneManager.
* @hide
*/
public class TelecommManager {
+
+ /**
+ * The extra used with an {@link android.content.Intent#ACTION_CALL} or
+ * {@link android.content.Intent#ACTION_DIAL} {@code Intent} to specify a {@link PhoneAccount}
+ * to use when making the call.
+ *
+ * <p class="note">
+ * Retrieve with
+ * {@link android.content.Intent#getParcelableExtra(String)}.
+ */
+ public static final String EXTRA_PHONE_ACCOUNT = "account";
+
private static final String TAG = "TelecommManager";
private static final String TELECOMM_SERVICE_NAME = "telecomm";
private final Context mContext;
- private final ITelecommService mService;
-
- /**
- * @hide
- */
- public TelecommManager(Context context, ITelecommService service) {
- Context appContext = context.getApplicationContext();
- if (appContext != null) {
- mContext = appContext;
- } else {
- mContext = context;
- }
-
- mService = service;
- }
/**
* @hide
@@ -59,6 +58,103 @@
/**
* @hide
*/
+ public TelecommManager(Context context) {
+ Context appContext = context.getApplicationContext();
+ if (appContext != null) {
+ mContext = appContext;
+ } else {
+ mContext = context;
+ }
+ }
+
+ /**
+ * Return a list of {@link PhoneAccount}s which can be used to make and receive phone calls.
+ *
+ * @see #EXTRA_PHONE_ACCOUNT
+ * @return A list of {@code PhoneAccount} objects.
+ */
+ public List<PhoneAccount> getEnabledPhoneAccounts() {
+ try {
+ if (isServiceConnected()) {
+ return getTelecommService().getEnabledPhoneAccounts();
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling ITelecommService#getEnabledPhoneAccounts", e);
+ }
+ return null;
+ }
+
+ /**
+ * Return the metadata for a specified {@link PhoneAccount}. Metadata includes resources which
+ * can be used in a user interface.
+ *
+ * @param account The {@link PhoneAccount}.
+ *
+ * @return The metadata for the account.
+ */
+ public PhoneAccountMetadata getPhoneAccountMetadata(PhoneAccount account) {
+ try {
+ if (isServiceConnected()) {
+ return getTelecommService().getPhoneAccountMetadata(account);
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling ITelecommService#getPhoneAccountMetadata", e);
+ }
+ return null;
+ }
+
+ /**
+ * Register a {@link PhoneAccount} for use by the system.
+ *
+ * @param account The {@link PhoneAccount}.
+ * @param metadata The metadata for the account.
+ */
+ public void registerPhoneAccount(PhoneAccount account, PhoneAccountMetadata metadata) {
+ try {
+ if (isServiceConnected()) {
+ getTelecommService().registerPhoneAccount(account, metadata);
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling ITelecommService#registerPhoneAccount", e);
+ }
+ }
+
+ /**
+ * Remove a {@link PhoneAccount} registration from the system.
+ *
+ * @param account An Account.
+ */
+ public void unregisterPhoneAccount(PhoneAccount account) {
+ try {
+ if (isServiceConnected()) {
+ getTelecommService().unregisterPhoneAccount(account);
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling ITelecommService#unregisterPhoneAccount", e);
+ }
+ }
+
+ /**
+ * Remove all Accounts for a given package from the system.
+ *
+ * @param packageName A package name that may have registered Accounts.
+ *
+ * @hide
+ */
+ @SystemApi
+ public void clearAccounts(String packageName) {
+ try {
+ if (isServiceConnected()) {
+ getTelecommService().clearAccounts(packageName);
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling ITelecommService#clearAccounts", e);
+ }
+ }
+
+ /**
+ * @hide
+ */
@SystemApi
public ComponentName getDefaultPhoneApp() {
try {
@@ -108,7 +204,7 @@
/**
* Ends an ongoing call. TODO(santoscordon): L-release - need to convert all invocations of
- * ITelephony#endCall to use this method (clockwork & gearhead).
+ * ITelecommService#endCall to use this method (clockwork & gearhead).
*
* @hide
*/
@@ -127,7 +223,7 @@
/**
* If there is a ringing incoming call, this method accepts the call on behalf of the user.
* TODO(santoscordon): L-release - need to convert all invocation of
- * ITelephony#answerRingingCall to use this method (clockwork & gearhead).
+ * ITelecommService#answerRingingCall to use this method (clockwork & gearhead).
*
* @hide
*/
diff --git a/telecomm/java/com/android/internal/telecomm/ITelecommService.aidl b/telecomm/java/com/android/internal/telecomm/ITelecommService.aidl
index 30e4bdc..3334385 100644
--- a/telecomm/java/com/android/internal/telecomm/ITelecommService.aidl
+++ b/telecomm/java/com/android/internal/telecomm/ITelecommService.aidl
@@ -18,6 +18,7 @@
import android.content.ComponentName;
import android.telecomm.PhoneAccount;
+import android.telecomm.PhoneAccountMetadata;
/**
* Interface used to interact with Telecomm. Mostly this is used by TelephonyManager for passing
@@ -33,22 +34,32 @@
void showCallScreen(boolean showDialpad);
/**
- * Gets a list of accounts.
+ * @see TelecommManager#getEnabledPhoneAccounts
*/
- List<PhoneAccount> getAccounts();
+ List<PhoneAccount> getEnabledPhoneAccounts();
/**
- * Sets the enabled state of a given account.
+ * @see TelecommManager#getPhoneAccountMetadata
*/
- void setEnabled(in PhoneAccount account, boolean enabled);
+ PhoneAccountMetadata getPhoneAccountMetadata(in PhoneAccount account);
/**
- * Sets a given account as the system default.
+ * @see TelecommManager#registerPhoneAccount
*/
- void setSystemDefault(in PhoneAccount account);
+ void registerPhoneAccount(in PhoneAccount account, in PhoneAccountMetadata metadata);
/**
- * Returns the component name of the default phone application.
+ * @see TelecommManager#unregisterPhoneAccount
+ */
+ void unregisterPhoneAccount(in PhoneAccount account);
+
+ /**
+ * @see TelecommManager#clearAccounts
+ */
+ void clearAccounts(String packageName);
+
+ /**
+ * @see TelecommManager#getDefaultPhoneApp
*/
ComponentName getDefaultPhoneApp();
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 91ce73a..c1eb843 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -298,17 +298,6 @@
public static final String EXTRA_INCOMING_NUMBER = "incoming_number";
/**
- * The lookup key used with an {@link android.content.Intent#ACTION_CALL} or
- * {@link android.content.Intent#ACTION_DIAL} {@code Intent} for a {@link PhoneAccount}
- * object indicating a preference when making a phone connection.
- *
- * <p class="note">
- * Retrieve with
- * {@link android.content.Intent#getParcelableExtra(String)}.
- */
- public static final String EXTRA_ACCOUNT = "account";
-
- /**
* Broadcast intent action indicating that a precise call state
* (cellular) on the device has changed.
*
@@ -3207,42 +3196,6 @@
}
/**
- * Return a list of Accounts that can be used to indicate a preference when making
- * a phone call.
- *
- * @see #EXTRA_ACCOUNT
- * @return A list of {@code Accouint} objects.
- */
- public List<PhoneAccount> getAccounts() {
- try {
- return getTelecommService().getAccounts();
- } catch (RemoteException e) {
- Log.e(TAG, "Error calling ITelephony#getAccounts", e);
- }
- return null;
- }
-
- /** @hide */
- @SystemApi
- public void setEnabled(PhoneAccount account, boolean enabled) {
- try {
- getTelecommService().setEnabled(account, enabled);
- } catch (RemoteException e) {
- Log.e(TAG, "Error calling ITelephony#setEnabled", e);
- }
- }
-
- /** @hide */
- @SystemApi
- public void setSystemDefault(PhoneAccount account) {
- try {
- getTelecommService().setSystemDefault(account);
- } catch (RemoteException e) {
- Log.e(TAG, "Error calling ITelephony#setSystemDefault", e);
- }
- }
-
- /**
* Set whether Android should display a simplified Mobile Network Settings UI.
* The setting won't be persisted during power cycle.
* <p>
diff --git a/test-runner/src/android/test/mock/MockContext.java b/test-runner/src/android/test/mock/MockContext.java
index a54936b..2ebce9b 100644
--- a/test-runner/src/android/test/mock/MockContext.java
+++ b/test-runner/src/android/test/mock/MockContext.java
@@ -175,6 +175,11 @@
}
@Override
+ public File getNoBackupFilesDir() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
public File getExternalFilesDir(String type) {
throw new UnsupportedOperationException();
}