I'm trying to track how an app is being installed from start and so far I've been able to trace the source code where the call goes to PackageManagerService which extends the PackageManager and calls the method installPackageWithVerification(), the code for which is shown below for reference:
#Override
public void installPackageWithVerification(Uri packageURI, IPackageInstallObserver observer, int flags, String installerPackageName, Uri verificationURI, ManifestDigest manifestDigest) {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.INSTALL_PACKAGES, null);
final int uid = Binder.getCallingUid();
final int filteredFlags;
if (uid == Process.SHELL_UID || uid == 0) {
if (DEBUG_INSTALL) {
Slog.v(TAG, "Install from ADB");
}
filteredFlags = flags | PackageManager.INSTALL_FROM_ADB;
} else {
filteredFlags = flags & ~PackageManager.INSTALL_FROM_ADB;
}
final Message msg = mHandler.obtainMessage(INIT_COPY);
msg.obj = new InstallParams(packageURI, observer, filteredFlags, installerPackageName,
verificationURI, manifestDigest);
mHandler.sendMessage(msg);
}
The last 3 lines of code above create a message handler object and basically encapsulates the package install params in the message and sends it. I'm not clearly sure where the message gets delivered.
I went and debugged the android.os.Handler which basically defines the sendMessage(), all i can find is this message gets added to the a queue natively using the instance of android.os.MessageQueue
Could someone kindly enlighten me on what happens next to the package? or even the message en-queued in the NativeMessageQueue.
Thanks in advance!
I've got it figured out. My bad the PackageHandler is defined somewhere on top in the same file. Its hard to find when you are browsing through file with 3K lines of code :-P
Related
My app is using a NotificationListener to read out messages from various 3rd party apps, for example WhatsApp.
So far I was able to send a reply if only one chat is unread, the code is below.
However, in the case with WhatsApp, getNotification().actions returns a null object when more than two chats are unread, as the messages are bundled together. As you can see in the pictures below, if the notifications are extended there is an option to send a direct reply as well, therefore I am certain that it is possible to utilize this, also I think apps like PushBullet are using this method.
How could I access the RemoteInput of that notification?
public static ReplyIntentSender sendReply(StatusBarNotification statusBarNotification, String name) {
Notification.Action actions[] = statusBarNotification.getNotification().actions;
for (Notification.Action act : actions) {
if (act != null && act.getRemoteInputs() != null) {
if (act.title.toString().contains(name)) {
if (act.getRemoteInputs() != null)
return new ReplyIntentSender(act);
}
}
}
return null;
}
public static class ReplyIntentSender {
[...]
public final Notification.Action action;
public ReplyIntentSender(Notification.Action extractedAction) {
action = extractedAction;
[...]
}
private boolean sendNativeIntent(Context context, String message) {
for (android.app.RemoteInput rem : action.getRemoteInputs()) {
Intent intent = new Intent();
Bundle bundle = new Bundle();
bundle.putCharSequence(rem.getResultKey(), message);
android.app.RemoteInput.addResultsToIntent(action.getRemoteInputs(), intent, bundle);
try {
action.actionIntent.send(context, 0, intent);
} catch (Exception e) {
e.printStackTrace();
return false;
}
return true;
}
return false;
}
}
Some explanation how the above code works: Once a notification is received the app tries to get the actions and checks if the name is in the title of a remoteInput (normally it is in the format of "Reply to $NAME"), if that is found the Action is saved into a ReplyIntentSender class, which, when triggered by sendNativeIntent, cycles through all RemoteInputs of that Action and adds the message to the intent. If more than one chat is unread, getNotification().actions returns null.
Below are two screenshots, the first one where it is working without any problems and the second one where it doesn't.
You can consider this as my suggestion. I have done bit research on this and come up with following conclusions.(Also it looks like you have done plenty of research on this so it might be possible that you aware about what I wrote below)
Numerous apps send Wear specific notifications, and many of those contain actions accessible from an Android Wear device. We can grab those Wear notifications on the device, extracting the actions, finding the reply action (if one exists), populating it with our own response and then executing the PendingIntent which sends our response back the original app for it to send on to the recipient.
To do so you can refer this link (A nice workaround by Rob J). You can also refer this link in this context (Great research work done by MichaĆ Tajchert).(You might need to work around with NotificationCompat.isGroupSummary)
This is what I feel(Might be I am totally wrong)
.actions method returns Array of all Notification.Action
structures attached to current notification by addAction(int,
CharSequence, PendingIntent), Here addAction method is deprecated
one so it might not working as intended.
I am not able to test this at my end otherwise I will love to provide a working solution with code.
Hope this will help you. Happy Coding!!!
I have a networkStateReceiver, that checks if I have internet or not.
If I do, I reinitiate instabug, if not, I want to deactivate. How can I do that?
I tried just setting it as null, but it doesn't work.
if(haveConnectedMobile || haveConnectedWifi){
//TODO will need to make a queue, and go through all that queue
PSLocationCenter.getInstance().initInstabug();
}else{
PSLocationCenter.getInstance().instabug = null;
}
This is my init:
public void initInstabug() {
String[] feedbackArray = getResources().getStringArray(R.array.feedback);
String randomStr = feedbackArray[new Random().nextInt(feedbackArray.length)];
Instabug.DEBUG = true;
instabug = Instabug.initialize(this)
.setAnnotationActivityClass(InstabugAnnotationActivity.class)
.setShowIntroDialog(true, PSTimelineActivity.class)
.enableEmailField(true, false)
.setEnableOverflowMenuItem(true)
.setDebugEnabled(true)
.setCommentRequired(true)
.setPostFeedbackMessage(randomStr)
.setPostBugReportMessage(randomStr) //TODO will be the post report message, random from array
.setCommentFieldHint("Please describe what went wrong")
.setPreSendingRunnable(new Runnable() {
#Override
public void run() {
String[] files = new String[2];
files[0] = Environment.getExternalStorageDirectory() + "/Passenger/passenger_log.txt";
files[1] = Environment.getExternalStorageDirectory() + "/Passenger/passenger_log2.txt";
Compress compress = new Compress(files, Environment.getExternalStorageDirectory() + "/Passenger/log.zip");
compress.zip(new CrudStateCallback() {
#Override
public void onResponse(String string) {
Log.i("", "ended making the archive");
}
});
}
})
.attachFileAtLocation(Environment.getExternalStorageDirectory() + "/Passenger/log.zip");
}
You can use this code to disable Instabug automatic invocation:
Instabug.getInstance().setInvocationEvent(IBGInvocationEvent.IBGInvocationEventNone)
This way it won't be invoked automatically. This will only affect the next Activity though (not the current one). You may force to stop and restart all listeners by calling onPause and onResume on the current Activity. (We may address that soon though, so that such changes are applied on the currently running Activity).
Don't forget to also enable the shake invocation event when internet access is restored.
Please keep in mind that Instabug SDK already caches all reports and will re-attempt to send them on next app launch until they're uploaded successfully.
Just wanted to post the updated answer.
The newer SDK has changed the name and now you can disable it by the following code:
Instabug.changeInvocationEvent(InstabugInvocationEvent.NONE)
Notice, if you want to disable it for entire application, just call this method in your Application class
I'm currently attempting to use AppSync to sync a piece of data between an android app and a pebble app. However, I seem to not be able to get the pebble to realize that any data is being transferred - that is, no logs are being produced where they should be. What is really bothering me is that this is essentially the code found in the pebble weather example. I've pasted the relevant bits of code below - could someone possibly look it over and suggest where any issues may be? I've made sure that the UUIDs in both programs (pebble app and android app) are the same, and that they are on the same network, and that the pebble is actually connected to the phone, and that the android function is actually being called and all.
Snippet of pebble app code:
static void sync_error_callback(DictionaryResult dict_error, AppMessageResult app_message_error, void *context) {
APP_LOG(APP_LOG_LEVEL_DEBUG, "App Message Sync Error: %d", app_message_error);
}
static void sync_tuple_changed_callback(const uint32_t key, const Tuple* new_tuple, const Tuple* old_tuple, void* context) {
APP_LOG(APP_LOG_LEVEL_DEBUG, new_tuple->value->cstring);
}
void home_screen_load() {
// set up each one of the SimpleMenuItems
Tuplet initial_values[] = {
TupletCString(0x0, "Initial 1")
};
app_sync_init(&sync, sync_buffer, sizeof(sync_buffer), initial_values, ARRAY_LENGTH(initial_values), sync_tuple_changed_callback, sync_error_callback, NULL);
}
Snippet of android app:
final UUID PEBBLE_APP_UUID = UUID.fromString("10549fd4-1fe4-4d30-8a18-6f2f8149f8fd");
public void sendDataToWatch(String toSend) {
// Build up a Pebble dictionary containing the weather icon and the current temperature in degrees celsius
PebbleDictionary data = new PebbleDictionary();
data.addString(0x0, toSend);
PebbleKit.sendDataToPebble(getApplicationContext(), PEBBLE_APP_UUID, data);
}
To debug this type of problem, you should set a inbox_dropped handler and see if you get anything there.
After initializing AppMessage and AppSync, call:
app_message_register_inbox_dropped(appmsg_in_dropped);
And add this function:
static void appmsg_in_dropped(AppMessageResult reason, void *context) {
APP_LOG(APP_LOG_LEVEL_DEBUG, "In dropped: %s", translate_error(reason));
}
Take a look at this question for the source of the translate_error function.
Hi I am running ADMMessenger sample application provided with SDK.
in which I am not able to get Registration ID in register() method of MainActivity.
Method is like this.
private void register()
{
final ADM adm = new ADM(this);
if (adm.isSupported())
{
if(adm.getRegistrationId() == null)
{
adm.startRegister();
} else {
// final MyServerMsgHandler srv = new MyServerMsgHandler();
// srv.registerAppInstance(getApplicationContext(), adm.getRegistrationId());
}
Log.v("log_tag","Reg_id:: "+adm.getRegistrationId());
}
}
in Log cat I am always getting Reg_id:: null
and onRegistrationError() method of SampleADMMessageHandler is calling.
and error at there is ERROR_SERVICE_NOT_AVAILABLE
I can not understand what is problem, please help me.
For the service to work correctly you need to be using the Kindle image (not a generic Android one) and also make sure you have logged into your account on the device (pull down the status bar at the top and ensure you have selected an account)
I am trying to do programmatic update to the application I am writing, since it is not a Google Play application and I want to provide a way to do updates.
I've been searching around and found out how to start the Android installer after I download the APK for the update, but I need to get a result from the installer, that tells me if the update succeeded or not, or if it was cancelled by the user.
I saw a bunch of questions on StackOverflow about this, and the answers usually involved using a broadcast receiver. The problem with that is that it can only receive intents about the package being installed, not about canceled installs of fails.
I did some more research and it seems the Intent API provides some extras such as Intent.EXTRA_RETURN_RESULT, which if set to true should return a result from the installer activity - I guess via onActivityResult. Unfortunately this doesn't work. Is there anybody that got this working/does it work like this?
Here is the code preparing the installer activity start, that I currently have:
Intent installApp = new Intent(Intent.ACTION_INSTALL_PACKAGE);
installApp.setData(downloadedApk);
installApp.putExtra(Intent.EXTRA_NOT_UNKNOWN_SOURCE, true);
installApp.putExtra(Intent.EXTRA_RETURN_RESULT, true);
installApp.putExtra(Intent.EXTRA_INSTALLER_PACKAGE_NAME, context.getApplicationInfo().packageName);
context.startActivityForResult(installApp, 1);
Do you use Fragments? The onActivityResult will be called from the Activity or Fragment you have called startActivity(...). Fragment#startActivity(...) does exist. Use it to get the Fragment's onActivityResult(...) called.
If you are not using Fragments, this Workaround will work.
Workaround Pseudocode
// CURRENT_VERSION is a const with the current APK version as int
Activity#onStart() {
super.onStart();
checkForUpdaterResult();
/*...*/
}
Activity#checkForUpdaterResult() {
final int updateVersion = preferences.getInt(UPDATE_VERSION, -1);
switch(updateVersion) {
case -1:break;
default:
// updateVersion = oldVersion is smaller than the new currentVersion
boolean success = updateVersion < CURRENT_VERSION;
onUpdaterPerformed(success, updateVersion , CURRENT_VERSION);
break;
}
}
Activity#startUpdate(File pAPK) {
perferences.putInt(UPDATE_VERSION, CURRENT_VERSION);
/*...*/
}
Activity#onUpdaterPerformed(boolean pSuccess, int pFromVersion, int pToVersion) {
Toast.show("Update success: " + pSuccess);
/* e.g. migrate DB */
/*...*/
}