I've been researching this issue for the whole day. Here are key points:
SharedPreferences should be persistent when user does app update
in my case, after updating the app, they are lost
the issue is reproducable every time (I install old APK from Play Store and then adb install -r new.apk with the new (updated, signed, versionCode incremented) APK)
8 hours later
For test I changed SharedPrefs filename in new.apk (SP2.xml) and upon updating, the old SharedPrefs file from old.apk (SP.xml) got deleted! Here is adb shell output:
adb install old.apk
adb shell "su -c 'ls /data/data/com.pkg.name/shared_prefs'":
CRC.xml
adb install -r new.apk
adb shell "su -c 'ls /data/data/com.pkg.name/shared_prefs'":
CRC2.xml (CRC.xml missing!)
My SharedPreferences singleton class (init: SharedPrefs.init(getApplicationContext());):
public final class SharedPrefs {
private static SharedPrefs sp;
private SharedPrefs() {
}
public static void init(Context context) {
if (sp == null)
sp = context.getSharedPreferences("CRC2", Context.MODE_PRIVATE);
}
public static void saveString(String name, String value) {
sp.edit().putString(name, value).apply();
}
public static String getString(String key, String defaultValue) {
sp.getString(key, defaultValue);
}
...
}
So basically I am loosing SharedPreferences and I have no clue why. Please help, any hint welcome!
If you changed a property in the application section of the manifest file, this error will occur and 90% of the time, the shared pref data will be reset. This is what I found from my test installing the signed apk on top of my play store app. Not sure what will happen if the app was installed from the play store as an update, but am pretty sure the data would be lost in that case as well.
EDIT- I republished the application and tested multiple times. This in fact is the issue.
Related
I want to check my android app first running. So my code is follwing.
This code is successfully executed when my app is first installed.
But, i'm not sure it is executed when my app is updated through App Store.
Is it executed when my android app is updated through App Store?
public boolean CheckAppFirstExecute(){
SharedPreferences pref = getSharedPreferences("isFirst" , Activity.MODE_PRIVATE);
boolean isFirst = pref.getBoolean("isFirst", false);
if(!isFirst) {
SharedPreferences.Editor editor = pref.edit();
editor.putBoolean("isFirst", true);
editor.commit();
....
}else{
Log.d("this is not first", "not first");
}
return !isFirst;
}
The shared preferences are not reset during an update. So your "first use" code will not run again.
Yes, the values in SharedPreference will not be dropped after an update through the Playstore or any manual apk install with install -r xx.apk
For my Android Project I tried to follow and implement Android Developers Data Backup Guidelines ( http://developer.android.com/guide/topics/data/backup.html ), but Data Backup and Restore doesn't work. Can someone help with examples?
With further investigation I figured out the steps to implement Android Data Backup and Restore. They are:
Add in Manifest xml file the following:
a. android:allowBackup="true" (This enables Android’s Data Backup Service)
b. meta-data android:name="com.google.android.backup.api_key"
android:value=”{Your unique Registration Key for Android Backup Service}” (You must register your application package name with a backup service. To generate a key, go to http://developer.android.com/google/backup/signup.html )
c. android:backupAgent=”.MyBackupAgent” (This is the name of class that implement’s your declared backup agent class). Note1: The first character of the name is a period for the purpose of a shorthand to locate your “com.example.project.MyBackupAgent”. Note2: If a run time Exception occurs (this may or may not happen depending on your project stucture) such as: java.lang.ClassNotFoundException: Didn’t find class “com.example.project.MyBackupAgent” then I suggest you Decompile your apk (Upload your apk package in http://www.decompileandroid.com/ ) and search for the absolute path to your MyBackupAgent and insert this path for android:backupAgent=”{absolute path}.MyBackupAgent”
Here’s an example of a Manifest xml file with Backup support:
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.project">
<application android:allowBackup="true" android:backupAgent="md5f576d3976d691fac04b078d1718cab3.MyBackupAgent">
<meta-data android:name="com.google.android.backup.api_key" >android:value="{Your unique Registration Key}" />
</application>
Add in project your MyBackupAgent class. The BackupAgentHelper class provides a convenient wrapper around the BackupAgent class, which minimizes the amount of code you need to write. In your BackupAgentHelper, you must use one or more "helper" objects, which automatically backup and restore certain types of data, so that you do not need to implement onBackup() and onRestore().
Note: Android currently provides backup helpers that will backup and restore complete files from SharedPreferences and internal storage.
Here’s a Java SharedPreferenceBackupHelper example for MyBackupAgent class:
import android.app.backup.BackupAgentHelper;
import android.app.backup.SharedPreferencesBackupHelper;
import android.util.Log;
public class MyBackupAgent extends BackupAgentHelper{
static final String PREFS = "myprefs";
static final String PREFS_BACKUP_KEY = "myprefs";
#Override
public void onCreate() {
Log.i("MyFileBackupAgent", "****** Enter BACKUP CLASS *******");
SharedPreferencesBackupHelper helper = new SharedPreferencesBackupHelper(this, PREFS);
addHelper(PREFS_BACKUP_KEY, helper);
Log.i("MyFileBackupAgent", "****** Exit BACKUP CLASS ********");
}
}
Here’s a C# Xamarin FileBackupHelper example for MyBackupAgent class:
public class MyBackupAgent: BackupAgentHelper
{
string myProtectData = "File.bin";
string myPersistentData = "Data.bin";
string myDBase = "Database.db";
public override void OnCreate()
{
Console.WriteLine ("****** Enter Backup Files Helpers *********");
base.OnCreate ();
try
{
FileBackupHelper dbkh = new FileBackupHelper (this, myProtectData);
this.AddHelper ("PROTECT_backup", dbkh);
FileBackupHelper persisth = new FileBackupHelper (this, myPersistentData);
this.AddHelper ("PERSIST_backup", persisth);
FileBackupHelper dbh = new FileBackupHelper (this, myDBase);
this.AddHelper ("DATABASE_backup", dbh);
Console.WriteLine ("********* All 3 files backuped *********");
}
catch {
Console.WriteLine ("******* Backup AddHelper Exception ERROR *********");
}
Console.WriteLine ("******** Exit Backup Files Helpers ********");
}
public override void OnBackup(ParcelFileDescriptor oldState, BackupDataOutput data, ParcelFileDescriptor newState)
{
Console.WriteLine ("****** Override OnBackup called ******");
base.OnBackup(oldState, data, newState);
}
public override void OnRestore (BackupDataInput data, int appVersionCode, ParcelFileDescriptor newState)
{
Console.WriteLine ("****** Override OnRestore called ******");
base.OnRestore(data, appVersionCode, newState);
}
}
To perform a backup, your code should make a backup request by calling the dataChanged(). A backup request does not result in an immediate call to your onBackup() method. Instead, the Backup Manager waits for an appropriate time*, then performs backup for all applications that have requested a backup since the last backup was performed. Note, The Backup Manager Service responds every hour as long as at least one DataChanged() was called in-between the hour since the last data backup request.
For test purposes, an immediate backup can be performed with the Android SDK Command Prompt Tool. Try these commands:
To ensure Data Backup Enabled:
adb shell bmgr enable true
To request a Data Backup:
adb shell bmgr backup
To initiate a Data Backup:
adb shell bmgr run
To uninstall your App:
adb uninstall
Then install your App:
adb install
What about your phone device Backup settings? Make sure a WiFi connection is established. Also, under device Settings, make sure "Back up my data" and "Automatic restore" are checked and you have entered in a valid Backup Account email id.
Lastly, to track your Backup upload time stamps, use www.google.com/settings/dashboard (this is your personal google account that matches your google account in your Android phone device Backup settings)
I am trying to get the BackupAgent working but I can't get it to work. Here is my sample code:
The layout is just a TextView and a Button.
MainActivity:
...
public static final String PREF_NAME = "TestPref";
private static final String TEST_KEY = "TEST";
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final SharedPreferences pref = getApplicationContext()
.getSharedPreferences(PREF_NAME, MODE_PRIVATE);
if (pref.getString(TEST_KEY, "").length() == 0) {
pref.edit().putString(TEST_KEY, "new Date())
.commit();
new BackupManager(getApplicationContext()).dataChanged();
}
Button button = (Button) findViewById(R.id.button1);
button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
TextView tv = (TextView) findViewById(R.id.textView1);
if ("START_VALUE".equalsIgnoreCase(tv.getText().toString())) {
tv.setText(pref.getString(TEST_KEY, ""));
}
}
});
}
The BackupHelper is just the ones I available here: http://developer.android.com/reference/android/app/backup/SharedPreferencesBackupHelper.html
I adjusted the name of the pref file with the one I used.
And in the Manifest I added
android:backupAgent="TheBackupAgent" (application tag)
and the backup-meta data
<meta-data android:name="com.google.android.backup.api_key"
android:value="{registered_key}" />
So its really a very simple app.
I am doing the following now:
1)Starting app
2) Textview is initialized with "START_VALUE" in xml file, so I press the Button and the pref-value is displayed
3) I run "adb shell bmgr run" from the console to run the backup immediately
4) I run "adb uninstall com.foo.backuptest"
5) I run "adb install com.foo.backuptest"
Now the value (timestamp) is not restored from the cloud. A new one is generated.
Where is my error??
Your Manifest file needs to include this to turn on Backup:
android:allowBackup="true"
android:backupAgent="TheBackupAgent"
What about your phone Backup settings? Make sure "Back up my data" and "Automatic restore" are checked and you have entered in a valid Backup Account email id.
To know when and how often data is backuped by Google, take a look at this link:
Android backup service - when and how often to backup?
According to this Tester (I also ran a backup frequency test just now): https://advancedweb.hu/2014/12/09/practical_measurement_of_the_android_backup_manager/
The Backup Manager Service responds every hour (I also proved this in my tests) as long as at least one DataChanged() was called in-between the hour since the last data backup request
For a quick test with command line, try these commands:
To ensure Data Backup Enabled: adb shell bmgr enable true
To request a Data Backup: adb shell bmgr backup your.package.name
To initiate a Data Backup: adb shell bmgr run
To uninstall your App: adb uninstall your.package.name
Then install your App: adb install your.package.name
I'm trying to install certificates without prompting the user. I know this is not good practice, but that's what PM wants.
Using KeyChain.createInstallIntent(), I can get Android to launch the certificate installation dialog by calling startActivity. However, when I pass the intent to sendBroadcast, nothing happens. Maybe the platform doesn't support this for security reasons?
String CERT_FILE = Environment.getExternalStorageDirectory() + "/test/IAT.crt";
Intent intent = KeyChain.createInstallIntent();
try {
FileInputStream certIs = new FileInputStream(CERT_FILE);
byte [] cert = new byte[(int)certFile.length()];
certIs.read(cert);
X509Certificate x509 = X509Certificate.getInstance(cert);
intent.putExtra(KeyChain.EXTRA_CERTIFICATE, x509.getEncoded());
intent.putExtra(KeyChain.EXTRA_NAME, "IAT Cert");
EapActivity.this.startActivityForResult(intent, 0); // this works but shows UI
EapActivity.this.sendBroadcast(intent); // this doesn't install cert
} catch (IOException e) {
You can only install certificates silently if you have system privileges. Showing up a confirmation dialog is intentional, since trusting certificates can have serious consequences -- Android could happily open phishing sites without a warning, etc. That said, the dialog in ICS/JB is pretty bad -- it doesn't tell you what certificate you are installing and who issued it, just that it's a CA certificate, which is kind of obvious.
So, either use the public KeyChain API and use startActivity() to get the confirmation dialog, or pre-provision devices before handling them to users.
Update: In Android 4.4, DevicePolicyManager has a hidden API (installCaCert) that allows you to install certificates silently. You need the MANAGE_CA_CERTIFICATES permission, which is signature|system, so still not doable for user-installed apps.
Using KeyChain.createInstallIntent(), I can get Android to launch the certificate installation dialog by calling startActivity. However, when I pass the intent to sendBroadcast, nothing happens.
Few if any Intent objects that you would pass to startActivity() would work with sendBroadcast(). They are independent channels of the quasi-message bus that is the Intent system.
For non-system app developers - the simple answer is it can not be done without user interaction.
For System App developers, I found the following solution, NB you must run the app with the system user id and sign the app with the system key or the service will reject your attempts to install the certificate.
Step 1 - Create interface
Create a new package in your project: android.security, then copy IKeyChainService.aidl into this package.
Step 2 - Bind to service and install certificate
The Activity gives an example of how to install a CA certificate:
public class KeyChainTest extends Activity {
private final Object mServiceLock = new Object();
private IKeyChainService mService;
private boolean mIsBoundService =false;
private ServiceConnection mServiceConnection = new ServiceConnection() {
#Override public void onServiceConnected(ComponentName name,
IBinder service) {
synchronized (mServiceLock) {
mService = IKeyChainService.Stub.asInterface(service);
mServiceLock.notifyAll();
try {
byte[] result = YOUR_CA_CERT_AS_BYTE_ARRAY
//The next line actually installs the certificate
mService.installCaCertificate(result);
} catch (Exception e) {
//EXception handling goes here
}
}
}
#Override public void onServiceDisconnected(ComponentName name) {
synchronized (mServiceLock) {
mService = null;
}
}
};
private void bindService() {
mIsBoundService = bindService(new Intent(IKeyChainService.class.getName()),
mServiceConnection,
Context.BIND_AUTO_CREATE);
}
private void unbindServices() {
if (mIsBoundService) {
unbindService(mServiceConnection);
mIsBoundService = false;
}
}
#Override public void onDestroy () {
unbindServices();
}
#Override
protected void onStart() {
super.onStart();
// Bind to KeyChainService
bindService();
}
}
I hope this helps someone - it took me a long time to work it out :)
This thread is a bit dated already, nevertheless since I stumbled upon the same issue and couldn't find any "out of the box" solution for Android O or later, I thought I'd share what I came up with and which worked well for me when trying to install Certificates (CA and others) to the Android Trusted credentials "User" store:
// Supply context, e.g. from "Context context = getApplicationContext();"
// String fileName points to the file holding the certificate to be installed. pem/der/pfx tested.
RandomAccessFile file = new RandomAccessFile(fileName, "r");
byte[] certificateBytes = new byte[(int)file.length()];
file.read(certificateBytes);
Class<?> keyChainConnectionClass = Objects.requireNonNull(context.getClassLoader()).loadClass("android.security.KeyChain$KeyChainConnection");
Class<?> iKeyChainServiceClass = Objects.requireNonNull(context.getClassLoader()).loadClass("android.security.IKeyChainService");
Method keyChainBindMethod = KeyChain.class.getMethod("bind", Context.class);
Method keyChainConnectionGetServiceMethod = keyChainConnectionClass.getMethod("getService");
Object keyChainConnectionObject = keyChainBindMethod.invoke(null, context);
Object iKeyChainServiceObject = keyChainConnectionGetServiceMethod.invoke(keyChainConnectionObject);
Method installCaCertificate = iKeyChainServiceClass.getDeclaredMethod("installCaCertificate", byte[].class);
installCaCertificate.invoke(iKeyChainServiceObject, certificateBytes);
Note that if you want to silently install a certificate this way, your app needs to be a system app, i.e. it needs to have
android:sharedUserId="android.uid.system"
declared in it's manifest.
Cheers!
If you have root privilege, you could copy the certs file to /data/misc/user/0/cacerts-added/
Based on the #ospider's answer, i managed to succesfully install the cert like this way:
adb shell mkdir -p /data/misc/user/0/cacerts-added
adb push certificate.cer /data/misc/user/0/cacerts-added/807e3b02.0
# Maybe these two lines are not strictly necessary...
adb shell chmod 644 /data/misc/user/0/cacerts-added/807e3b02.0
adb shell chown system:system /data/misc/user/0/cacerts-added/807e3b02.0
I got the name of the copied file (807e3b02.0) by installing manually the cert i wanted to automate and seeing how Android saved it (whith adb shell ls -l /data/misc/user/0/cacerts-added/)
Hope this help.
Regards.
Only a system user application can silently install a CA certificate. On Lollipop though, Google introduced silent certificate management API through DevicePolicyManager, but you would either have to be Android-for-Work profile owner or device owner.
I've researched and followed how to get my android app to backup the data to google backup so if user loses phone or upgrades to a new phone, they don't lose their data. However, when I test it out (by using the app myself, then uninstalling and reinstalling), no data is restored. Here's what I've done. Perhaps someone can figure out what is wrong.
Applied for a backup key from google
Placed following code in Manifest File (in place of key I did add the key value and for packageName I used my app package name)
android:backupAgent="packageName.MyPrefsBackup">
<meta-data android:name="com.google.android.backup.api_key" android:value="key" />
Created class MyPrefsBackup with following code. The name of the sharedpreference file I want to backup is called UserDB. As far as the PREFS_BACKIP_KEY, I just called it prefs. From what I understand, this is not the same key as the one that goes in the manifest file.
Code:
package packageName;
import android.app.backup.BackupAgentHelper;
import android.app.backup.SharedPreferencesBackupHelper;
public class MyPrefsBackup extends BackupAgentHelper {
// The name of the SharedPreferences file
static final String PREFS = "UserDB";
// A key to uniquely identify the set of backup data
static final String PREFS_BACKUP_KEY = "prefs";
// Allocate a helper and add it to the backup agent
public void onCreate() {
SharedPreferencesBackupHelper helper = new SharedPreferencesBackupHelper(this, PREFS);
addHelper(PREFS_BACKUP_KEY, helper);
}
}
Added BackupManager mBackupManager = new BackupManager(this); in my main class where I call the backup manager in next step
Lastly, in my main program I call the backupHelper when data is changed by the following line:
mBackupManager.dataChanged();
Any help is much appreciated.
try new BackupManager(this).dataChanged()
If you are testing this from AVD - you'll need to enable backups - AVD are disabled for backups by default.
adb shell bmgr enable true
Then you can actually debug it to see if the onCreate is called after running
adb shell bmgr run