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();
     }