UsageStatsBackup Bug Fix

Fixed a bug which caused a null pointer exception on restore
in deleteDirectoryContents() method.
Also made other cosmetic changes.

Change-Id: I75712ec585fdfdbca462c468acf2229a47331b59
This commit is contained in:
Ritesh Reddy
2016-02-03 17:06:57 +00:00
parent ee081d5bd0
commit e35ae5cce1
2 changed files with 70 additions and 50 deletions

View File

@@ -2,11 +2,8 @@ package com.android.server.backup;
import android.app.backup.BlobBackupHelper;
import android.app.usage.IUsageStatsManager;
import android.app.usage.UsageStatsManagerInternal;
import android.content.Context;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.UserHandle;
import android.util.Log;
@@ -26,7 +23,7 @@ public class UsageStatsBackupHelper extends BlobBackupHelper {
static final int BLOB_VERSION = 1;
// Key under which the payload blob is stored
// same as UsageStatsBackupHelperAssistant.KEY_USAGE_STATS
// same as UsageStatsDatabase.KEY_USAGE_STATS
static final String KEY_USAGE_STATS = "usage_stats";
public UsageStatsBackupHelper(Context context) {
@@ -35,14 +32,15 @@ public class UsageStatsBackupHelper extends BlobBackupHelper {
@Override
protected byte[] getBackupPayload(String key) {
if(KEY_USAGE_STATS.equals(key)) {
UsageStatsManagerInternal localUsageStatsManager = LocalServices.getService(UsageStatsManagerInternal.class);
if (KEY_USAGE_STATS.equals(key)) {
UsageStatsManagerInternal localUsageStatsManager =
LocalServices.getService(UsageStatsManagerInternal.class);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
DataOutputStream out = new DataOutputStream(baos);
try{
try {
out.writeInt(UserHandle.USER_SYSTEM);
out.write(localUsageStatsManager.getBackupPayload(UserHandle.USER_SYSTEM, key));
} catch (IOException ioe){
} catch (IOException ioe) {
if (DEBUG) Log.e(TAG, "Failed to backup Usage Stats", ioe);
baos.reset();
}
@@ -55,14 +53,15 @@ public class UsageStatsBackupHelper extends BlobBackupHelper {
@Override
protected void applyRestoredPayload(String key, byte[] payload) {
if (KEY_USAGE_STATS.equals(key)) {
UsageStatsManagerInternal localUsageStatsManager = LocalServices.getService(UsageStatsManagerInternal.class);
UsageStatsManagerInternal localUsageStatsManager =
LocalServices.getService(UsageStatsManagerInternal.class);
DataInputStream in = new DataInputStream(new ByteArrayInputStream(payload));
try{
try {
int user = in.readInt();
byte[] restoreData = new byte[payload.length - 4];
in.read(restoreData, 0, payload.length-4);
in.read(restoreData, 0, restoreData.length);
localUsageStatsManager.applyRestoredPayload(user, key, restoreData);
} catch (IOException ioe){
} catch (IOException ioe) {
if (DEBUG) Log.e(TAG, "Failed to restore Usage Stats", ioe);
}
}

View File

@@ -17,7 +17,6 @@
package com.android.server.usage;
import android.app.usage.TimeSparseArray;
import android.app.usage.UsageStats;
import android.app.usage.UsageStatsManager;
import android.os.Build;
import android.util.AtomicFile;
@@ -45,7 +44,7 @@ class UsageStatsDatabase {
private static final int CURRENT_VERSION = 3;
// Current version of the backup schema
static final int BACKUP_STATE_VERSION = 1;
static final int BACKUP_VERSION = 1;
// Key under which the payload blob is stored
// same as UsageStatsBackupHelper.KEY_USAGE_STATS
@@ -536,6 +535,7 @@ class UsageStatsDatabase {
* Update the stats in the database. They may not be written to disk immediately.
*/
public void putUsageStats(int intervalType, IntervalStats stats) throws IOException {
if (stats == null) return;
synchronized (mLock) {
if (intervalType < 0 || intervalType >= mIntervalDirs.length) {
throw new IllegalArgumentException("Bad interval type " + intervalType);
@@ -555,36 +555,44 @@ class UsageStatsDatabase {
/* Backup/Restore Code */
protected byte[] getBackupPayload(String key){
byte[] getBackupPayload(String key) {
synchronized (mLock) {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
if (KEY_USAGE_STATS.equals(key)) {
prune(System.currentTimeMillis());
DataOutputStream out = new DataOutputStream(baos);
try {
out.writeInt(BACKUP_STATE_VERSION);
out.writeInt(BACKUP_VERSION);
out.writeInt(mSortedStatFiles[UsageStatsManager.INTERVAL_DAILY].size());
for(int i = 0; i<mSortedStatFiles[UsageStatsManager.INTERVAL_DAILY].size(); i++){
writeIntervalStatsToStream(out, mSortedStatFiles[UsageStatsManager.INTERVAL_DAILY].valueAt(i));
for (int i = 0; i < mSortedStatFiles[UsageStatsManager.INTERVAL_DAILY].size();
i++) {
writeIntervalStatsToStream(out,
mSortedStatFiles[UsageStatsManager.INTERVAL_DAILY].valueAt(i));
}
out.writeInt(mSortedStatFiles[UsageStatsManager.INTERVAL_WEEKLY].size());
for(int i = 0; i<mSortedStatFiles[UsageStatsManager.INTERVAL_WEEKLY].size(); i++){
writeIntervalStatsToStream(out, mSortedStatFiles[UsageStatsManager.INTERVAL_WEEKLY].valueAt(i));
for (int i = 0; i < mSortedStatFiles[UsageStatsManager.INTERVAL_WEEKLY].size();
i++) {
writeIntervalStatsToStream(out,
mSortedStatFiles[UsageStatsManager.INTERVAL_WEEKLY].valueAt(i));
}
out.writeInt(mSortedStatFiles[UsageStatsManager.INTERVAL_MONTHLY].size());
for(int i = 0; i<mSortedStatFiles[UsageStatsManager.INTERVAL_MONTHLY].size(); i++){
writeIntervalStatsToStream(out, mSortedStatFiles[UsageStatsManager.INTERVAL_MONTHLY].valueAt(i));
for (int i = 0; i < mSortedStatFiles[UsageStatsManager.INTERVAL_MONTHLY].size();
i++) {
writeIntervalStatsToStream(out,
mSortedStatFiles[UsageStatsManager.INTERVAL_MONTHLY].valueAt(i));
}
out.writeInt(mSortedStatFiles[UsageStatsManager.INTERVAL_YEARLY].size());
for(int i = 0; i<mSortedStatFiles[UsageStatsManager.INTERVAL_YEARLY].size(); i++){
writeIntervalStatsToStream(out, mSortedStatFiles[UsageStatsManager.INTERVAL_YEARLY].valueAt(i));
for (int i = 0; i < mSortedStatFiles[UsageStatsManager.INTERVAL_YEARLY].size();
i++) {
writeIntervalStatsToStream(out,
mSortedStatFiles[UsageStatsManager.INTERVAL_YEARLY].valueAt(i));
}
if (DEBUG) Slog.i(TAG, "Written " + baos.size() + " bytes of data");
} catch (IOException ioe){
} catch (IOException ioe) {
Slog.d(TAG, "Failed to write data to output stream", ioe);
baos.reset();
}
@@ -594,55 +602,63 @@ class UsageStatsDatabase {
}
protected void applyRestoredPayload(String key, byte[] payload){
void applyRestoredPayload(String key, byte[] payload) {
synchronized (mLock) {
if (KEY_USAGE_STATS.equals(key)) {
// Read stats files for the current device configs
IntervalStats dailyConfigSource = getLatestUsageStats(UsageStatsManager.INTERVAL_DAILY);
IntervalStats weeklyConfigSource = getLatestUsageStats(UsageStatsManager.INTERVAL_WEEKLY);
IntervalStats monthlyConfigSource = getLatestUsageStats(UsageStatsManager.INTERVAL_MONTHLY);
IntervalStats yearlyConfigSource = getLatestUsageStats(UsageStatsManager.INTERVAL_YEARLY);
IntervalStats dailyConfigSource =
getLatestUsageStats(UsageStatsManager.INTERVAL_DAILY);
IntervalStats weeklyConfigSource =
getLatestUsageStats(UsageStatsManager.INTERVAL_WEEKLY);
IntervalStats monthlyConfigSource =
getLatestUsageStats(UsageStatsManager.INTERVAL_MONTHLY);
IntervalStats yearlyConfigSource =
getLatestUsageStats(UsageStatsManager.INTERVAL_YEARLY);
// Delete all stats files
for(int i = 0; i<mIntervalDirs.length; i++){
deleteDirectoryContents(mIntervalDirs[i]);
}
try {
DataInputStream in = new DataInputStream(new ByteArrayInputStream(payload));
int stateVersion = in.readInt();
int backupDataVersion = in.readInt();
// Can't handle this backup set
if (backupDataVersion < 1 || backupDataVersion > BACKUP_VERSION) return;
// Delete all stats files
// Do this after reading version and before actually restoring
for (int i = 0; i < mIntervalDirs.length; i++) {
deleteDirectoryContents(mIntervalDirs[i]);
}
int fileCount = in.readInt();
for(int i = 0; i<fileCount; i++){
for (int i = 0; i < fileCount; i++) {
IntervalStats stats = deserializeIntervalStats(getIntervalStatsBytes(in));
stats = mergeStats(stats, dailyConfigSource);
putUsageStats(UsageStatsManager.INTERVAL_DAILY, stats);
}
fileCount = in.readInt();
for(int i = 0; i<fileCount; i++){
for (int i = 0; i < fileCount; i++) {
IntervalStats stats = deserializeIntervalStats(getIntervalStatsBytes(in));
stats = mergeStats(stats, weeklyConfigSource);
putUsageStats(UsageStatsManager.INTERVAL_WEEKLY, stats);
}
fileCount = in.readInt();
for(int i = 0; i<fileCount; i++){
for (int i = 0; i < fileCount; i++) {
IntervalStats stats = deserializeIntervalStats(getIntervalStatsBytes(in));
stats = mergeStats(stats, monthlyConfigSource);
putUsageStats(UsageStatsManager.INTERVAL_MONTHLY, stats);
}
fileCount = in.readInt();
for(int i = 0; i<fileCount; i++){
for (int i = 0; i < fileCount; i++) {
IntervalStats stats = deserializeIntervalStats(getIntervalStatsBytes(in));
stats = mergeStats(stats, yearlyConfigSource);
putUsageStats(UsageStatsManager.INTERVAL_YEARLY, stats);
}
if (DEBUG) Slog.i(TAG, "Completed Restoring UsageStats");
} catch (IOException ioe){
} catch (IOException ioe) {
Slog.d(TAG, "Failed to read data from input stream", ioe);
}
finally {
} finally {
indexFilesLocked();
}
}
@@ -654,13 +670,16 @@ class UsageStatsDatabase {
* with the backed up usage statistics.
*/
private IntervalStats mergeStats(IntervalStats beingRestored, IntervalStats onDevice) {
if (onDevice == null) return beingRestored;
if (beingRestored == null) return null;
beingRestored.activeConfiguration = onDevice.activeConfiguration;
beingRestored.configurations.putAll(onDevice.configurations);
beingRestored.events = onDevice.events;
return beingRestored;
}
private void writeIntervalStatsToStream(DataOutputStream out, AtomicFile statsFile) throws IOException{
private void writeIntervalStatsToStream(DataOutputStream out, AtomicFile statsFile)
throws IOException {
IntervalStats stats = new IntervalStats();
try {
UsageStatsXml.read(statsFile, stats);
@@ -716,7 +735,7 @@ class UsageStatsDatabase {
return stats;
}
private static void deleteDirectoryContents(File directory){
private static void deleteDirectoryContents(File directory) {
File[] files = directory.listFiles();
for (File file : files) {
deleteDirectory(file);
@@ -725,13 +744,15 @@ class UsageStatsDatabase {
private static void deleteDirectory(File directory) {
File[] files = directory.listFiles();
for (File file : files) {
if (!file.isDirectory()) {
file.delete();
} else {
deleteDirectory(file);
if (files != null) {
for (File file : files) {
if (!file.isDirectory()) {
file.delete();
} else {
deleteDirectory(file);
}
}
}
directory.delete();
}
}
}