Android BackupAgent never called - android

I am trying to implement a custom backupAgentHelper to backup my app's database file. I've gone through the docs several times but whenever I try to force backup, the backupAgentHelper on Create/onBackup/onRestore are never called.
Manifest has the following under application:
android:allowBackup="true"
android:backupAgent="myBackupHelper"
android:restoreAnyVersion="true"
and metadata
<meta-data
android:name="com.google.android.backup.api_key"
android:value="<my-api-key>" />
myBackupHelper:
public class myBackupHelper extends BackupAgentHelper{
public static String DATABASE_NAME = "db.dat";
#Override
public void onCreate(){
log.d("Backup oncreate called");
FileBackupHelper hosts = new FileBackupHelper(this, this.getExternalFilesDir(DATABASE_NAME).getAbsolutePath());
addHelper(DATABASE_NAME,hosts);
}
#Override
public void onBackup(ParcelFileDescriptor oldState, BackupDataOutput data, ParcelFileDescriptor newState) {
log.d("backup onbackup called");
try {
//class is the lock since we are using static synchronized methods to read/write
synchronized (DBManager.class) {
super.onBackup(oldState, data, newState);
log.d("Backedup");
}
} catch (IOException e) {
log.d("Backup error, Unable to write to file: " + e);
}
}
#Override
public void onRestore(BackupDataInput data, int appVersionCode, ParcelFileDescriptor newState){
log.d("Backup onrestore called");
try {
//class is the lock since we are using static synchronized methods to read/write
synchronized (DBManager.class) {
super.onRestore(data, appVersionCode, newState);
}
} catch (IOException e) {
log.d("Backup error, Unable to read from file: " + e);
}
}
I initialize the BackupManager in the mainactivity as follows:
BackupManager bm = new BackupManager(getApplicationContext());
and call bm.dataChanged(); when the database changes.
In testing, I use adb to force backup:
./adb shell bmgr backup com.test.android.backuptest
./adb shell bmgr run
but the logs are never hit and when i reinstall, data is never restored.
Note: backup and restore settings are enabled and the device has over the required api 8 so I have no idea why its not being hit!

The reason why my backupAgentHelper functions were never called is because of the transport being used.
Doing
./adb shell bmgr list transports
showed
android/com.android.internal.backup.LocalTransport
*com.google.android.gms/.backup.BackupTransportService
For some reason the goodle transportservice wasn't working but changing it to the internal localtransport with
./adb shell bmgr transport android/com.android.internal.backup.LocalTransport
fixed my problem and now logs are showing up.

Related

BackupAgent subclass used for automatic restore but not manual restore

I've set up the Android Backup Service in my app using a custom class that extends BackupAgentHelper ... it basically looks like this:
public class MyBackups extends BackupAgentHelper {
#Override
public void onCreate() {
Log.d("MyBackups", "creating backup class");
this.addDefaultHelper();
String defaultSharedPrefsName = this.getPackageName() + "_preferences";
SharedPreferencesBackupHelper defaultPrefsHelper = new SharedPreferencesBackupHelper(this, defaultSharedPrefsName);
this.addHelper("default_prefs", defaultPrefsHelper);
}
#Override
public void onBackup(ParcelFileDescriptor oldState, BackupDataOutput data, ParcelFileDescriptor newState) throws IOException {
Log.d("MyBackups", "backing up " + data);
super.onBackup(oldState, data, newState);
}
#Override
public void onRestore(BackupDataInput data, int appVersionCode, ParcelFileDescriptor newState) throws IOException {
Log.d("MyBackups", "restoring");
super.onRestore(data, appVersionCode, newState);
// post-processing code goes here
}
}
I have this registered in the manifest file, and if I delete and reinstall the app, it runs as expected, with all the log messages appearing.
However, if I manually request a restore, like this...
BackupManager backupManager = new BackupManager(getApplicationContext());
int error = backupManager.requestRestore(
new RestoreObserver() {
public void restoreStarting(int numPackages) {
Log.d("MyBackups", "restoreStarting");
}
public void restoreFinished(int error) {
Log.d("MyBackups", "restoreFinished");
}
public void onUpdate(int nowBeingRestored, String currentPackage) {
Log.d("MyBackups", "onUpdate");
}
}
);
Log.d("MyBackups", "requestRestore result: " + error);
...restoreStarting and restoreFinished are called, and the error result is 0, but none of the BackupAgentHelper methods are called -- the "creating backup class" and "restoring" logs don't appear, and my post-processing code doesn't run. It seems as if a manual requestRestore bypasses my custom BackupAgentHelper subclass.
Is there anything else I need to hook up to make a manual restore work the same way as an automatic restore? Have you tried this and is it working for you?
This is an old question, but I was able to do just this today. I hope it helps someone having the same issue.
You need to call BackupManager dataChanged() to send a backup request.
Then, to test it and kick start the backup, you need to run
adb shell bmgr run
This will call OnCreate, OnBackup.
Then after you run backupManager.requestRestore, your OnCreate, OnRestore methods will be called.
Check out the sample here, it does exactly this (when you click the Restore button):
https://android.googlesource.com/platform/development/+/0b3758ea4e53f9bfd0b112eaa4a7dd7b7f4040f5/samples/BackupRestore?autodive=0%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F
See the FileHelperExampleAgent.java agent.

Android - mount OBB in service

I'm writing an application which includes a service and mounts an OBB file from service.
I'm referring this documentation: http://developer.android.com/reference/android/os/storage/StorageManager.html
http://developer.android.com/reference/android/os/storage/OnObbStateChangeListener.html
However, onObbStateChange() callback never happens.
If I use the very same code in application's activity, it successfully mounts and I get access to the internals of the OBB file.
Below is the code snippet:
// Extend OnObbStateChangeListener class
public class MyService extends Service {
static StorageManager storageManager;
static class Observer extends OnObbStateChangeListener {
#Override
public void onObbStateChange(String path, int state) {
Log.v(TAG, path + " " + state); // 1
}
}
}
// Mount obb
public void getMountedPath() {
File file = new File("<path to obb file>");
if(!obbFile.exists())
return;
Observer observer = new Observer();
boolean state = storageManager.mountObb(obbFile.getAbsolutePath(), null, obbObserver);
Log.v(TAG, "state: " + state); // 2 It always prints TRUE
}
Any idea why it's not able to mount from my service class?
More observations:
When I execute the app for the first time, I get mount state as TRUE in the second log statement and onObbStateChange() doesn't get invoked.
However, when I run my app second time, I see the first log from onObbStateChange() and it prints state as 24 which means ERROR_ALREADY_MOUNTED.
So it seems that the obb got mounted in the first attempt, however, onObbStateChange() didn't get invoked for some reason.
Any explanations on this behavior?

Install / Unistall from shell command in Android

I want to implement a silent installer-from-apk-file and unistaller-package in Android.
The topic has largely been discussed on SO and elsewhere but I can't apply any for some reason that I'm missing.
The scope is obviously hard to achieve because, if successful, it would be a serious security breach in Android. BUT, I need to implement it for a special project, not for the consumer market.
There are two approaches:
to generate a custom ROM from a source code (AOSP or Cyanogen mod, for example), by tweaking the PackageManager installer (in fact just to remove the user acceptance dialog boxes).
to do it programmatically by creating a process as super user and executing an 'adb shell pm install'. I previously installed 'su' in /system/xbin and I test during run time that RootTools.rootIsAvailable().
For the first case, I digged into the Froyo source code but got into a dead end with a #hide marked method.
For the second I've first tried the commands from the terminal
adb shell pm install /mnt/sdcard/HelloAndroid.apk
and
adb shell pm uninstall com.example.helloandroid
Both work OK. Then, I used the following code, the development being tested on a rooted emulator (2.2 - Froyo):
#Override
public void onClick(View v) {
switch (v.getId())
{
case R.id.btnInstall:
try {
install = Runtime.getRuntime().exec("su\n");
DataOutputStream os = new DataOutputStream(install.getOutputStream());
os.writeBytes("pm install /mnt/sdcard/HelloAndroid.apk\n");
os.writeBytes("exit\n");
os.flush();
install.waitFor();
if (install.exitValue() == 0) {
Toast.makeText(MainActivity.this, "Success!", Toast.LENGTH_LONG).show();
}
else {
Toast.makeText(MainActivity.this, "Failure. Exit code: "+String.valueOf(install.exitValue()), Toast.LENGTH_LONG).show();
}
}
catch (InterruptedException e) {
logError(e);
}
catch (IOException e) {
logError(e);
}
break;
case R.id.btnUninstall:
try {
install = Runtime.getRuntime().exec("su\n");
install=Runtime.getRuntime().exec("pm uninstall "+txtPackageName.getText().toString()+"\n");
} catch (Exception e) {
logError(e);
}
break;
}
}
To avoid typos and other trims I hardcoded the apk file parameter of the command for the installation; on 'case R.id.btnInstall' the command is not executed and the exit is on "Failure" with exit value 1, meaning that "the class cannot be found"; no clue what that means ...
I appreciate your help!
EDITED: I have the clean solution, I shall post the answer from A-Z as soon as I have the time and the code in the right form!!
As I promised here is the solution to this problem, without doing any forcing to the system other than having to install the whole application in the /system/app directory. I have followed, then did some fixing to the excellent article here: http://paulononaka.wordpress.com/2011/07/02/how-to-install-a-application-in-background-on-android/. I have downloaded the zip file referenced in the article then, (I tried to keep the same class names where possible):
created a new project and a main activity as entry point
package com.example.silentinstuninst;
import java.io.File;
import java.lang.reflect.InvocationTargetException;
import com.example.instuninsthelper.ApplicationManager;
import com.example.instuninsthelper.OnDeletedPackage;
import com.example.instuninsthelper.OnInstalledPackage;
import android.os.Bundle;
import android.os.Environment;
import android.app.Activity;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;
public class MainActivity extends Activity implements OnClickListener {
Process install;
Button btnInstall, btnUninstall;
EditText txtApkFileName, txtPackageName;
public static final String TAG = "SilentInstall/Uninstall";
private static ApplicationManager am;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initializeValues();
}
private void initializeValues() {
btnInstall = (Button) findViewById(R.id.btnInstall);
btnUninstall = (Button) findViewById(R.id.btnUninstall);
txtApkFileName = (EditText) findViewById(R.id.txtApkFilePath);
txtPackageName = (EditText) findViewById(R.id.txtPackageName);
btnInstall.setOnClickListener(this);
btnUninstall.setOnClickListener(this);
try {
am = new ApplicationManager(this);
am.setOnInstalledPackage(new OnInstalledPackage() {
public void packageInstalled(String packageName, int returnCode) {
if (returnCode == ApplicationManager.INSTALL_SUCCEEDED) {
Log.d(TAG, "Install succeeded");
} else {
Log.d(TAG, "Install failed: " + returnCode);
}
}
});
am.setOnDeletedPackage(new OnDeletedPackage() {
public void packageDeleted(boolean succeeded) {
Log.d(TAG, "Uninstall succeeded");
}
});
} catch (Exception e) {
logError(e);
}
}
private void logError(Exception e) {
e.printStackTrace();
Toast.makeText(this, R.string.error+e.getMessage(), Toast.LENGTH_LONG).show();
}
#Override
public void onClick(View v) {
switch (v.getId())
{
case R.id.btnInstall:
// InstallUninstall.Install(txtApkFileName.getText().toString());
try {
am.installPackage(Environment.getExternalStorageDirectory() +
File.separator + txtApkFileName.getText().toString());
} catch (IllegalArgumentException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
} catch (IllegalAccessException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
} catch (InvocationTargetException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
} // install package
break;
case R.id.btnUninstall:
// InstallUninstall.Uninstall(txtPackageName.getText().toString());
try {
am.uninstallPackage(txtPackageName.getText().toString());
} catch (IllegalArgumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
logError(e);
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
logError(e);
} catch (InvocationTargetException e) {
// TODO Auto-generated catch block
e.printStackTrace();
logError(e);
}
break;
}
}
}
create in /src the package com.example.instuninsthelper. I have added there the ApplicationManager.java and OnInstalledPackage.java files
inserted the following code inside the ApplicationManager class:
private OnDeletedPackage onDeletedPackage;
class PackageDeleteObserver extends IPackageDeleteObserver.Stub {
public void packageDeleted(boolean succeeded) throws RemoteException {
if (onDeletedPackage != null) {
onDeletedPackage.packageDeleted(succeeded);
}
}
}
created, under the same com.example.instuninsthelper package the file OnDeletedPackage.java with the following code:
package com.example.instuninsthelper;
public interface OnDeletedPackage {
public void packageDeleted(boolean succeeded);
}
in the android.content.pm package (the namespace SHOULD not be changed) I modified the IPackageDeleteObserver.java, with this result:
package android.content.pm;
public interface IPackageDeleteObserver extends android.os.IInterface {
public abstract static class Stub extends android.os.Binder implements android.content.pm.IPackageDeleteObserver {
public Stub() {
throw new RuntimeException("Stub!");
}
public static android.content.pm.IPackageDeleteObserver asInterface(android.os.IBinder obj) {
throw new RuntimeException("Stub!");
}
public android.os.IBinder asBinder() {
throw new RuntimeException("Stub!");
}
public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags)
throws android.os.RemoteException {
throw new RuntimeException("Stub!");
}
}
public abstract void packageDeleted(boolean succeeded)
throws android.os.RemoteException;
}
build the application in Eclipse and deploy it to the emulator
in the emulator: home button > Settings > applications > ...uninstall the application (because it is not installed in /system/app, and we just needed the generation of the apk file)
do the following to root the emulator (so that we can write in /system/app; other solution, that I have used, is to generate a custom ROM with this app included into the /system/app):
download the su file from here http://forum.xda-developers.com/showthread.php?t=682828http://forum.xda-developers.com/showthread.php?t=682828. Rename it to su.zip
then from the console:
* adb shell mount -o rw,remount -t yaffs2 /dev/block/mtdblock03 /system
* adb push su.zip /system/xbin/su
* adb shell chmod 06755 /system
* adb shell chmod 06755 /system/xbin/su
from the console, go to the /bin directory of the project, then enter:
* adb push .apk /system/app
finally, always from the console, enter:
* adb shell am start -n com.example.silentinstuninst/com.example.silentinstuninst.MainActivity
enjoy!
Don't know, but just a idea:
I think that you are writing in the standarout, not executing a command nor giving extra data to the process via its input. I think it should be:
Runtime.getRuntime().exec("pm install /mnt/sdcard/HelloAndroid.apk\n");
Hope this helps.
Installing in the /system/app directory is essentially the same as requiring root.
Assuming you have root, check out RootTools. Then you can do:
if (RootTools.isAccessGiven()) {
CommandCapture command = new CommandCapture(0, "pm install " + PATH_TO_APK);
RootTools.getShell(true).add(command).waitForFinish();
}
Note that waitForFinish() is a blocking call!
Well you can do this also with the PackageManager directly (requires root access):
Create an app with a platform-sdk which has the interfaces publicly (create or download it, and configure eclipse)
In the app directly call the hidden API functions which allow silent install/remove.
Install the APK on your device as a system app by copying it to /system/app (root needed)
See this: http://forum.xda-developers.com/showthread.php?t=1711653
Runtime.getRuntime().exec("pm install /mnt/sdcard/HelloAndroid.apk\n");
This works for me, although two more additional details have to be done:
Add android:sharedUserId="android.uid.system" in AndroidManifest.xml.
Signed the apk with the system key.
But in this way it seems there is no way to tell whether the installation is succeeded, so I will try #Ginger's method later.
For all who are still having problem: you will need a rooted device and use
Process result = Runtime.getRuntime().exec("pm install -r -d MyApp.apk /system/app")
If you are getting result code 9 (error code 9) you will need to delete your apk from the device and push it back (PUSH not INSTAL!).
Go to the device shell and Push the apk
launcher=MyApp.apk
$adb shell su -c "mount -o remount,rw -t rfs /dev/stl5 /system"
$adb push $launcher /sdcard/$launcher
$adb shell su -c "chmod 644 /system/app/$launcher"
Now you are able to use pm install without getting an error. Hope it will help somebody.

Requesting root access for android app

I know there are already questions about that here, but I tried all the answers given without success.
There's a simple CheckBoxPreference (titled "Root"):
<CheckBoxPreference
android:key="root"
android:title="#string/root"
android:summary="#string/root_summary"
android:defaultValue="false" />
Now I need to set the OnPreferenceChangeListener on it and gain root access. If so the checkbox should be checked, otherwise it should not:
public class Settings extends PreferenceActivity implements OnPreferenceChangeListener {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.settings);
findPreference("root").setOnPreferenceChangeListener(this);
}
#Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
String key = preference.getKey();
if ("root".equals(key) && !((CheckBoxPreference) preference).isChecked()) {
try {
Process p = Runtime.getRuntime().exec("su");
p.waitFor();
Log.d("Settings", Integer.toString(p.exitValue()));
if (p.exitValue() == 255) {
Log.d("Settings", "###NO ROOT###");
return false;
}
} catch (Exception e) {
Log.d("Settings", "###NO ROOT###");
Log.d("Settings", e.getMessage());
return false;
}
Log.d("Settings", "!!!ROOT!!!");
}
return true;
}
}
Superuser prompts correctly for root access. Denying however also returns true, as exitValue is 1 (???) and allowing freezes the whole app (I guess at p.waitFor).
I'm currently running Superuser 3.1.3 with su binary 3.1.1 (newest versions).
Taking a look into logcat I can see the following message: Activity pause timeout for ActivityRecord{42c0ebb8 com.example/.gui.Settings}
The command you're running is just su which will, I suspect, run a shell as superuser. You're waiting (indefinitely) for that shell to finish.
You need to specify su some-command-here-which-needs-to-run-as-root.
Unfortunately, there is no way to achieve superuser permissions for the Java code within your Android project. The root-ness applies only to commands which are spawned by su itself.

Backwards-compatible BackupAgent

I am looking into using the new Backup API that available since Android 2.2, but need to maintain backwards compatibility (to 1.5 to be exact).
The docs state:
The backup service and the APIs you must use are available only on devices running API Level 8 (Android 2.2) or greater, so you should also set your android:minSdkVersion attribute to "8". However, if you implement proper backward compatibility in your application, you can support this feature for devices running API Level 8 or greater, while remaining compatible with older devices.
I indeed build against the level 8 targetSdkVersion with level 3 minSdkVersion and try to use a wrapper class (with reflection) to overcome the problem that the application will not run if you implement a class that extends an nonexisting class.
Here is the problem: since we don't make actual calls to the BackupHelper class ourselves, we can't check upfront if the class indeed exists. (As is explained in the Android Backwards Compatibility documentation with a checkAvailable() method.) The class will therefore be instantiated and cast to a BackupAgent. But since we use reflection, it doesn't actually override BackupAgent and an exception occurs at runtime when the backup is requested:
java.lang.RuntimeException: Unable to create BackupAgent org.transdroid.service.BackupAgent: java.lang.ClassCastException: org.transdroid.service.BackupAgent
Here is my approach to a backwards compatible BackupAgent: http://code.google.com/p/transdroid/source/browse/#svn/trunk/src/org/transdroid/service where the BackupAgent.java is the 'regular' BackupAgentHelper-extending class and BackupAgentHelperWrapper is the reflection-based wrapper class.
Anyone successfull in implementing a BackupAgent with backwards compatibility?
As an alternative, you can just use pure reflection to talk to the BackupManager:
public void scheduleBackup() {
Log.d(TAG, "Scheduling backup");
try {
Class managerClass = Class.forName("android.app.backup.BackupManager");
Constructor managerConstructor = managerClass.getConstructor(Context.class);
Object manager = managerConstructor.newInstance(context);
Method m = managerClass.getMethod("dataChanged");
m.invoke(manager);
Log.d(TAG, "Backup requested");
} catch(ClassNotFoundException e) {
Log.d(TAG, "No backup manager found");
} catch(Throwable t) {
Log.d(TAG, "Scheduling backup failed " + t);
t.printStackTrace();
}
}
Point the android:backupAgent straight at a v2.2 class; it will never be loaded on a pre-v2.2 VM, so there won't be any linkage problems.
I don't see why you run into this problem.
I have the same issue: I want to support backup with a app that supports also 1.5 (API 3).
There is no problem in creating my BackupAgentHelper class, since that class is never called from my own code, but from the BackupManager i.e. the system itself. Therefore I don't need to wrap it, and I don't see why you should be doing that:
public class MyBackupAgentHelper extends BackupAgentHelper {
#override onCreate()
{
\\do something usefull
}
However, you do want to get a backup running, to do that you need to call on BackupManager.dataChanged() whenever your data changes and you want to inform the system to backup it (using your BackupAgent or BackupAgentHelper).
You do need to wrap that class, since you call it from you application code.
public class WrapBackupManager {
private BackupManager wrappedInstance;
static
{
try
{
Class.forName("android.app.backup.BackupManager");
}
catch (Exception e)
{
throw new RuntimeException(e);
}
}
public static void checkAvailable() {}
public void dataChanged()
{
wrappedInstance.dataChanged();
}
public WrapBackupManager(Context context)
{
wrappedInstance = new BackupManager(context);
}
}
You then call it from your code when you change a preference or save some data.
Some code from my app:
private static Boolean backupManagerAvailable = null;
private static void postCommitAction() {
if (backupManagerAvailable == null) {
try {
WrapBackupManager.checkAvailable();
backupManagerAvailable = true;
} catch (Throwable t) {
backupManagerAvailable = false;
}
}
if (backupManagerAvailable == true) {
Log.d("Fretter", "Backup Manager available, using it now.");
WrapBackupManager wrapBackupManager = new WrapBackupManager(
FretterApplication.getApplication());
wrapBackupManager.dataChanged();
} else {
Log.d("Fretter", "Backup Manager not available, not using it now.");
}
So, hopefully this works for you!
(If you call adb shell bmgr run every time you want to emulate the actual system initiated backupprocess it should properly backup and restore when you reinstall the app.)
You need to set the minSDK version to the following:
<uses-sdk android:minSdkVersion="3" android:targetSdkVersion="8"/>
and setting the build target to sdk 8 (project properties in eclipse '.default.properties'):
# Project target.
target=android-8
Now to call new stuff added in SDK 8 you have to use reflection: http://developer.android.com/resources/articles/backward-compatibility.html
I ran into the same problem and here's what I did to work it out.
You don't extend BackupAgent with the wrapper, you extend it with the wrapped class. So you make your real backup class:
public class MyBackup extends BackupAgent {
#Override
public void onBackup(ParcelFileDescriptor oldState, BackupDataOutput data,
ParcelFileDescriptor newState) throws IOException {
// TODO Auto-generated method stub
}
#Override
public void onRestore(BackupDataInput data, int appVersionCode,
ParcelFileDescriptor newState) throws IOException {
// TODO Auto-generated method stub
}
Okay, and then you make a wrapper like the android developer backwards compatibility article said to do. Note that this class does not extend BackupAgent:
public class WrapMyBackup {
private MyBackup wb;
static {
try {
Class.forName("MyBackup");
}
catch (Exception ex) {
throw new RuntimeException(ex);
}
}
/** call this wrapped in a try/catch to see if we can instantiate **/
public static void checkAvailable() {}
public WrapMyBackup() {
wb = new MyBackup();
}
public void onBackup(ParcelFileDescriptor oldState, BackupDataOutput data,
ParcelFileDescriptor newState) throws IOException {
wb.onBackup(oldState, data, newState);
}
public void onRestore(BackupDataInput data, int appVersionCode,
ParcelFileDescriptor newState) throws IOException {
wb.onRestore(data, appVersionCode, newState);
}
public void onCreate() {
wb.onCreate();
}
public void onDestroy() {
wb.onDestroy();
}
}
Finally, in your manifest, you declare the wrapper as your backup agent:
<application
android:label="#string/app_name"
android:icon="#drawable/ic_launch_scale"
android:backupAgent="WrapMyBackup"
>
Since your wrapper has the proper methods defined you won't run into a problem when the backup manager casts it to a BackupAgent. Since lower API levels won't have a BackupManager the code will never get called, so you won't run into any runtime exceptions there either.
How about
if (android.os.Build.VERSION.SDK_INT >= 8)
{
BackupManager bm = new BackupManager(this);
bm.dataChanged();
}
Insted of just calling BackupManager.dataChanged, check if the class exists first.
try {
Class.forName("android.app.backup.BackupManager");
BackupManager.dataChanged(context.getPackageName());
} catch (ClassNotFoundException e) {
}

Categories

Resources