Merge change 5319 into donut
* changes:
Add new APIs to LauncherActivity to be used elsewhere.
diff --git a/cmds/am/src/com/android/commands/am/Am.java b/cmds/am/src/com/android/commands/am/Am.java
index 2a4a672..3782136 100644
--- a/cmds/am/src/com/android/commands/am/Am.java
+++ b/cmds/am/src/com/android/commands/am/Am.java
@@ -26,10 +26,13 @@
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
+import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.view.IWindowManager;
+import java.io.File;
+import java.io.FileNotFoundException;
import java.util.Iterator;
import java.util.Set;
@@ -446,6 +449,8 @@
return;
}
+ ParcelFileDescriptor fd = null;
+
String cmd = nextArg();
if ("start".equals(cmd)) {
start = true;
@@ -455,6 +460,16 @@
showUsage();
return;
}
+ try {
+ fd = ParcelFileDescriptor.open(
+ new File(profileFile),
+ ParcelFileDescriptor.MODE_CREATE |
+ ParcelFileDescriptor.MODE_TRUNCATE |
+ ParcelFileDescriptor.MODE_READ_WRITE);
+ } catch (FileNotFoundException e) {
+ System.err.println("Error: Unable to open file: " + profileFile);
+ return;
+ }
} else if (!"stop".equals(cmd)) {
System.err.println("Error: Profile command " + cmd + " not valid");
showUsage();
@@ -462,8 +477,8 @@
}
try {
- if (!mAm.profileControl(process, start, profileFile)) {
- System.out.println("PROFILE FAILED on process " + process);
+ if (!mAm.profileControl(process, start, profileFile, fd)) {
+ System.err.println("PROFILE FAILED on process " + process);
return;
}
} catch (IllegalArgumentException e) {
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index b6f855a..dfa8139 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -986,7 +986,9 @@
String process = data.readString();
boolean start = data.readInt() != 0;
String path = data.readString();
- boolean res = profileControl(process, start, path);
+ ParcelFileDescriptor fd = data.readInt() != 0
+ ? data.readFileDescriptor() : null;
+ boolean res = profileControl(process, start, path, fd);
reply.writeNoException();
reply.writeInt(res ? 1 : 0);
return true;
@@ -2232,7 +2234,7 @@
}
public boolean profileControl(String process, boolean start,
- String path) throws RemoteException
+ String path, ParcelFileDescriptor fd) throws RemoteException
{
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
@@ -2240,6 +2242,12 @@
data.writeString(process);
data.writeInt(start ? 1 : 0);
data.writeString(path);
+ if (fd != null) {
+ data.writeInt(1);
+ fd.writeToParcel(data, Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
+ } else {
+ data.writeInt(0);
+ }
mRemote.transact(PROFILE_CONTROL_TRANSACTION, data, reply, 0);
reply.readException();
boolean res = reply.readInt() != 0;
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 98bd45a..79588ea 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -48,6 +48,7 @@
import android.os.Looper;
import android.os.Message;
import android.os.MessageQueue;
+import android.os.ParcelFileDescriptor;
import android.os.Process;
import android.os.RemoteException;
import android.os.ServiceManager;
@@ -74,6 +75,7 @@
import java.io.File;
import java.io.FileDescriptor;
import java.io.FileOutputStream;
+import java.io.IOException;
import java.io.PrintWriter;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
@@ -1236,6 +1238,11 @@
String who;
}
+ private static final class ProfilerControlData {
+ String path;
+ ParcelFileDescriptor fd;
+ }
+
private final class ApplicationThread extends ApplicationThreadNative {
private static final String HEAP_COLUMN = "%17s %8s %8s %8s %8s";
private static final String ONE_COUNT_COLUMN = "%17s %8d";
@@ -1494,8 +1501,11 @@
}
}
- public void profilerControl(boolean start, String path) {
- queueOrSendMessage(H.PROFILER_CONTROL, path, start ? 1 : 0);
+ public void profilerControl(boolean start, String path, ParcelFileDescriptor fd) {
+ ProfilerControlData pcd = new ProfilerControlData();
+ pcd.path = path;
+ pcd.fd = fd;
+ queueOrSendMessage(H.PROFILER_CONTROL, pcd, start ? 1 : 0);
}
public void setSchedulingGroup(int group) {
@@ -1838,7 +1848,7 @@
handleActivityConfigurationChanged((IBinder)msg.obj);
break;
case PROFILER_CONTROL:
- handleProfilerControl(msg.arg1 != 0, (String)msg.obj);
+ handleProfilerControl(msg.arg1 != 0, (ProfilerControlData)msg.obj);
break;
case CREATE_BACKUP_AGENT:
handleCreateBackupAgent((CreateBackupAgentData)msg.obj);
@@ -3618,15 +3628,20 @@
performConfigurationChanged(r.activity, mConfiguration);
}
- final void handleProfilerControl(boolean start, String path) {
+ final void handleProfilerControl(boolean start, ProfilerControlData pcd) {
if (start) {
- File file = new File(path);
- file.getParentFile().mkdirs();
try {
- Debug.startMethodTracing(file.toString(), 8 * 1024 * 1024);
+ Debug.startMethodTracing(pcd.path, pcd.fd.getFileDescriptor(),
+ 8 * 1024 * 1024, 0);
} catch (RuntimeException e) {
- Log.w(TAG, "Profiling failed on path " + path
+ Log.w(TAG, "Profiling failed on path " + pcd.path
+ " -- can the process access this path?");
+ } finally {
+ try {
+ pcd.fd.close();
+ } catch (IOException e) {
+ Log.w(TAG, "Failure closing profile fd", e);
+ }
}
} else {
Debug.stopMethodTracing();
diff --git a/core/java/android/app/ApplicationThreadNative.java b/core/java/android/app/ApplicationThreadNative.java
index 4b64c94..b052c99 100644
--- a/core/java/android/app/ApplicationThreadNative.java
+++ b/core/java/android/app/ApplicationThreadNative.java
@@ -26,6 +26,7 @@
import android.content.res.Configuration;
import android.os.Binder;
import android.os.Bundle;
+import android.os.Parcelable;
import android.os.RemoteException;
import android.os.IBinder;
import android.os.Parcel;
@@ -331,7 +332,9 @@
data.enforceInterface(IApplicationThread.descriptor);
boolean start = data.readInt() != 0;
String path = data.readString();
- profilerControl(start, path);
+ ParcelFileDescriptor fd = data.readInt() != 0
+ ? data.readFileDescriptor() : null;
+ profilerControl(start, path, fd);
return true;
}
@@ -711,11 +714,18 @@
data.recycle();
}
- public void profilerControl(boolean start, String path) throws RemoteException {
+ public void profilerControl(boolean start, String path,
+ ParcelFileDescriptor fd) throws RemoteException {
Parcel data = Parcel.obtain();
data.writeInterfaceToken(IApplicationThread.descriptor);
data.writeInt(start ? 1 : 0);
data.writeString(path);
+ if (fd != null) {
+ data.writeInt(1);
+ fd.writeToParcel(data, Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
+ } else {
+ data.writeInt(0);
+ }
mRemote.transact(PROFILER_CONTROL_TRANSACTION, data, null,
IBinder.FLAG_ONEWAY);
data.recycle();
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index 66bc85b..3ec7938 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -250,7 +250,7 @@
// Turn on/off profiling in a particular process.
public boolean profileControl(String process, boolean start,
- String path) throws RemoteException;
+ String path, ParcelFileDescriptor fd) throws RemoteException;
public boolean shutdown(int timeout) throws RemoteException;
diff --git a/core/java/android/app/IApplicationThread.java b/core/java/android/app/IApplicationThread.java
index 029c650..c0bc2a0 100644
--- a/core/java/android/app/IApplicationThread.java
+++ b/core/java/android/app/IApplicationThread.java
@@ -25,6 +25,7 @@
import android.content.pm.ServiceInfo;
import android.content.res.Configuration;
import android.os.Bundle;
+import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
import android.os.IBinder;
import android.os.IInterface;
@@ -92,7 +93,8 @@
void scheduleLowMemory() throws RemoteException;
void scheduleActivityConfigurationChanged(IBinder token) throws RemoteException;
void requestPss() throws RemoteException;
- void profilerControl(boolean start, String path) throws RemoteException;
+ void profilerControl(boolean start, String path, ParcelFileDescriptor fd)
+ throws RemoteException;
void setSchedulingGroup(int group) throws RemoteException;
String descriptor = "android.app.IApplicationThread";
diff --git a/core/java/android/backup/AbsoluteFileBackupHelper.java b/core/java/android/backup/AbsoluteFileBackupHelper.java
new file mode 100644
index 0000000..ab24675
--- /dev/null
+++ b/core/java/android/backup/AbsoluteFileBackupHelper.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2009 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.backup;
+
+import android.content.Context;
+import android.os.ParcelFileDescriptor;
+import android.util.Log;
+
+import java.io.File;
+import java.io.FileDescriptor;
+
+/**
+ * Like FileBackupHelper, but takes absolute paths for the files instead of
+ * subpaths of getFilesDir()
+ *
+ * @hide
+ */
+public class AbsoluteFileBackupHelper extends FileBackupHelperBase implements BackupHelper {
+ private static final String TAG = "AbsoluteFileBackupHelper";
+
+ Context mContext;
+ String[] mFiles;
+
+ public AbsoluteFileBackupHelper(Context context, String... files) {
+ super(context);
+
+ mContext = context;
+ mFiles = files;
+ }
+
+ /**
+ * Based on oldState, determine which of the files from the application's data directory
+ * need to be backed up, write them to the data stream, and fill in newState with the
+ * state as it exists now.
+ */
+ public void performBackup(ParcelFileDescriptor oldState, BackupDataOutput data,
+ ParcelFileDescriptor newState) {
+ // use the file paths as the keys, too
+ performBackup_checked(oldState, data, newState, mFiles, mFiles);
+ }
+
+ public void restoreEntity(BackupDataInputStream data) {
+ // TODO: turn this off before ship
+ Log.d(TAG, "got entity '" + data.getKey() + "' size=" + data.size());
+ String key = data.getKey();
+ if (isKeyInList(key, mFiles)) {
+ File f = new File(key);
+ writeFile(f, data);
+ }
+ }
+}
+
diff --git a/core/java/android/backup/RestoreSet.java b/core/java/android/backup/RestoreSet.java
index 96a99ae..eeca148 100644
--- a/core/java/android/backup/RestoreSet.java
+++ b/core/java/android/backup/RestoreSet.java
@@ -43,14 +43,14 @@
* transport. This is guaranteed to be valid for the duration of a restore
* session, but is meaningless once the session has ended.
*/
- public int token;
+ public long token;
public RestoreSet() {
// Leave everything zero / null
}
- public RestoreSet(String _name, String _dev, int _token) {
+ public RestoreSet(String _name, String _dev, long _token) {
name = _name;
device = _dev;
token = _token;
@@ -65,7 +65,7 @@
public void writeToParcel(Parcel out, int flags) {
out.writeString(name);
out.writeString(device);
- out.writeInt(token);
+ out.writeLong(token);
}
public static final Parcelable.Creator<RestoreSet> CREATOR
@@ -82,6 +82,6 @@
private RestoreSet(Parcel in) {
name = in.readString();
device = in.readString();
- token = in.readInt();
+ token = in.readLong();
}
-}
\ No newline at end of file
+}
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 609fa74..6fe5506 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -1578,6 +1578,16 @@
public static final String ACTION_REBOOT =
"android.intent.action.REBOOT";
+ /**
+ * @hide
+ * TODO: This will be unhidden in a later CL.
+ * Broadcast Action: The TextToSpeech synthesizer has completed processing
+ * all of the text in the speech queue.
+ */
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String ACTION_TTS_QUEUE_PROCESSING_COMPLETED =
+ "android.intent.action.TTS_QUEUE_PROCESSING_COMPLETED";
+
// ---------------------------------------------------------------------
// ---------------------------------------------------------------------
// Standard intent categories (see addCategory()).
diff --git a/core/java/android/content/res/CompatibilityInfo.java b/core/java/android/content/res/CompatibilityInfo.java
index 680fef8..179b9bd 100644
--- a/core/java/android/content/res/CompatibilityInfo.java
+++ b/core/java/android/content/res/CompatibilityInfo.java
@@ -17,8 +17,15 @@
package android.content.res;
import android.content.pm.ApplicationInfo;
+import android.graphics.Canvas;
+import android.graphics.Rect;
+import android.graphics.Region;
import android.util.DisplayMetrics;
+import android.util.Log;
import android.view.Gravity;
+import android.view.MotionEvent;
+import android.view.WindowManager;
+import android.view.WindowManager.LayoutParams;
/**
* CompatibilityInfo class keeps the information about compatibility mode that the application is
@@ -27,6 +34,9 @@
* {@hide}
*/
public class CompatibilityInfo {
+ private static final boolean DBG = false;
+ private static final String TAG = "CompatibilityInfo";
+
/** default compatibility info object for compatible applications */
public static final CompatibilityInfo DEFAULT_COMPATIBILITY_INFO = new CompatibilityInfo();
@@ -41,36 +51,75 @@
public static final int DEFAULT_PORTRAIT_HEIGHT = 480;
/**
+ * The x-shift mode that controls the position of the content or the window under
+ * compatibility mode.
+ * {@see getTranslator}
+ * {@see Translator#mShiftMode}
+ */
+ private static final int X_SHIFT_NONE = 0;
+ private static final int X_SHIFT_CONTENT = 1;
+ private static final int X_SHIFT_AND_CLIP_CONTENT = 2;
+ private static final int X_SHIFT_WINDOW = 3;
+
+
+ /**
+ * A compatibility flags
+ */
+ private int compatibilityFlags;
+
+ /**
+ * A flag mask to tell if the application needs scaling (when mApplicationScale != 1.0f)
+ * {@see compatibilityFlag}
+ */
+ private static final int SCALING_REQUIRED = 1;
+
+ /**
+ * A flag mask to indicates that the application can expand over the original size.
+ * The flag is set to true if
+ * 1) Application declares its expandable in manifest file using <expandable /> or
+ * 2) The screen size is same as (320 x 480) * density.
+ * {@see compatibilityFlag}
+ */
+ private static final int EXPANDABLE = 2;
+
+ /**
+ * A flag mask to tell if the application is configured to be expandable. This differs
+ * from EXPANDABLE in that the application that is not expandable will be
+ * marked as expandable if it runs in (320x 480) * density screen size.
+ */
+ private static final int CONFIGURED_EXPANDABLE = 4;
+
+ private static final int SCALING_EXPANDABLE_MASK = SCALING_REQUIRED | EXPANDABLE;
+
+ /**
* Application's scale.
*/
- public final float mApplicationScale;
+ public final float applicationScale;
/**
* Application's inverted scale.
*/
- public final float mApplicationInvertedScale;
-
- /**
- * A boolean flag to indicates that the application can expand over the original size.
- * The flag is set to true if
- * 1) Application declares its expandable in manifest file using <expandable /> or
- * 2) The screen size is same as (320 x 480) * density.
- */
- public boolean mExpandable;
+ public final float applicationInvertedScale;
+
/**
- * A expandable flag in the configuration.
+ * Window size in Compatibility Mode, in real pixels. This is updated by
+ * {@link DisplayMetrics#updateMetrics}.
*/
- public final boolean mConfiguredExpandable;
+ private int mWidth;
+ private int mHeight;
/**
- * A boolean flag to tell if the application needs scaling (when mApplicationScale != 1.0f)
+ * The x offset to center the window content. In X_SHIFT_WINDOW mode, the offset is added
+ * to the window's layout. In X_SHIFT_CONTENT/X_SHIFT_AND_CLIP_CONTENT mode, the offset
+ * is used to translate the Canvas.
*/
- public final boolean mScalingRequired;
+ private int mXOffset;
public CompatibilityInfo(ApplicationInfo appInfo) {
- mExpandable = mConfiguredExpandable =
- (appInfo.flags & ApplicationInfo.FLAG_SUPPORTS_LARGE_SCREENS) != 0;
+ if ((appInfo.flags & ApplicationInfo.FLAG_SUPPORTS_LARGE_SCREENS) != 0) {
+ compatibilityFlags = EXPANDABLE | CONFIGURED_EXPANDABLE;
+ }
float packageDensityScale = -1.0f;
if (appInfo.supportsDensities != null) {
@@ -93,23 +142,323 @@
}
}
if (packageDensityScale > 0.0f) {
- mApplicationScale = packageDensityScale;
+ applicationScale = packageDensityScale;
} else {
- mApplicationScale = DisplayMetrics.DEVICE_DENSITY / (float) DisplayMetrics.DEFAULT_DENSITY;
+ applicationScale =
+ DisplayMetrics.DEVICE_DENSITY / (float) DisplayMetrics.DEFAULT_DENSITY;
}
- mApplicationInvertedScale = 1.0f / mApplicationScale;
- mScalingRequired = mApplicationScale != 1.0f;
+ applicationInvertedScale = 1.0f / applicationScale;
+ if (applicationScale != 1.0f) {
+ compatibilityFlags |= SCALING_REQUIRED;
+ }
}
private CompatibilityInfo() {
- mApplicationScale = mApplicationInvertedScale = 1.0f;
- mExpandable = mConfiguredExpandable = true;
- mScalingRequired = false;
+ applicationScale = applicationInvertedScale = 1.0f;
+ compatibilityFlags = EXPANDABLE | CONFIGURED_EXPANDABLE;
}
+ /**
+ * Sets the application's visible rect in compatibility mode.
+ * @param xOffset the application's x offset that is added to center the content.
+ * @param widthPixels the application's width in real pixels on the screen.
+ * @param heightPixels the application's height in real pixels on the screen.
+ */
+ public void setVisibleRect(int xOffset, int widthPixels, int heightPixels) {
+ this.mXOffset = xOffset;
+ mWidth = widthPixels;
+ mHeight = heightPixels;
+ }
+
+ /**
+ * Sets expandable bit in the compatibility flag.
+ */
+ public void setExpandable(boolean expandable) {
+ if (expandable) {
+ compatibilityFlags |= CompatibilityInfo.EXPANDABLE;
+ } else {
+ compatibilityFlags &= ~CompatibilityInfo.EXPANDABLE;
+ }
+ }
+
+ /**
+ * @return true if the application is configured to be expandable.
+ */
+ public boolean isConfiguredExpandable() {
+ return (compatibilityFlags & CompatibilityInfo.CONFIGURED_EXPANDABLE) != 0;
+ }
+
+ /**
+ * @return true if the scaling is required
+ */
+ public boolean isScalingRequired() {
+ return (compatibilityFlags & SCALING_REQUIRED) != 0;
+ }
+
@Override
public String toString() {
- return "CompatibilityInfo{scale=" + mApplicationScale +
- ", expandable=" + mExpandable + "}";
+ return "CompatibilityInfo{scale=" + applicationScale +
+ ", compatibility flag=" + compatibilityFlags + "}";
}
-}
+
+ /**
+ * Returns the translator which can translate the coordinates of the window.
+ * There are five different types of Translator.
+ *
+ * 1) {@link CompatibilityInfo#X_SHIFT_AND_CLIP_CONTENT}
+ * Shift and clip the content of the window at drawing time. Used for activities'
+ * main window (with no gravity).
+ * 2) {@link CompatibilityInfo#X_SHIFT_CONTENT}
+ * Shift the content of the window at drawing time. Used for windows that is created by
+ * an application and expected to be aligned with the application window.
+ * 3) {@link CompatibilityInfo#X_SHIFT_WINDOW}
+ * Create the window with adjusted x- coordinates. This is typically used
+ * in popup window, where it has to be placed relative to main window.
+ * 4) {@link CompatibilityInfo#X_SHIFT_NONE}
+ * No adjustment required, such as dialog.
+ * 5) Same as X_SHIFT_WINDOW, but no scaling. This is used by {@link SurfaceView}, which
+ * does not require scaling, but its window's location has to be adjusted.
+ *
+ * @param params the window's parameter
+ */
+ public Translator getTranslator(WindowManager.LayoutParams params) {
+ if ( (compatibilityFlags & CompatibilityInfo.SCALING_EXPANDABLE_MASK)
+ == CompatibilityInfo.EXPANDABLE) {
+ if (DBG) Log.d(TAG, "no translation required");
+ return null;
+ }
+
+ if ((compatibilityFlags & CompatibilityInfo.EXPANDABLE) == 0) {
+ if ((params.flags & WindowManager.LayoutParams.FLAG_NO_COMPATIBILITY_SCALING) != 0) {
+ if (DBG) Log.d(TAG, "translation for surface view selected");
+ return new Translator(X_SHIFT_WINDOW, false, 1.0f, 1.0f);
+ } else {
+ int shiftMode;
+ if (params.gravity == Gravity.NO_GRAVITY) {
+ // For Regular Application window
+ shiftMode = X_SHIFT_AND_CLIP_CONTENT;
+ if (DBG) Log.d(TAG, "shift and clip translator");
+ } else if (params.width == WindowManager.LayoutParams.FILL_PARENT) {
+ // For Regular Application window
+ shiftMode = X_SHIFT_CONTENT;
+ if (DBG) Log.d(TAG, "shift content translator");
+ } else if ((params.gravity & Gravity.LEFT) != 0 && params.x > 0) {
+ shiftMode = X_SHIFT_WINDOW;
+ if (DBG) Log.d(TAG, "shift window translator");
+ } else {
+ shiftMode = X_SHIFT_NONE;
+ if (DBG) Log.d(TAG, "no content/window translator");
+ }
+ return new Translator(shiftMode);
+ }
+ } else if (isScalingRequired()) {
+ return new Translator();
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * A helper object to translate the screen and window coordinates back and forth.
+ * @hide
+ */
+ public class Translator {
+ final private int mShiftMode;
+ final public boolean scalingRequired;
+ final public float applicationScale;
+ final public float applicationInvertedScale;
+
+ private Rect mContentInsetsBuffer = null;
+ private Rect mVisibleInsets = null;
+
+ Translator(int shiftMode, boolean scalingRequired, float applicationScale,
+ float applicationInvertedScale) {
+ mShiftMode = shiftMode;
+ this.scalingRequired = scalingRequired;
+ this.applicationScale = applicationScale;
+ this.applicationInvertedScale = applicationInvertedScale;
+ }
+
+ Translator(int shiftMode) {
+ this(shiftMode,
+ isScalingRequired(),
+ CompatibilityInfo.this.applicationScale,
+ CompatibilityInfo.this.applicationInvertedScale);
+ }
+
+ Translator() {
+ this(X_SHIFT_NONE);
+ }
+
+ /**
+ * Translate the screen rect to the application frame.
+ */
+ public void translateRectInScreenToAppWinFrame(Rect rect) {
+ if (rect.isEmpty()) return; // skip if the window size is empty.
+ switch (mShiftMode) {
+ case X_SHIFT_AND_CLIP_CONTENT:
+ rect.intersect(0, 0, mWidth, mHeight);
+ break;
+ case X_SHIFT_CONTENT:
+ rect.intersect(0, 0, mWidth + mXOffset, mHeight);
+ break;
+ case X_SHIFT_WINDOW:
+ case X_SHIFT_NONE:
+ break;
+ }
+ if (scalingRequired) {
+ rect.scale(applicationInvertedScale);
+ }
+ }
+
+ /**
+ * Translate the region in window to screen.
+ */
+ public void translateRegionInWindowToScreen(Region transparentRegion) {
+ switch (mShiftMode) {
+ case X_SHIFT_AND_CLIP_CONTENT:
+ case X_SHIFT_CONTENT:
+ transparentRegion.scale(applicationScale);
+ transparentRegion.translate(mXOffset, 0);
+ break;
+ case X_SHIFT_WINDOW:
+ case X_SHIFT_NONE:
+ transparentRegion.scale(applicationScale);
+ }
+ }
+
+ /**
+ * Apply translation to the canvas that is necessary to draw the content.
+ */
+ public void translateCanvas(Canvas canvas) {
+ if (mShiftMode == X_SHIFT_CONTENT ||
+ mShiftMode == X_SHIFT_AND_CLIP_CONTENT) {
+ // TODO: clear outside when rotation is changed.
+
+ // Translate x-offset only when the content is shifted.
+ canvas.translate(mXOffset, 0);
+ }
+ if (scalingRequired) {
+ canvas.scale(applicationScale, applicationScale);
+ }
+ }
+
+ /**
+ * Translate the motion event captured on screen to the application's window.
+ */
+ public void translateEventInScreenToAppWindow(MotionEvent event) {
+ if (mShiftMode == X_SHIFT_CONTENT ||
+ mShiftMode == X_SHIFT_AND_CLIP_CONTENT) {
+ event.translate(-mXOffset, 0);
+ }
+ if (scalingRequired) {
+ event.scale(applicationInvertedScale);
+ }
+ }
+
+ /**
+ * Translate the window's layout parameter, from application's view to
+ * Screen's view.
+ */
+ public void translateWindowLayout(WindowManager.LayoutParams params) {
+ switch (mShiftMode) {
+ case X_SHIFT_NONE:
+ case X_SHIFT_AND_CLIP_CONTENT:
+ case X_SHIFT_CONTENT:
+ params.scale(applicationScale);
+ break;
+ case X_SHIFT_WINDOW:
+ params.scale(applicationScale);
+ params.x += mXOffset;
+ break;
+ }
+ }
+
+ /**
+ * Translate a Rect in application's window to screen.
+ */
+ public void translateRectInAppWindowToScreen(Rect rect) {
+ // TODO Auto-generated method stub
+ if (scalingRequired) {
+ rect.scale(applicationScale);
+ }
+ switch(mShiftMode) {
+ case X_SHIFT_NONE:
+ case X_SHIFT_WINDOW:
+ break;
+ case X_SHIFT_CONTENT:
+ case X_SHIFT_AND_CLIP_CONTENT:
+ rect.offset(mXOffset, 0);
+ break;
+ }
+ }
+
+ /**
+ * Translate a Rect in screen coordinates into the app window's coordinates.
+ */
+ public void translateRectInScreenToAppWindow(Rect rect) {
+ switch (mShiftMode) {
+ case X_SHIFT_NONE:
+ case X_SHIFT_WINDOW:
+ break;
+ case X_SHIFT_CONTENT: {
+ rect.intersects(mXOffset, 0, rect.right, rect.bottom);
+ int dx = Math.min(mXOffset, rect.left);
+ rect.offset(-dx, 0);
+ break;
+ }
+ case X_SHIFT_AND_CLIP_CONTENT: {
+ rect.intersects(mXOffset, 0, mWidth + mXOffset, mHeight);
+ int dx = Math.min(mXOffset, rect.left);
+ rect.offset(-dx, 0);
+ break;
+ }
+ }
+ if (scalingRequired) {
+ rect.scale(applicationInvertedScale);
+ }
+ }
+
+ /**
+ * Translate the location of the sub window.
+ * @param params
+ */
+ public void translateLayoutParamsInAppWindowToScreen(LayoutParams params) {
+ if (scalingRequired) {
+ params.scale(applicationScale);
+ }
+ switch (mShiftMode) {
+ // the window location on these mode does not require adjustmenet.
+ case X_SHIFT_NONE:
+ case X_SHIFT_WINDOW:
+ break;
+ case X_SHIFT_CONTENT:
+ case X_SHIFT_AND_CLIP_CONTENT:
+ params.x += mXOffset;
+ break;
+ }
+ }
+
+ /**
+ * Translate the content insets in application window to Screen. This uses
+ * the internal buffer for content insets to avoid extra object allocation.
+ */
+ public Rect getTranslatedContentInsets(Rect contentInsets) {
+ if (mContentInsetsBuffer == null) mContentInsetsBuffer = new Rect();
+ mContentInsetsBuffer.set(contentInsets);
+ translateRectInAppWindowToScreen(mContentInsetsBuffer);
+ return mContentInsetsBuffer;
+ }
+
+ /**
+ * Translate the visible insets in application window to Screen. This uses
+ * the internal buffer for content insets to avoid extra object allocation.
+ */
+ public Rect getTranslatedVisbileInsets(Rect visibleInsets) {
+ if (mVisibleInsets == null) mVisibleInsets = new Rect();
+ mVisibleInsets.set(visibleInsets);
+ translateRectInAppWindowToScreen(mVisibleInsets);
+ return mVisibleInsets;
+ }
+ }
+}
\ No newline at end of file
diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java
index 71dbd38..cb9d46e 100644
--- a/core/java/android/content/res/Resources.java
+++ b/core/java/android/content/res/Resources.java
@@ -158,10 +158,10 @@
}
updateConfiguration(config, metrics);
assets.ensureStringBlocks();
- if (!mCompatibilityInfo.mScalingRequired) {
- mPreloadedDrawables = sPreloadedDrawables;
- } else {
+ if (mCompatibilityInfo.isScalingRequired()) {
mPreloadedDrawables = emptySparseArray();
+ } else {
+ mPreloadedDrawables = sPreloadedDrawables;
}
}
diff --git a/core/java/android/os/Debug.java b/core/java/android/os/Debug.java
index 8fcb4d7..d40ea6b 100644
--- a/core/java/android/os/Debug.java
+++ b/core/java/android/os/Debug.java
@@ -21,6 +21,7 @@
import android.util.Config;
import android.util.Log;
+import java.io.FileDescriptor;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileReader;
@@ -378,6 +379,20 @@
}
/**
+ * Like startMethodTracing(String, int, int), but taking an already-opened
+ * FileDescriptor in which the trace is written. The file name is also
+ * supplied simply for logging. Makes a dup of the file descriptor.
+ *
+ * Not exposed in the SDK unless we are really comfortable with supporting
+ * this and find it would be useful.
+ * @hide
+ */
+ public static void startMethodTracing(String traceName, FileDescriptor fd,
+ int bufferSize, int flags) {
+ VMDebug.startMethodTracing(traceName, fd, bufferSize, flags);
+ }
+
+ /**
* Determine whether method tracing is currently active.
* @hide
*/
diff --git a/core/java/android/util/DisplayMetrics.java b/core/java/android/util/DisplayMetrics.java
index a095913..d89ada0 100644
--- a/core/java/android/util/DisplayMetrics.java
+++ b/core/java/android/util/DisplayMetrics.java
@@ -106,16 +106,8 @@
* {@hide}
*/
public void updateMetrics(CompatibilityInfo compatibilityInfo, int orientation) {
- if (compatibilityInfo.mScalingRequired) {
- float invertedRatio = compatibilityInfo.mApplicationInvertedScale;
- density *= invertedRatio;
- scaledDensity *= invertedRatio;
- xdpi *= invertedRatio;
- ydpi *= invertedRatio;
- widthPixels *= invertedRatio;
- heightPixels *= invertedRatio;
- }
- if (!compatibilityInfo.mConfiguredExpandable) {
+ int xOffset = 0;
+ if (!compatibilityInfo.isConfiguredExpandable()) {
// Note: this assume that configuration is updated before calling
// updateMetrics method.
int defaultWidth;
@@ -141,11 +133,13 @@
if (defaultWidth == widthPixels && defaultHeight == heightPixels) {
// the screen size is same as expected size. make it expandable
- compatibilityInfo.mExpandable = true;
+ compatibilityInfo.setExpandable(true);
} else {
- compatibilityInfo.mExpandable = false;
+ compatibilityInfo.setExpandable(false);
// adjust the size only when the device's screen is bigger.
if (defaultWidth < widthPixels) {
+ // content/window's x offset in original pixels
+ xOffset = ((widthPixels - defaultWidth) / 2);
widthPixels = defaultWidth;
}
if (defaultHeight < heightPixels) {
@@ -153,8 +147,19 @@
}
}
}
+ compatibilityInfo.setVisibleRect(xOffset, widthPixels, heightPixels);
+ if (compatibilityInfo.isScalingRequired()) {
+ float invertedRatio = compatibilityInfo.applicationInvertedScale;
+ density *= invertedRatio;
+ scaledDensity *= invertedRatio;
+ xdpi *= invertedRatio;
+ ydpi *= invertedRatio;
+ widthPixels *= invertedRatio;
+ heightPixels *= invertedRatio;
+ }
}
+ @Override
public String toString() {
return "DisplayMetrics{density=" + density + ", width=" + widthPixels +
", height=" + heightPixels + ", scaledDensity=" + scaledDensity +
diff --git a/core/java/android/view/MotionEvent.java b/core/java/android/view/MotionEvent.java
index ca01448..a224ed3 100644
--- a/core/java/android/view/MotionEvent.java
+++ b/core/java/android/view/MotionEvent.java
@@ -212,28 +212,47 @@
}
/**
- * Scales down the cood of this event by the given scale.
+ * Scales down the coordination of this event by the given scale.
*
* @hide
*/
public void scale(float scale) {
- if (scale != 1.0f) {
- mX *= scale;
- mY *= scale;
- mRawX *= scale;
- mRawY *= scale;
- mSize *= scale;
- mXPrecision *= scale;
- mYPrecision *= scale;
- if (mHistory != null) {
- float[] history = mHistory;
- int length = history.length;
- for (int i = 0; i < length; i += 4) {
- history[i] *= scale; // X
- // history[i + 2] == pressure
- history[i + 1] *= scale; // Y
- history[i + 3] *= scale; // Size, TODO: square this?
- }
+ mX *= scale;
+ mY *= scale;
+ mRawX *= scale;
+ mRawY *= scale;
+ mSize *= scale;
+ mXPrecision *= scale;
+ mYPrecision *= scale;
+ if (mHistory != null) {
+ float[] history = mHistory;
+ int length = history.length;
+ for (int i = 0; i < length; i += 4) {
+ history[i] *= scale; // X
+ history[i + 1] *= scale; // Y
+ // no need to scale pressure ([i+2])
+ history[i + 3] *= scale; // Size, TODO: square this?
+ }
+ }
+ }
+
+ /**
+ * Translate the coordination of the event by given x and y.
+ *
+ * @hide
+ */
+ public void translate(float dx, float dy) {
+ mX += dx;
+ mY += dy;
+ mRawX += dx;
+ mRawY += dx;
+ if (mHistory != null) {
+ float[] history = mHistory;
+ int length = history.length;
+ for (int i = 0; i < length; i += 4) {
+ history[i] += dx; // X
+ history[i + 1] += dy; // Y
+ // no need to translate pressure (i+2) and size (i+3)
}
}
}
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index 082cca2..45b0f0a 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -18,6 +18,7 @@
import android.content.Context;
import android.content.res.CompatibilityInfo;
+import android.content.res.CompatibilityInfo.Translator;
import android.graphics.Canvas;
import android.graphics.PixelFormat;
import android.graphics.PorterDuff;
@@ -138,24 +139,21 @@
int mFormat = -1;
int mType = -1;
final Rect mSurfaceFrame = new Rect();
- private final CompatibilityInfo mCompatibilityInfo;
+ private Translator mTranslator;
public SurfaceView(Context context) {
super(context);
setWillNotDraw(true);
- mCompatibilityInfo = context.getResources().getCompatibilityInfo();
}
public SurfaceView(Context context, AttributeSet attrs) {
super(context, attrs);
setWillNotDraw(true);
- mCompatibilityInfo = context.getResources().getCompatibilityInfo();
}
public SurfaceView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
setWillNotDraw(true);
- mCompatibilityInfo = context.getResources().getCompatibilityInfo();
}
/**
@@ -258,9 +256,9 @@
public boolean dispatchTouchEvent(MotionEvent event) {
// SurfaceView uses pre-scaled size unless fixed size is requested. This hook
// scales the event back to the pre-scaled coordinates for such surface.
- if (mRequestedWidth < 0 && mCompatibilityInfo.mScalingRequired) {
+ if (mRequestedWidth < 0 && mTranslator != null) {
MotionEvent scaledBack = MotionEvent.obtain(event);
- scaledBack.scale(mCompatibilityInfo.mApplicationScale);
+ scaledBack.scale(mTranslator.applicationScale);
try {
return super.dispatchTouchEvent(scaledBack);
} finally {
@@ -297,15 +295,18 @@
if (!mHaveFrame) {
return;
}
- float appScale = mCompatibilityInfo.mApplicationScale;
+ mTranslator = ((ViewRoot)getRootView().getParent()).mTranslator;
+
+ float appScale = mTranslator == null ? 1.0f : mTranslator.applicationScale;
int myWidth = mRequestedWidth;
if (myWidth <= 0) myWidth = getWidth();
int myHeight = mRequestedHeight;
if (myHeight <= 0) myHeight = getHeight();
- // Use original size for surface unless fixed size is requested.
- if (mRequestedWidth <= 0 && mCompatibilityInfo.mScalingRequired) {
+ // Use original size if the app specified the size of the view,
+ // and let the flinger to scale up.
+ if (mRequestedWidth <= 0 && mTranslator != null && mTranslator.scalingRequired) {
myWidth *= appScale;
myHeight *= appScale;
}
@@ -325,7 +326,7 @@
+ " visible=" + visibleChanged
+ " left=" + (mLeft != mLocation[0])
+ " top=" + (mTop != mLocation[1]));
-
+
try {
final boolean visible = mVisible = mRequestedVisible;
mLeft = mLocation[0];
@@ -335,16 +336,23 @@
mFormat = mRequestedFormat;
mType = mRequestedType;
- // Scaling window's layout here because mLayout is not used elsewhere.
- mLayout.x = (int) (mLeft * appScale);
- mLayout.y = (int) (mTop * appScale);
- mLayout.width = (int) (getWidth() * appScale);
- mLayout.height = (int) (getHeight() * appScale);
+ // Scaling/Translate window's layout here because mLayout is not used elsewhere.
+
+ // Places the window relative
+ mLayout.x = mLeft;
+ mLayout.y = mTop;
+ mLayout.width = getWidth();
+ mLayout.height = getHeight();
+ if (mTranslator != null) {
+ mTranslator.translateLayoutParamsInAppWindowToScreen(mLayout);
+ }
+
mLayout.format = mRequestedFormat;
mLayout.flags |=WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS
| WindowManager.LayoutParams.FLAG_SCALED
| WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
| WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
+ | WindowManager.LayoutParams.FLAG_NO_COMPATIBILITY_SCALING
;
mLayout.memoryType = mRequestedType;
@@ -371,13 +379,6 @@
visible ? VISIBLE : GONE, false, mWinFrame, mContentInsets,
mVisibleInsets, mSurface);
- if (mCompatibilityInfo.mScalingRequired) {
- float invertedScale = mCompatibilityInfo.mApplicationInvertedScale;
- mContentInsets.scale(invertedScale);
- mVisibleInsets.scale(invertedScale);
- mWinFrame.scale(invertedScale);
- }
-
if (localLOGV) Log.i(TAG, "New surface: " + mSurface
+ ", vis=" + visible + ", frame=" + mWinFrame);
mSurfaceFrame.left = 0;
@@ -446,24 +447,14 @@
private static class MyWindow extends IWindow.Stub {
private final WeakReference<SurfaceView> mSurfaceView;
- private final CompatibilityInfo mCompatibilityInfo;
public MyWindow(SurfaceView surfaceView) {
mSurfaceView = new WeakReference<SurfaceView>(surfaceView);
- mCompatibilityInfo = surfaceView.getContext().getResources().getCompatibilityInfo();
}
public void resized(int w, int h, Rect coveredInsets,
Rect visibleInsets, boolean reportDraw) {
SurfaceView surfaceView = mSurfaceView.get();
- if (mCompatibilityInfo.mScalingRequired) {
- float scale = mCompatibilityInfo.mApplicationInvertedScale;
- w *= scale;
- h *= scale;
- coveredInsets.scale(scale);
- visibleInsets.scale(scale);
- }
-
if (surfaceView != null) {
if (localLOGV) Log.v(
"SurfaceView", surfaceView + " got resized: w=" +
@@ -626,9 +617,6 @@
Canvas c = null;
if (!mDrawingStopped && mWindow != null) {
Rect frame = dirty != null ? dirty : mSurfaceFrame;
- if (mCompatibilityInfo.mScalingRequired) {
- frame.scale(mCompatibilityInfo.mApplicationScale);
- }
try {
c = mSurface.lockCanvas(frame);
} catch (Exception e) {
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index f17f0e4..3bfdde8 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -6543,7 +6543,7 @@
boolean changed = false;
if (DBG) {
- System.out.println(this + " View.setFrame(" + left + "," + top + ","
+ Log.d("View", this + " View.setFrame(" + left + "," + top + ","
+ right + "," + bottom + ")");
}
diff --git a/core/java/android/view/ViewRoot.java b/core/java/android/view/ViewRoot.java
index d35b048..65457c5 100644
--- a/core/java/android/view/ViewRoot.java
+++ b/core/java/android/view/ViewRoot.java
@@ -128,13 +128,12 @@
Rect mDirty; // will be a graphics.Region soon
boolean mIsAnimating;
- private CompatibilityInfo mCompatibilityInfo;
+ CompatibilityInfo.Translator mTranslator;
final View.AttachInfo mAttachInfo;
final Rect mTempRect; // used in the transaction to not thrash the heap.
final Rect mVisRect; // used to retrieve visible rect of focused view.
- final Point mVisPoint; // used to retrieve global offset of focused view.
boolean mTraversalScheduled;
boolean mWillDrawSoon;
@@ -218,7 +217,6 @@
mDirty = new Rect();
mTempRect = new Rect();
mVisRect = new Rect();
- mVisPoint = new Point();
mWinFrame = new Rect();
mWindow = new W(this, context);
mInputMethodCallback = new InputMethodCallback(this);
@@ -387,20 +385,25 @@
if (mView == null) {
mView = view;
mWindowAttributes.copyFrom(attrs);
- mCompatibilityInfo = mView.getContext().getResources().getCompatibilityInfo();
+
+ CompatibilityInfo compatibilityInfo =
+ mView.getContext().getResources().getCompatibilityInfo();
+ mTranslator = compatibilityInfo.getTranslator(attrs);
boolean restore = false;
- if (mCompatibilityInfo.mScalingRequired || !mCompatibilityInfo.mExpandable) {
+ if (attrs != null && mTranslator != null) {
restore = true;
- mWindowAttributes.backup();
+ attrs.backup();
+ mTranslator.translateWindowLayout(attrs);
}
- if (!mCompatibilityInfo.mExpandable) {
- adjustWindowAttributesForCompatibleMode(mWindowAttributes);
- }
+ if (DEBUG_LAYOUT) Log.d(TAG, "WindowLayout in setView:" + attrs);
+
mSoftInputMode = attrs.softInputMode;
mWindowAttributesChanged = true;
mAttachInfo.mRootView = view;
- mAttachInfo.mScalingRequired = mCompatibilityInfo.mScalingRequired;
- mAttachInfo.mApplicationScale = mCompatibilityInfo.mApplicationScale;
+ mAttachInfo.mScalingRequired =
+ mTranslator == null ? false : mTranslator.scalingRequired;
+ mAttachInfo.mApplicationScale =
+ mTranslator == null ? 1.0f : mTranslator.applicationScale;
if (panelParentView != null) {
mAttachInfo.mPanelParentWindowToken
= panelParentView.getApplicationWindowToken();
@@ -421,15 +424,14 @@
mAttachInfo.mRootView = null;
unscheduleTraversals();
throw new RuntimeException("Adding window failed", e);
+ } finally {
+ if (restore) {
+ attrs.restore();
+ }
}
- if (restore) {
- mWindowAttributes.restore();
- }
-
- if (mCompatibilityInfo.mScalingRequired) {
- mAttachInfo.mContentInsets.scale(
- mCompatibilityInfo.mApplicationInvertedScale);
+ if (mTranslator != null) {
+ mTranslator.translateRectInScreenToAppWindow(mAttachInfo.mContentInsets);
}
mPendingContentInsets.set(mAttachInfo.mContentInsets);
mPendingVisibleInsets.set(0, 0, 0, 0);
@@ -541,14 +543,14 @@
public void invalidateChild(View child, Rect dirty) {
checkThread();
- if (LOCAL_LOGV) Log.v(TAG, "Invalidate child: " + dirty);
- if (mCurScrollY != 0 || mCompatibilityInfo.mScalingRequired) {
+ if (DEBUG_DRAW) Log.v(TAG, "Invalidate child: " + dirty);
+ if (mCurScrollY != 0 || mTranslator != null) {
mTempRect.set(dirty);
if (mCurScrollY != 0) {
mTempRect.offset(0, -mCurScrollY);
}
- if (mCompatibilityInfo.mScalingRequired) {
- mTempRect.scale(mCompatibilityInfo.mApplicationScale);
+ if (mTranslator != null) {
+ mTranslator.translateRectInAppWindowToScreen(mTempRect);
}
dirty = mTempRect;
}
@@ -567,7 +569,7 @@
return null;
}
- public boolean getChildVisibleRect(View child, Rect r, android.graphics.Point offset) {
+ public boolean getChildVisibleRect(View child, Rect r, android.graphics.Point offset) {
if (child != mView) {
throw new RuntimeException("child is not mine, honest!");
}
@@ -628,14 +630,14 @@
boolean viewVisibilityChanged = mViewVisibility != viewVisibility
|| mNewSurfaceNeeded;
- float appScale = mCompatibilityInfo.mApplicationScale;
+ float appScale = mAttachInfo.mApplicationScale;
WindowManager.LayoutParams params = null;
if (mWindowAttributesChanged) {
mWindowAttributesChanged = false;
params = lp;
}
-
+ Rect frame = mWinFrame;
if (mFirst) {
fullRedrawNeeded = true;
mLayoutRequested = true;
@@ -660,11 +662,11 @@
//Log.i(TAG, "Screen on initialized: " + attachInfo.mKeepScreenOn);
} else {
- desiredWindowWidth = mWinFrame.width();
- desiredWindowHeight = mWinFrame.height();
+ desiredWindowWidth = frame.width();
+ desiredWindowHeight = frame.height();
if (desiredWindowWidth != mWidth || desiredWindowHeight != mHeight) {
if (DEBUG_ORIENTATION) Log.v("ViewRoot",
- "View " + host + " resized to: " + mWinFrame);
+ "View " + host + " resized to: " + frame);
fullRedrawNeeded = true;
mLayoutRequested = true;
windowResizesToFitContent = true;
@@ -810,7 +812,6 @@
}
}
- final Rect frame = mWinFrame;
boolean initialized = false;
boolean contentInsetsChanged = false;
boolean visibleInsetsChanged;
@@ -883,7 +884,7 @@
} catch (RemoteException e) {
}
if (DEBUG_ORIENTATION) Log.v(
- "ViewRoot", "Relayout returned: frame=" + mWinFrame + ", surface=" + mSurface);
+ "ViewRoot", "Relayout returned: frame=" + frame + ", surface=" + mSurface);
attachInfo.mWindowLeft = frame.left;
attachInfo.mWindowTop = frame.top;
@@ -958,7 +959,6 @@
if (Config.DEBUG && ViewDebug.profileLayout) {
startTime = SystemClock.elapsedRealtime();
}
-
host.layout(0, 0, host.mMeasuredWidth, host.mMeasuredHeight);
if (Config.DEBUG && ViewDebug.consistencyCheckEnabled) {
@@ -985,7 +985,10 @@
mTmpLocation[1] + host.mBottom - host.mTop);
host.gatherTransparentRegion(mTransparentRegion);
- mTransparentRegion.scale(appScale);
+ if (mTranslator != null) {
+ mTranslator.translateRegionInWindowToScreen(mTransparentRegion);
+ }
+
if (!mTransparentRegion.equals(mPreviousTransparentRegion)) {
mPreviousTransparentRegion.set(mTransparentRegion);
// reconfigure window manager
@@ -1016,15 +1019,17 @@
= givenContent.bottom = givenVisible.left = givenVisible.top
= givenVisible.right = givenVisible.bottom = 0;
attachInfo.mTreeObserver.dispatchOnComputeInternalInsets(insets);
- if (mCompatibilityInfo.mScalingRequired) {
- insets.contentInsets.scale(appScale);
- insets.visibleInsets.scale(appScale);
+ Rect contentInsets = insets.contentInsets;
+ Rect visibleInsets = insets.visibleInsets;
+ if (mTranslator != null) {
+ contentInsets = mTranslator.getTranslatedContentInsets(contentInsets);
+ visibleInsets = mTranslator.getTranslatedVisbileInsets(visibleInsets);
}
if (insetsPending || !mLastGivenInsets.equals(insets)) {
mLastGivenInsets.set(insets);
try {
sWindowSession.setInsets(mWindow, insets.mTouchableInsets,
- insets.contentInsets, insets.visibleInsets);
+ contentInsets, visibleInsets);
} catch (RemoteException e) {
}
}
@@ -1167,8 +1172,8 @@
mCurScrollY = yoff;
fullRedrawNeeded = true;
}
- float appScale = mCompatibilityInfo.mApplicationScale;
- boolean scalingRequired = mCompatibilityInfo.mScalingRequired;
+ float appScale = mAttachInfo.mApplicationScale;
+ boolean scalingRequired = mAttachInfo.mScalingRequired;
Rect dirty = mDirty;
if (mUseGL) {
@@ -1187,8 +1192,8 @@
int saveCount = canvas.save(Canvas.MATRIX_SAVE_FLAG);
try {
canvas.translate(0, -yoff);
- if (scalingRequired) {
- canvas.scale(appScale, appScale);
+ if (mTranslator != null) {
+ mTranslator.translateCanvas(canvas);
}
mView.draw(canvas);
if (Config.DEBUG && ViewDebug.consistencyCheckEnabled) {
@@ -1239,7 +1244,6 @@
int top = dirty.top;
int right = dirty.right;
int bottom = dirty.bottom;
-
canvas = surface.lockCanvas(dirty);
if (left != dirty.left || top != dirty.top || right != dirty.right ||
@@ -1295,8 +1299,8 @@
int saveCount = canvas.save(Canvas.MATRIX_SAVE_FLAG);
try {
canvas.translate(0, -yoff);
- if (scalingRequired) {
- canvas.scale(appScale, appScale);
+ if (mTranslator != null) {
+ mTranslator.translateCanvas(canvas);
}
mView.draw(canvas);
} finally {
@@ -1599,10 +1603,9 @@
} else {
didFinish = event.getAction() == MotionEvent.ACTION_OUTSIDE;
}
- if (event != null && mCompatibilityInfo.mScalingRequired) {
- event.scale(mCompatibilityInfo.mApplicationInvertedScale);
+ if (event != null && mTranslator != null) {
+ mTranslator.translateEventInScreenToAppWindow(event);
}
-
try {
boolean handled;
if (mView != null && mAdded && event != null) {
@@ -1688,6 +1691,7 @@
case RESIZED:
Rect coveredInsets = ((Rect[])msg.obj)[0];
Rect visibleInsets = ((Rect[])msg.obj)[1];
+
if (mWinFrame.width() == msg.arg1 && mWinFrame.height() == msg.arg2
&& mPendingContentInsets.equals(coveredInsets)
&& mPendingVisibleInsets.equals(visibleInsets)) {
@@ -1722,7 +1726,7 @@
if (mGlWanted && !mUseGL) {
initializeGL();
if (mGlCanvas != null) {
- float appScale = mCompatibilityInfo.mApplicationScale;
+ float appScale = mAttachInfo.mApplicationScale;
mGlCanvas.setViewport(
(int) (mWidth * appScale), (int) (mHeight * appScale));
}
@@ -2356,18 +2360,16 @@
private int relayoutWindow(WindowManager.LayoutParams params, int viewVisibility,
boolean insetsPending) throws RemoteException {
+
+ float appScale = mAttachInfo.mApplicationScale;
boolean restore = false;
- float appScale = mCompatibilityInfo.mApplicationScale;
- boolean scalingRequired = mCompatibilityInfo.mScalingRequired;
- if (params != null && !mCompatibilityInfo.mExpandable) {
+ if (params != null && mTranslator != null) {
restore = true;
params.backup();
- adjustWindowAttributesForCompatibleMode(params);
+ mTranslator.translateWindowLayout(params);
}
- if (params != null && scalingRequired) {
- if (!restore) params.backup();
- restore = true;
- params.scale(appScale);
+ if (params != null) {
+ if (DBG) Log.d(TAG, "WindowLayout in layoutWindow:" + params);
}
int relayoutResult = sWindowSession.relayout(
mWindow, params,
@@ -2378,44 +2380,16 @@
if (restore) {
params.restore();
}
- if (scalingRequired) {
- float invertedScale = mCompatibilityInfo.mApplicationInvertedScale;
- mPendingContentInsets.scale(invertedScale);
- mPendingVisibleInsets.scale(invertedScale);
- mWinFrame.scale(invertedScale);
+
+ if (mTranslator != null) {
+ mTranslator.translateRectInScreenToAppWinFrame(mWinFrame);
+ mTranslator.translateRectInScreenToAppWindow(mPendingContentInsets);
+ mTranslator.translateRectInScreenToAppWindow(mPendingVisibleInsets);
}
return relayoutResult;
}
/**
- * Adjust the window's layout parameter for compatibility mode. It replaces FILL_PARENT
- * with the default window size, and centers if the window wanted to fill
- * horizontally.
- *
- * @param attrs the window's layout params to adjust
- */
- private void adjustWindowAttributesForCompatibleMode(WindowManager.LayoutParams attrs) {
- // fix app windows only
- if (attrs.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW) {
- DisplayMetrics metrics = mView.getContext().getResources().getDisplayMetrics();
- // TODO: improve gravity logic
- if (attrs.width == ViewGroup.LayoutParams.FILL_PARENT) {
- attrs.width = metrics.widthPixels;
- attrs.gravity |= Gravity.CENTER_HORIZONTAL;
- mWindowAttributesChanged = attrs == mWindowAttributes;
- }
- if (attrs.height == ViewGroup.LayoutParams.FILL_PARENT) {
- attrs.height = metrics.heightPixels;
- attrs.gravity |= Gravity.TOP;
- mWindowAttributesChanged = attrs == mWindowAttributes;
- }
- if (DEBUG_LAYOUT) {
- Log.d(TAG, "Adjusted Attributes for compatibility : " + attrs);
- }
- }
- }
-
- /**
* {@inheritDoc}
*/
public void playSoundEffect(int effectId) {
@@ -2518,16 +2492,14 @@
+ " visibleInsets=" + visibleInsets.toShortString()
+ " reportDraw=" + reportDraw);
Message msg = obtainMessage(reportDraw ? RESIZED_REPORT :RESIZED);
- if (mCompatibilityInfo.mScalingRequired) {
- float invertedScale = mCompatibilityInfo.mApplicationInvertedScale;
- coveredInsets.scale(invertedScale);
- visibleInsets.scale(invertedScale);
- msg.arg1 = (int) (w * invertedScale);
- msg.arg2 = (int) (h * invertedScale);
- } else {
- msg.arg1 = w;
- msg.arg2 = h;
+ if (mTranslator != null) {
+ mTranslator.translateRectInScreenToAppWindow(coveredInsets);
+ mTranslator.translateRectInScreenToAppWindow(visibleInsets);
+ w *= mTranslator.applicationInvertedScale;
+ h *= mTranslator.applicationInvertedScale;
}
+ msg.arg1 = w;
+ msg.arg2 = h;
msg.obj = new Rect[] { new Rect(coveredInsets), new Rect(visibleInsets) };
sendMessage(msg);
}
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index e295d15..bdb86d7 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -483,6 +483,12 @@
* {@hide} */
public static final int FLAG_SHOW_WHEN_LOCKED = 0x00080000;
+ /** Window flag: special flag to let a window ignore the compatibility scaling.
+ * This is used by SurfaceView to create a window that does not scale the content.
+ *
+ * {@hide} */
+ public static final int FLAG_NO_COMPATIBILITY_SCALING = 0x00100000;
+
/** Window flag: a special option intended for system dialogs. When
* this flag is set, the window will demand focus unconditionally when
* it is created.
@@ -978,8 +984,9 @@
/**
* Scale the layout params' coordinates and size.
+ * @hide
*/
- void scale(float scale) {
+ public void scale(float scale) {
x *= scale;
y *= scale;
if (width > 0) {
@@ -997,14 +1004,13 @@
void backup() {
int[] backup = mCompatibilityParamsBackup;
if (backup == null) {
- // we backup 5 elements, x, y, width, height and gravity.
- backup = mCompatibilityParamsBackup = new int[5];
+ // we backup 4 elements, x, y, width, height
+ backup = mCompatibilityParamsBackup = new int[4];
}
backup[0] = x;
backup[1] = y;
backup[2] = width;
backup[3] = height;
- backup[4] = gravity;
}
/**
@@ -1018,7 +1024,6 @@
y = backup[1];
width = backup[2];
height = backup[3];
- gravity = backup[4];
}
}
diff --git a/core/java/android/widget/PopupWindow.java b/core/java/android/widget/PopupWindow.java
index bd6edfb..0c2cd55 100644
--- a/core/java/android/widget/PopupWindow.java
+++ b/core/java/android/widget/PopupWindow.java
@@ -990,7 +990,7 @@
int bottomEdge = displayFrame.bottom;
if (ignoreBottomDecorations) {
- bottomEdge = WindowManagerImpl.getDefault().getDefaultDisplay().getHeight();
+ bottomEdge = anchor.getContext().getResources().getDisplayMetrics().heightPixels;
}
final int distanceToBottom = bottomEdge - (anchorPos[1] + anchor.getHeight()) - yOffset;
final int distanceToTop = anchorPos[1] - displayFrame.top + yOffset;
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 9479f9e..d8ed4f0 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -3681,12 +3681,13 @@
@Override
protected boolean isPaddingOffsetRequired() {
- return mShadowRadius != 0;
+ return mShadowRadius != 0 || mDrawables != null;
}
@Override
protected int getLeftPaddingOffset() {
- return (int) Math.min(0, mShadowDx - mShadowRadius);
+ return getCompoundPaddingLeft() - mPaddingLeft +
+ (int) Math.min(0, mShadowDx - mShadowRadius);
}
@Override
@@ -3701,7 +3702,8 @@
@Override
protected int getRightPaddingOffset() {
- return (int) Math.max(0, mShadowDx + mShadowRadius);
+ return -(getCompoundPaddingRight() - mPaddingRight) +
+ (int) Math.max(0, mShadowDx + mShadowRadius);
}
@Override
@@ -6665,9 +6667,10 @@
} else if (getLineCount() == 1) {
switch (mGravity & Gravity.HORIZONTAL_GRAVITY_MASK) {
case Gravity.LEFT:
- return (mLayout.getLineRight(0) - mScrollX - (mRight - mLeft) -
- getCompoundPaddingLeft() - getCompoundPaddingRight()) /
- getHorizontalFadingEdgeLength();
+ final int textWidth = (mRight - mLeft) - getCompoundPaddingLeft() -
+ getCompoundPaddingRight();
+ final float lineWidth = mLayout.getLineWidth(0);
+ return (lineWidth - textWidth) / getHorizontalFadingEdgeLength();
case Gravity.RIGHT:
return 0.0f;
case Gravity.CENTER_HORIZONTAL:
diff --git a/core/java/com/android/internal/backup/IBackupTransport.aidl b/core/java/com/android/internal/backup/IBackupTransport.aidl
index 84ed729..ec63528 100644
--- a/core/java/com/android/internal/backup/IBackupTransport.aidl
+++ b/core/java/com/android/internal/backup/IBackupTransport.aidl
@@ -54,63 +54,70 @@
long requestBackupTime();
/**
- * Establish a connection to the back-end data repository, if necessary. If the transport
- * needs to initialize state that is not tied to individual applications' backup operations,
- * this is where it should be done.
- *
- * @return Zero on success; a nonzero error code on failure.
- */
- int startSession();
-
- /**
- * Send one application's data to the backup destination.
+ * Send one application's data to the backup destination. The transport may send
+ * the data immediately, or may buffer it. After this is called, {@link #finishBackup}
+ * must be called to ensure the data is sent and recorded successfully.
*
* @param packageInfo The identity of the application whose data is being backed up.
* This specifically includes the signature list for the package.
* @param data The data stream that resulted from invoking the application's
* BackupService.doBackup() method. This may be a pipe rather than a file on
* persistent media, so it may not be seekable.
- * @return Zero on success; a nonzero error code on failure.
+ * @return false if errors occurred (the backup should be aborted and rescheduled),
+ * true if everything is OK so far (but {@link #finishBackup} must be called).
*/
- int performBackup(in PackageInfo packageInfo, in ParcelFileDescriptor data);
+ boolean performBackup(in PackageInfo packageInfo, in ParcelFileDescriptor inFd);
+
+ /**
+ * Finish sending application data to the backup destination. This must be
+ * called after {@link #performBackup} to ensure that all data is sent. Only
+ * when this method returns true can the backup be assumed to have succeeded.
+ *
+ * @return false if errors occurred (the backup should be aborted and rescheduled),
+ * true if everything is OK so far (but {@link #finishBackup} must be called).
+ */
+ boolean finishBackup();
/**
* Get the set of backups currently available over this transport.
*
- * @return Descriptions of the set of restore images available for this device.
+ * @return Descriptions of the set of restore images available for this device,
+ * or null if an error occurred (the attempt should be rescheduled).
**/
RestoreSet[] getAvailableRestoreSets();
/**
- * Get the set of applications from a given restore image.
+ * Start restoring application data from backup. After calling this function,
+ * alternate calls to {@link #nextRestorePackage} and {@link #nextRestoreData}
+ * to walk through the actual application data.
*
* @param token A backup token as returned by {@link #getAvailableRestoreSets}.
- * @return An array of PackageInfo objects describing all of the applications
- * available for restore from this restore image. This should include the list
- * of signatures for each package so that the Backup Manager can filter using that
- * information.
+ * @param packages List of applications to restore (if data is available).
+ * Application data will be restored in the order given.
+ * @return false if errors occurred (the restore should be aborted and rescheduled),
+ * true if everything is OK so far (go ahead and call {@link #nextRestorePackage}).
*/
- PackageInfo[] getAppSet(int token);
+ boolean startRestore(long token, in PackageInfo[] packages);
/**
- * Retrieve one application's data from the backing store.
- *
- * @param token The backup record from which a restore is being requested.
- * @param packageInfo The identity of the application whose data is being restored.
- * This must include the signature list for the package; it is up to the transport
- * to verify that the requested app's signatures match the saved backup record
- * because the transport cannot necessarily trust the client device.
- * @param data An open, writable file into which the backup image should be stored.
- * @return Zero on success; a nonzero error code on failure.
+ * Get the package name of the next application with data in the backup store.
+ * @return The name of one of the packages supplied to {@link #startRestore},
+ * or "" (the empty string) if no more backup data is available,
+ * or null if an error occurred (the restore should be aborted and rescheduled).
*/
- int getRestoreData(int token, in PackageInfo packageInfo, in ParcelFileDescriptor data);
+ String nextRestorePackage();
/**
- * Terminate the backup session, closing files, freeing memory, and cleaning up whatever
- * other state the transport required.
- *
- * @return Zero on success; a nonzero error code on failure. Even on failure, the session
- * is torn down and must be restarted if another backup is attempted.
+ * Get the data for the application returned by {@link #nextRestorePackage}.
+ * @param data An open, writable file into which the backup data should be stored.
+ * @return false if errors occurred (the restore should be aborted and rescheduled),
+ * true if everything is OK so far (go ahead and call {@link #nextRestorePackage}).
*/
- int endSession();
+ boolean getRestoreData(in ParcelFileDescriptor outFd);
+
+ /**
+ * End a restore session (aborting any in-process data transfer as necessary),
+ * freeing any resources and connections used during the restore process.
+ */
+ void finishRestore();
}
diff --git a/core/java/com/android/internal/backup/LocalTransport.java b/core/java/com/android/internal/backup/LocalTransport.java
index 3dd71f3..0fbbb3f 100644
--- a/core/java/com/android/internal/backup/LocalTransport.java
+++ b/core/java/com/android/internal/backup/LocalTransport.java
@@ -33,11 +33,8 @@
private Context mContext;
private PackageManager mPackageManager;
private File mDataDir = new File(Environment.getDownloadCacheDirectory(), "backup");
- private FileFilter mDirFileFilter = new FileFilter() {
- public boolean accept(File f) {
- return f.isDirectory();
- }
- };
+ private PackageInfo[] mRestorePackages = null;
+ private int mRestorePackage = -1; // Index into mRestorePackages
public LocalTransport(Context context) {
@@ -51,21 +48,9 @@
return 0;
}
- public int startSession() throws RemoteException {
- if (DEBUG) Log.v(TAG, "session started");
- mDataDir.mkdirs();
- return 0;
- }
-
- public int endSession() throws RemoteException {
- if (DEBUG) Log.v(TAG, "session ended");
- return 0;
- }
-
- public int performBackup(PackageInfo packageInfo, ParcelFileDescriptor data)
+ public boolean performBackup(PackageInfo packageInfo, ParcelFileDescriptor data)
throws RemoteException {
if (DEBUG) Log.v(TAG, "performBackup() pkg=" + packageInfo.packageName);
- int err = 0;
File packageDir = new File(mDataDir, packageInfo.packageName);
packageDir.mkdirs();
@@ -101,9 +86,8 @@
try {
entity.write(buf, 0, dataSize);
} catch (IOException e) {
- Log.e(TAG, "Unable to update key file "
- + entityFile.getAbsolutePath());
- err = -1;
+ Log.e(TAG, "Unable to update key file " + entityFile.getAbsolutePath());
+ return false;
} finally {
entity.close();
}
@@ -111,14 +95,17 @@
entityFile.delete();
}
}
+ return true;
} catch (IOException e) {
// oops, something went wrong. abort the operation and return error.
- Log.v(TAG, "Exception reading backup input:");
- e.printStackTrace();
- err = -1;
+ Log.v(TAG, "Exception reading backup input:", e);
+ return false;
}
+ }
- return err;
+ public boolean finishBackup() throws RemoteException {
+ if (DEBUG) Log.v(TAG, "finishBackup()");
+ return true;
}
// Restore handling
@@ -129,72 +116,66 @@
return array;
}
- public PackageInfo[] getAppSet(int token) throws android.os.RemoteException {
- if (DEBUG) Log.v(TAG, "getting app set " + token);
- // the available packages are the extant subdirs of mDatadir
- File[] packageDirs = mDataDir.listFiles(mDirFileFilter);
- ArrayList<PackageInfo> packages = new ArrayList<PackageInfo>();
- for (File dir : packageDirs) {
- try {
- PackageInfo pkg = mPackageManager.getPackageInfo(dir.getName(),
- PackageManager.GET_SIGNATURES);
- if (pkg != null) {
- packages.add(pkg);
- }
- } catch (NameNotFoundException e) {
- // restore set contains data for a package not installed on the
- // phone -- just ignore it.
- }
- }
-
- if (DEBUG) {
- Log.v(TAG, "Built app set of " + packages.size() + " entries:");
- for (PackageInfo p : packages) {
- Log.v(TAG, " + " + p.packageName);
- }
- }
-
- PackageInfo[] result = new PackageInfo[packages.size()];
- return packages.toArray(result);
+ public boolean startRestore(long token, PackageInfo[] packages) {
+ if (DEBUG) Log.v(TAG, "start restore " + token);
+ mRestorePackages = packages;
+ mRestorePackage = -1;
+ return true;
}
- public int getRestoreData(int token, PackageInfo packageInfo, ParcelFileDescriptor outFd)
- throws android.os.RemoteException {
- if (DEBUG) Log.v(TAG, "getting restore data " + token + " : " + packageInfo.packageName);
- // we only support one hardcoded restore set
- if (token != 0) return -1;
+ public String nextRestorePackage() {
+ if (mRestorePackages == null) throw new IllegalStateException("startRestore not called");
+ while (++mRestorePackage < mRestorePackages.length) {
+ String name = mRestorePackages[mRestorePackage].packageName;
+ if (new File(mDataDir, name).isDirectory()) {
+ if (DEBUG) Log.v(TAG, " nextRestorePackage() = " + name);
+ return name;
+ }
+ }
- // the data for a given package is at a known location
- File packageDir = new File(mDataDir, packageInfo.packageName);
+ if (DEBUG) Log.v(TAG, " no more packages to restore");
+ return "";
+ }
+
+ public boolean getRestoreData(ParcelFileDescriptor outFd) {
+ if (mRestorePackages == null) throw new IllegalStateException("startRestore not called");
+ if (mRestorePackage < 0) throw new IllegalStateException("nextRestorePackage not called");
+ File packageDir = new File(mDataDir, mRestorePackages[mRestorePackage].packageName);
// The restore set is the concatenation of the individual record blobs,
// each of which is a file in the package's directory
File[] blobs = packageDir.listFiles();
- if (DEBUG) Log.v(TAG, " found " + blobs.length + " key files");
- int err = 0;
- if (blobs != null && blobs.length > 0) {
- BackupDataOutput out = new BackupDataOutput(outFd.getFileDescriptor());
- try {
- for (File f : blobs) {
- copyToRestoreData(f, out);
- }
- } catch (Exception e) {
- Log.e(TAG, "Unable to read backup records");
- err = -1;
- }
+ if (blobs == null) {
+ Log.e(TAG, "Error listing directory: " + packageDir);
+ return false; // nextRestorePackage() ensures the dir exists, so this is an error
}
- return err;
+
+ // We expect at least some data if the directory exists in the first place
+ if (DEBUG) Log.v(TAG, " getRestoreData() found " + blobs.length + " key files");
+ BackupDataOutput out = new BackupDataOutput(outFd.getFileDescriptor());
+ try {
+ for (File f : blobs) {
+ FileInputStream in = new FileInputStream(f);
+ try {
+ int size = (int) f.length();
+ byte[] buf = new byte[size];
+ in.read(buf);
+ String key = new String(Base64.decode(f.getName()));
+ if (DEBUG) Log.v(TAG, " ... key=" + key + " size=" + size);
+ out.writeEntityHeader(key, size);
+ out.writeEntityData(buf, size);
+ } finally {
+ in.close();
+ }
+ }
+ return true;
+ } catch (IOException e) {
+ Log.e(TAG, "Unable to read backup records", e);
+ return false;
+ }
}
- private void copyToRestoreData(File f, BackupDataOutput out) throws IOException {
- FileInputStream in = new FileInputStream(f);
- int size = (int) f.length();
- byte[] buf = new byte[size];
- in.read(buf);
- String key = new String(Base64.decode(f.getName()));
- if (DEBUG) Log.v(TAG, " ... copy to stream: key=" + key
- + " size=" + size);
- out.writeEntityHeader(key, size);
- out.writeEntityData(buf, size);
+ public void finishRestore() {
+ if (DEBUG) Log.v(TAG, "finishRestore()");
}
}
diff --git a/core/java/com/android/internal/backup/SystemBackupAgent.java b/core/java/com/android/internal/backup/SystemBackupAgent.java
new file mode 100644
index 0000000..6b396d7
--- /dev/null
+++ b/core/java/com/android/internal/backup/SystemBackupAgent.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.backup;
+
+import android.backup.AbsoluteFileBackupHelper;
+import android.backup.BackupHelperAgent;
+
+/**
+ * Backup agent for various system-managed data
+ */
+public class SystemBackupAgent extends BackupHelperAgent {
+ // the set of files that we back up whole, as absolute paths
+ String[] mFiles = {
+ /* WallpaperService.WALLPAPER_FILE */
+ "/data/data/com.android.settings/files/wallpaper",
+ };
+
+ public void onCreate() {
+ addHelper("system_files", new AbsoluteFileBackupHelper(this, mFiles));
+ }
+}
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index df6151d..7f24dcc 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -1000,6 +1000,7 @@
android:hasCode="false"
android:label="@string/android_system_label"
android:allowClearUserData="false"
+ android:backupAgent="com.android.internal.backup.SystemBackupAgent"
android:icon="@drawable/ic_launcher_android">
<activity android:name="com.android.internal.app.ChooserActivity"
android:theme="@style/Theme.Dialog.Alert"
diff --git a/services/java/com/android/server/BackupManagerService.java b/services/java/com/android/server/BackupManagerService.java
index 197404e..efa6179 100644
--- a/services/java/com/android/server/BackupManagerService.java
+++ b/services/java/com/android/server/BackupManagerService.java
@@ -76,8 +76,7 @@
private static final boolean DEBUG = true;
// Default time to wait after data changes before we back up the data
- private static final long COLLECTION_INTERVAL = 1000;
- //private static final long COLLECTION_INTERVAL = 3 * 60 * 1000;
+ private static final long COLLECTION_INTERVAL = 3 * 60 * 1000;
private static final int MSG_RUN_BACKUP = 1;
private static final int MSG_RUN_FULL_BACKUP = 2;
@@ -351,28 +350,29 @@
void addPackageParticipantsLocked(String packageName) {
// Look for apps that define the android:backupAgent attribute
if (DEBUG) Log.v(TAG, "addPackageParticipantsLocked: " + packageName);
- List<ApplicationInfo> targetApps = allAgentApps();
+ List<PackageInfo> targetApps = allAgentPackages();
addPackageParticipantsLockedInner(packageName, targetApps);
}
private void addPackageParticipantsLockedInner(String packageName,
- List<ApplicationInfo> targetApps) {
+ List<PackageInfo> targetPkgs) {
if (DEBUG) {
- Log.v(TAG, "Adding " + targetApps.size() + " backup participants:");
- for (ApplicationInfo a : targetApps) {
- Log.v(TAG, " " + a + " agent=" + a.backupAgentName);
+ Log.v(TAG, "Adding " + targetPkgs.size() + " backup participants:");
+ for (PackageInfo p : targetPkgs) {
+ Log.v(TAG, " " + p + " agent=" + p.applicationInfo.backupAgentName
+ + " uid=" + p.applicationInfo.uid);
}
}
- for (ApplicationInfo app : targetApps) {
- if (packageName == null || app.packageName.equals(packageName)) {
- int uid = app.uid;
+ for (PackageInfo pkg : targetPkgs) {
+ if (packageName == null || pkg.packageName.equals(packageName)) {
+ int uid = pkg.applicationInfo.uid;
HashSet<ApplicationInfo> set = mBackupParticipants.get(uid);
if (set == null) {
set = new HashSet<ApplicationInfo>();
mBackupParticipants.put(uid, set);
}
- set.add(app);
+ set.add(pkg.applicationInfo);
backUpPackageManagerData();
}
}
@@ -382,67 +382,67 @@
// 'packageName' is null, *all* participating apps will be removed.
void removePackageParticipantsLocked(String packageName) {
if (DEBUG) Log.v(TAG, "removePackageParticipantsLocked: " + packageName);
- List<ApplicationInfo> allApps = null;
+ List<PackageInfo> allApps = null;
if (packageName != null) {
- allApps = new ArrayList<ApplicationInfo>();
+ allApps = new ArrayList<PackageInfo>();
try {
- ApplicationInfo app = mPackageManager.getApplicationInfo(packageName, 0);
- allApps.add(app);
+ int flags = PackageManager.GET_SIGNATURES;
+ allApps.add(mPackageManager.getPackageInfo(packageName, flags));
} catch (Exception e) {
- // just skip it
+ // just skip it (???)
}
} else {
// all apps with agents
- allApps = allAgentApps();
+ allApps = allAgentPackages();
}
removePackageParticipantsLockedInner(packageName, allApps);
}
private void removePackageParticipantsLockedInner(String packageName,
- List<ApplicationInfo> agents) {
+ List<PackageInfo> agents) {
if (DEBUG) {
Log.v(TAG, "removePackageParticipantsLockedInner (" + packageName
+ ") removing " + agents.size() + " entries");
- for (ApplicationInfo a : agents) {
- Log.v(TAG, " - " + a);
+ for (PackageInfo p : agents) {
+ Log.v(TAG, " - " + p);
}
}
- for (ApplicationInfo app : agents) {
- if (packageName == null || app.packageName.equals(packageName)) {
- int uid = app.uid;
+ for (PackageInfo pkg : agents) {
+ if (packageName == null || pkg.packageName.equals(packageName)) {
+ int uid = pkg.applicationInfo.uid;
HashSet<ApplicationInfo> set = mBackupParticipants.get(uid);
if (set != null) {
// Find the existing entry with the same package name, and remove it.
// We can't just remove(app) because the instances are different.
for (ApplicationInfo entry: set) {
- if (entry.packageName.equals(app.packageName)) {
+ if (entry.packageName.equals(pkg.packageName)) {
set.remove(entry);
backUpPackageManagerData();
break;
}
}
if (set.size() == 0) {
- mBackupParticipants.delete(uid); }
+ mBackupParticipants.delete(uid);
+ }
}
}
}
}
// Returns the set of all applications that define an android:backupAgent attribute
- private List<ApplicationInfo> allAgentApps() {
+ private List<PackageInfo> allAgentPackages() {
// !!! TODO: cache this and regenerate only when necessary
- List<ApplicationInfo> allApps = mPackageManager.getInstalledApplications(0);
- int N = allApps.size();
- if (N > 0) {
- for (int a = N-1; a >= 0; a--) {
- ApplicationInfo app = allApps.get(a);
- if (((app.flags&ApplicationInfo.FLAG_ALLOW_BACKUP) == 0)
- || app.backupAgentName == null) {
- allApps.remove(a);
- }
+ int flags = PackageManager.GET_SIGNATURES;
+ List<PackageInfo> packages = mPackageManager.getInstalledPackages(flags);
+ int N = packages.size();
+ for (int a = N-1; a >= 0; a--) {
+ ApplicationInfo app = packages.get(a).applicationInfo;
+ if (((app.flags&ApplicationInfo.FLAG_ALLOW_BACKUP) == 0)
+ || app.backupAgentName == null) {
+ packages.remove(a);
}
}
- return allApps;
+ return packages;
}
// Reset the given package's known backup participants. Unlike add/remove, the update
@@ -455,7 +455,7 @@
if (DEBUG) Log.v(TAG, "updatePackageParticipantsLocked: " + packageName);
// brute force but small code size
- List<ApplicationInfo> allApps = allAgentApps();
+ List<PackageInfo> allApps = allAgentPackages();
removePackageParticipantsLockedInner(packageName, allApps);
addPackageParticipantsLockedInner(packageName, allApps);
}
@@ -578,17 +578,7 @@
public void run() {
if (DEBUG) Log.v(TAG, "Beginning backup of " + mQueue.size() + " targets");
- // start up the transport
- try {
- mTransport.startSession();
- } catch (Exception e) {
- Log.e(TAG, "Error session transport");
- e.printStackTrace();
- return;
- }
-
- // The transport is up and running. First, back up the package manager
- // metadata if necessary
+ // First, back up the package manager metadata if necessary
boolean doPackageManager;
synchronized (BackupManagerService.this) {
doPackageManager = mDoPackageManager;
@@ -601,7 +591,7 @@
if (DEBUG) Log.i(TAG, "Running PM backup pass as well");
PackageManagerBackupAgent pmAgent = new PackageManagerBackupAgent(
- mPackageManager, allAgentApps());
+ mPackageManager, allAgentPackages());
BackupRequest pmRequest = new BackupRequest(new ApplicationInfo(), false);
pmRequest.appInfo.packageName = PACKAGE_MANAGER_SENTINEL;
processOneBackup(pmRequest,
@@ -614,10 +604,12 @@
// Finally, tear down the transport
try {
- mTransport.endSession();
- } catch (Exception e) {
- Log.e(TAG, "Error ending transport");
- e.printStackTrace();
+ if (!mTransport.finishBackup()) {
+ // STOPSHIP TODO: handle errors
+ Log.e(TAG, "Backup failure in finishBackup()");
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error in finishBackup()", e);
}
if (!mJournal.delete()) {
@@ -712,24 +704,18 @@
if (DEBUG) Log.v(TAG, "doBackup() success; calling transport");
backupData =
ParcelFileDescriptor.open(backupDataName, ParcelFileDescriptor.MODE_READ_ONLY);
- int error = transport.performBackup(packInfo, backupData);
+ if (!transport.performBackup(packInfo, backupData)) {
+ // STOPSHIP TODO: handle errors
+ Log.e(TAG, "Backup failure in performBackup()");
+ }
// !!! TODO: After successful transport, delete the now-stale data
// and juggle the files so that next time the new state is passed
//backupDataName.delete();
newStateName.renameTo(savedStateName);
}
- } catch (NameNotFoundException e) {
- Log.e(TAG, "Package not found on backup: " + packageName);
- } catch (FileNotFoundException fnf) {
- Log.w(TAG, "File not found on backup: ");
- fnf.printStackTrace();
- } catch (RemoteException e) {
- Log.d(TAG, "Remote target " + request.appInfo.packageName + " threw during backup:");
- e.printStackTrace();
} catch (Exception e) {
- Log.w(TAG, "Final exception guard in backup: ");
- e.printStackTrace();
+ Log.e(TAG, "Error backing up " + packageName, e);
}
}
}
@@ -737,25 +723,6 @@
// ----- Restore handling -----
- // Is the given package restorable on this device? Returns the on-device app's
- // ApplicationInfo struct if it is; null if not.
- //
- // !!! TODO: also consider signatures
- PackageInfo isRestorable(PackageInfo packageInfo) {
- if (packageInfo.packageName != null) {
- try {
- PackageInfo app = mPackageManager.getPackageInfo(packageInfo.packageName,
- PackageManager.GET_SIGNATURES);
- if ((app.applicationInfo.flags & ApplicationInfo.FLAG_ALLOW_BACKUP) != 0) {
- return app;
- }
- } catch (Exception e) {
- // doesn't exist on this device, or other error -- just ignore it.
- }
- }
- return null;
- }
-
private boolean signaturesMatch(Signature[] storedSigs, Signature[] deviceSigs) {
// Allow unsigned apps, but not signed on one device and unsigned on the other
// !!! TODO: is this the right policy?
@@ -816,130 +783,141 @@
/**
* Restore sequence:
*
- * 1. start up the transport session
- * 2. get the restore set description for our identity
- * 3. for each app in the restore set:
+ * 1. get the restore set description for our identity
+ * 2. for each app in the restore set:
* 3.a. if it's restorable on this device, add it to the restore queue
- * 4. for each app in the restore queue:
- * 4.a. clear the app data
- * 4.b. get the restore data for the app from the transport
- * 4.c. launch the backup agent for the app
- * 4.d. agent.doRestore() with the data from the server
- * 4.e. unbind the agent [and kill the app?]
- * 5. shut down the transport
+ * 3. for each app in the restore queue:
+ * 3.a. clear the app data
+ * 3.b. get the restore data for the app from the transport
+ * 3.c. launch the backup agent for the app
+ * 3.d. agent.doRestore() with the data from the server
+ * 3.e. unbind the agent [and kill the app?]
+ * 4. shut down the transport
*/
- int err = -1;
+ // build the set of apps to restore
try {
- err = mTransport.startSession();
- } catch (Exception e) {
- Log.e(TAG, "Error starting transport for restore");
- e.printStackTrace();
- }
+ RestoreSet[] images = mTransport.getAvailableRestoreSets();
+ if (images == null) {
+ // STOPSHIP TODO: Handle the failure somehow?
+ Log.e(TAG, "Error getting restore sets");
+ return;
+ }
- if (err == 0) {
- // build the set of apps to restore
- try {
- RestoreSet[] images = mTransport.getAvailableRestoreSets();
- if (images.length > 0) {
- // !!! TODO: pick out the set for this token
- mImage = images[0];
+ if (images.length == 0) {
+ Log.i(TAG, "No restore sets available");
+ return;
+ }
- // Pull the Package Manager metadata from the restore set first
- PackageManagerBackupAgent pmAgent = new PackageManagerBackupAgent(
- mPackageManager, allAgentApps());
- PackageInfo pmApp = new PackageInfo();
- pmApp.packageName = PACKAGE_MANAGER_SENTINEL;
- // !!! TODO: version currently ignored when 'restoring' the PM metadata
- processOneRestore(pmApp, 0,
- IBackupAgent.Stub.asInterface(pmAgent.onBind()));
+ mImage = images[0];
- // build the set of apps we will attempt to restore
- PackageInfo[] packages = mTransport.getAppSet(mImage.token);
- HashSet<RestoreRequest> appsToRestore = new HashSet<RestoreRequest>();
- for (PackageInfo pkg: packages) {
- // get the real PackageManager idea of the package
- PackageInfo app = isRestorable(pkg);
- if (app != null) {
- // Validate against the backed-up signature block, too
- Metadata info = pmAgent.getRestoredMetadata(app.packageName);
- if (info != null) {
- if (app.versionCode >= info.versionCode) {
- if (DEBUG) Log.v(TAG, "Restore version "
- + info.versionCode
- + " compatible with app version "
- + app.versionCode);
- if (signaturesMatch(info.signatures, app.signatures)) {
- appsToRestore.add(
- new RestoreRequest(app, info.versionCode));
- } else {
- Log.w(TAG, "Sig mismatch restoring "
- + app.packageName);
- }
- } else {
- Log.i(TAG, "Restore set for " + app.packageName
- + " is too new [" + info.versionCode
- + "] for installed app version "
- + app.versionCode);
- }
- } else {
- Log.d(TAG, "Unable to get metadata for "
- + app.packageName);
- }
- }
- }
+ // Get the list of all packages which have backup enabled.
+ // (Include the Package Manager metadata pseudo-package first.)
+ ArrayList<PackageInfo> restorePackages = new ArrayList<PackageInfo>();
+ PackageInfo omPackage = new PackageInfo();
+ omPackage.packageName = PACKAGE_MANAGER_SENTINEL;
+ restorePackages.add(omPackage);
- // now run the restore queue
- doQueuedRestores(appsToRestore);
+ List<PackageInfo> agentPackages = allAgentPackages();
+ restorePackages.addAll(agentPackages);
+
+ // STOPSHIP TODO: pick out the set for this token (instead of images[0])
+ long token = images[0].token;
+ if (!mTransport.startRestore(token, restorePackages.toArray(new PackageInfo[0]))) {
+ // STOPSHIP TODO: Handle the failure somehow?
+ Log.e(TAG, "Error starting restore operation");
+ return;
+ }
+
+ String packageName = mTransport.nextRestorePackage();
+ if (packageName == null) {
+ // STOPSHIP TODO: Handle the failure somehow?
+ Log.e(TAG, "Error getting first restore package");
+ return;
+ } else if (packageName.equals("")) {
+ Log.i(TAG, "No restore data available");
+ return;
+ } else if (!packageName.equals(PACKAGE_MANAGER_SENTINEL)) {
+ Log.e(TAG, "Expected restore data for \"" + PACKAGE_MANAGER_SENTINEL
+ + "\", found only \"" + packageName + "\"");
+ return;
+ }
+
+ // Pull the Package Manager metadata from the restore set first
+ PackageManagerBackupAgent pmAgent = new PackageManagerBackupAgent(
+ mPackageManager, agentPackages);
+ processOneRestore(omPackage, 0, IBackupAgent.Stub.asInterface(pmAgent.onBind()));
+
+ for (;;) {
+ packageName = mTransport.nextRestorePackage();
+ if (packageName == null) {
+ // STOPSHIP TODO: Handle the failure somehow?
+ Log.e(TAG, "Error getting next restore package");
+ return;
+ } else if (packageName.equals("")) {
+ break;
}
- } catch (RemoteException e) {
- // can't happen; transports run locally
- }
- // done; shut down the transport
- try {
- mTransport.endSession();
- } catch (Exception e) {
- Log.e(TAG, "Error ending transport for restore");
- e.printStackTrace();
- }
- }
+ Metadata metaInfo = pmAgent.getRestoredMetadata(packageName);
+ if (metaInfo == null) {
+ Log.e(TAG, "Missing metadata for " + packageName);
+ continue;
+ }
- // even if the initial session startup failed, report that we're done here
- }
+ int flags = PackageManager.GET_SIGNATURES;
+ PackageInfo packageInfo = mPackageManager.getPackageInfo(packageName, flags);
+ if (metaInfo.versionCode > packageInfo.versionCode) {
+ Log.w(TAG, "Package " + packageName
+ + " restore version [" + metaInfo.versionCode
+ + "] is too new for installed version ["
+ + packageInfo.versionCode + "]");
+ continue;
+ }
- // restore each app in the queue
- void doQueuedRestores(HashSet<RestoreRequest> appsToRestore) {
- for (RestoreRequest req : appsToRestore) {
- PackageInfo app = req.app;
- Log.d(TAG, "starting agent for restore of " + app);
+ if (!signaturesMatch(metaInfo.signatures, packageInfo.signatures)) {
+ Log.w(TAG, "Signature mismatch restoring " + packageName);
+ continue;
+ }
- try {
- // Remove the app's data first
- clearApplicationDataSynchronous(app.packageName);
+ if (DEBUG) Log.v(TAG, "Package " + packageName
+ + " restore version [" + metaInfo.versionCode
+ + "] is compatible with installed version ["
+ + packageInfo.versionCode + "]");
- // Now perform the restore into the clean app
- IBackupAgent agent = bindToAgentSynchronous(app.applicationInfo,
+ // Now perform the actual restore
+ clearApplicationDataSynchronous(packageName);
+ IBackupAgent agent = bindToAgentSynchronous(
+ packageInfo.applicationInfo,
IApplicationThread.BACKUP_MODE_RESTORE);
- if (agent != null) {
- processOneRestore(app, req.storedAppVersion, agent);
+ if (agent == null) {
+ Log.w(TAG, "Can't find backup agent for " + packageName);
+ continue;
}
- // unbind even on timeout, just in case
- mActivityManager.unbindBackupAgent(app.applicationInfo);
- } catch (SecurityException ex) {
- // Try for the next one.
- Log.d(TAG, "error in bind", ex);
- } catch (RemoteException e) {
- // can't happen
+ try {
+ processOneRestore(packageInfo, metaInfo.versionCode, agent);
+ } finally {
+ // unbind even on timeout or failure, just in case
+ mActivityManager.unbindBackupAgent(packageInfo.applicationInfo);
+ }
}
-
+ } catch (NameNotFoundException e) {
+ // STOPSHIP TODO: Handle the failure somehow?
+ Log.e(TAG, "Invalid paackage restoring data", e);
+ } catch (RemoteException e) {
+ // STOPSHIP TODO: Handle the failure somehow?
+ Log.e(TAG, "Error restoring data", e);
+ } finally {
+ try {
+ mTransport.finishRestore();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error finishing restore", e);
+ }
}
}
- // Do the guts of a restore of one application, derived from the 'mImage'
- // restore set via the 'mTransport' transport.
- void processOneRestore(PackageInfo app, int storedAppVersion, IBackupAgent agent) {
+ // Do the guts of a restore of one application, using mTransport.getRestoreData().
+ void processOneRestore(PackageInfo app, int appVersionCode, IBackupAgent agent) {
// !!! TODO: actually run the restore through mTransport
final String packageName = app.packageName;
@@ -954,11 +932,12 @@
// Run the transport's restore pass
// Run the target's backup pass
- int err = -1;
try {
- err = mTransport.getRestoreData(mImage.token, app, backupData);
- } catch (RemoteException e) {
- // can't happen
+ if (!mTransport.getRestoreData(backupData)) {
+ // STOPSHIP TODO: Handle this error somehow?
+ Log.e(TAG, "Error getting restore data for " + packageName);
+ return;
+ }
} finally {
backupData.close();
}
@@ -973,30 +952,18 @@
backupData = ParcelFileDescriptor.open(backupDataName,
ParcelFileDescriptor.MODE_READ_ONLY);
- boolean success = false;
try {
- agent.doRestore(backupData, storedAppVersion, newState);
- success = true;
- } catch (Exception e) {
- Log.e(TAG, "Restore failed for " + packageName);
- e.printStackTrace();
+ agent.doRestore(backupData, appVersionCode, newState);
} finally {
newState.close();
backupData.close();
}
// if everything went okay, remember the recorded state now
- if (success) {
- File savedStateName = new File(mStateDir, packageName);
- newStateName.renameTo(savedStateName);
- }
- } catch (FileNotFoundException fnfe) {
- Log.v(TAG, "Couldn't open file for restore: " + fnfe);
- } catch (IOException ioe) {
- Log.e(TAG, "Unable to process restore file: " + ioe);
+ File savedStateName = new File(mStateDir, packageName);
+ newStateName.renameTo(savedStateName);
} catch (Exception e) {
- Log.e(TAG, "Final exception guard in restore:");
- e.printStackTrace();
+ Log.e(TAG, "Error restoring data for " + packageName, e);
}
}
}
@@ -1206,7 +1173,7 @@
mContext.enforceCallingPermission("android.permission.BACKUP",
"endRestoreSession");
- mRestoreTransport.endSession();
+ mRestoreTransport.finishRestore();
mRestoreTransport = null;
synchronized(BackupManagerService.this) {
if (BackupManagerService.this.mActiveRestoreSession == this) {
diff --git a/services/java/com/android/server/PackageManagerBackupAgent.java b/services/java/com/android/server/PackageManagerBackupAgent.java
index d620eb1..66fb86d 100644
--- a/services/java/com/android/server/PackageManagerBackupAgent.java
+++ b/services/java/com/android/server/PackageManagerBackupAgent.java
@@ -57,7 +57,7 @@
// is stored using the package name as a key)
private static final String GLOBAL_METADATA_KEY = "@meta@";
- private List<ApplicationInfo> mAllApps;
+ private List<PackageInfo> mAllPackages;
private PackageManager mPackageManager;
private HashMap<String, Metadata> mRestoredSignatures;
@@ -73,9 +73,9 @@
// We're constructed with the set of applications that are participating
// in backup. This set changes as apps are installed & removed.
- PackageManagerBackupAgent(PackageManager packageMgr, List<ApplicationInfo> apps) {
+ PackageManagerBackupAgent(PackageManager packageMgr, List<PackageInfo> packages) {
mPackageManager = packageMgr;
- mAllApps = apps;
+ mAllPackages = packages;
mRestoredSignatures = null;
}
@@ -118,8 +118,8 @@
// For each app we have on device, see if we've backed it up yet. If not,
// write its signature block to the output, keyed on the package name.
- for (ApplicationInfo app : mAllApps) {
- String packName = app.packageName;
+ for (PackageInfo pkg : mAllPackages) {
+ String packName = pkg.packageName;
if (!existing.contains(packName)) {
// We haven't stored this app's signatures yet, so we do that now
try {
@@ -186,7 +186,7 @@
}
// Finally, write the new state blob -- just the list of all apps we handled
- writeStateFile(mAllApps, newState);
+ writeStateFile(mAllPackages, newState);
}
// "Restore" here is a misnomer. What we're really doing is reading back the
@@ -327,7 +327,7 @@
}
// Util: write out our new backup state file
- private void writeStateFile(List<ApplicationInfo> apps, ParcelFileDescriptor stateFile) {
+ private void writeStateFile(List<PackageInfo> pkgs, ParcelFileDescriptor stateFile) {
FileOutputStream outstream = new FileOutputStream(stateFile.getFileDescriptor());
DataOutputStream out = new DataOutputStream(outstream);
@@ -338,8 +338,8 @@
out.write(metaNameBuf);
// now write all the app names too
- for (ApplicationInfo app : apps) {
- byte[] pkgNameBuf = app.packageName.getBytes();
+ for (PackageInfo pkg : pkgs) {
+ byte[] pkgNameBuf = pkg.packageName.getBytes();
out.writeInt(pkgNameBuf.length);
out.write(pkgNameBuf);
}
diff --git a/services/java/com/android/server/PackageManagerService.java b/services/java/com/android/server/PackageManagerService.java
index 048669a..972aa98 100644
--- a/services/java/com/android/server/PackageManagerService.java
+++ b/services/java/com/android/server/PackageManagerService.java
@@ -968,35 +968,7 @@
if (Config.LOGV) Log.v(TAG, "getActivityInfo " + component + ": " + a);
if (a != null && mSettings.isEnabledLP(a.info, flags)) {
- ActivityInfo ainfo = PackageParser.generateActivityInfo(a, flags);
- if (ainfo != null) {
- ApplicationInfo appInfo = getApplicationInfo(component.getPackageName(),
- PackageManager.GET_SUPPORTS_DENSITIES);
- if (appInfo != null &&
- (appInfo.flags & ApplicationInfo.FLAG_SUPPORTS_LARGE_SCREENS) == 0) {
- // Check if the screen size is same as what the application expect.
- CompatibilityInfo info = new CompatibilityInfo(appInfo);
- DisplayMetrics metrics = new DisplayMetrics();
- metrics.setTo(mMetrics);
- int orientation = mMetrics.widthPixels > mMetrics.heightPixels ?
- Configuration.ORIENTATION_LANDSCAPE :
- Configuration.ORIENTATION_PORTRAIT;
- metrics.updateMetrics(info, orientation);
- if (!info.mExpandable) {
- // Don't allow an app that cannot expand to handle rotation.
- ainfo.configChanges &= ~ ActivityInfo.CONFIG_ORIENTATION;
- } else {
- appInfo.flags |= ApplicationInfo.FLAG_SUPPORTS_LARGE_SCREENS;
- }
- if (DEBUG_SETTINGS) {
- Log.d(TAG, "component=" + component +
- ", expandable:" +
- ((appInfo.flags &
- ApplicationInfo.FLAG_SUPPORTS_LARGE_SCREENS) != 0));
- }
- }
- }
- return ainfo;
+ return PackageParser.generateActivityInfo(a, flags);
}
if (mResolveComponentName.equals(component)) {
return mResolveActivity;
diff --git a/services/java/com/android/server/WallpaperService.java b/services/java/com/android/server/WallpaperService.java
index 5532894..d921baf 100644
--- a/services/java/com/android/server/WallpaperService.java
+++ b/services/java/com/android/server/WallpaperService.java
@@ -18,8 +18,10 @@
import static android.os.FileObserver.*;
import static android.os.ParcelFileDescriptor.*;
+
import android.app.IWallpaperService;
import android.app.IWallpaperServiceCallback;
+import android.backup.BackupManager;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
@@ -154,7 +156,16 @@
public ParcelFileDescriptor setWallpaper() {
checkPermission(android.Manifest.permission.SET_WALLPAPER);
try {
- return ParcelFileDescriptor.open(WALLPAPER_FILE, MODE_CREATE|MODE_READ_WRITE);
+ ParcelFileDescriptor fd = ParcelFileDescriptor.open(WALLPAPER_FILE,
+ MODE_CREATE|MODE_READ_WRITE);
+
+ // changing the wallpaper means we'll need to back up the new one
+ long origId = Binder.clearCallingIdentity();
+ BackupManager bm = new BackupManager(mContext);
+ bm.dataChanged();
+ Binder.restoreCallingIdentity(origId);
+
+ return fd;
} catch (FileNotFoundException e) {
if (Config.LOGD) Log.d(TAG, "Error setting wallpaper", e);
}
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index 62b4d5e..0d9d2b0 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -12601,51 +12601,63 @@
}
public boolean profileControl(String process, boolean start,
- String path) throws RemoteException {
+ String path, ParcelFileDescriptor fd) throws RemoteException {
- synchronized (this) {
- // note: hijacking SET_ACTIVITY_WATCHER, but should be changed to
- // its own permission.
- if (checkCallingPermission(android.Manifest.permission.SET_ACTIVITY_WATCHER)
- != PackageManager.PERMISSION_GRANTED) {
- throw new SecurityException("Requires permission "
- + android.Manifest.permission.SET_ACTIVITY_WATCHER);
- }
-
- ProcessRecord proc = null;
- try {
- int pid = Integer.parseInt(process);
- synchronized (mPidsSelfLocked) {
- proc = mPidsSelfLocked.get(pid);
+ try {
+ synchronized (this) {
+ // note: hijacking SET_ACTIVITY_WATCHER, but should be changed to
+ // its own permission.
+ if (checkCallingPermission(android.Manifest.permission.SET_ACTIVITY_WATCHER)
+ != PackageManager.PERMISSION_GRANTED) {
+ throw new SecurityException("Requires permission "
+ + android.Manifest.permission.SET_ACTIVITY_WATCHER);
}
- } catch (NumberFormatException e) {
- }
-
- if (proc == null) {
- HashMap<String, SparseArray<ProcessRecord>> all
- = mProcessNames.getMap();
- SparseArray<ProcessRecord> procs = all.get(process);
- if (procs != null && procs.size() > 0) {
- proc = procs.valueAt(0);
+
+ if (start && fd == null) {
+ throw new IllegalArgumentException("null fd");
}
- }
-
- if (proc == null || proc.thread == null) {
- throw new IllegalArgumentException("Unknown process: " + process);
- }
-
- boolean isSecure = "1".equals(SystemProperties.get(SYSTEM_SECURE, "0"));
- if (isSecure) {
- if ((proc.info.flags&ApplicationInfo.FLAG_DEBUGGABLE) == 0) {
- throw new SecurityException("Process not debuggable: " + proc);
+
+ ProcessRecord proc = null;
+ try {
+ int pid = Integer.parseInt(process);
+ synchronized (mPidsSelfLocked) {
+ proc = mPidsSelfLocked.get(pid);
+ }
+ } catch (NumberFormatException e) {
}
- }
+
+ if (proc == null) {
+ HashMap<String, SparseArray<ProcessRecord>> all
+ = mProcessNames.getMap();
+ SparseArray<ProcessRecord> procs = all.get(process);
+ if (procs != null && procs.size() > 0) {
+ proc = procs.valueAt(0);
+ }
+ }
+
+ if (proc == null || proc.thread == null) {
+ throw new IllegalArgumentException("Unknown process: " + process);
+ }
+
+ boolean isSecure = "1".equals(SystemProperties.get(SYSTEM_SECURE, "0"));
+ if (isSecure) {
+ if ((proc.info.flags&ApplicationInfo.FLAG_DEBUGGABLE) == 0) {
+ throw new SecurityException("Process not debuggable: " + proc);
+ }
+ }
- try {
- proc.thread.profilerControl(start, path);
+ proc.thread.profilerControl(start, path, fd);
+ fd = null;
return true;
- } catch (RemoteException e) {
- throw new IllegalStateException("Process disappeared");
+ }
+ } catch (RemoteException e) {
+ throw new IllegalStateException("Process disappeared");
+ } finally {
+ if (fd != null) {
+ try {
+ fd.close();
+ } catch (IOException e) {
+ }
}
}
}
diff --git a/tools/aidl/AST.h b/tools/aidl/AST.h
index 1dedd04..aec2164 100755
--- a/tools/aidl/AST.h
+++ b/tools/aidl/AST.h
@@ -5,6 +5,7 @@
#include <vector>
#include <set>
#include <stdarg.h>
+#include <stdio.h>
using namespace std;
diff --git a/tools/localize/Perforce.cpp b/tools/localize/Perforce.cpp
index 3425668..1c644ed 100644
--- a/tools/localize/Perforce.cpp
+++ b/tools/localize/Perforce.cpp
@@ -1,6 +1,7 @@
#include "Perforce.h"
#include "log.h"
#include <string.h>
+#include <cstdio>
#include <stdlib.h>
#include <sstream>
#include <sys/types.h>
diff --git a/tools/localize/SourcePos.cpp b/tools/localize/SourcePos.cpp
index 9d7c5c6..2533f0a 100644
--- a/tools/localize/SourcePos.cpp
+++ b/tools/localize/SourcePos.cpp
@@ -1,6 +1,7 @@
#include "SourcePos.h"
#include <stdarg.h>
+#include <cstdio>
#include <set>
using namespace std;
diff --git a/tools/localize/Values.cpp b/tools/localize/Values.cpp
index e396f8b..8623b97 100644
--- a/tools/localize/Values.cpp
+++ b/tools/localize/Values.cpp
@@ -1,5 +1,6 @@
#include "Values.h"
#include <stdlib.h>
+#include <cstdio>
// =====================================================================================
diff --git a/tools/localize/XLIFFFile.cpp b/tools/localize/XLIFFFile.cpp
index 51f81de..4e217d9 100644
--- a/tools/localize/XLIFFFile.cpp
+++ b/tools/localize/XLIFFFile.cpp
@@ -3,6 +3,7 @@
#include <algorithm>
#include <sys/time.h>
#include <time.h>
+#include <cstdio>
const char* const XLIFF_XMLNS = "urn:oasis:names:tc:xliff:document:1.2";
diff --git a/tools/localize/file_utils.cpp b/tools/localize/file_utils.cpp
index 8792b9e..293e50e 100644
--- a/tools/localize/file_utils.cpp
+++ b/tools/localize/file_utils.cpp
@@ -1,4 +1,5 @@
#include <string.h>
+#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include "file_utils.h"
diff --git a/tools/localize/localize_test.cpp b/tools/localize/localize_test.cpp
index 63d904c..678cad8 100644
--- a/tools/localize/localize_test.cpp
+++ b/tools/localize/localize_test.cpp
@@ -1,6 +1,7 @@
#include "XLIFFFile.h"
#include "ValuesFile.h"
#include "localize.h"
+#include <stdio.h>
int pseudolocalize_xliff(XLIFFFile* xliff, bool expand);
diff --git a/tools/localize/merge_res_and_xliff.cpp b/tools/localize/merge_res_and_xliff.cpp
index 58a6554..1fdaa0e 100644
--- a/tools/localize/merge_res_and_xliff.cpp
+++ b/tools/localize/merge_res_and_xliff.cpp
@@ -3,6 +3,7 @@
#include "file_utils.h"
#include "Perforce.h"
#include "log.h"
+#include <stdio.h>
static set<StringResource>::const_iterator
find_id(const set<StringResource>& s, const string& id, int index)
diff --git a/tools/localize/merge_res_and_xliff_test.cpp b/tools/localize/merge_res_and_xliff_test.cpp
index 5a2b0f4..e4ab562 100644
--- a/tools/localize/merge_res_and_xliff_test.cpp
+++ b/tools/localize/merge_res_and_xliff_test.cpp
@@ -1,5 +1,5 @@
#include "merge_res_and_xliff.h"
-
+#include <stdio.h>
int
merge_test()
diff --git a/tools/localize/xmb.cpp b/tools/localize/xmb.cpp
index 236705f..d8f6ff0 100644
--- a/tools/localize/xmb.cpp
+++ b/tools/localize/xmb.cpp
@@ -7,6 +7,7 @@
#include "XLIFFFile.h"
#include <map>
+#include <cstdio>
using namespace std;