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.
Related
Recently I have uploaded my android apk on the app store and its been told that the next upload to Google play store will get rejected and we need to check and resolve it. Below is the screenshot of the message:
They are referring to package name also. Below is the code:
#Override
public void onDestroy() {
cleanup();
super.onDestroy();
Intent intent = new Intent("com.test.dummyapp");
sendBroadcast(intent);
}
Please assist me how to resolve this.
Below is the code where the component is triggered:
IntentFilter restartFilter = new IntentFilter("com.test.dummyapp");
registerReceiver(restartBroadcastReciver, restartFilter);
private BroadcastReceiver restartBroadcastReciver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
doBindService();
}
};
When you do this, you are broadcasting an "implicit Intent". This is dangerous because any app can register to get this (potential leak of information) and any app can also broadcast this Intent (triggering your app).
Intent intent = new Intent("com.test.dummyapp");
sendBroadcast(intent);
To fix this you can use LocalBroadcastManager (it is deprecated, but still works). Using a local broadcast ensures that other apps cannot see your broadcast Intent and other apps cannot trigger your app this way.
See https://developer.android.com/reference/androidx/localbroadcastmanager/content/LocalBroadcastManager
As an alternative, you should be able to make the Intent explicit by setting the package name:
Intent intent = new Intent("com.test.dummyapp");
intent.setPackage("my.package.name");
sendBroadcast(intent);
It seems really weird to send a Broadcast in onDestroy. I can't possibly see a use for that, and I can see a lot of problems due to onDestroy being called unexpectedly (rotation, screen size change, etc).
But if you have to do it, use new Intent(getPackageName()). What they're looking for is a hardcoded package name like that. The problem is that if you run 'com.facebook.whateveritscalled' and a piece of malware is installed that named itself that, you would be sending the intent to it. Which if you have extras in the intent could be leaking information to it.
Thanks for the information.
I made some changes to the posted code. Let me know if this works fine.
#Override
public void onDestroy() {
cleanup();
super.onDestroy();
openApp((Context) context,"com.test.dummyapp");
}
public static boolean openApp(Context context, String packageName) {
PackageManager manager = context.getPackageManager();
try {
Intent i = manager.getLaunchIntentForPackage(packageName);
if (i == null) {
return false;
}
i.addCategory(Intent.CATEGORY_LAUNCHER);
context.sendBroadcast(i);
return true;
} catch (ActivityNotFoundException e) {
return false;
}
}
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.
I've been looking into this quite a time already and I haven't found an answer yet, so the thing is that currently on one of my activities I'm applying a coded intentFilter like this:
filter = new IntentFilter(this.getResources().getString(
R.string.download_action_intent));
try {
filter.addDataType(HTTP.PLAIN_TEXT_TYPE);
}
catch (MalformedMimeTypeException e) {
// TODO
}
filter.addDataPath(md5String, PatternMatcher.PATTERN_LITERAL);
The application is downloading a lot of images, this images are being downloaded by a Thread and they get to the activity through a BroadcastReciever, this particular activity only needs to get the downloaded image which corresponds to that md5String, so I set this filter to the broadcastReciever of this activity.
But even with the type and datapath I keep recieving every image the app downloads, how should I configure the IntentFilter so it only gets the call of that broadcasted md5String image??.
The broadcast send is like this:
public boolean handleMessage(Message msg) {
String md5String = (String) msg.obj;
Intent broadcastIntent = new Intent(
this.mContext.getResources().getString(
R.string.download_action_intent));
broadcastIntent.setDataAndType(Uri.parse(md5String), HTTP.PLAIN_TEXT_TYPE);
this.mContext.sendBroadcast(broadcastIntent);
return true;
}
thanks for your help and I hope I was clear enough, it's my first question here.
I have a BroadcastReceiver (Android 4.1) which must test for the existence of a file located in the external storage area. The receiver doesn't need to read or write to the file; just test for its existence.
I haven't found anything in the Android documentation which indicates that BroadcastReceivers cannot access external files, yet the code below always returns false.
The logcat output shows getExternalStorageState() returns "mounted" and I can access the file using an ordinary App, just not in the Receiver. No exceptions are thrown.
public class FileCheckReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
boolean b = checkFile();
Log.d(TAG, "FileCheckReceiver: " + b);
}
boolean checkFile() {
String state = Environment.getExternalStorageState();
Log.d(TAG, "FileCheckReceiver, Environment.getExternalStorageState: " + state);
String name = "file.txt";
File f = new File(Environment.getExternalStorageDirectory() + "/" + name);
try
{
if (f.exists()) {
System.out.println(f.getCanonicalPath().toString());
return true;
}
}catch(Exception e) {
e.printStackTrace();
}
return false;
}
}
Are BroadcastReceivers stopped from accessing files?
A good approach would to be start a new Service from the BroadcastReceiver to handle tasks that you wish to perform.
Other things:
Make sure you have declared the receiver in manifest.xml
Also you need to consider the following as well.
As of Android 3.1 the Android system will by default exclude all BroadcastReceiver from receiving intents if the corresponding application has never been started by the user or if the user explicitly stopped the application via the Android menu (in Manage Application).
This is an additional security features as the user can be sure that only the applications he started will receive broadcast intents.
I eventually discovered that System Apps (which my app was) do not have any access to files stored on external storage.
Moving my BroadcastReceiver to another (non-system) APK fixed the problem.
I have an app that has a feature to launch an app, Pandora station, or shortcut. That all works fine. Later I want to stop the app I started. This works for most things except Pandora and Spotify don't always close. Sometimes they do but not always. It seems to be related to the current UI state. For instance, it works fine when I have Pandora showing or the home screen showing. When Home Dock or Car Mode is active it does not work. You can see all my source code here: http://code.google.com/p/a2dpvolume/
service.java is the file that has this functionality.
Here is the part of that code that tries to stop the music from playing and then stop the app.
if (bt2.hasIntent()) {
// if music is playing, pause it
if (am2.isMusicActive()) {
// first pause the music so it removes the notify icon
Intent i = new Intent("com.android.music.musicservicecommand");
i.putExtra("command", "pause");
sendBroadcast(i);
// for more stubborn players, try this too...
Intent downIntent2 = new Intent(Intent.ACTION_MEDIA_BUTTON, null);
KeyEvent downEvent2 = new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_MEDIA_STOP);
downIntent2.putExtra(Intent.EXTRA_KEY_EVENT, downEvent2);
sendOrderedBroadcast(downIntent2, null);
}
// if we opened a package for this device, try to close it now
if (bt2.getPname().length() > 3 && bt2.isAppkill()) {
// also open the home screen to make music app revert to
// background
Intent startMain = new Intent(Intent.ACTION_MAIN);
startMain.addCategory(Intent.CATEGORY_HOME);
startMain.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(startMain);
// now we can kill the app is asked to
final String kpackage = bt2.getPname();
CountDownTimer killTimer = new CountDownTimer(6000, 3000) {
#Override
public void onFinish() {
try {
stopApp(kpackage);
} catch (Exception e) {
e.printStackTrace();
Log.e(LOG_TAG, "Error " + e.getMessage());
}
}
#Override
public void onTick(long arg0) {
if (am2.isMusicActive()) {
// for more stubborn players, try this too...
Intent downIntent2 = new Intent(Intent.ACTION_MEDIA_BUTTON, null);
KeyEvent downEvent2 = new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_MEDIA_STOP);
downIntent2.putExtra(Intent.EXTRA_KEY_EVENT, downEvent2);
sendOrderedBroadcast(downIntent2, null);
}
try {
stopApp(kpackage);
} catch (Exception e) {
e.printStackTrace();
Log.e(LOG_TAG, "Error " + e.getMessage());
}
}
};
killTimer.start();
}
}
Here is the function stopApp().
protected void stopApp(String packageName) {
Intent mIntent = getPackageManager().getLaunchIntentForPackage(
packageName);
if (mIntent != null) {
try {
ActivityManager act1 = (ActivityManager) this
.getSystemService(ACTIVITY_SERVICE);
// act1.restartPackage(packageName);
act1.killBackgroundProcesses(packageName);
List<ActivityManager.RunningAppProcessInfo> processes;
processes = act1.getRunningAppProcesses();
for (ActivityManager.RunningAppProcessInfo info : processes) {
for (int i = 0; i < info.pkgList.length; i++) {
if (info.pkgList[i].contains(packageName)) {
android.os.Process.killProcess(info.pid);
}
}
}
} catch (ActivityNotFoundException err) {
err.printStackTrace();
Toast t = Toast.makeText(getApplicationContext(),
R.string.app_not_found, Toast.LENGTH_SHORT);
if (notify)
t.show();
}
}
}
Has someone else run into this problem? How can I reliably stop the launched app? I need to first get it to pause and put it in the background. That is the problem I am having. It works for most situations but not all. Some cases Pandora and Spotify will not respond to the key event being sent and they just keep playing. This keeps the notify icon active and makes the app a foreground activity so I can't stop it.
I finally figured out that Pandora does pause music when it sees a headset disconnect. So, I just had to send that disconnect intent so Pandora would pause. Once paused, it was able to be pushed to background and killed.
//Try telling the system the headset just disconnected to stop other players
Intent j = new Intent("android.intent.action.HEADSET_PLUG");
j.putExtra("state", 0);
sendBroadcast(j);
For anyone else trying this; The android.intent.action.HEADSET_PLUG intent is no longer allowed to be broadcast unless you are running as the system.
As the "HEADSET_PLUG" intent is now only supported if called by a system, I found app specific intents to be the way to go:
Intent pauseSpotify = new Intent("com.spotify.mobile.android.ui.widget.PLAY");
pauseSpotify.setPackage("com.spotify.music");
sendBroadcast(pauseSpotify);
Essentially, what this does, is it calls "PLAY" from the spotify app.
I got the idea from an article and applied it to normal android.