Broadcast Receiver MY_PACKAGE_REPLACED never called - android

I have the following app scenario:
1) an app which updates by itself
2) the device is rooted
3) the checks for the version online and if new version is online it downloads the 'apk' file and installs it
Everything works fine but the APP does not restart after the new version install. I tried to set the MY_PACKAGE_REPLACED Broadcast Receiver, but it is never called. The app install new and stops but the receiver in the app is never triggered.
What am I doing wrong?
The code:
MANIFEST
<receiver android:name=".receivers.OnUpgradeReceiver">
<intent-filter>
<action android:name="android.intent.action.MY_PACKAGE_REPLACED"/>
<data android:scheme="package"/>
</intent-filter>
</receiver>
I tried the receiver manifest code with the DATA part and without ... and it still does not work!!
BROADCAST RECEIVER CLASS
public class OnUpgradeReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
final String msg="intent:"+intent+" action:"+intent.getAction();
Log.e("OLE","RECEIVEEEEEEEEEEEEEEEEEEEEEED: "+msg);
}}
APP UPDATE PART
Process p;
try {
//Runtime.getRuntime().exec (new String[]{"su", "-c", "pm install -r " + apkLocation + "party.net"});
// Preform su to get root privledges
p = Runtime.getRuntime().exec("su");
// Attempt to write a file to a root-only
DataOutputStream os = new DataOutputStream(p.getOutputStream());
os.writeBytes("/system/bin/pm install -r"+apkLocation+"\n");
// Close the terminal
os.writeBytes("exit\n");
os.flush();
try {
p.waitFor();
if (p.exitValue() != 255) {
Log.e("OLE","Sucess :-)");
}
else {
Log.e("OLE","Fail 1");
}
} catch (InterruptedException e) {
Log.e("OLE","Fail 2");
}
} catch (IOException e) {
Log.e("OLE","Fail 3 "+e.getMessage());
}
SOLVED!
The problem was that the new VERSION which was installed above the previous one did not have the broadcast receiver set!!!

There is no way to receive intent for new app if the new one is not running, the best solution is to use another app-B to receive intent and run the new app-A.

Related

Pushing other Android activity to foreground

From a package A, I triggered running service from privileged package B.
Package B performs an update of package A.
Target device is using Android 9 (API Level 28).
The update is successful (app version code has changed).
But my issue is that after the update, package A is in background ; on my device, it is on the background app list, I have to manually press on it to bring it to the foreground.
I would like it to come back to the foreground after install.
What I tried:
Sending a BroadcastIntent from package B to package A after install ; it looks like the intent is not received on package's A BroadcastReceiver (maybe due to the fact that it is in background or the app has been updated?)
After install this command works if I run it manually through adb: "$ adb shell am start -n "com.mma.mainapp/com.mma.mainapp.MainActivity" -a android.intent.action.MAIN -c android.intent.category.LAUNCHER". So I tried performing it from package B after install after a delay: "am start -n "$mainAppPackage/$mainAppPackage.MainActivity" -a ${Intent.ACTION_MAIN} -c ${Intent.CATEGORY_LAUNCHER}".runCommand(File(".")).
I also tried performing this from package B after install after a delay with correct permission but package A is not listed:
private fun moveToFront(mainAppPackage: String) {
val activityManager = getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
val recentTasks = activityManager.getRunningTasks(Int.MAX_VALUE)//activityManager.appTasks.map { it.taskInfo }
for (i in recentTasks.indices) {
log("PUSH Application executed : "
+ recentTasks[i].baseActivity.toShortString()
+ "\t\t ID: " + recentTasks[i].id + "")
if (recentTasks[i].baseActivity.toShortString().indexOf(mainAppPackage) > -1) {
log("PUSH TO FRONT : "
+ recentTasks[i].baseActivity.toShortString()
+ "\t\t ID: " + recentTasks[i].id + "")
activityManager.moveTaskToFront(recentTasks[i].id, ActivityManager.MOVE_TASK_WITH_HOME)
}
}
}
Overriding package A's Application::onCreate(), to bring activity to front: val i = Intent(context, MainActivity::class.java); i.setAction(Intent.ACTION_MAIN); i.addCategory(Intent.CATEGORY_LAUNCHER); context.startActivity(i) // working in isolation but onCreate is not trigerred after apk update.
Is there some way to perform this task? (this should be doable, given package B is privileged (installed under /system/priv-app on rooted device using the standard procedure) and has the same signature as package A)
You need to start an Activity after the installation is complete. It is possible you start activity before installation is complete. To get app A update completion register a broadcast receiver like this:
private static final IntentFilter appUpdateIntentFilter = new IntentFilter();
static {
appUpdateIntentFilter.addDataScheme("package");
appUpdateIntentFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
}
private AppUpdateReceiver appUpdateReceiver = new AppUpdateReceiver();
then if you are starting the download from app B's service, register in onCreate and stop in onDestroy of the service:
private void registerBroadcastReceiver() {
try {
registerReceiver(appUpdateReceiver, appUpdateIntentFilter);
} catch (IllegalArgumentException e) {
// already registered
}
}
private void unregisterBroadcastReceiver() {
try {
unregisterReceiver(appUpdateReceiver);
} catch (IllegalArgumentException e) {
// already unregistered
}
}
class AppUpdateReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
//if packageName is for app A then start an activity of app A
int uid = intent.getIntegerExtra(Intent.EXTRA_UID);
String appName = context.getPackageManager().getNameForUid(uid);
if(appName.equals("com.appA.PackageName")) {
//start an activity of app A
}
}
}

Running tasks from broadcast receiver while app is closed [Android]

I have an app that downloads data and put it into an SQLite Database when a notification is issued. This works fine while the app is in use but I need it to work when the app is closed too.
I have set up a BroadcastReceiver within that is called when the app is closed but I'm not sure how to get it to continue with adding to the database.
Here is the code I am using:
AndroidManifest.xml
<manifest....
<application...
<receiver android:name=".broadcast.PacksReceiver" >
<intent-filter>
<action android:name="ADD_PACK" >
</action>
</intent-filter>
</receiver>
PacksReceiver
public class PacksReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
Log.d("PacksReceiver", "onReceive");
String message = intent.getStringExtra("message");
PacksActivity pa = new PacksActivity();
pa.downloadPack(null, message);
}
}
PacksActivity
public void downloadPack(View v, String thisPackID){
Log.d("download", "pack");
//THIS LOG IS CALLED EVERYTIME
vRef = v;
if(vRef != null){
runOnUiThread(new Runnable() {
#Override
public void run() {
onScreenProgressBar = (ProgressBar) vRef.findViewById(R.id.onScreenProgress);
onScreenProgressCircle = (ProgressBar) vRef.findViewById(R.id.onScreenProgressCircle);
dlPercent = (TextView) vRef.findViewById(R.id.dlPercent);
onScreenProgressCircle.setVisibility(View.VISIBLE);
onScreenProgressBar.setVisibility(View.VISIBLE);
onScreenProgressCircle.setProgress(0);
}
});
}
if(thisPackID == null){
thisPackID = pack_id;
}
String url = MyApp.getAppContext().getString(R.string.serverURL) +
MyApp.getAppContext().getString(R.string.getAppendixA) + "/" + thisPackID;
Intent appA_Intent = new Intent(Intent.ACTION_SYNC, null, this, DownloadService.class);
appA_Intent.putExtra("url", url);
appA_Intent.putExtra("onCreate", "false");
appA_Intent.putExtra("receiver", downloadPackReceiver);
appA_Intent.putExtra("downloadType", "GET_APPENDIX_A");
appA_Intent.putExtra("requestId", 101);
MyApp.getAppContext().startService(appA_Intent);
}
start the Service from
onReceive()
method because you can get mutiple broadcast one after another.
Write the code to add data in your database in your PackReciever inside OnRecieve() Method because that is where you recieve push notifications.
Don't call activity inside the receiver. Instead, use IntentService to download all packs. IntentService automatically finishes once it completes its work.
Override its onHandleIntent() method and download packs and save to database there.

Properly tracking install referrals on Play Store

I have a simple task: I want to track the referral id of an app install and pass it to backend.
What I did: I created a link with an extra parameter referrer and appended it to the invite link. When it is opened, the javascript detects if the browser is an Android mobile browser and then prepares an intent and issues a redirect to that intent. While preparing the intent, referrer field is extracted from the url and appended to the intent like this:
intent://scan/#Intent;scheme=com.example.android;package=com.example.android&referrer=4;end
And here is my code for BroadCastReceiver :
public class InstallReferrerReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
TinyDB tinyDB = new TinyDB(context);
String referrer = intent.getStringExtra("referrer");
tinyDB.putString(AppConstants.REFERRAL_ID, referrer);
tinyDB.putBoolean(AppConstants.REFERRAL_SENT, false);
}
}
So, what I expect to get here as the value of referrer is 4 based on the above intent. But the value that I am getting is this String utm_source=google-play&utm_medium=organic
What am I doing wrong and how can I fix it to get the correct value for referrer field?
Edit
I don't have any issues in creating the url or extracting values from referrer field once the app is installed.
Once the invite link is clicked through any button click or opened directly in the mobile browser, I use the above to "either open the app if it is already installed or open the app's page on Play Store app for users to install it".
The issue is, how should I pass the value of referrer field from the invite link to the Play Store app through the above intent so that the Play Store receives this value and passes it to the app when it is installed.
You need to test it properly, I am posting mine use case, hope it will solve your problem :)
Refferal URL -
https://play.google.com/store/apps/details?id=com.mypackage&referrer=utm_source%3Dmobisoc%26utm_content%3D{transaction_id}%26utm_campaign%3D1
Code to receive refferal -
public static final String KEY_UTM_SOURCE = "utm_source";
public static final String KEY_UTM_CONTENT = "utm_content";
public static final String KEY_UTM_CAMPAIGN = "utm_campaign";
public void onReceive(Context context, Intent intent) {
Utils.log("Referral Received");
try {
String referrer = intent.getStringExtra("referrer");
if (referrer != null && !referrer.equals("")) {
Utils.log("Referral Received - " + referrer);
String[] referrerParts = referrer.split("&");
String utmSource = getData(KEY_UTM_SOURCE, referrerParts);
String utmContent = getData(KEY_UTM_CONTENT, referrerParts);
String utmCampaign = getData(KEY_UTM_CAMPAIGN, referrerParts);
if (utmSource != null && utmSource.equals("mobisoc")) {
sendLogToMobisocServer(context, utmContent);
} else if (utmSource != null && utmSource.equals("app_share")) {
RawStorageProvider.getInstance(context).dumpDataToStorage(RaghuKakaConstants.REFFERAL_FOR, utmContent);
}
updateRKServerForReferral(context, utmSource, utmCampaign, utmContent);
}
} catch (Exception e) {
e.printStackTrace();
}
}
private String getData(String key, String[] allData) {
for (String selected : allData)
if (selected.contains(key)) {
return selected.split("=")[1];
}
return "";
}
Now the most important part testing. You can test the referral locally. Just you need to attach your phone, open the shell prompt by using adb shell. And broadcast the referral data.
Here are the command sequence example -
C:\Users\Neo\Desktop>adb shell
$ am broadcast -a com.android.vending.INSTALL_REFERRER -n com.mypackage/<className of your ReferralReceiver with package> --es "referrer" "utm_source%3Dmobisoc%26utm_content%3D{transaction_id}%26utm_campaign%3D1"
Additional -
https://play.google.com/store/apps/details?id=com.mypackage&referrer=utm_source%3Dmobisoc%26utm_content%3D{transaction_id}%26utm_campaign%3D1
Just see my link. If user will go to the playstore via that link, and install the app. Then first time when the app will launch, your onReceive method will be fired automatically, and you will get all the data after referrer=.
Broadcast -
$ am broadcast -a com.android.vending.INSTALL_REFERRER -n com.mypackage/<className of your ReferralReceiver with package> --es "referrer" "utm_source%3Dmobisoc%26utm_content%3D{transaction_id}%26utm_campaign%3D1"
For testing it you no need to publish your app on playstore, Just put a debug point on first point of onReceive, launch in debug mode, and fire the command sequences I have posted, you will get all the data after "referrer" tag. So by this you can decide what data you need to add while creating the referrer link.
Let me know in case of more clarification you need :)
It is better and more reliable to track referrer via Firebase Dynamic Link.
Below this how it work.
https://domain/?link=your_deep_link&apn=package_name[&amv=minimum_version][&ad=1][&al=android_link][&afl=fallback_link]
Here's the example of link after fill in the parameters.
https://example.app.goo.gl/?link=https://www.example.com/someresource&apn=com.example.android&amv=3&al=exampleapp://someresource&ibi=com.example.ios&isi=1234567&ius=exampleapp
Of course, you can shorten the link to something like https://example.app.goo.gl/abcde directly at Firebase console. It will take only few minutes to setup the Dynamic Link.
Then in the Android app on your main Activity you can call AppInvite.AppInviteApi.getInvitation(mGoogleApiClient, this, false) to retrieve link information.
More information can be found here https://firebase.google.com/docs/dynamic-links/
I have used utm tagging
you can see full source at https://github.com/dheeraj9198/Utm-Test
I am providing the basic code
public class CustomCampaignTrackingReceiver extends BroadcastReceiver {
private static final String TAG = CustomCampaignTrackingReceiver.class.getSimpleName();
private static final Logger LOGGER = LoggerFactory.getLogger(TAG);
private static final Marker MARKER = MarkerFactory.getMarker(TAG);
#Override
public void onReceive(Context context,final Intent intentx) {
LOGGER.info(MARKER, "on Receive called");
ExecutorService executorService = Executors.newSingleThreadExecutor();
executorService.execute(new Runnable() {
#Override
public void run() {
try {
for (String key : intentx.getExtras().keySet()) {
try {
LOGGER.info(MARKER, key + " => " + String.valueOf(intentx.getExtras().get(key)));
} catch (Exception e) {
LOGGER.error(MARKER, "caught exception in on key retrieval ", e);
}
}
} catch (Exception e) {
LOGGER.error(MARKER, "caught exception in key loop ", e);
}
}
});
executorService.shutdown();
}
}
--------------------------Manifest---------------------------------------
<receiver
android:name="com.google.android.gms.analytics.CampaignTrackingReceiver"
android:exported="true" >
<intent-filter>
<action android:name="com.android.vending.INSTALL_REFERRER" />
</intent-filter>
</receiver>
<receiver
android:name=".receivers.CustomCampaignTrackingReceiver"
android:exported="true" >
<intent-filter>
<action android:name="com.android.vending.INSTALL_REFERRER" />
</intent-filter>
</receiver>

IntentService onHandleIntent behaviour on device shutdown

What would be the behaviour of onHandleIntent on device shutdown?
I know on IntentService, the service keeps running as long as onHandleIntent didn't finish its job.
so coming to think about it its a general question about services behaviour on device shutdown, will they "wake" up once we start up the device again?
if not, theres a way to do so? I want my intentservice to keep running until onHandleIntent finished, no matter what happened.
EDIT:
I will add my code for better understanding.
I'm trying to save request on SQLite and keep running until that table is empty, then the service will shut down.
so incase of device shutdown, I will still continue from the same spot I was before the shudown.
P.S - I try to use executor for better performances (its a test and not proven yet)
and if someone got better suggestion I’d love to hear them cause Im new in this subject
onHandleIntent
#Override
protected void onHandleIntent(Intent intent) {
helper = new DBHelper(getApplicationContext());
executor = Executors.newFixedThreadPool(5);
File file;
Log.e("requestsExists",helper.requestsExists()+"");
while (helper.requestsExists()) {
ArrayList<String> requestArr = helper.getRequestsToExcute(5);
//checks if the DB requests exists
if (!requestArr.isEmpty()) {
//execute them and delete the DB entry
for(int i = 0; i < requestArr.size(); i++) {
file = new File(requestArr.get(i));
Log.e("file",file.toString());
Future<String> future = executor.submit(new MyThread(file,getApplicationContext()));
Log.e("future object", future.toString());
try {
long idToDelete = Long.parseLong(future.get());
Log.e("THREAD ANSWER", future.get() + "");
helper.deleteRequest(idToDelete);
} catch (InterruptedException e) {
Log.e("future try", "");
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
}
executor.shutdown();
}
MyThread
public class MyThread implements Callable {
private File _file;
private Context context;
private DBHelper helper;
public MyThread(File file, Context context) {
this._file = file;
this.context = context;
}
#Override
public String call() throws Exception {
HttpClient client = Utility.getNewHttpClient();
HttpContext localContext = new BasicHttpContext();
HttpPost post = new HttpPost("http://192.168.9.62/mobile_api/timeline/moment/upload");
try {
MultipartEntityBuilder builder = MultipartEntityBuilder.create();
builder.setMode(HttpMultipartMode.BROWSER_COMPATIBLE);
FileBody fileBody = new FileBody(_file);
builder.addPart("content", fileBody);
builder.addPart("type", new StringBody("file", ContentType.TEXT_PLAIN));
builder.addPart("title", new StringBody("service test", ContentType.TEXT_PLAIN));
builder.addPart("userType", new StringBody("user", ContentType.TEXT_PLAIN));
builder.addPart("uid", new StringBody(MyInfiActivity.friends_uid, ContentType.TEXT_PLAIN));
builder.addPart("momentId", new StringBody("1", ContentType.TEXT_PLAIN));
builder.addPart("storyId", new StringBody("8", ContentType.TEXT_PLAIN));
Utility.addCookiesToPost(post);
post.setEntity(builder.build());
client.execute(post, localContext);
} catch (IOException e) {
Log.e("Callable try", post.toString());
}
return "1";
}
}
I'll try to go point by point to answer your question
What would be the behaviour of onHandleIntent on device shutdown?
Since the device is going to be totaly turned off, all services will be forced and stopped including your IntentService.
You can subscrive to ACTION_SHUTDOWN to know when the device is going to be turned off and do some stuff before it actually goes off.
public class ShutdownHandlerReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
//Handle here, but don't make actions that takes too long
}
}
Also you will have to add this to the manifest
<receiver android:name=".ShutdownHandlerReceiver">
<intent-filter>
<action android:name="android.intent.action.ACTION_SHUTDOWN" />
</intent-filter>
</receiver>
Will they "wake" up once we start up the device again?
No they will not, because they have already disposed and stuff like that, but you can subscribe to ACTION_BOOT_COMPLETED to know when the device is ready. From the doc:
Broadcast Action: This is broadcast once, after the system has
finished booting. It can be used to perform application-specific
initialization, such as installing alarms. You must hold the
RECEIVE_BOOT_COMPLETED permission in order to receive this broadcast.
public class BootedReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
//Handle here, you can start again your stopped intentservices
}
}
And in the manifest:
<receiver android:name=".BootedReceiver">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
And then you can restart the quee of your intenter service, and I guess this is the best way to go for it.
AFAIK, your IntentService will stop when the device shutdown if it was running at that time and the system will NOT automatically start it for us on boot, we will have to do this ourselves.
See this for how to do it.

How to find out when an installation is completed

I am creating an application that installs apps downloaded from a server. I would like to Install these application After the file is downloaded the code for the method I am using to install is here:
public void Install(String name)
{
//prompts user to accept any installation of the apk with provided name
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(Uri.fromFile(new File
(Environment.getExternalStorageDirectory() + "/ContentManager/" + name)), "application/vnd.android.package-archive");
startActivity(intent);
//this code should execute after the install finishes
File file = new File(Environment.getExternalStorageDirectory() + "/ContentManager/"+name);
file.delete();
}
I would like to have the apk file deleted from the sd card after the install is completed. This code deletes it once the install is started, causing the installation to fail. I am fairly neew to android and would much appreciate some help. I am basically trying to wait for the installation to complete before continuing with the process.
The Android package manager sends various broadcast intents while installing (or updating / removing) applications.
You can register broadcast receivers, so you will get notifications e.g. when a new application has been installed.
Intents that might be interesting for you are:
ACTION_PACKAGE_INSTALL
ACTION_PACKAGE_REPLACED
ACTION_PACKAGE_CHANGED
ACTION_PACKAGE_ADDED
Using broadcast receivers is not a big deal:
BroadcastReceiver myReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
// do whatever you want to do
}
};
registerReceiver(myReceiver, new IntentFilter("ACTION"));
unregisterReceiver(myReceiver);
This might not be the best way but I solved the problem. Here is my new code for the method.
public void Install(final String name,View view)
{
//prompts user to accept any installation of the apk with provided name
printstatus("Installing apk please accept permissions");
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(Uri.fromFile(new File
(Environment.getExternalStorageDirectory() + "/ContentManager/" + name)), "application/vnd.android.package-archive");
startActivity(intent);
try {
Thread.sleep(1500);
} catch (InterruptedException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
for(int i=0;i<100;)
{
System.gc();
if(view.getWindowVisibility()==0)
{
i=200;
System.gc();
}
try {
Thread.sleep(500);
System.gc();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
File file = new File(Environment.getExternalStorageDirectory() + "/ContentManager/"+name);
file.delete();
}
I created a loop that will wait until the window is in the front to let the method continue executing. The garbage collector and thread sleeping prevents it from slowing down the system or the Linux kernel killing the process. The sleep before the loop is needed so the package manager has time to start before the loop begins.

Categories

Resources