Loading core/java/android/app/INotificationManager.aidl +3 −0 Original line number Diff line number Diff line Loading @@ -89,4 +89,7 @@ interface INotificationManager boolean isNotificationPolicyTokenValid(String pkg, in NotificationManager.Policy.Token token); NotificationManager.Policy getNotificationPolicy(in NotificationManager.Policy.Token token); void setNotificationPolicy(in NotificationManager.Policy.Token token, in NotificationManager.Policy policy); byte[] getBackupPayload(int user); void applyRestore(in byte[] payload, int user); } core/java/android/app/backup/BlobBackupHelper.java 0 → 100644 +296 −0 Original line number Diff line number Diff line /* * Copyright (C) 2015 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.app.backup; import android.os.ParcelFileDescriptor; import android.util.ArrayMap; import android.util.Log; import java.io.BufferedInputStream; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.EOFException; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.util.zip.CRC32; import java.util.zip.DeflaterOutputStream; import java.util.zip.InflaterInputStream; /** * Utility class for writing BackupHelpers whose underlying data is a * fixed set of byte-array blobs. The helper manages diff detection * and compression on the wire. * * @hide */ public abstract class BlobBackupHelper implements BackupHelper { private static final String TAG = "BlobBackupHelper"; private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); private final int mCurrentBlobVersion; private final String[] mKeys; public BlobBackupHelper(int currentBlobVersion, String... keys) { mCurrentBlobVersion = currentBlobVersion; mKeys = keys; } // Client interface /** * Generate and return the byte array containing the backup payload describing * the current data state. During a backup operation this method is called once * per key that was supplied to the helper's constructor. * * @return A byte array containing the data blob that the caller wishes to store, * or {@code null} if the current state is empty or undefined. */ abstract protected byte[] getBackupPayload(String key); /** * Given a byte array that was restored from backup, do whatever is appropriate * to apply that described state in the live system. This method is called once * per key/value payload that was delivered for restore. Typically data is delivered * for restore in lexical order by key, <i>not</i> in the order in which the keys * were supplied in the constructor. * * @param payload The byte array that was passed to {@link #getBackupPayload()} * on the ancestral device. */ abstract protected void applyRestoredPayload(String key, byte[] payload); // Internal implementation /* * State on-disk format: * [Int] : overall blob version number * [Int=N] : number of keys represented in the state blob * N* : * [String] key * [Long] blob checksum, calculated after compression */ @SuppressWarnings("resource") private ArrayMap<String, Long> readOldState(ParcelFileDescriptor oldStateFd) { final ArrayMap<String, Long> state = new ArrayMap<String, Long>(); FileInputStream fis = new FileInputStream(oldStateFd.getFileDescriptor()); BufferedInputStream bis = new BufferedInputStream(fis); DataInputStream in = new DataInputStream(bis); try { int version = in.readInt(); if (version <= mCurrentBlobVersion) { final int numKeys = in.readInt(); for (int i = 0; i < numKeys; i++) { String key = in.readUTF(); long checksum = in.readLong(); state.put(key, checksum); } } else { Log.w(TAG, "Prior state from unrecognized version " + version); } } catch (EOFException e) { // Empty file is expected on first backup, so carry on. If the state // is truncated we just treat it the same way. state.clear(); } catch (Exception e) { Log.e(TAG, "Error examining prior backup state " + e.getMessage()); state.clear(); } return state; } /** * New overall state record */ private void writeBackupState(ArrayMap<String, Long> state, ParcelFileDescriptor stateFile) { try { FileOutputStream fos = new FileOutputStream(stateFile.getFileDescriptor()); // We explicitly don't close 'out' because we must not close the backing fd. // The FileOutputStream will not close it implicitly. @SuppressWarnings("resource") DataOutputStream out = new DataOutputStream(fos); out.writeInt(mCurrentBlobVersion); final int N = state.size(); out.writeInt(N); for (int i = 0; i < N; i++) { out.writeUTF(state.keyAt(i)); out.writeLong(state.valueAt(i).longValue()); } } catch (IOException e) { Log.e(TAG, "Unable to write updated state", e); } } // Also versions the deflated blob internally in case we need to revise it private byte[] deflate(byte[] data) { byte[] result = null; if (data != null) { try { ByteArrayOutputStream sink = new ByteArrayOutputStream(); DataOutputStream headerOut = new DataOutputStream(sink); // write the header directly to the sink ahead of the deflated payload headerOut.writeInt(mCurrentBlobVersion); DeflaterOutputStream out = new DeflaterOutputStream(sink); out.write(data); out.close(); // finishes and commits the compression run result = sink.toByteArray(); if (DEBUG) { Log.v(TAG, "Deflated " + data.length + " bytes to " + result.length); } } catch (IOException e) { Log.w(TAG, "Unable to process payload: " + e.getMessage()); } } return result; } // Returns null if inflation failed private byte[] inflate(byte[] compressedData) { byte[] result = null; if (compressedData != null) { try { ByteArrayInputStream source = new ByteArrayInputStream(compressedData); DataInputStream headerIn = new DataInputStream(source); int version = headerIn.readInt(); if (version > mCurrentBlobVersion) { Log.w(TAG, "Saved payload from unrecognized version " + version); return null; } InflaterInputStream in = new InflaterInputStream(source); ByteArrayOutputStream inflated = new ByteArrayOutputStream(); byte[] buffer = new byte[4096]; int nRead; while ((nRead = in.read(buffer)) > 0) { inflated.write(buffer, 0, nRead); } in.close(); inflated.flush(); result = inflated.toByteArray(); if (DEBUG) { Log.v(TAG, "Inflated " + compressedData.length + " bytes to " + result.length); } } catch (IOException e) { // result is still null here Log.w(TAG, "Unable to process restored payload: " + e.getMessage()); } } return result; } private long checksum(byte[] buffer) { if (buffer != null) { try { CRC32 crc = new CRC32(); ByteArrayInputStream bis = new ByteArrayInputStream(buffer); byte[] buf = new byte[4096]; int nRead = 0; while ((nRead = bis.read(buf)) >= 0) { crc.update(buf, 0, nRead); } return crc.getValue(); } catch (Exception e) { // whoops; fall through with an explicitly bogus checksum } } return -1; } // BackupHelper interface @Override public void performBackup(ParcelFileDescriptor oldStateFd, BackupDataOutput data, ParcelFileDescriptor newStateFd) { final ArrayMap<String, Long> oldState = readOldState(oldStateFd); final ArrayMap<String, Long> newState = new ArrayMap<String, Long>(); try { for (String key : mKeys) { final byte[] payload = deflate(getBackupPayload(key)); final long checksum = checksum(payload); newState.put(key, checksum); Long oldChecksum = oldState.get(key); if (oldChecksum == null || checksum != oldChecksum) { if (DEBUG) { Log.i(TAG, "State has changed for key " + key + ", writing"); } if (payload != null) { data.writeEntityHeader(key, payload.length); data.writeEntityData(payload, payload.length); } else { // state's changed but there's no current payload => delete data.writeEntityHeader(key, -1); } } else { if (DEBUG) { Log.i(TAG, "No change under key " + key + " => not writing"); } } } } catch (Exception e) { Log.w(TAG, "Unable to record notification state: " + e.getMessage()); newState.clear(); } finally { // Always recommit the state even if nothing changed writeBackupState(newState, newStateFd); } } @Override public void restoreEntity(BackupDataInputStream data) { final String key = data.getKey(); try { // known key? int which; for (which = 0; which < mKeys.length; which++) { if (key.equals(mKeys[which])) { break; } } if (which >= mKeys.length) { Log.e(TAG, "Unrecognized key " + key + ", ignoring"); return; } byte[] compressed = new byte[data.size()]; data.read(compressed); byte[] payload = inflate(compressed); applyRestoredPayload(key, payload); } catch (Exception e) { Log.e(TAG, "Exception restoring entity " + key + " : " + e.getMessage()); } } @Override public void writeNewStateDescription(ParcelFileDescriptor newState) { // Just ensure that we do a full backup the first time after a restore writeBackupState(null, newState); } } core/java/com/android/server/backup/NotificationBackupHelper.java 0 → 100644 +76 −0 Original line number Diff line number Diff line /* * Copyright (C) 2015 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.server.backup; import android.app.INotificationManager; import android.app.backup.BlobBackupHelper; import android.content.Context; import android.os.ServiceManager; import android.os.UserHandle; import android.util.Log; import android.util.Slog; public class NotificationBackupHelper extends BlobBackupHelper { static final String TAG = "NotifBackupHelper"; // must be < 23 chars static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); // Current version of the blob schema static final int BLOB_VERSION = 1; // Key under which the payload blob is stored static final String KEY_NOTIFICATIONS = "notifications"; public NotificationBackupHelper(Context context) { super(BLOB_VERSION, KEY_NOTIFICATIONS); // context is currently unused } @Override protected byte[] getBackupPayload(String key) { byte[] newPayload = null; if (KEY_NOTIFICATIONS.equals(key)) { try { INotificationManager nm = INotificationManager.Stub.asInterface( ServiceManager.getService("notification")); newPayload = nm.getBackupPayload(UserHandle.USER_OWNER); } catch (Exception e) { // Treat as no data Slog.e(TAG, "Couldn't communicate with notification manager"); newPayload = null; } } return newPayload; } @Override protected void applyRestoredPayload(String key, byte[] payload) { if (DEBUG) { Slog.v(TAG, "Got restore of " + key); } if (KEY_NOTIFICATIONS.equals(key)) { try { INotificationManager nm = INotificationManager.Stub.asInterface( ServiceManager.getService("notification")); nm.applyRestore(payload, UserHandle.USER_OWNER); } catch (Exception e) { Slog.e(TAG, "Couldn't communicate with notification manager"); } } } } core/java/com/android/server/backup/SystemBackupAgent.java +2 −0 Original line number Diff line number Diff line Loading @@ -48,6 +48,7 @@ public class SystemBackupAgent extends BackupAgentHelper { private static final String RECENTS_HELPER = "recents"; private static final String SYNC_SETTINGS_HELPER = "account_sync_settings"; private static final String PREFERRED_HELPER = "preferred_activities"; private static final String NOTIFICATION_HELPER = "notifications"; // These paths must match what the WallpaperManagerService uses. The leaf *_FILENAME // are also used in the full-backup file format, so must not change unless steps are Loading Loading @@ -94,6 +95,7 @@ public class SystemBackupAgent extends BackupAgentHelper { addHelper(RECENTS_HELPER, new RecentsBackupHelper(this)); addHelper(SYNC_SETTINGS_HELPER, new AccountSyncSettingsBackupHelper(this)); addHelper(PREFERRED_HELPER, new PreferredActivityBackupHelper(this)); addHelper(NOTIFICATION_HELPER, new NotificationBackupHelper(this)); super.onBackup(oldState, data, newState); } Loading services/core/java/com/android/server/notification/NotificationManagerService.java +12 −0 Original line number Diff line number Diff line Loading @@ -1610,6 +1610,18 @@ public class NotificationManagerService extends SystemService { return mConditionProviders.isSystemProviderEnabled(path); } // Backup/restore interface @Override public byte[] getBackupPayload(int user) { // TODO: build a payload of whatever is appropriate return null; } @Override public void applyRestore(byte[] payload, int user) { // TODO: apply the restored payload as new current state } @Override public Policy.Token getPolicyTokenFromListener(INotificationListener listener) { final long identity = Binder.clearCallingIdentity(); Loading Loading
core/java/android/app/INotificationManager.aidl +3 −0 Original line number Diff line number Diff line Loading @@ -89,4 +89,7 @@ interface INotificationManager boolean isNotificationPolicyTokenValid(String pkg, in NotificationManager.Policy.Token token); NotificationManager.Policy getNotificationPolicy(in NotificationManager.Policy.Token token); void setNotificationPolicy(in NotificationManager.Policy.Token token, in NotificationManager.Policy policy); byte[] getBackupPayload(int user); void applyRestore(in byte[] payload, int user); }
core/java/android/app/backup/BlobBackupHelper.java 0 → 100644 +296 −0 Original line number Diff line number Diff line /* * Copyright (C) 2015 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.app.backup; import android.os.ParcelFileDescriptor; import android.util.ArrayMap; import android.util.Log; import java.io.BufferedInputStream; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.EOFException; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.util.zip.CRC32; import java.util.zip.DeflaterOutputStream; import java.util.zip.InflaterInputStream; /** * Utility class for writing BackupHelpers whose underlying data is a * fixed set of byte-array blobs. The helper manages diff detection * and compression on the wire. * * @hide */ public abstract class BlobBackupHelper implements BackupHelper { private static final String TAG = "BlobBackupHelper"; private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); private final int mCurrentBlobVersion; private final String[] mKeys; public BlobBackupHelper(int currentBlobVersion, String... keys) { mCurrentBlobVersion = currentBlobVersion; mKeys = keys; } // Client interface /** * Generate and return the byte array containing the backup payload describing * the current data state. During a backup operation this method is called once * per key that was supplied to the helper's constructor. * * @return A byte array containing the data blob that the caller wishes to store, * or {@code null} if the current state is empty or undefined. */ abstract protected byte[] getBackupPayload(String key); /** * Given a byte array that was restored from backup, do whatever is appropriate * to apply that described state in the live system. This method is called once * per key/value payload that was delivered for restore. Typically data is delivered * for restore in lexical order by key, <i>not</i> in the order in which the keys * were supplied in the constructor. * * @param payload The byte array that was passed to {@link #getBackupPayload()} * on the ancestral device. */ abstract protected void applyRestoredPayload(String key, byte[] payload); // Internal implementation /* * State on-disk format: * [Int] : overall blob version number * [Int=N] : number of keys represented in the state blob * N* : * [String] key * [Long] blob checksum, calculated after compression */ @SuppressWarnings("resource") private ArrayMap<String, Long> readOldState(ParcelFileDescriptor oldStateFd) { final ArrayMap<String, Long> state = new ArrayMap<String, Long>(); FileInputStream fis = new FileInputStream(oldStateFd.getFileDescriptor()); BufferedInputStream bis = new BufferedInputStream(fis); DataInputStream in = new DataInputStream(bis); try { int version = in.readInt(); if (version <= mCurrentBlobVersion) { final int numKeys = in.readInt(); for (int i = 0; i < numKeys; i++) { String key = in.readUTF(); long checksum = in.readLong(); state.put(key, checksum); } } else { Log.w(TAG, "Prior state from unrecognized version " + version); } } catch (EOFException e) { // Empty file is expected on first backup, so carry on. If the state // is truncated we just treat it the same way. state.clear(); } catch (Exception e) { Log.e(TAG, "Error examining prior backup state " + e.getMessage()); state.clear(); } return state; } /** * New overall state record */ private void writeBackupState(ArrayMap<String, Long> state, ParcelFileDescriptor stateFile) { try { FileOutputStream fos = new FileOutputStream(stateFile.getFileDescriptor()); // We explicitly don't close 'out' because we must not close the backing fd. // The FileOutputStream will not close it implicitly. @SuppressWarnings("resource") DataOutputStream out = new DataOutputStream(fos); out.writeInt(mCurrentBlobVersion); final int N = state.size(); out.writeInt(N); for (int i = 0; i < N; i++) { out.writeUTF(state.keyAt(i)); out.writeLong(state.valueAt(i).longValue()); } } catch (IOException e) { Log.e(TAG, "Unable to write updated state", e); } } // Also versions the deflated blob internally in case we need to revise it private byte[] deflate(byte[] data) { byte[] result = null; if (data != null) { try { ByteArrayOutputStream sink = new ByteArrayOutputStream(); DataOutputStream headerOut = new DataOutputStream(sink); // write the header directly to the sink ahead of the deflated payload headerOut.writeInt(mCurrentBlobVersion); DeflaterOutputStream out = new DeflaterOutputStream(sink); out.write(data); out.close(); // finishes and commits the compression run result = sink.toByteArray(); if (DEBUG) { Log.v(TAG, "Deflated " + data.length + " bytes to " + result.length); } } catch (IOException e) { Log.w(TAG, "Unable to process payload: " + e.getMessage()); } } return result; } // Returns null if inflation failed private byte[] inflate(byte[] compressedData) { byte[] result = null; if (compressedData != null) { try { ByteArrayInputStream source = new ByteArrayInputStream(compressedData); DataInputStream headerIn = new DataInputStream(source); int version = headerIn.readInt(); if (version > mCurrentBlobVersion) { Log.w(TAG, "Saved payload from unrecognized version " + version); return null; } InflaterInputStream in = new InflaterInputStream(source); ByteArrayOutputStream inflated = new ByteArrayOutputStream(); byte[] buffer = new byte[4096]; int nRead; while ((nRead = in.read(buffer)) > 0) { inflated.write(buffer, 0, nRead); } in.close(); inflated.flush(); result = inflated.toByteArray(); if (DEBUG) { Log.v(TAG, "Inflated " + compressedData.length + " bytes to " + result.length); } } catch (IOException e) { // result is still null here Log.w(TAG, "Unable to process restored payload: " + e.getMessage()); } } return result; } private long checksum(byte[] buffer) { if (buffer != null) { try { CRC32 crc = new CRC32(); ByteArrayInputStream bis = new ByteArrayInputStream(buffer); byte[] buf = new byte[4096]; int nRead = 0; while ((nRead = bis.read(buf)) >= 0) { crc.update(buf, 0, nRead); } return crc.getValue(); } catch (Exception e) { // whoops; fall through with an explicitly bogus checksum } } return -1; } // BackupHelper interface @Override public void performBackup(ParcelFileDescriptor oldStateFd, BackupDataOutput data, ParcelFileDescriptor newStateFd) { final ArrayMap<String, Long> oldState = readOldState(oldStateFd); final ArrayMap<String, Long> newState = new ArrayMap<String, Long>(); try { for (String key : mKeys) { final byte[] payload = deflate(getBackupPayload(key)); final long checksum = checksum(payload); newState.put(key, checksum); Long oldChecksum = oldState.get(key); if (oldChecksum == null || checksum != oldChecksum) { if (DEBUG) { Log.i(TAG, "State has changed for key " + key + ", writing"); } if (payload != null) { data.writeEntityHeader(key, payload.length); data.writeEntityData(payload, payload.length); } else { // state's changed but there's no current payload => delete data.writeEntityHeader(key, -1); } } else { if (DEBUG) { Log.i(TAG, "No change under key " + key + " => not writing"); } } } } catch (Exception e) { Log.w(TAG, "Unable to record notification state: " + e.getMessage()); newState.clear(); } finally { // Always recommit the state even if nothing changed writeBackupState(newState, newStateFd); } } @Override public void restoreEntity(BackupDataInputStream data) { final String key = data.getKey(); try { // known key? int which; for (which = 0; which < mKeys.length; which++) { if (key.equals(mKeys[which])) { break; } } if (which >= mKeys.length) { Log.e(TAG, "Unrecognized key " + key + ", ignoring"); return; } byte[] compressed = new byte[data.size()]; data.read(compressed); byte[] payload = inflate(compressed); applyRestoredPayload(key, payload); } catch (Exception e) { Log.e(TAG, "Exception restoring entity " + key + " : " + e.getMessage()); } } @Override public void writeNewStateDescription(ParcelFileDescriptor newState) { // Just ensure that we do a full backup the first time after a restore writeBackupState(null, newState); } }
core/java/com/android/server/backup/NotificationBackupHelper.java 0 → 100644 +76 −0 Original line number Diff line number Diff line /* * Copyright (C) 2015 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.server.backup; import android.app.INotificationManager; import android.app.backup.BlobBackupHelper; import android.content.Context; import android.os.ServiceManager; import android.os.UserHandle; import android.util.Log; import android.util.Slog; public class NotificationBackupHelper extends BlobBackupHelper { static final String TAG = "NotifBackupHelper"; // must be < 23 chars static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); // Current version of the blob schema static final int BLOB_VERSION = 1; // Key under which the payload blob is stored static final String KEY_NOTIFICATIONS = "notifications"; public NotificationBackupHelper(Context context) { super(BLOB_VERSION, KEY_NOTIFICATIONS); // context is currently unused } @Override protected byte[] getBackupPayload(String key) { byte[] newPayload = null; if (KEY_NOTIFICATIONS.equals(key)) { try { INotificationManager nm = INotificationManager.Stub.asInterface( ServiceManager.getService("notification")); newPayload = nm.getBackupPayload(UserHandle.USER_OWNER); } catch (Exception e) { // Treat as no data Slog.e(TAG, "Couldn't communicate with notification manager"); newPayload = null; } } return newPayload; } @Override protected void applyRestoredPayload(String key, byte[] payload) { if (DEBUG) { Slog.v(TAG, "Got restore of " + key); } if (KEY_NOTIFICATIONS.equals(key)) { try { INotificationManager nm = INotificationManager.Stub.asInterface( ServiceManager.getService("notification")); nm.applyRestore(payload, UserHandle.USER_OWNER); } catch (Exception e) { Slog.e(TAG, "Couldn't communicate with notification manager"); } } } }
core/java/com/android/server/backup/SystemBackupAgent.java +2 −0 Original line number Diff line number Diff line Loading @@ -48,6 +48,7 @@ public class SystemBackupAgent extends BackupAgentHelper { private static final String RECENTS_HELPER = "recents"; private static final String SYNC_SETTINGS_HELPER = "account_sync_settings"; private static final String PREFERRED_HELPER = "preferred_activities"; private static final String NOTIFICATION_HELPER = "notifications"; // These paths must match what the WallpaperManagerService uses. The leaf *_FILENAME // are also used in the full-backup file format, so must not change unless steps are Loading Loading @@ -94,6 +95,7 @@ public class SystemBackupAgent extends BackupAgentHelper { addHelper(RECENTS_HELPER, new RecentsBackupHelper(this)); addHelper(SYNC_SETTINGS_HELPER, new AccountSyncSettingsBackupHelper(this)); addHelper(PREFERRED_HELPER, new PreferredActivityBackupHelper(this)); addHelper(NOTIFICATION_HELPER, new NotificationBackupHelper(this)); super.onBackup(oldState, data, newState); } Loading
services/core/java/com/android/server/notification/NotificationManagerService.java +12 −0 Original line number Diff line number Diff line Loading @@ -1610,6 +1610,18 @@ public class NotificationManagerService extends SystemService { return mConditionProviders.isSystemProviderEnabled(path); } // Backup/restore interface @Override public byte[] getBackupPayload(int user) { // TODO: build a payload of whatever is appropriate return null; } @Override public void applyRestore(byte[] payload, int user) { // TODO: apply the restored payload as new current state } @Override public Policy.Token getPolicyTokenFromListener(INotificationListener listener) { final long identity = Binder.clearCallingIdentity(); Loading