Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add retry for get provider at ServiceStarter[Using handler] #535

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
150 changes: 94 additions & 56 deletions starter/src/main/java/moe/shizuku/starter/ServiceStarter.java
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
package moe.shizuku.starter;

import android.content.IContentProvider;
import android.os.Binder;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.util.Log;
Expand Down Expand Up @@ -58,11 +58,16 @@ public static String commandForUserService(String appProcess, String managerApkP
token, packageName, classname, callingUid, debug ? (" " + "--debug-name=" + processName) : "");
}

private static final int MAX_RETRIES = 50;
private static final int RETRY_DELAY_MS = 200;
private static Handler handler;

public static void main(String[] args) {
if (Looper.getMainLooper() == null) {
Looper.prepareMainLooper();
}

handler = new Handler(Looper.getMainLooper());
retryCount = 0;
IBinder service;
String token;

Expand All @@ -77,86 +82,119 @@ public static void main(String[] args) {
service = result.first;
token = result.second;

if (!sendBinder(service, token)) {
System.exit(1);
}
sendBinder(service, token);

Looper.loop();
System.exit(0);

Log.i(TAG, "service exited");
}

private static boolean sendBinder(IBinder binder, String token) {
return sendBinder(binder, token, true);
}
private static int retryCount;
static String packageName = "moe.shizuku.privileged.api";
static IContentProvider provider = null;

private static boolean sendBinder(IBinder binder, String token, boolean retry) {
String packageName = "moe.shizuku.privileged.api";
private static void sendBinder(IBinder binder, String token) {
String name = packageName + ".shizuku";
int userId = 0;
IContentProvider provider = null;
Runnable retryRunnable = new Runnable() {
@Override
public void run() {
try {
provider = ActivityManagerApis.getContentProviderExternal(name, userId, null, name);
if (provider == null) {
retryCount++;
Log.w(TAG, String.format("provider is null %s %d,try times %d", name, userId, retryCount));
if (retryCount < MAX_RETRIES) {
handler.postDelayed(this, RETRY_DELAY_MS);
} else {
Log.e(TAG, String.format("provider is null %s %d", name, userId));
handler.removeCallbacks(this);
System.exit(1);
}
} else {
processProvider(provider,binder,token,packageName,userId,this);
}

try {
provider = ActivityManagerApis.getContentProviderExternal(name, userId, null, name);
if (provider == null) {
Log.e(TAG, String.format("provider is null %s %d", name, userId));
return false;
}
if (!provider.asBinder().pingBinder()) {
Log.e(TAG, String.format("provider is dead %s %d", name, userId));

if (retry) {
// For unknown reason, sometimes this could happens
// Kill Shizuku app and try again could work
ActivityManagerApis.forceStopPackageNoThrow(packageName, userId);
Log.e(TAG, String.format("kill %s in user %d and try again", packageName, userId));
Thread.sleep(1000);
return sendBinder(binder, token, false);
} catch (Throwable tr) {
Log.e(TAG, String.format("failed send binder to %s in user %d", packageName, userId), tr);
handler.removeCallbacks(this);
System.exit(1);
} finally {
if (provider != null) {
try {
ActivityManagerApis.removeContentProviderExternal(name, null);
} catch (Throwable tr) {
Log.w(TAG, "removeContentProviderExternal", tr);
}
}
}
return false;
}

if (!retry) {
Log.e(TAG, "retry works");
};
handler.post(retryRunnable);
}
private static boolean retryProviderPingBinder = true;
private static void processProvider(IContentProvider provider, IBinder binder, String token, String packageName, int userId, Runnable retryRunnable) {
String name = packageName + ".shizuku";
if (!provider.asBinder().pingBinder()) {
Log.e(TAG, String.format("provider is dead %s %d", name, userId));

if (retryProviderPingBinder) {
// For unknown reason, sometimes this could happens
// Kill Shizuku app and try again could work
ActivityManagerApis.forceStopPackageNoThrow(packageName, userId);
Log.e(TAG, String.format("kill %s in user %d and try again", packageName, userId));
handler.postDelayed(retryRunnable, 1000);
retryProviderPingBinder = false;
return;
}
handler.removeCallbacks(retryRunnable);
System.exit(1);
}

Bundle extra = new Bundle();
extra.putParcelable(EXTRA_BINDER, new BinderContainer(binder));
extra.putString(ShizukuApiConstants.USER_SERVICE_ARG_TOKEN, token);
if (!retryProviderPingBinder) {
Log.e(TAG, "retry works");
}

Bundle reply = IContentProviderCompat.call(provider, null, null, name, "sendUserService", null, extra);
Bundle extra = new Bundle();
extra.putParcelable(EXTRA_BINDER, new BinderContainer(binder));
extra.putString(ShizukuApiConstants.USER_SERVICE_ARG_TOKEN, token);

if (reply != null) {
reply.setClassLoader(BinderContainer.class.getClassLoader());
Bundle reply = null;
try {
reply = IContentProviderCompat.call(provider, null, null, name, "sendUserService", null, extra);
} catch (Throwable tr) {
Log.e(TAG, String.format("failed send binder to %s in user %d", packageName, userId), tr);
handler.removeCallbacks(retryRunnable);
System.exit(1);
}

if (reply != null) {
reply.setClassLoader(BinderContainer.class.getClassLoader());

Log.i(TAG, String.format("send binder to %s in user %d", packageName, userId));
BinderContainer container = reply.getParcelable(EXTRA_BINDER);
Log.i(TAG, String.format("send binder to %s in user %d", packageName, userId));
BinderContainer container = reply.getParcelable(EXTRA_BINDER);

if (container != null && container.binder != null && container.binder.pingBinder()) {
shizukuBinder = container.binder;
if (container != null && container.binder != null && container.binder.pingBinder()) {
shizukuBinder = container.binder;
try {
shizukuBinder.linkToDeath(() -> {
Log.i(TAG, "exiting...");
handler.removeCallbacks(retryRunnable);
System.exit(0);
}, 0);
return true;
} else {
Log.w(TAG, "server binder not received");
}
}

return false;
} catch (Throwable tr) {
Log.e(TAG, String.format("failed send binder to %s in user %d", packageName, userId), tr);
return false;
} finally {
if (provider != null) {
try {
ActivityManagerApis.removeContentProviderExternal(name, null);
} catch (Throwable tr) {
Log.w(TAG, "removeContentProviderExternal", tr);
Log.e(TAG, String.format("failed send binder to %s in user %d", packageName, userId), tr);
handler.removeCallbacks(retryRunnable);
System.exit(1);
}
return;
} else {
Log.w(TAG, "server binder not received");
}
}
handler.removeCallbacks(retryRunnable);
System.exit(1);

}
}