Merge change 3800 into donut
* changes:
Add SUGGEST_SPINNER_WHILE_REFRESHING column constant to the SearchManager for use in implementing Google suggestion refreshing in global search.
diff --git a/core/java/android/backup/BackupManager.java b/core/java/android/backup/BackupManager.java
index c3b6a02..8df7eae 100644
--- a/core/java/android/backup/BackupManager.java
+++ b/core/java/android/backup/BackupManager.java
@@ -45,7 +45,7 @@
/**
* Defined backup transports understood by {@link IBackupManager.selectBackupTransport}.
*/
- public static final int TRANSPORT_ADB = 1;
+ public static final int TRANSPORT_LOCAL = 1;
public static final int TRANSPORT_GOOGLE = 2;
/**
diff --git a/core/java/com/android/internal/backup/AdbTransport.java b/core/java/com/android/internal/backup/AdbTransport.java
deleted file mode 100644
index 8d3bd1c..0000000
--- a/core/java/com/android/internal/backup/AdbTransport.java
+++ /dev/null
@@ -1,55 +0,0 @@
-package com.android.internal.backup;
-
-import android.backup.RestoreSet;
-import android.content.pm.PackageInfo;
-import android.os.ParcelFileDescriptor;
-import android.os.RemoteException;
-
-/**
- * Backup transport for full backup over adb. This transport pipes everything to
- * a file in a known location in /cache, which 'adb backup' then pulls to the desktop
- * (deleting it afterwards).
- */
-
-public class AdbTransport extends IBackupTransport.Stub {
-
- public long requestBackupTime() throws RemoteException {
- return 0;
- }
-
- public int startSession() throws RemoteException {
- // TODO Auto-generated method stub
- return 0;
- }
-
- public int endSession() throws RemoteException {
- // TODO Auto-generated method stub
- return 0;
- }
-
- public int performBackup(PackageInfo packageInfo, ParcelFileDescriptor data)
- throws RemoteException {
- // TODO Auto-generated method stub
- return 0;
- }
-
- // Restore handling
- public RestoreSet[] getAvailableRestoreSets() throws android.os.RemoteException {
- RestoreSet[] set = new RestoreSet[1];
- set[0].device = "USB";
- set[0].name = "adb";
- set[0].token = 0;
- return set;
- }
-
- public PackageInfo[] getAppSet(int token) throws android.os.RemoteException {
- // !!! TODO: real implementation
- return new PackageInfo[0];
- }
-
- public int getRestoreData(int token, PackageInfo packageInfo, ParcelFileDescriptor data)
- throws android.os.RemoteException {
- // !!! TODO: real implementation
- return 0;
- }
-}
diff --git a/core/java/com/android/internal/backup/LocalTransport.java b/core/java/com/android/internal/backup/LocalTransport.java
new file mode 100644
index 0000000..62fba4a
--- /dev/null
+++ b/core/java/com/android/internal/backup/LocalTransport.java
@@ -0,0 +1,140 @@
+package com.android.internal.backup;
+
+import android.backup.RestoreSet;
+import android.content.Context;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.os.Environment;
+import android.os.ParcelFileDescriptor;
+import android.os.RemoteException;
+import android.util.Log;
+
+import java.io.File;
+import java.io.FileFilter;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+
+/**
+ * Backup transport for stashing stuff into a known location on disk, and
+ * later restoring from there. For testing only.
+ */
+
+public class LocalTransport extends IBackupTransport.Stub {
+ private static final String TAG = "LocalTransport";
+ private static final String DATA_FILE_NAME = "data";
+
+ 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();
+ }
+ };
+
+
+ public LocalTransport(Context context) {
+ mContext = context;
+ mPackageManager = context.getPackageManager();
+ }
+
+ public long requestBackupTime() throws RemoteException {
+ // any time is a good time for local backup
+ return 0;
+ }
+
+ public int startSession() throws RemoteException {
+ return 0;
+ }
+
+ public int endSession() throws RemoteException {
+ return 0;
+ }
+
+ public int performBackup(PackageInfo packageInfo, ParcelFileDescriptor data)
+ throws RemoteException {
+ File packageDir = new File(mDataDir, packageInfo.packageName);
+ File imageFileName = new File(packageDir, DATA_FILE_NAME);
+
+ //!!! TODO: process the (partial) update into the persistent restore set:
+
+ // Parse out the existing image file into the key/value map
+
+ // Parse out the backup data into the key/value updates
+
+ // Apply the backup key/value updates to the image
+
+ // Write out the image in the canonical format
+
+ return -1;
+ }
+
+ // Restore handling
+ public RestoreSet[] getAvailableRestoreSets() throws android.os.RemoteException {
+ // one hardcoded restore set
+ RestoreSet[] set = new RestoreSet[1];
+ set[0].device = "flash";
+ set[0].name = "Local disk image";
+ set[0].token = 0;
+ return set;
+ }
+
+ public PackageInfo[] getAppSet(int token) throws android.os.RemoteException {
+ // 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.
+ }
+ }
+
+ 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 int getRestoreData(int token, PackageInfo packageInfo, ParcelFileDescriptor output)
+ throws android.os.RemoteException {
+ // we only support one hardcoded restore set
+ if (token != 0) return -1;
+
+ // the data for a given package is at a known location
+ File packageDir = new File(mDataDir, packageInfo.packageName);
+ File imageFile = new File(packageDir, DATA_FILE_NAME);
+
+ // restore is relatively easy: we already maintain the full data set in
+ // the canonical form understandable to the BackupAgent
+ return copyFileToFD(imageFile, output);
+ }
+
+ private int copyFileToFD(File source, ParcelFileDescriptor dest) {
+ try {
+ FileInputStream in = new FileInputStream(source);
+ FileOutputStream out = new FileOutputStream(dest.getFileDescriptor());
+ byte[] buffer = new byte[4096];
+ int bytesRead;
+ while ((bytesRead = in.read(buffer)) >= 0) {
+ out.write(buffer, 0, bytesRead);
+ }
+ } catch (IOException e) {
+ // something went wrong; claim failure
+ return -1;
+ }
+ return 0;
+ }
+}
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 99a381c..16a3bad 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -30,9 +30,11 @@
import android.util.Printer;
import android.util.SparseArray;
+import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
+import java.io.FileReader;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -53,7 +55,7 @@
private static final int MAGIC = 0xBA757475; // 'BATSTATS'
// Current on-disk Parcel version
- private static final int VERSION = 38;
+ private static final int VERSION = 39;
private final File mFile;
private final File mBackupFile;
@@ -94,7 +96,7 @@
boolean mScreenOn;
StopwatchTimer mScreenOnTimer;
-
+
int mScreenBrightnessBin = -1;
final StopwatchTimer[] mScreenBrightnessTimer = new StopwatchTimer[NUM_SCREEN_BRIGHTNESS_BINS];
@@ -137,10 +139,10 @@
long mTrackBatteryUptimeStart;
long mTrackBatteryPastRealtime;
long mTrackBatteryRealtimeStart;
-
+
long mUnpluggedBatteryUptime;
long mUnpluggedBatteryRealtime;
-
+
/*
* These keep track of battery levels (1-100) at the last plug event and the last unplug event.
*/
@@ -149,6 +151,15 @@
long mLastWriteTime = 0; // Milliseconds
+ // Mobile data transferred while on battery
+ private long[] mMobileDataTx = new long[4];
+ private long[] mMobileDataRx = new long[4];
+ private long[] mTotalDataTx = new long[4];
+ private long[] mTotalDataRx = new long[4];
+
+ private long mRadioDataUptime;
+ private long mRadioDataStart;
+
/*
* Holds a SamplingTimer associated with each kernel wakelock name being tracked.
*/
@@ -893,7 +904,40 @@
}
return kwlt;
}
-
+
+ private void doDataPlug(long[] dataTransfer, long currentBytes) {
+ dataTransfer[STATS_LAST] = dataTransfer[STATS_UNPLUGGED];
+ dataTransfer[STATS_UNPLUGGED] = -1;
+ }
+
+ private void doDataUnplug(long[] dataTransfer, long currentBytes) {
+ dataTransfer[STATS_UNPLUGGED] = currentBytes;
+ }
+
+ private long getCurrentRadioDataUptimeMs() {
+ try {
+ File awakeTimeFile = new File("/sys/devices/virtual/net/rmnet0/awake_time_ms");
+ if (!awakeTimeFile.exists()) return 0;
+ BufferedReader br = new BufferedReader(new FileReader(awakeTimeFile));
+ String line = br.readLine();
+ br.close();
+ return Long.parseLong(line);
+ } catch (NumberFormatException nfe) {
+ // Nothing
+ } catch (IOException ioe) {
+ // Nothing
+ }
+ return 0;
+ }
+
+ public long getRadioDataUptimeMs() {
+ if (mRadioDataStart == -1) {
+ return mRadioDataUptime;
+ } else {
+ return getCurrentRadioDataUptimeMs() - mRadioDataStart;
+ }
+ }
+
public void doUnplug(long batteryUptime, long batteryRealtime) {
for (int iu = mUidStats.size() - 1; iu >= 0; iu--) {
Uid u = mUidStats.valueAt(iu);
@@ -905,8 +949,16 @@
for (int i = mUnpluggables.size() - 1; i >= 0; i--) {
mUnpluggables.get(i).unplug(batteryUptime, batteryRealtime);
}
+ // Track total mobile data
+ doDataUnplug(mMobileDataRx, NetStat.getMobileRxBytes());
+ doDataUnplug(mMobileDataTx, NetStat.getMobileTxBytes());
+ doDataUnplug(mTotalDataRx, NetStat.getTotalRxBytes());
+ doDataUnplug(mTotalDataTx, NetStat.getTotalTxBytes());
+ // Track radio awake time
+ mRadioDataStart = getCurrentRadioDataUptimeMs();
+ mRadioDataUptime = 0;
}
-
+
public void doPlug(long batteryUptime, long batteryRealtime) {
for (int iu = mUidStats.size() - 1; iu >= 0; iu--) {
Uid u = mUidStats.valueAt(iu);
@@ -922,8 +974,15 @@
for (int i = mUnpluggables.size() - 1; i >= 0; i--) {
mUnpluggables.get(i).plug(batteryUptime, batteryRealtime);
}
+ doDataPlug(mMobileDataRx, NetStat.getMobileRxBytes());
+ doDataPlug(mMobileDataTx, NetStat.getMobileTxBytes());
+ doDataPlug(mTotalDataRx, NetStat.getTotalRxBytes());
+ doDataPlug(mTotalDataTx, NetStat.getTotalTxBytes());
+ // Track radio awake time
+ mRadioDataUptime = getRadioDataUptimeMs();
+ mRadioDataStart = -1;
}
-
+
public void noteStartGps(int uid) {
mUidStats.get(uid).noteStartGps();
}
@@ -931,7 +990,7 @@
public void noteStopGps(int uid) {
mUidStats.get(uid).noteStopGps();
}
-
+
public void noteScreenOnLocked() {
if (!mScreenOn) {
mScreenOn = true;
@@ -1039,6 +1098,7 @@
break;
}
}
+ if (DEBUG) Log.i(TAG, "Phone Data Connection -> " + dataType + " = " + hasData);
if (mPhoneDataConnectionType != bin) {
if (mPhoneDataConnectionType >= 0) {
mPhoneDataConnectionsTimer[mPhoneDataConnectionType].stopRunningLocked(this);
@@ -2701,7 +2761,44 @@
public long getBatteryRealtime(long curTime) {
return getBatteryRealtimeLocked(curTime);
}
-
+
+ private long getTcpBytes(long current, long[] dataBytes, int which) {
+ if (which == STATS_LAST) {
+ return dataBytes[STATS_LAST];
+ } else {
+ if (which == STATS_UNPLUGGED) {
+ if (dataBytes[STATS_UNPLUGGED] < 0) {
+ return dataBytes[STATS_LAST];
+ } else {
+ return current - dataBytes[STATS_UNPLUGGED];
+ }
+ } else if (which == STATS_TOTAL) {
+ return (current - dataBytes[STATS_CURRENT]) + dataBytes[STATS_TOTAL];
+ }
+ return current - dataBytes[STATS_CURRENT];
+ }
+ }
+
+ /** Only STATS_UNPLUGGED works properly */
+ public long getMobileTcpBytesSent(int which) {
+ return getTcpBytes(NetStat.getMobileTxBytes(), mMobileDataTx, which);
+ }
+
+ /** Only STATS_UNPLUGGED works properly */
+ public long getMobileTcpBytesReceived(int which) {
+ return getTcpBytes(NetStat.getMobileRxBytes(), mMobileDataRx, which);
+ }
+
+ /** Only STATS_UNPLUGGED works properly */
+ public long getTotalTcpBytesSent(int which) {
+ return getTcpBytes(NetStat.getTotalTxBytes(), mTotalDataTx, which);
+ }
+
+ /** Only STATS_UNPLUGGED works properly */
+ public long getTotalTcpBytesReceived(int which) {
+ return getTcpBytes(NetStat.getTotalRxBytes(), mTotalDataRx, which);
+ }
+
@Override
public int getDischargeStartLevel() {
synchronized(this) {
@@ -3227,6 +3324,18 @@
mDischargeCurrentLevel = in.readInt();
mLastWriteTime = in.readLong();
+ mMobileDataRx[STATS_LAST] = in.readLong();
+ mMobileDataRx[STATS_UNPLUGGED] = -1;
+ mMobileDataTx[STATS_LAST] = in.readLong();
+ mMobileDataTx[STATS_UNPLUGGED] = -1;
+ mTotalDataRx[STATS_LAST] = in.readLong();
+ mTotalDataRx[STATS_UNPLUGGED] = -1;
+ mTotalDataTx[STATS_LAST] = in.readLong();
+ mTotalDataTx[STATS_UNPLUGGED] = -1;
+
+ mRadioDataUptime = in.readLong();
+ mRadioDataStart = -1;
+
mKernelWakelockStats.clear();
int NKW = in.readInt();
for (int ikw = 0; ikw < NKW; ikw++) {
@@ -3301,6 +3410,14 @@
out.writeInt(mDischargeCurrentLevel);
out.writeLong(mLastWriteTime);
+ out.writeLong(getMobileTcpBytesReceived(STATS_UNPLUGGED));
+ out.writeLong(getMobileTcpBytesSent(STATS_UNPLUGGED));
+ out.writeLong(getTotalTcpBytesReceived(STATS_UNPLUGGED));
+ out.writeLong(getTotalTcpBytesSent(STATS_UNPLUGGED));
+
+ // Write radio uptime for data
+ out.writeLong(getRadioDataUptimeMs());
+
out.writeInt(mKernelWakelockStats.size());
for (Map.Entry<String, SamplingTimer> ent : mKernelWakelockStats.entrySet()) {
SamplingTimer kwlt = ent.getValue();
diff --git a/core/java/com/android/internal/os/PowerProfile.java b/core/java/com/android/internal/os/PowerProfile.java
index f08dddd..a37bf6e 100644
--- a/core/java/com/android/internal/os/PowerProfile.java
+++ b/core/java/com/android/internal/os/PowerProfile.java
@@ -26,6 +26,7 @@
import org.xmlpull.v1.XmlPullParserException;
import java.io.IOException;
+import java.util.ArrayList;
import java.util.HashMap;
/**
@@ -118,26 +119,28 @@
*/
public static final String POWER_VIDEO = "dsp.video";
- static final HashMap<String, Double> sPowerMap = new HashMap<String, Double>();
+ static final HashMap<String, Object> sPowerMap = new HashMap<String, Object>();
private static final String TAG_DEVICE = "device";
private static final String TAG_ITEM = "item";
+ private static final String TAG_ARRAY = "array";
+ private static final String TAG_ARRAYITEM = "value";
private static final String ATTR_NAME = "name";
- public PowerProfile(Context context, CharSequence profile) {
+ public PowerProfile(Context context) {
// Read the XML file for the given profile (normally only one per
// device)
if (sPowerMap.size() == 0) {
- readPowerValuesFromXml(context, profile);
+ readPowerValuesFromXml(context);
}
}
- private void readPowerValuesFromXml(Context context, CharSequence profile) {
- // FIXME
- //int id = context.getResources().getIdentifier(profile.toString(), "xml",
- // "com.android.internal");
- int id = com.android.internal.R.xml.power_profile_default;
+ private void readPowerValuesFromXml(Context context) {
+ int id = com.android.internal.R.xml.power_profile;
XmlResourceParser parser = context.getResources().getXml(id);
+ boolean parsingArray = false;
+ ArrayList<Double> array = new ArrayList<Double>();
+ String arrayName = null;
try {
XmlUtils.beginDocument(parser, TAG_DEVICE);
@@ -145,22 +148,39 @@
while (true) {
XmlUtils.nextElement(parser);
- String element = parser.getName();
- if (element == null || !(element.equals(TAG_ITEM))) {
- break;
+ String element = parser.getName();
+ if (element == null) break;
+
+ if (parsingArray && !element.equals(TAG_ARRAYITEM)) {
+ // Finish array
+ sPowerMap.put(arrayName, array.toArray(new Double[array.size()]));
+ parsingArray = false;
}
-
- String name = parser.getAttributeValue(null, ATTR_NAME);
- if (parser.next() == XmlPullParser.TEXT) {
- String power = parser.getText();
- double value = 0;
- try {
- value = Double.valueOf(power);
- } catch (NumberFormatException nfe) {
+ if (element.equals(TAG_ARRAY)) {
+ parsingArray = true;
+ array.clear();
+ arrayName = parser.getAttributeValue(null, ATTR_NAME);
+ } else if (element.equals(TAG_ITEM) || element.equals(TAG_ARRAYITEM)) {
+ String name = null;
+ if (!parsingArray) name = parser.getAttributeValue(null, ATTR_NAME);
+ if (parser.next() == XmlPullParser.TEXT) {
+ String power = parser.getText();
+ double value = 0;
+ try {
+ value = Double.valueOf(power);
+ } catch (NumberFormatException nfe) {
+ }
+ if (element.equals(TAG_ITEM)) {
+ sPowerMap.put(name, value);
+ } else if (parsingArray) {
+ array.add(value);
+ }
}
- sPowerMap.put(name, value);
}
}
+ if (parsingArray) {
+ sPowerMap.put(arrayName, array.toArray(new Double[array.size()]));
+ }
} catch (XmlPullParserException e) {
throw new RuntimeException(e);
} catch (IOException e) {
@@ -177,7 +197,40 @@
*/
public double getAveragePower(String type) {
if (sPowerMap.containsKey(type)) {
- return sPowerMap.get(type);
+ Object data = sPowerMap.get(type);
+ if (data instanceof Double[]) {
+ return ((Double[])data)[0];
+ } else {
+ return (Double) sPowerMap.get(type);
+ }
+ } else {
+ return 0;
+ }
+ }
+
+ /**
+ * Returns the average current in mA consumed by the subsystem for the given level.
+ * @param type the subsystem type
+ * @param level the level of power at which the subsystem is running. For instance, the
+ * signal strength of the cell network between 0 and 4 (if there are 4 bars max.).
+ * If there is no data for multiple levels, the level is ignored.
+ * @return the average current in milliAmps.
+ */
+ public double getAveragePower(String type, int level) {
+ if (sPowerMap.containsKey(type)) {
+ Object data = sPowerMap.get(type);
+ if (data instanceof double[]) {
+ final double[] values = (double[]) data;
+ if (values.length > level) {
+ return values[level];
+ } else if (values.length < 0) {
+ return values[0];
+ } else {
+ return values[values.length - 1];
+ }
+ } else {
+ return (Double) data;
+ }
} else {
return 0;
}
diff --git a/core/res/res/xml/power_profile.xml b/core/res/res/xml/power_profile.xml
new file mode 100644
index 0000000..859902e
--- /dev/null
+++ b/core/res/res/xml/power_profile.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** Copyright 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.
+*/
+-->
+
+<device name="Android">
+ <item name="none">0</item>
+ <item name="screen.on">0.1</item>
+ <item name="bluetooth.active">0.1</item>
+ <item name="bluetooth.on">0.1</item>
+ <item name="screen.full">0.1</item>
+ <item name="wifi.on">0.1</item>
+ <item name="wifi.active">0.1</item>
+ <item name="wifi.scan">0.1</item>
+ <item name="cpu.idle">0.1</item>
+ <item name="cpu.normal">0.2</item>
+ <item name="cpu.full">1</item>
+ <item name="dsp.audio">0.1</item>
+ <item name="dsp.video">0.1</item>
+ <item name="radio.active">1</item>
+ <item name="gps.on">1</item>
+ <array name="radio.on"> <!-- Strength 0 to BINS-1 -->
+ <value>1</value>
+ <value>0.1</value>
+ </array>
+</device>
diff --git a/core/res/res/xml/power_profile_default.xml b/core/res/res/xml/power_profile_default.xml
deleted file mode 100644
index ceecb1a..0000000
--- a/core/res/res/xml/power_profile_default.xml
+++ /dev/null
@@ -1,37 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-**
-** Copyright 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.
-*/
--->
-
-<device name="Android">
- <item name="none">0</item>
- <item name="screen.on">30</item>
- <item name="bluetooth.active">103</item>
- <item name="bluetooth.on">5</item>
- <item name="screen.full">114</item>
- <item name="wifi.on">23</item>
- <item name="wifi.active">200</item>
- <item name="wifi.scan">200</item>
- <item name="cpu.idle">1.6</item>
- <item name="cpu.normal">100</item>
- <item name="cpu.full">140</item>
- <item name="dsp.audio">70</item>
- <item name="dsp.video">100</item>
- <item name="radio.on">3</item>
- <item name="radio.active">175</item>
- <item name="gps.on">120</item>
-</device>
diff --git a/docs/html/search.jd b/docs/html/search.jd
index 0a802a6..defba30 100644
--- a/docs/html/search.jd
+++ b/docs/html/search.jd
@@ -2,7 +2,7 @@
@jd:body
<script src="http://www.google.com/jsapi" type="text/javascript"></script>
-<script src="/assets/jquery-history.js" type="text/javascript"></script>
+<script src="{@docRoot}assets/jquery-history.js" type="text/javascript"></script>
<script type="text/javascript">
google.load('search', '1');
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaPlayerApiTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaPlayerApiTest.java
index e01bd53..ea42f53 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaPlayerApiTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaPlayerApiTest.java
@@ -80,7 +80,6 @@
assertTrue("MIDI getDuration", duratoinWithinTolerence);
}
- @Suppress
@MediumTest
public void testWMA9GetDuration() throws Exception {
int duration = CodecTest.getDuration(MediaNames.WMA9);
@@ -122,7 +121,6 @@
assertTrue("MIDI GetCurrentPosition", currentPosition);
}
- @Suppress
@LargeTest
public void testWMA9GetCurrentPosition() throws Exception {
boolean currentPosition = CodecTest.getCurrentPosition(MediaNames.WMA9);
@@ -160,7 +158,6 @@
assertTrue("MIDI Pause", isPaused);
}
- @Suppress
@LargeTest
public void testWMA9Pause() throws Exception {
boolean isPaused = CodecTest.pause(MediaNames.WMA9);
@@ -232,7 +229,6 @@
assertTrue("MIDI setLooping", isLoop);
}
- @Suppress
@LargeTest
public void testWMA9SetLooping() throws Exception {
boolean isLoop = CodecTest.setLooping(MediaNames.WMA9);
@@ -271,7 +267,6 @@
assertTrue("MIDI seekTo", isLoop);
}
- @Suppress
@LargeTest
public void testWMA9SeekTo() throws Exception {
boolean isLoop = CodecTest.seekTo(MediaNames.WMA9);
@@ -310,7 +305,7 @@
boolean isEnd = CodecTest.seekToEnd(MediaNames.MIDI);
assertTrue("MIDI seekToEnd", isEnd);
}
-
+
@Suppress
@LargeTest
public void testWMA9SeekToEnd() throws Exception {
@@ -388,7 +383,6 @@
assertTrue("H264AMR SeekTo", isSeek);
}
- @Suppress
@LargeTest
public void testVideoWMVSeekTo() throws Exception {
boolean isSeek = CodecTest.videoSeekTo(MediaNames.VIDEO_WMV);
diff --git a/packages/TtsService/src/android/tts/TtsService.java b/packages/TtsService/src/android/tts/TtsService.java
index c2c76fd..8a64113 100755
--- a/packages/TtsService/src/android/tts/TtsService.java
+++ b/packages/TtsService/src/android/tts/TtsService.java
@@ -117,16 +117,7 @@
// app.
prefs = PreferenceManager.getDefaultSharedPreferences(this);
- PackageManager pm = this.getPackageManager();
- String soLibPath = "";
- try {
- soLibPath = pm.getApplicationInfo("com.svox.pico", 0).dataDir;
- } catch (NameNotFoundException e) {
- // This exception cannot actually happen as com.svox.pico is
- // included with the system image.
- e.printStackTrace();
- }
- soLibPath = soLibPath + "/lib/libttspico.so";
+ String soLibPath = "/system/lib/libttspico.so";
nativeSynth = new SynthProxy(soLibPath);
mSelf = this;
diff --git a/services/java/com/android/server/BackupManagerService.java b/services/java/com/android/server/BackupManagerService.java
index 42a895c..d3067ec 100644
--- a/services/java/com/android/server/BackupManagerService.java
+++ b/services/java/com/android/server/BackupManagerService.java
@@ -25,6 +25,7 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.ApplicationInfo;
+import android.content.pm.IPackageDataObserver;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
@@ -46,13 +47,14 @@
import android.backup.BackupManager;
import android.backup.RestoreSet;
-import com.android.internal.backup.AdbTransport;
+import com.android.internal.backup.LocalTransport;
import com.android.internal.backup.GoogleTransport;
import com.android.internal.backup.IBackupTransport;
import java.io.File;
import java.io.FileDescriptor;
import java.io.FileNotFoundException;
+import java.io.IOException;
import java.io.PrintWriter;
import java.lang.String;
import java.util.ArrayList;
@@ -70,7 +72,11 @@
private static final int MSG_RUN_BACKUP = 1;
private static final int MSG_RUN_FULL_BACKUP = 2;
-
+ private static final int MSG_RUN_RESTORE = 3;
+
+ // Timeout interval for deciding that a bind or clear-data has taken too long
+ static final long TIMEOUT_INTERVAL = 10 * 1000;
+
private Context mContext;
private PackageManager mPackageManager;
private final IActivityManager mActivityManager;
@@ -108,6 +114,10 @@
private IBackupAgent mConnectedAgent;
private volatile boolean mConnecting;
+ // A similar synchronicity mechanism around clearing apps' data for restore
+ private final Object mClearDataLock = new Object();
+ private volatile boolean mClearingData;
+
private int mTransportId;
private File mStateDir;
@@ -122,7 +132,9 @@
mStateDir = new File(Environment.getDataDirectory(), "backup");
mStateDir.mkdirs();
mDataDir = Environment.getDownloadCacheDirectory();
- mTransportId = BackupManager.TRANSPORT_GOOGLE;
+
+ //!!! TODO: default to cloud transport, not local
+ mTransportId = BackupManager.TRANSPORT_LOCAL;
// Build our mapping of uid to backup client services
synchronized (mBackupParticipants) {
@@ -203,81 +215,15 @@
case MSG_RUN_FULL_BACKUP:
break;
+
+ case MSG_RUN_RESTORE:
+ {
+ int token = msg.arg1;
+ IBackupTransport transport = (IBackupTransport)msg.obj;
+ (new PerformRestoreThread(transport, token)).run();
+ break;
}
- }
- }
-
- void processOneBackup(BackupRequest request, IBackupAgent agent, IBackupTransport transport) {
- final String packageName = request.appInfo.packageName;
- Log.d(TAG, "processOneBackup doBackup() on " + packageName);
-
- try {
- // Look up the package info & signatures. This is first so that if it
- // throws an exception, there's no file setup yet that would need to
- // be unraveled.
- PackageInfo packInfo = mPackageManager.getPackageInfo(packageName,
- PackageManager.GET_SIGNATURES);
-
- // !!! TODO: get the state file dir from the transport
- File savedStateName = new File(mStateDir, packageName);
- File backupDataName = new File(mDataDir, packageName + ".data");
- File newStateName = new File(mStateDir, packageName + ".new");
-
- // In a full backup, we pass a null ParcelFileDescriptor as
- // the saved-state "file"
- ParcelFileDescriptor savedState = (request.fullBackup) ? null
- : ParcelFileDescriptor.open(savedStateName,
- ParcelFileDescriptor.MODE_READ_ONLY |
- ParcelFileDescriptor.MODE_CREATE);
-
- backupDataName.delete();
- ParcelFileDescriptor backupData =
- ParcelFileDescriptor.open(backupDataName,
- ParcelFileDescriptor.MODE_READ_WRITE |
- ParcelFileDescriptor.MODE_CREATE);
-
- newStateName.delete();
- ParcelFileDescriptor newState =
- ParcelFileDescriptor.open(newStateName,
- ParcelFileDescriptor.MODE_READ_WRITE |
- ParcelFileDescriptor.MODE_CREATE);
-
- // Run the target's backup pass
- boolean success = false;
- try {
- agent.doBackup(savedState, backupData, newState);
- success = true;
- } finally {
- if (savedState != null) {
- savedState.close();
- }
- backupData.close();
- newState.close();
}
-
- // Now propagate the newly-backed-up data to the transport
- if (success) {
- if (DEBUG) Log.v(TAG, "doBackup() success; calling transport");
- backupData =
- ParcelFileDescriptor.open(backupDataName, ParcelFileDescriptor.MODE_READ_ONLY);
- int error = transport.performBackup(packInfo, backupData);
-
- // !!! 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();
}
}
@@ -396,9 +342,9 @@
private IBackupTransport createTransport(int transportID) {
IBackupTransport transport = null;
switch (transportID) {
- case BackupManager.TRANSPORT_ADB:
- if (DEBUG) Log.v(TAG, "Initializing adb transport");
- transport = new AdbTransport();
+ case BackupManager.TRANSPORT_LOCAL:
+ if (DEBUG) Log.v(TAG, "Initializing local transport");
+ transport = new LocalTransport(mContext);
break;
case BackupManager.TRANSPORT_GOOGLE:
@@ -424,11 +370,14 @@
Log.d(TAG, "awaiting agent for " + app);
// success; wait for the agent to arrive
- while (mConnecting && mConnectedAgent == null) {
+ // only wait 10 seconds for the clear data to happen
+ long timeoutMark = System.currentTimeMillis() + TIMEOUT_INTERVAL;
+ while (mConnecting && mConnectedAgent == null
+ && (System.currentTimeMillis() < timeoutMark)) {
try {
- mAgentConnectLock.wait(10000);
+ mAgentConnectLock.wait(5000);
} catch (InterruptedException e) {
- // just retry
+ // just bail
return null;
}
}
@@ -447,6 +396,37 @@
return agent;
}
+ // clear an application's data, blocking until the operation completes or times out
+ void clearApplicationDataSynchronous(String packageName) {
+ ClearDataObserver observer = new ClearDataObserver();
+
+ synchronized(mClearDataLock) {
+ mClearingData = true;
+ mPackageManager.clearApplicationUserData(packageName, observer);
+
+ // only wait 10 seconds for the clear data to happen
+ long timeoutMark = System.currentTimeMillis() + TIMEOUT_INTERVAL;
+ while (mClearingData && (System.currentTimeMillis() < timeoutMark)) {
+ try {
+ mClearDataLock.wait(5000);
+ } catch (InterruptedException e) {
+ // won't happen, but still.
+ mClearingData = false;
+ }
+ }
+ }
+ }
+
+ class ClearDataObserver extends IPackageDataObserver.Stub {
+ public void onRemoveCompleted(String packageName, boolean succeeded)
+ throws android.os.RemoteException {
+ synchronized(mClearDataLock) {
+ mClearingData = false;
+ notifyAll();
+ }
+ }
+ }
+
// ----- Back up a set of applications via a worker thread -----
class PerformBackupThread extends Thread {
@@ -508,13 +488,88 @@
mActivityManager.unbindBackupAgent(request.appInfo);
} catch (SecurityException ex) {
// Try for the next one.
- Log.d(TAG, "error in bind", ex);
+ Log.d(TAG, "error in bind/backup", ex);
} catch (RemoteException e) {
- // can't happen
+ Log.v(TAG, "bind/backup threw");
+ e.printStackTrace();
}
}
}
+
+ void processOneBackup(BackupRequest request, IBackupAgent agent, IBackupTransport transport) {
+ final String packageName = request.appInfo.packageName;
+ Log.d(TAG, "processOneBackup doBackup() on " + packageName);
+
+ try {
+ // Look up the package info & signatures. This is first so that if it
+ // throws an exception, there's no file setup yet that would need to
+ // be unraveled.
+ PackageInfo packInfo = mPackageManager.getPackageInfo(packageName,
+ PackageManager.GET_SIGNATURES);
+
+ // !!! TODO: get the state file dir from the transport
+ File savedStateName = new File(mStateDir, packageName);
+ File backupDataName = new File(mDataDir, packageName + ".data");
+ File newStateName = new File(mStateDir, packageName + ".new");
+
+ // In a full backup, we pass a null ParcelFileDescriptor as
+ // the saved-state "file"
+ ParcelFileDescriptor savedState = (request.fullBackup) ? null
+ : ParcelFileDescriptor.open(savedStateName,
+ ParcelFileDescriptor.MODE_READ_ONLY |
+ ParcelFileDescriptor.MODE_CREATE);
+
+ backupDataName.delete();
+ ParcelFileDescriptor backupData =
+ ParcelFileDescriptor.open(backupDataName,
+ ParcelFileDescriptor.MODE_READ_WRITE |
+ ParcelFileDescriptor.MODE_CREATE);
+
+ newStateName.delete();
+ ParcelFileDescriptor newState =
+ ParcelFileDescriptor.open(newStateName,
+ ParcelFileDescriptor.MODE_READ_WRITE |
+ ParcelFileDescriptor.MODE_CREATE);
+
+ // Run the target's backup pass
+ boolean success = false;
+ try {
+ agent.doBackup(savedState, backupData, newState);
+ success = true;
+ } finally {
+ if (savedState != null) {
+ savedState.close();
+ }
+ backupData.close();
+ newState.close();
+ }
+
+ // Now propagate the newly-backed-up data to the transport
+ if (success) {
+ if (DEBUG) Log.v(TAG, "doBackup() success; calling transport");
+ backupData =
+ ParcelFileDescriptor.open(backupDataName, ParcelFileDescriptor.MODE_READ_ONLY);
+ int error = transport.performBackup(packInfo, backupData);
+
+ // !!! 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();
+ }
+ }
}
@@ -524,12 +579,12 @@
// ApplicationInfo struct if it is; null if not.
//
// !!! TODO: also consider signatures
- ApplicationInfo isRestorable(PackageInfo packageInfo) {
+ PackageInfo isRestorable(PackageInfo packageInfo) {
if (packageInfo.packageName != null) {
try {
- ApplicationInfo app = mPackageManager.getApplicationInfo(packageInfo.packageName,
+ PackageInfo app = mPackageManager.getPackageInfo(packageInfo.packageName,
PackageManager.GET_SIGNATURES);
- if ((app.flags & ApplicationInfo.FLAG_ALLOW_BACKUP) != 0) {
+ if ((app.applicationInfo.flags & ApplicationInfo.FLAG_ALLOW_BACKUP) != 0) {
return app;
}
} catch (Exception e) {
@@ -541,9 +596,12 @@
class PerformRestoreThread extends Thread {
private IBackupTransport mTransport;
+ private int mToken;
+ private RestoreSet mImage;
- PerformRestoreThread(IBackupTransport transport) {
+ PerformRestoreThread(IBackupTransport transport, int restoreSetToken) {
mTransport = transport;
+ mToken = restoreSetToken;
}
@Override
@@ -556,6 +614,7 @@
* 3. 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
@@ -576,14 +635,15 @@
try {
RestoreSet[] images = mTransport.getAvailableRestoreSets();
if (images.length > 0) {
- // !!! for now we always take the first set
- RestoreSet image = images[0];
+ // !!! TODO: pick out the set for this token
+ mImage = images[0];
// build the set of apps we will attempt to restore
- PackageInfo[] packages = mTransport.getAppSet(image.token);
- HashSet<ApplicationInfo> appsToRestore = new HashSet<ApplicationInfo>();
+ PackageInfo[] packages = mTransport.getAppSet(mImage.token);
+ HashSet<PackageInfo> appsToRestore = new HashSet<PackageInfo>();
for (PackageInfo pkg: packages) {
- ApplicationInfo app = isRestorable(pkg);
+ // get the real PackageManager idea of the package
+ PackageInfo app = isRestorable(pkg);
if (app != null) {
appsToRestore.add(app);
}
@@ -609,19 +669,23 @@
}
// restore each app in the queue
- void doQueuedRestores(HashSet<ApplicationInfo> appsToRestore) {
- for (ApplicationInfo app : appsToRestore) {
+ void doQueuedRestores(HashSet<PackageInfo> appsToRestore) {
+ for (PackageInfo app : appsToRestore) {
Log.d(TAG, "starting agent for restore of " + app);
- IBackupAgent agent = null;
try {
- agent = bindToAgentSynchronous(app, IApplicationThread.BACKUP_MODE_RESTORE);
+ // Remove the app's data first
+ clearApplicationDataSynchronous(app.packageName);
+
+ // Now perform the restore into the clean app
+ IBackupAgent agent = bindToAgentSynchronous(app.applicationInfo,
+ IApplicationThread.BACKUP_MODE_RESTORE);
if (agent != null) {
processOneRestore(app, agent);
}
// unbind even on timeout, just in case
- mActivityManager.unbindBackupAgent(app);
+ mActivityManager.unbindBackupAgent(app.applicationInfo);
} catch (SecurityException ex) {
// Try for the next one.
Log.d(TAG, "error in bind", ex);
@@ -632,9 +696,67 @@
}
}
- // do the guts of a restore
- void processOneRestore(ApplicationInfo app, IBackupAgent agent) {
+ // Do the guts of a restore of one application, derived from the 'mImage'
+ // restore set via the 'mTransport' transport.
+ void processOneRestore(PackageInfo app, IBackupAgent agent) {
// !!! TODO: actually run the restore through mTransport
+ final String packageName = app.packageName;
+
+ // !!! TODO: get the dirs from the transport
+ File backupDataName = new File(mDataDir, packageName + ".restore");
+ backupDataName.delete();
+ try {
+ ParcelFileDescriptor backupData =
+ ParcelFileDescriptor.open(backupDataName,
+ ParcelFileDescriptor.MODE_READ_WRITE |
+ ParcelFileDescriptor.MODE_CREATE);
+
+ // 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
+ } finally {
+ backupData.close();
+ }
+
+ // Okay, we have the data. Now have the agent do the restore.
+ File newStateName = new File(mStateDir, packageName + ".new");
+ ParcelFileDescriptor newState =
+ ParcelFileDescriptor.open(newStateName,
+ ParcelFileDescriptor.MODE_READ_WRITE |
+ ParcelFileDescriptor.MODE_CREATE);
+
+ backupData = ParcelFileDescriptor.open(backupDataName,
+ ParcelFileDescriptor.MODE_READ_ONLY);
+
+ boolean success = false;
+ try {
+ agent.doRestore(backupData, newState);
+ success = true;
+ } catch (Exception e) {
+ Log.e(TAG, "Restore failed for " + packageName);
+ e.printStackTrace();
+ } 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);
+ } catch (Exception e) {
+ Log.e(TAG, "Final exception guard in restore:");
+ e.printStackTrace();
+ }
}
}
@@ -761,6 +883,9 @@
// --- Binder interface ---
public RestoreSet[] getAvailableRestoreSets() throws android.os.RemoteException {
+ mContext.enforceCallingPermission("android.permission.BACKUP",
+ "getAvailableRestoreSets");
+
synchronized(this) {
if (mRestoreSets == null) {
mRestoreSets = mRestoreTransport.getAvailableRestoreSets();
@@ -770,10 +895,26 @@
}
public int performRestore(int token) throws android.os.RemoteException {
+ mContext.enforceCallingPermission("android.permission.BACKUP", "performRestore");
+
+ if (mRestoreSets != null) {
+ for (int i = 0; i < mRestoreSets.length; i++) {
+ if (token == mRestoreSets[i].token) {
+ Message msg = mBackupHandler.obtainMessage(MSG_RUN_RESTORE,
+ mRestoreTransport);
+ msg.arg1 = token;
+ mBackupHandler.sendMessage(msg);
+ return 0;
+ }
+ }
+ }
return -1;
}
public void endRestoreSession() throws android.os.RemoteException {
+ mContext.enforceCallingPermission("android.permission.BACKUP",
+ "endRestoreSession");
+
mRestoreTransport.endSession();
mRestoreTransport = null;
}
diff --git a/services/java/com/android/server/LocationManagerService.java b/services/java/com/android/server/LocationManagerService.java
index adf07dd..fc37290 100644
--- a/services/java/com/android/server/LocationManagerService.java
+++ b/services/java/com/android/server/LocationManagerService.java
@@ -769,8 +769,8 @@
if (!record.mReceiver.callProviderEnabledLocked(provider, enabled)) {
if (deadReceivers == null) {
deadReceivers = new ArrayList<Receiver>();
- deadReceivers.add(record.mReceiver);
}
+ deadReceivers.add(record.mReceiver);
}
listeners++;
}
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index 14dcfd5..6a81178 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -10422,11 +10422,14 @@
// Not backing this app up any more; reset its OOM adjustment
updateOomAdjLocked(proc);
- try {
- proc.thread.scheduleDestroyBackupAgent(appInfo);
- } catch (Exception e) {
- Log.e(TAG, "Exception when unbinding backup agent:");
- e.printStackTrace();
+ // If the app crashed during backup, 'thread' will be null here
+ if (proc.thread != null) {
+ try {
+ proc.thread.scheduleDestroyBackupAgent(appInfo);
+ } catch (Exception e) {
+ Log.e(TAG, "Exception when unbinding backup agent:");
+ e.printStackTrace();
+ }
}
}
}