My Android Advanced Journey

Keywords: Android xml Attribute shell

For every company's APP, of course, they want their own APP process as far as possible not to be killed, so there are a number of ways to keep the process alive. There are many articles about this kind of process on the Internet, but many information on the Internet often only tells the idea, not the realization of code display. This blog will be divided into two parts, elaborating on all parties about process survival. Law, as well as the way to achieve, if there are mistakes and omissions, you can leave a message on the blog.

**

1. Process Survival - Background Knowledge

**
(1) When will the system kill the process?
Android system will kill the process when the memory is insufficient, commonly known as Low Memory Killer. It is based on the OOM Killer (Out-Of-Memory killer) mechanism of linux kernel. When the memory is insufficient, the process with high oom_adj value will be killed first.

Now that you know the oom_adj value, you must want to know how to view the oom_adj value of the application.
The oom value of the system process is less than 0, and the application process is more than 0. It can be found that the system is a snatch.
We can view the oom_adj value of the corresponding process through the adb command. The command is as follows:

View command: adb shell ps | grep process name | awk'{print $2}'| xargs -i adb shell cat /proc/{}/oom_adj

Here I summarize the oom_adj values for various types of processes

(2) What are the scenarios in which a process is killed?
There are many scenarios in which processes are killed, such as being killed by third-party applications (360 housekeepers, etc.), shutdown, etc. Different scenarios call different system interfaces, and the range of oom_adj values killed is also different. So I summarize these scenarios into a table for you to understand:

2. Common ways to keep alive and pull up

Understanding the relevant scenes of process killing, I believe that you have a preliminary understanding of process survival. Next, I will introduce to you the various common ways of survival pull-up in the market. These ways are as follows:

** a) Set Service as Front Desk Service
b) Return STATR_STICK in service onstart method
c) Add the Manifest file attribute value android:persistent= "true"
d) Overwriting Service's onDestroy Method
e) Listening for a bunch of system static broadcasts
f) Monitoring static broadcasting of third-party applications
G) Alarm Manager Wake-up
h) Account synchronization, regular wake-up
I) 1-pixel suspension layer
J) GCM or other three-party push
k) Applications pull up from each other
l) Heart beat Awakening
m)Native process pulled up
n) Two-Process Guardian**

1) Set Service as Front Office Service
Idea: Enable front-end services, mainly startForeground()
Survival: Normally not killed. Some custom ROM s will be killed when the application is cut to the background.
It will be killed by force stop
Code implementation:

Notificationnotification = newNotification(R.drawable.queen2, "Here comes the news."
        , System.currentTimeMillis());


notification.setLatestEventInfo(this, "Double 11, God Cat!",
        "All 50 percent off", null);

//Setting the default effect of notification
notification.flags = Notification.FLAG_SHOW_LIGHTS;

startForeground(1, notification);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

2) Return STATR_STICK in service onstart method
Idea: In fact, it's returning STATR_STICK in onStartCommand
Survival: Limited number and time
It will be killed by force stop
Code implementation:

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
    // TODO Auto-generated method stub
    return START_STICKY;
    //return super.onStartCommand(intent, flags, startId);

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

3) Add the Manifest file attribute value android:persistent= "true"
Code implementation (configuration in manifest file):
Survival: Normally not killed, will be killed by force stop

<application android:name="PhoneApp"
    android:persistent="true"
    android:label="@string/dialerIconLabel"
    android:icon="@drawable/ic_launcher_phone">
  • 1
  • 2
  • 3
  • 4
  • 5

Note: This method requires system signature

4) Override Service's onDestroy Method
Idea: Start the service again in onDestroy
Survival: Very weak, work only in two situations: killing service in operation, stop process in DDMS
Code implementation:

@Override
public void onDestroy() {
    Intent intent = new Intent(this, KeeLiveService.class);
    startService(intent);
    super.onDestroy();
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

5) Monitor a bunch of system static broadcasts
Idea: When a specific system event occurs, the system will issue a response broadcast. By registering the corresponding broadcast listener in Android Manifest "statically", the system can be activated when a response event occurs.
The list of system static broadcasts that can be monitored is as follows:

Live Strength: We can find that this method is to monitor some broadcasts of the system, so we need to register static broadcasts in our applications, but static broadcasts also have problems, that is, over version 4.0, static broadcasts can not be received after the application has not been started or Force-Stop, that is to say, after 4.0, if our application has never been started, or for. Ce-Stop has been killed and cannot receive static broadcasts.

If two applications pull each other up, then an Intent with FLAG_INCLUDE_STOPPED_PACKAGES can be sent in one application. Even if the other application is in the above two cases, it can receive the broadcasting of the system.
Application 1 code implementation:

//application1,Send pull-up Service broadcasts
Intent intent = new Intent();
intent.setAction("com.action.keepLive");
intent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES);
this.sendBroadcast(intent);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

Application 2 code implementation:

<receiver android:name="com.yzy.supercleanmaster.receiver.KeepLiveReceiver">
    <intent-filter>
        <action android:name="com.action.keepLive" />
    </intent-filter>
</receiver>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
public class KeepLiveReceiver extends BroadcastReceiver{
    //In App 2, receive the broadcast sent by App 1 and pull up the service
    @Override
    public void onReceive(Context context, Intent intent) {
        Intent i = new Intent(context, KeeLiveService.class);
        context.startService(i);
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

6) Monitoring static broadcasting of third-party applications
Ideas: through decompile third party Top applications, such as mobile QQ, WeChat, Alipay, UC browser, and friends, homing pigeons, push SDK and so on, find out their outgoing broadcast, monitor in the application, so when these applications are broadcast, they will activate our application.

Preservation strength:
In addition to the same factors as system broadcasting, the limitations of the scheme are mainly limited by the following factors:
1) How many third-party applications have been decompiled and analyzed
2) The broadcasting of third-party applications belongs to the private application. The effective broadcasting in the current version may be removed or changed to non-outgoing at any time in the subsequent version. All these factors affect the effect of the pull-up.

7) Alarm Manager Wake-up
Idea: Setting up a timer through Alarm Manager to provide a regular wake-up service
** Live Intensity: ** Kill BackgroundProcess, in most cases work,
force-stop, the alarm clock will be cleared.
Code implementation:

public void startKeepLiveService(Context context, int timeMillis,String action) {
    //Getting Alarm Manager System Services
    AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
    //Packaging Intent
    Intent intent = newIntent(context,KeepLiveServie.class);
    intent.setAction(action);
    PendingIntent pendingIntent = PendingIntent.getService(context,0,intent, PendingIntent.FLAG_UPDATE_CURRENT);
    //Add to Alarm Manager
    alarmManager.setRepeating(AlarmManager.RTC_WAKEUP,System.currentTimeMillis(),timeMillis,pendingIntent);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

8) Account synchronization, regular wake-up
** Thought: ** android system has an account system, the system regularly wakes up the account update service, synchronous event interval is limited, the shortest one minute.
Difficulty: You need to set up an account manually. How can you trick your users into setting up an account manually before uninstalling you? You must connect to the Internet.
Code implementation:
Establishment of Content Provider
A Content Provider is used for data synchronization. Since there is no actual data synchronization, an empty Content Provider can be created directly here.

public class XXAccountProvider extends ContentProvider {
    public static final String AUTHORITY = "Package name.provider";
    public static final String CONTENT_URI_BASE = "content://" + AUTHORITY;
    public static final String TABLE_NAME = "data";
    public static final Uri CONTENT_URI = Uri.parse(CONTENT_URI_BASE + "/" + TABLE_NAME);

    @Override
    public boolean onCreate() {
        return true;
    }

    @Nullable
    @Override
    public Cursor query(Uri uri, String[] projection, String selection,
                        String[] selectionArgs, String sortOrder) {
        return null;
    }

    @Nullable
    @Override
    public String getType(Uri uri) {
        return new String();
    }

    @Nullable
    @Override
    public Uri insert(Uri uri, ContentValues values) {
        return null;
    }

    @Override
    public int delete(Uri uri, String selection, String[] selectionArgs) {
        return 0;
    }

    @Override
    public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
        return 0;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41

Then it's declared in Manifest

<provider
    android:name="**.XXAccountProvider"
    android:authorities="@string/account_auth_provider"
    android:exported="false"
    android:syncable="true"/>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

(2) Establishing Sync Adapter
After the implementation of SyncAdapter system service, the program data ContentProvider is updated with the timer of the system. The specific steps are as follows:
- Creating Sync Services

public class XXSyncService extends Service {
    private static final Object sSyncAdapterLock = new Object();
    private static XXSyncAdapter sSyncAdapter = null;

    @Override
    public void onCreate() {
        synchronized (sSyncAdapterLock) {
            if (sSyncAdapter == null) {
                sSyncAdapter = new XXSyncAdapter(getApplicationContext(), true);
            }
        }
    }

    @Override
    public IBinder onBind(Intent intent) {
        return sSyncAdapter.getSyncAdapterBinder();
    }

    static class XXSyncAdapter extends AbstractThreadedSyncAdapter {
        public XXSyncAdapter(Context context, boolean autoInitialize) {
            super(context, autoInitialize);
        }

        @Override
        public void onPerformSync(Account account, Bundle extras, String authority, ContentProviderClient provider, SyncResult syncResult) {
            getContext().getContentResolver().notifyChange(XXAccountProvider.CONTENT_URI, null, false);
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • Declare Sync Services
<service
    android:name="**.XXSyncService"
    android:exported="true"
    android:process=":core">
    <intent-filter>
        <action
            android:name="android.content.SyncAdapter"/>
    </intent-filter>
    <meta-data
        android:name="android.content.SyncAdapter"
        android:resource="@xml/sync_adapter"/>
</service>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

Where sync_adapter is:

<sync-adapter xmlns:android="http://schemas.android.com/apk/res/android"
    android:accountType="@string/account_auth_type"
    android:allowParallelSyncs="false"
    android:contentAuthority="@string/account_auth_provide"
    android:isAlwaysSyncable="true"
    android:supportsUploading="false"
    android:userVisible="true"/>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

Description of parameters:
android:contentAuthority specifies that the ContentProvider to be synchronized has an android:authorities attribute in its Android Manifest. XML file.
android:accountType represents the type of account that is synchronized.
android:userVisible settings are displayed in Settings
Android: Support Uploading setting whether notify Change notification is required to synchronize
Android: Does Allow Parallel Syncs support multiple accounts synchronization at the same time?
Android: isAlways Syncable sets isSyncable for all accounts to 1
Android: SyncAdapter Settings Action specifies an Action that can set up synchronous activity.

  • Account call Sync service
    First configure Account (Step 3) and then implement it through ContentProvider
    manual update
public void triggerRefresh() {
    Bundle b = new Bundle();
    b.putBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, true);
    b.putBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, true);
    ContentResolver.requestSync(
            account,
            CONTENT_AUTHORITY,
            b);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

Add account

Account account = AccountService.GetAccount();
AccountManager accountManager = (AccountManager) context.getSystemService(Context.ACCOUNT_SERVICE);
accountManager.addAccountExplicitly(...)

``
Synchronization cycle settings
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

ContentResolver.setIsSyncable(account, CONTENT_AUTHORITY, 1);
ContentResolver.setSyncAutomatically(account, CONTENT_AUTHORITY, true);
ContentResolver.addPeriodicSync(account, CONTENT_AUTHORITY, new Bundle(), SYNC_FREQUENCY);

"
Establishment of Account Authenticator
Synchronization is achieved by establishing an Account account and associating SyncAdapter services
- Create Account Service

public class XXAuthService extends Service {
    private XXAuthenticator mAuthenticator;

    @Override
    public void onCreate() {
        mAuthenticator = new XXAuthenticator(this);
    }

    private XXAuthenticator getAuthenticator() {
        if (mAuthenticator == null)
            mAuthenticator = new XXAuthenticator(this);
        return mAuthenticator;
    }

    @Override
    public IBinder onBind(Intent intent) {
        return getAuthenticator().getIBinder();
    }

    class XXAuthenticator extends AbstractAccountAuthenticator {
        private final Context context;
        private AccountManager accountManager;

        public XXAuthenticator(Context context) {
            super(context);
            this.context = context;
            accountManager = AccountManager.get(context);
        }

        @Override
        public Bundle addAccount(AccountAuthenticatorResponse response, String accountType, String authTokenType, String[] requiredFeatures, Bundle options)
                throws NetworkErrorException {
            // Add Account Sample Code
            final Bundle bundle = new Bundle();
            final Intent intent = new Intent(context, AuthActivity.class);
            intent.putExtra(AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE, response);
            bundle.putParcelable(AccountManager.KEY_INTENT, intent);
            return bundle;
        }

        @Override
        public Bundle getAuthToken(AccountAuthenticatorResponse response, Account account, String authTokenType, Bundle options)
                throws NetworkErrorException {
            // Authentication sample code
            String authToken = accountManager.peekAuthToken(account, getString(R.string.account_token_type));
            //if not, might be expired, register again
            if (TextUtils.isEmpty(authToken)) {
                final String password = accountManager.getPassword(account);
                if (password != null) {
                    //get new token
                    authToken = account.name + password;
                }
            }
            //without password, need to sign again
            final Bundle bundle = new Bundle();
            if (!TextUtils.isEmpty(authToken)) {
                bundle.putString(AccountManager.KEY_ACCOUNT_NAME, account.name);
                bundle.putString(AccountManager.KEY_ACCOUNT_TYPE, account.type);
                bundle.putString(AccountManager.KEY_AUTHTOKEN, authToken);
                return bundle;
            }

            //no account data at all, need to do a sign
            final Intent intent = new Intent(context, AuthActivity.class);
            intent.putExtra(AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE, response);
            intent.putExtra(AuthActivity.ARG_ACCOUNT_NAME, account.name);
            bundle.putParcelable(AccountManager.KEY_INTENT, intent);
            return bundle;
        }

        @Override
        public String getAuthTokenLabel(String authTokenType) {
//            throw new UnsupportedOperationException();
            return null;
        }

        @Override
        public Bundle editProperties(AccountAuthenticatorResponse response, String accountType) {
            return null;
        }

        @Override
        public Bundle confirmCredentials(AccountAuthenticatorResponse response, Account account, Bundle options)
                throws NetworkErrorException {
            return null;
        }

        @Override
        public Bundle updateCredentials(AccountAuthenticatorResponse response, Account account, String authTokenType, Bundle options)
                throws NetworkErrorException {
            return null;
        }

        @Override
        public Bundle hasFeatures(AccountAuthenticatorResponse response, Account account, String[] features)
                throws NetworkErrorException {
            return null;
        }
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • Declare Account Service
<service
    android:name="**.XXAuthService"
    android:exported="true"
    android:process=":core">
    <intent-filter>
        <action
            android:name="android.accounts.AccountAuthenticator"/>
    </intent-filter>
    <meta-data
        android:name="android.accounts.AccountAuthenticator"
        android:resource="@xml/authenticator"/>
</service>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

The authenticator is:

<?xml version="1.0" encoding="utf-8"?>
<account-authenticator xmlns:android="http://schemas.android.com/apk/res/android"
    android:accountType="@string/account_auth_type"
    android:icon="@drawable/icon"
    android:smallIcon="@drawable/icon"
    android:label="@string/app_name"
    />
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • Using Account Service
    With SyncAdapter, using Account Manager
    - Application for Token is mainly through the [AccountManager.getAuthToken] series of methods
    - Adding an account is done through [AccountManager.addAccount]
    - Check if an account exists through [AccountManager.getAccountsByType]

Preservation strength:
This solution applies to all Android versions, including processes that have been dropped by forestop. In the latest Android version (Android N), the system seems to have changed the account synchronization here, and this method is no longer effective.

Posted by lupes on Fri, 10 May 2019 15:33:42 -0700