Pushing other Android activity to foreground - android

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
}
}
}

Related

How to open app when notification received without user interaction In React Native

How to open app when notification received without user interaction
I am using react native push notification library for Push notification. App should auto start from background and quit state without user interaction and getInitialNotification method should be called. I want to invoke app on specific notification type.
===============================================
File: RNPushNotificationHelper
===============================================
public void invokeApp(Bundle bundle) {
String packageName = context.getPackageName();
Intent launchIntent = context.getPackageManager().getLaunchIntentForPackage(packageName);
String className = launchIntent.getComponent().getClassName();
try {
Class<?> activityClass = Class.forName(className);
Intent activityIntent = new Intent(context, activityClass);
if(bundle != null) {
activityIntent.putExtra("notification", bundle);
}
activityIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(activityIntent);
} catch(Exception e) {
Log.e(LOG_TAG, "Class not found", e);
return;
}
}
===============================================
File: RNReceivedMessageHandler
===============================================
// If notification ID is not provided by the user for push notification, generate one at random
if (bundle.getString("id") == null) {
SecureRandom randomNumberGenerator = new SecureRandom();
bundle.putString("id", String.valueOf(randomNumberGenerator.nextInt()));
}
Application applicationContext = (Application) context.getApplicationContext();
RNPushNotificationConfig config = new RNPushNotificationConfig(mFirebaseMessagingService.getApplication());
RNPushNotificationHelper pushNotificationHelper = new RNPushNotificationHelper(applicationContext);
boolean isForeground = pushNotificationHelper.isApplicationInForeground();
RNPushNotificationJsDelivery jsDelivery = new RNPushNotificationJsDelivery(context);
bundle.putBoolean("foreground", isForeground);
bundle.putBoolean("userInteraction", false);
jsDelivery.notifyNotification(bundle);
// If contentAvailable is set to true, then send out a remote fetch event
if (bundle.getString("contentAvailable", "false").equalsIgnoreCase("true")) {
jsDelivery.notifyRemoteFetch(bundle);
}
Log.v(LOG_TAG, "invokeApp: " + bundle);
pushNotificationHelper.invokeApp(bundle);
if (config.getNotificationForeground() || !isForeground) {
Log.v(LOG_TAG, "sendNotification: " + bundle);
pushNotificationHelper.sendToNotificationCentre(bundle);
}
But It does not seem to work in Background OR Quit state. I checked logcat it showing logs when in foreground.
Notification actions
I am assuming that this is not possible because it would be a major security gap.
In the Android docs, only the tap on the notification and notification actions are listed as options for actions in the background.
The section.
Restrictions bypasses
There is a so-called "Restrictions on starting activities from the background" from Android 10 (API level 29).
However, these have several exceptions.
I think the most interesting for you are these:
The app receives a notification PendingIntent from the system. In the
case of pending intents for services and broadcast receivers, the app
can start activities for a few seconds after the pending intent is
sent.
The app has been granted the SYSTEM_ALERT_WINDOW permission by the
user.

Kill another app and all its services programmatically in android

I'm creating an app that turns on another application whenever the user gets a phone call and turns that application off when the phone call ends.
This is my code:
public class MyPhoneReceiver extends BroadcastReceiver {
#Override
public void onReceive( Context context, Intent intent ) {
final String PROXIMITY_SERVICE_PACKAGE_NAME = "package_name";
Bundle extras = intent.getExtras();
String state = extras.getString( TelephonyManager.EXTRA_STATE );
if (state.equals( TelephonyManager.EXTRA_STATE_RINGING ) ) {
PackageManager packageManager = context.getPackageManager();
context.startActivity( packageManager.getLaunchIntentForPackage(
PROXIMITY_SERVICE_PACKAGE_NAME ) );
}
if ( state.equals(TelephonyManager.EXTRA_STATE_IDLE ) ) {
killProcess(context, PROXIMITY_SERVICE_PACKAGE_NAME);
}
}
The killProcess method is currently implemented this way:
private void killProcess(Context context, String packageName)
{
ActivityManager am = (ActivityManager)context.getSystemService(Activity.ACTIVITY_SERVICE);
am.killBackgroundProcesses(packageName);
}
I also ask for permission to kill background applications in the manifest:
<uses-permission android:name="android.permission.KILL_BACKGROUND_PROCESSES" />
The opening of the app is working, but the closing of the app doesn't.
I know I reach the code in killProcess, but it doesn't kill the app.
Is this a problem with permissions? Am I not allowed to kill another process? not even a process I created?
Or maybe, from what I know about the process I'm running, it creates a service that does all the work for it. Maybe the problem is that the service does not terminate?
Is there any way to terminate this process and all the services and sub processes that are related to it (like for example when you do FORCE STOP in settings)?
Thanks.
Yes you can kill any process.
Find the app process ID
Kill it.
Once you have the app process ID, just pass in this function:
Process.killProcess( APP-PROCESS-ID )
Note that the process class, should be imported from Android:
import android.os.Process
You can also try this if you know the package name of the app:
ActivityManager am = (ActivityManager)
getApplicationContext().getSystemService(Context.ACTIVITY_SERVICE);
am.killBackgroundProcesses("app-package-name");

Broadcast Receiver MY_PACKAGE_REPLACED never called

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.

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>

Delete my application programmatically (Android)

I want to uninstall my application on button click. For this I am using following code.
Uri packageURI = Uri.parse("package:"+packageName);
Intent uninstallIntent = new Intent(Intent.ACTION_DELETE, packageURI);
startActivity(uninstallIntent);
It gives me result, but I want to delete directly without click on "Ok" button of dialog with message "This application will be Uninstalled".
I just want uninstalling application directly.
Uninstalling without user confirmation is not allowed to 3rd party applications.
As xDragonZ points out, a root process can crudely do this by literally removing the directory and leaving the package manager to deal with the loss, but that's not a very widely deployable solution, since AFAIK no devices ship with that capability for apps to run their own root helper process - that's a risky aftermarket modification.
Yes it is possible to uninstall a package in Android. Moreover you can also skip asking user to press OK button on uninstall screen. You can do it by using Accessibility service in Android.
public class MyAccessibilityService extends AccessibilityService {
private static final String TAG = MyAccessibilityService.class
.getSimpleName();
#Override
public void onAccessibilityEvent(AccessibilityEvent event) {
Log.i(TAG, "ACC::onAccessibilityEvent: " + event.getEventType());
//TYPE_WINDOW_STATE_CHANGED == 32
if (AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED == event
.getEventType()) {
AccessibilityNodeInfo nodeInfo = event.getSource();
Log.i(TAG, "ACC::onAccessibilityEvent: nodeInfo=" + nodeInfo);
if (nodeInfo == null) {
return;
}
List<AccessibilityNodeInfo> list = nodeInfo
.findAccessibilityNodeInfosByViewId("com.android.settings:id/left_button");
for (AccessibilityNodeInfo node : list) {
Log.i(TAG, "ACC::onAccessibilityEvent: left_button " + node);
node.performAction(AccessibilityNodeInfo.ACTION_CLICK);
}
list = nodeInfo
.findAccessibilityNodeInfosByViewId("android:id/button1");
for (AccessibilityNodeInfo node : list) {
Log.i(TAG, "ACC::onAccessibilityEvent: button1 " + node);
node.performAction(AccessibilityNodeInfo.ACTION_CLICK);
}
}
}
#Override
public void onServiceConnected() {
Log.i(TAG, "ACC::onServiceConnected: ");
}
#Override
public void onInterrupt() {
// TODO Auto-generated method stub
}
}
You should first look into the Android native PackageInstaller. I would recommendating you to update all the code you use.
Next step is to inspect PackageInstaller which is an normal class. You will find that uninstall function there. The bad news is that this needs Manifest.permission.DELETE_PACKAGES permission and its only granted to system apps. This means that this is not available directly to other developers. But we can access it using device owner permission.
This requires:
Android 6.0 or newer
Device owner permission to uninstall the package
Generally the DELETE_PACKAGES permission says:
Allows an application to delete packages.
Not for use by third-party applications.
Once your app gets the device owner permission, you can uninstall an package like this:
String appPackage = "com.your.app.package";
Intent intent = new Intent(getApplicationContext(),
getApplicationContext().getClass()); //getActivity() is undefined!
PendingIntent sender = PendingIntent.getActivity(getActivity(), 0, intent, 0);
PackageInstaller mPackageInstaller =
getActivity().getPackageManager().getPackageInstaller();
mPackageInstaller.uninstall(appPackage, sender.getIntentSender());
The code used available here:
PackageInstaller "Silent install and uninstall of apps by Device Owner” - Android M Preview

Categories

Resources