mirror of
https://github.com/TeamVanced/VancedMicroG
synced 2025-02-09 01:06:48 +01:00
26f2e85 + Wrapper update
This commit is contained in:
parent
8d0586487e
commit
cb719fce32
@ -35,7 +35,7 @@ buildscript {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
classpath "com.android.tools.build:gradle:${androidBuildGradleVersion}"
|
classpath "com.android.tools.build:gradle:4.1.0"
|
||||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion"
|
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion"
|
||||||
classpath "com.squareup.wire:wire-gradle-plugin:$wireVersion"
|
classpath "com.squareup.wire:wire-gradle-plugin:$wireVersion"
|
||||||
}
|
}
|
||||||
|
4
gradle/wrapper/gradle-wrapper.properties
vendored
4
gradle/wrapper/gradle-wrapper.properties
vendored
@ -1,6 +1,6 @@
|
|||||||
#Tue Sep 29 23:46:06 CEST 2020
|
#Fri Oct 16 00:14:59 CEST 2020
|
||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
zipStorePath=wrapper/dists
|
zipStorePath=wrapper/dists
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-6.6.1-all.zip
|
distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-all.zip
|
||||||
|
@ -118,9 +118,7 @@
|
|||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.BOOT_COMPLETED" />
|
<action android:name="android.intent.action.BOOT_COMPLETED" />
|
||||||
<action android:name="android.intent.action.AIRPLANE_MODE" />
|
<action android:name="android.intent.action.AIRPLANE_MODE" />
|
||||||
<action android:name="android.net.conn.CONNECTIVITY_CHANGE" />
|
|
||||||
<action android:name="android.net.conn.BACKGROUND_DATA_SETTING_CHANGED" />
|
|
||||||
|
|
||||||
<action android:name="android.server.checkin.CHECKIN" />
|
<action android:name="android.server.checkin.CHECKIN" />
|
||||||
|
|
||||||
<action android:name="android.intent.action.MY_PACKAGE_REPLACED" />
|
<action android:name="android.intent.action.MY_PACKAGE_REPLACED" />
|
||||||
@ -314,7 +312,7 @@
|
|||||||
android:name="org.microg.gms.ui.AskPushPermission"
|
android:name="org.microg.gms.ui.AskPushPermission"
|
||||||
android:excludeFromRecents="true"
|
android:excludeFromRecents="true"
|
||||||
android:process=":ui"
|
android:process=":ui"
|
||||||
android:theme="@style/Theme.AppCompat.DayNight.Dialog.Alert" />
|
android:theme="@style/Theme.AppCompat.DayNight.Dialog.Alert.NoActionBar" />
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name="org.microg.gms.ui.AboutFragment$AsActivity"
|
android:name="org.microg.gms.ui.AboutFragment$AsActivity"
|
||||||
|
@ -18,13 +18,16 @@ package org.microg.gms.checkin;
|
|||||||
|
|
||||||
import android.accounts.Account;
|
import android.accounts.Account;
|
||||||
import android.accounts.AccountManager;
|
import android.accounts.AccountManager;
|
||||||
|
import android.app.Activity;
|
||||||
import android.app.AlarmManager;
|
import android.app.AlarmManager;
|
||||||
import android.app.IntentService;
|
import android.app.IntentService;
|
||||||
import android.app.PendingIntent;
|
import android.app.PendingIntent;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
|
import android.os.Bundle;
|
||||||
import android.os.IBinder;
|
import android.os.IBinder;
|
||||||
import android.os.RemoteException;
|
import android.os.RemoteException;
|
||||||
|
import android.os.ResultReceiver;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
import androidx.legacy.content.WakefulBroadcastReceiver;
|
import androidx.legacy.content.WakefulBroadcastReceiver;
|
||||||
@ -38,11 +41,15 @@ import org.microg.gms.people.PeopleManager;
|
|||||||
|
|
||||||
public class CheckinService extends IntentService {
|
public class CheckinService extends IntentService {
|
||||||
private static final String TAG = "GmsCheckinSvc";
|
private static final String TAG = "GmsCheckinSvc";
|
||||||
|
public static final long MAX_VALID_CHECKIN_AGE = 24 * 60 * 60 * 1000; // 12 hours
|
||||||
public static final long REGULAR_CHECKIN_INTERVAL = 12 * 60 * 60 * 1000; // 12 hours
|
public static final long REGULAR_CHECKIN_INTERVAL = 12 * 60 * 60 * 1000; // 12 hours
|
||||||
public static final long BACKUP_CHECKIN_DELAY = 3 * 60 * 60 * 1000; // 3 hours
|
public static final long BACKUP_CHECKIN_DELAY = 3 * 60 * 60 * 1000; // 3 hours
|
||||||
public static final String BIND_ACTION = "com.google.android.gms.checkin.BIND_TO_SERVICE";
|
public static final String BIND_ACTION = "com.google.android.gms.checkin.BIND_TO_SERVICE";
|
||||||
public static final String EXTRA_FORCE_CHECKIN = "force";
|
public static final String EXTRA_FORCE_CHECKIN = "force";
|
||||||
|
@Deprecated
|
||||||
public static final String EXTRA_CALLBACK_INTENT = "callback";
|
public static final String EXTRA_CALLBACK_INTENT = "callback";
|
||||||
|
public static final String EXTRA_RESULT_RECEIVER = "receiver";
|
||||||
|
public static final String EXTRA_NEW_CHECKIN_TIME = "checkin_time";
|
||||||
|
|
||||||
private ICheckinService iface = new ICheckinService.Stub() {
|
private ICheckinService iface = new ICheckinService.Stub() {
|
||||||
@Override
|
@Override
|
||||||
@ -72,6 +79,14 @@ public class CheckinService extends IntentService {
|
|||||||
if (intent.hasExtra(EXTRA_CALLBACK_INTENT)) {
|
if (intent.hasExtra(EXTRA_CALLBACK_INTENT)) {
|
||||||
startService((Intent) intent.getParcelableExtra(EXTRA_CALLBACK_INTENT));
|
startService((Intent) intent.getParcelableExtra(EXTRA_CALLBACK_INTENT));
|
||||||
}
|
}
|
||||||
|
if (intent.hasExtra(EXTRA_RESULT_RECEIVER)) {
|
||||||
|
ResultReceiver receiver = intent.getParcelableExtra(EXTRA_RESULT_RECEIVER);
|
||||||
|
if (receiver != null) {
|
||||||
|
Bundle bundle = new Bundle();
|
||||||
|
bundle.putLong(EXTRA_NEW_CHECKIN_TIME, info.lastCheckin);
|
||||||
|
receiver.send(Activity.RESULT_OK, bundle);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
|
@ -31,20 +31,20 @@ import static org.microg.gms.checkin.CheckinService.REGULAR_CHECKIN_INTERVAL;
|
|||||||
|
|
||||||
public class TriggerReceiver extends WakefulBroadcastReceiver {
|
public class TriggerReceiver extends WakefulBroadcastReceiver {
|
||||||
private static final String TAG = "GmsCheckinTrigger";
|
private static final String TAG = "GmsCheckinTrigger";
|
||||||
|
private static boolean registered = false;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onReceive(Context context, Intent intent) {
|
public void onReceive(Context context, Intent intent) {
|
||||||
try {
|
try {
|
||||||
boolean force = "android.provider.Telephony.SECRET_CODE".equals(intent.getAction());
|
boolean force = "android.provider.Telephony.SECRET_CODE".equals(intent.getAction());
|
||||||
ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
|
|
||||||
|
|
||||||
if (CheckinPrefs.get(context).isEnabled() || force) {
|
if (CheckinPrefs.get(context).isEnabled() || force) {
|
||||||
if (ConnectivityManager.CONNECTIVITY_ACTION.equals(intent.getAction()) &&
|
if (LastCheckinInfo.read(context).lastCheckin > System.currentTimeMillis() - REGULAR_CHECKIN_INTERVAL && !force) {
|
||||||
LastCheckinInfo.read(context).lastCheckin > System.currentTimeMillis() - REGULAR_CHECKIN_INTERVAL) {
|
|
||||||
CheckinService.schedule(context);
|
CheckinService.schedule(context);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
|
||||||
NetworkInfo networkInfo = cm.getActiveNetworkInfo();
|
NetworkInfo networkInfo = cm.getActiveNetworkInfo();
|
||||||
if (networkInfo != null && networkInfo.isConnected() || force) {
|
if (networkInfo != null && networkInfo.isConnected() || force) {
|
||||||
Intent subIntent = new Intent(context, CheckinService.class);
|
Intent subIntent = new Intent(context, CheckinService.class);
|
||||||
|
@ -1,196 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2018 microG Project Team
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.microg.gms.gcm;
|
|
||||||
|
|
||||||
import android.app.PendingIntent;
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.Intent;
|
|
||||||
import android.os.Binder;
|
|
||||||
import android.os.Build;
|
|
||||||
import android.os.Bundle;
|
|
||||||
import android.os.Handler;
|
|
||||||
import android.os.Message;
|
|
||||||
import android.os.Messenger;
|
|
||||||
import android.os.RemoteException;
|
|
||||||
import android.util.Log;
|
|
||||||
|
|
||||||
import org.microg.gms.checkin.LastCheckinInfo;
|
|
||||||
import org.microg.gms.common.ForegroundServiceContext;
|
|
||||||
import org.microg.gms.common.PackageUtils;
|
|
||||||
import org.microg.gms.common.Utils;
|
|
||||||
|
|
||||||
import static org.microg.gms.gcm.GcmConstants.ACTION_C2DM_REGISTRATION;
|
|
||||||
import static org.microg.gms.gcm.GcmConstants.ERROR_SERVICE_NOT_AVAILABLE;
|
|
||||||
import static org.microg.gms.gcm.GcmConstants.EXTRA_APP;
|
|
||||||
import static org.microg.gms.gcm.GcmConstants.EXTRA_APP_OVERRIDE;
|
|
||||||
import static org.microg.gms.gcm.GcmConstants.EXTRA_ERROR;
|
|
||||||
import static org.microg.gms.gcm.McsConstants.ACTION_ACK;
|
|
||||||
import static org.microg.gms.gcm.McsConstants.ACTION_SEND;
|
|
||||||
|
|
||||||
class PushRegisterHandler extends Handler {
|
|
||||||
private static final String TAG = "GmsGcmRegisterHdl";
|
|
||||||
|
|
||||||
private Context context;
|
|
||||||
private int callingUid;
|
|
||||||
private GcmDatabase database;
|
|
||||||
|
|
||||||
public PushRegisterHandler(Context context, GcmDatabase database) {
|
|
||||||
this.context = context;
|
|
||||||
this.database = database;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
|
|
||||||
this.callingUid = Binder.getCallingUid();
|
|
||||||
return super.sendMessageAtTime(msg, uptimeMillis);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void sendReplyViaMessage(int what, int id, Messenger replyTo, Bundle messageData) {
|
|
||||||
Message response = Message.obtain();
|
|
||||||
response.what = what;
|
|
||||||
response.arg1 = id;
|
|
||||||
response.setData(messageData);
|
|
||||||
try {
|
|
||||||
replyTo.send(response);
|
|
||||||
} catch (RemoteException e) {
|
|
||||||
Log.w(TAG, e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void sendReplyViaIntent(Intent outIntent, Messenger replyTo) {
|
|
||||||
Message message = Message.obtain();
|
|
||||||
message.obj = outIntent;
|
|
||||||
try {
|
|
||||||
replyTo.send(message);
|
|
||||||
} catch (RemoteException e) {
|
|
||||||
Log.w(TAG, e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void sendReply(int what, int id, Messenger replyTo, Bundle data, boolean oneWay) {
|
|
||||||
if (what == 0) {
|
|
||||||
Intent outIntent = new Intent(ACTION_C2DM_REGISTRATION);
|
|
||||||
outIntent.putExtras(data);
|
|
||||||
sendReplyViaIntent(outIntent, replyTo);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
Bundle messageData = new Bundle();
|
|
||||||
messageData.putBundle("data", data);
|
|
||||||
sendReplyViaMessage(what, id, replyTo, messageData);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void replyError(int what, int id, Messenger replyTo, String errorMessage, boolean oneWay) {
|
|
||||||
Bundle bundle = new Bundle();
|
|
||||||
bundle.putString(EXTRA_ERROR, errorMessage);
|
|
||||||
sendReply(what, id, replyTo, bundle, oneWay);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void replyNotAvailable(int what, int id, Messenger replyTo) {
|
|
||||||
replyError(what, id, replyTo, ERROR_SERVICE_NOT_AVAILABLE, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
private PendingIntent getSelfAuthIntent() {
|
|
||||||
Intent intent = new Intent();
|
|
||||||
intent.setPackage("com.google.example.invalidpackage");
|
|
||||||
return PendingIntent.getBroadcast(context, 0, intent, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void handleMessage(Message msg) {
|
|
||||||
if (msg.what == 0) {
|
|
||||||
if (msg.obj instanceof Intent) {
|
|
||||||
Message nuMsg = Message.obtain();
|
|
||||||
nuMsg.what = msg.what;
|
|
||||||
nuMsg.arg1 = 0;
|
|
||||||
nuMsg.replyTo = null;
|
|
||||||
PendingIntent pendingIntent = ((Intent) msg.obj).getParcelableExtra(EXTRA_APP);
|
|
||||||
String packageName = PackageUtils.packageFromPendingIntent(pendingIntent);
|
|
||||||
Bundle data = new Bundle();
|
|
||||||
data.putBoolean("oneWay", false);
|
|
||||||
data.putString("pkg", packageName);
|
|
||||||
data.putBundle("data", msg.getData());
|
|
||||||
nuMsg.setData(data);
|
|
||||||
msg = nuMsg;
|
|
||||||
} else {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int what = msg.what;
|
|
||||||
int id = msg.arg1;
|
|
||||||
Messenger replyTo = msg.replyTo;
|
|
||||||
if (replyTo == null) {
|
|
||||||
Log.w(TAG, "replyTo is null");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
Bundle data = msg.getData();
|
|
||||||
|
|
||||||
String packageName = data.getString("pkg");
|
|
||||||
Bundle subdata = data.getBundle("data");
|
|
||||||
|
|
||||||
try {
|
|
||||||
PackageUtils.checkPackageUid(context, packageName, callingUid);
|
|
||||||
} catch (SecurityException e) {
|
|
||||||
Log.w(TAG, e);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Log.d(TAG, "handleMessage: package=" + packageName + " what=" + what + " id=" + id);
|
|
||||||
|
|
||||||
boolean oneWay = data.getBoolean("oneWay", false);
|
|
||||||
|
|
||||||
switch (what) {
|
|
||||||
case 0:
|
|
||||||
case 1:
|
|
||||||
// TODO: We should checkin and/or ask for permission here.
|
|
||||||
String sender = subdata.getString("sender");
|
|
||||||
boolean delete = subdata.get("delete") != null;
|
|
||||||
|
|
||||||
PushRegisterManager.completeRegisterRequest(context, database,
|
|
||||||
new RegisterRequest()
|
|
||||||
.build(Utils.getBuild(context))
|
|
||||||
.sender(sender)
|
|
||||||
.checkin(LastCheckinInfo.read(context))
|
|
||||||
.app(packageName)
|
|
||||||
.delete(delete)
|
|
||||||
.extraParams(subdata),
|
|
||||||
bundle -> sendReply(what, id, replyTo, bundle, oneWay));
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
String messageId = subdata.getString("google.message_id");
|
|
||||||
Log.d(TAG, "Ack " + messageId + " for " + packageName);
|
|
||||||
Intent i = new Intent(context, McsService.class);
|
|
||||||
i.setAction(ACTION_ACK);
|
|
||||||
i.putExtra(EXTRA_APP, getSelfAuthIntent());
|
|
||||||
if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
|
||||||
new ForegroundServiceContext(context).startService(i);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
Bundle bundle = new Bundle();
|
|
||||||
bundle.putBoolean("unsupported", true);
|
|
||||||
sendReplyViaMessage(what, id, replyTo, bundle);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (oneWay) {
|
|
||||||
Bundle bundle = new Bundle();
|
|
||||||
bundle.putBoolean("ack", true);
|
|
||||||
sendReplyViaMessage(what, id, replyTo, bundle);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,41 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2018 microG Project Team
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.microg.gms.gcm;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.Intent;
|
|
||||||
|
|
||||||
import androidx.legacy.content.WakefulBroadcastReceiver;
|
|
||||||
|
|
||||||
import static org.microg.gms.gcm.GcmConstants.ACTION_C2DM_REGISTER;
|
|
||||||
import static org.microg.gms.gcm.GcmConstants.ACTION_C2DM_UNREGISTER;
|
|
||||||
|
|
||||||
public class PushRegisterReceiver extends WakefulBroadcastReceiver {
|
|
||||||
private static final String TAG = "GmsGcmRegisterRcv";
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onReceive(Context context, Intent intent) {
|
|
||||||
Intent intent2 = new Intent(context, PushRegisterService.class);
|
|
||||||
if (intent.getExtras().get("delete") != null) {
|
|
||||||
intent2.setAction(ACTION_C2DM_UNREGISTER);
|
|
||||||
} else {
|
|
||||||
intent2.setAction(ACTION_C2DM_REGISTER);
|
|
||||||
}
|
|
||||||
intent2.putExtras(intent.getExtras());
|
|
||||||
startWakefulService(context, intent2);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,200 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2013-2017 microG Project Team
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.microg.gms.gcm;
|
|
||||||
|
|
||||||
import android.app.IntentService;
|
|
||||||
import android.app.PendingIntent;
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.Intent;
|
|
||||||
import android.content.pm.PackageManager;
|
|
||||||
import android.os.IBinder;
|
|
||||||
import android.os.Message;
|
|
||||||
import android.os.Messenger;
|
|
||||||
import android.util.Log;
|
|
||||||
|
|
||||||
import androidx.annotation.Nullable;
|
|
||||||
import androidx.legacy.content.WakefulBroadcastReceiver;
|
|
||||||
|
|
||||||
import org.microg.gms.checkin.CheckinService;
|
|
||||||
import org.microg.gms.checkin.LastCheckinInfo;
|
|
||||||
import org.microg.gms.common.PackageUtils;
|
|
||||||
import org.microg.gms.common.Utils;
|
|
||||||
import org.microg.gms.ui.AskPushPermission;
|
|
||||||
|
|
||||||
import static org.microg.gms.gcm.GcmConstants.ACTION_C2DM_REGISTER;
|
|
||||||
import static org.microg.gms.gcm.GcmConstants.ACTION_C2DM_REGISTRATION;
|
|
||||||
import static org.microg.gms.gcm.GcmConstants.ACTION_C2DM_UNREGISTER;
|
|
||||||
import static org.microg.gms.gcm.GcmConstants.ERROR_SERVICE_NOT_AVAILABLE;
|
|
||||||
import static org.microg.gms.gcm.GcmConstants.EXTRA_APP;
|
|
||||||
import static org.microg.gms.gcm.GcmConstants.EXTRA_DELETE;
|
|
||||||
import static org.microg.gms.gcm.GcmConstants.EXTRA_ERROR;
|
|
||||||
import static org.microg.gms.gcm.GcmConstants.EXTRA_KID;
|
|
||||||
import static org.microg.gms.gcm.GcmConstants.EXTRA_MESSENGER;
|
|
||||||
import static org.microg.gms.gcm.GcmConstants.EXTRA_PENDING_INTENT;
|
|
||||||
import static org.microg.gms.gcm.GcmConstants.EXTRA_SENDER;
|
|
||||||
|
|
||||||
public class PushRegisterService extends IntentService {
|
|
||||||
private static final String TAG = "GmsGcmRegisterSvc";
|
|
||||||
private static final String EXTRA_SKIP_TRY_CHECKIN = "skip_checkin";
|
|
||||||
|
|
||||||
private GcmDatabase database;
|
|
||||||
private static boolean requestPending = false;
|
|
||||||
|
|
||||||
public PushRegisterService() {
|
|
||||||
super(TAG);
|
|
||||||
setIntentRedelivery(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onCreate() {
|
|
||||||
super.onCreate();
|
|
||||||
database = new GcmDatabase(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onDestroy() {
|
|
||||||
super.onDestroy();
|
|
||||||
database.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onHandleIntent(Intent intent) {
|
|
||||||
WakefulBroadcastReceiver.completeWakefulIntent(intent);
|
|
||||||
Log.d(TAG, "onHandleIntent: " + intent);
|
|
||||||
|
|
||||||
String requestId = null;
|
|
||||||
if (intent.hasExtra(EXTRA_KID) && intent.getStringExtra(EXTRA_KID).startsWith("|")) {
|
|
||||||
String[] kid = intent.getStringExtra(EXTRA_KID).split("\\|");
|
|
||||||
if (kid.length >= 3 && "ID".equals(kid[1])) {
|
|
||||||
requestId = kid[2];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (LastCheckinInfo.read(this).lastCheckin > 0) {
|
|
||||||
try {
|
|
||||||
if (ACTION_C2DM_UNREGISTER.equals(intent.getAction()) ||
|
|
||||||
(ACTION_C2DM_REGISTER.equals(intent.getAction()) && "1".equals(intent.getStringExtra(EXTRA_DELETE)))) {
|
|
||||||
unregister(intent, requestId);
|
|
||||||
} else if (ACTION_C2DM_REGISTER.equals(intent.getAction())) {
|
|
||||||
register(intent, requestId);
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
Log.w(TAG, e);
|
|
||||||
}
|
|
||||||
} else if (!intent.getBooleanExtra(EXTRA_SKIP_TRY_CHECKIN, false)) {
|
|
||||||
Log.d(TAG, "No checkin yet, trying to checkin");
|
|
||||||
intent.putExtra(EXTRA_SKIP_TRY_CHECKIN, true);
|
|
||||||
Intent subIntent = new Intent(this, CheckinService.class);
|
|
||||||
subIntent.putExtra(CheckinService.EXTRA_FORCE_CHECKIN, true);
|
|
||||||
subIntent.putExtra(CheckinService.EXTRA_CALLBACK_INTENT, intent);
|
|
||||||
startService(subIntent);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void register(final Intent intent, String requestId) {
|
|
||||||
PendingIntent pendingIntent = intent.getParcelableExtra(EXTRA_APP);
|
|
||||||
final String packageName = PackageUtils.packageFromPendingIntent(pendingIntent);
|
|
||||||
|
|
||||||
GcmDatabase.App app = database.getApp(packageName);
|
|
||||||
if (app == null && GcmPrefs.get(this).isConfirmNewApps()) {
|
|
||||||
try {
|
|
||||||
getPackageManager().getApplicationInfo(packageName, 0); // Check package exists
|
|
||||||
Intent i = new Intent(this, AskPushPermission.class);
|
|
||||||
i.putExtra(EXTRA_PENDING_INTENT, intent);
|
|
||||||
i.putExtra(EXTRA_APP, packageName);
|
|
||||||
i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
|
||||||
startActivity(i);
|
|
||||||
} catch (PackageManager.NameNotFoundException e) {
|
|
||||||
replyNotAvailable(this, intent, packageName, requestId);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
registerAndReply(this, database, intent, packageName, requestId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void replyNotAvailable(Context context, Intent intent, String packageName, String requestId) {
|
|
||||||
Intent outIntent = new Intent(ACTION_C2DM_REGISTRATION);
|
|
||||||
outIntent.putExtra(EXTRA_ERROR, PushRegisterManager.attachRequestId(ERROR_SERVICE_NOT_AVAILABLE, requestId));
|
|
||||||
sendReply(context, intent, packageName, outIntent);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void registerAndReply(Context context, GcmDatabase database, Intent intent, String packageName, String requestId) {
|
|
||||||
Log.d(TAG, "register[req]: " + intent.toString() + " extras=" + intent.getExtras());
|
|
||||||
PushRegisterManager.completeRegisterRequest(context, database,
|
|
||||||
new RegisterRequest()
|
|
||||||
.build(Utils.getBuild(context))
|
|
||||||
.sender(intent.getStringExtra(EXTRA_SENDER))
|
|
||||||
.checkin(LastCheckinInfo.read(context))
|
|
||||||
.app(packageName)
|
|
||||||
.extraParams(intent.getExtras()),
|
|
||||||
bundle -> {
|
|
||||||
Intent outIntent = new Intent(ACTION_C2DM_REGISTRATION);
|
|
||||||
outIntent.putExtras(bundle);
|
|
||||||
Log.d(TAG, "register[res]: " + outIntent.toString() + " extras=" + outIntent.getExtras());
|
|
||||||
sendReply(context, intent, packageName, outIntent);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void sendReply(Context context, Intent intent, String packageName, Intent outIntent) {
|
|
||||||
try {
|
|
||||||
if (intent != null && intent.hasExtra(EXTRA_MESSENGER)) {
|
|
||||||
Messenger messenger = intent.getParcelableExtra(EXTRA_MESSENGER);
|
|
||||||
Message message = Message.obtain();
|
|
||||||
message.obj = outIntent;
|
|
||||||
messenger.send(message);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
Log.w(TAG, e);
|
|
||||||
}
|
|
||||||
|
|
||||||
outIntent.setPackage(packageName);
|
|
||||||
context.sendOrderedBroadcast(outIntent, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void unregister(Intent intent, String requestId) {
|
|
||||||
PendingIntent pendingIntent = intent.getParcelableExtra(EXTRA_APP);
|
|
||||||
String packageName = PackageUtils.packageFromPendingIntent(pendingIntent);
|
|
||||||
Log.d(TAG, "unregister[req]: " + intent.toString() + " extras=" + intent.getExtras());
|
|
||||||
|
|
||||||
PushRegisterManager.completeRegisterRequest(this, database,
|
|
||||||
new RegisterRequest()
|
|
||||||
.build(Utils.getBuild(this))
|
|
||||||
.sender(intent.getStringExtra(EXTRA_SENDER))
|
|
||||||
.checkin(LastCheckinInfo.read(this))
|
|
||||||
.app(packageName)
|
|
||||||
.extraParams(intent.getExtras()),
|
|
||||||
bundle -> {
|
|
||||||
Intent outIntent = new Intent(ACTION_C2DM_REGISTRATION);
|
|
||||||
outIntent.putExtras(bundle);
|
|
||||||
Log.d(TAG, "unregister[res]: " + outIntent.toString() + " extras=" + outIntent.getExtras());
|
|
||||||
sendReply(this, intent, packageName, outIntent);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
@Override
|
|
||||||
public IBinder onBind(Intent intent) {
|
|
||||||
Log.d(TAG, "onBind: " + intent.toString());
|
|
||||||
if (ACTION_C2DM_REGISTER.equals(intent.getAction())) {
|
|
||||||
Messenger messenger = new Messenger(new PushRegisterHandler(this, database));
|
|
||||||
return messenger.getBinder();
|
|
||||||
}
|
|
||||||
return super.onBind(intent);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -50,7 +50,6 @@ public class TriggerReceiver extends WakefulBroadcastReceiver {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onReceive(Context context, Intent intent) {
|
public void onReceive(Context context, Intent intent) {
|
||||||
try {
|
try {
|
||||||
|
@ -1,9 +1,11 @@
|
|||||||
package org.microg.gms.ui;
|
package org.microg.gms.ui;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.pm.ApplicationInfo;
|
import android.content.pm.ApplicationInfo;
|
||||||
import android.content.pm.PackageManager;
|
import android.content.pm.PackageManager;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
import android.os.ResultReceiver;
|
||||||
import android.text.Html;
|
import android.text.Html;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
@ -19,13 +21,15 @@ import static org.microg.gms.gcm.GcmConstants.EXTRA_KID;
|
|||||||
import static org.microg.gms.gcm.GcmConstants.EXTRA_PENDING_INTENT;
|
import static org.microg.gms.gcm.GcmConstants.EXTRA_PENDING_INTENT;
|
||||||
|
|
||||||
public class AskPushPermission extends FragmentActivity {
|
public class AskPushPermission extends FragmentActivity {
|
||||||
|
public static final String EXTRA_REQUESTED_PACKAGE = "package";
|
||||||
|
public static final String EXTRA_RESULT_RECEIVER = "receiver";
|
||||||
|
public static final String EXTRA_EXPLICIT = "explicit";
|
||||||
|
|
||||||
private GcmDatabase database;
|
private GcmDatabase database;
|
||||||
|
|
||||||
private String packageName;
|
private String packageName;
|
||||||
private Intent intent;
|
private ResultReceiver resultReceiver;
|
||||||
private boolean answered;
|
private boolean answered;
|
||||||
private String requestId;
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(Bundle savedInstanceState) {
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
@ -33,18 +37,17 @@ public class AskPushPermission extends FragmentActivity {
|
|||||||
|
|
||||||
database = new GcmDatabase(this);
|
database = new GcmDatabase(this);
|
||||||
|
|
||||||
packageName = getIntent().getStringExtra(EXTRA_APP);
|
packageName = getIntent().getStringExtra(EXTRA_REQUESTED_PACKAGE);
|
||||||
intent = getIntent().getParcelableExtra(EXTRA_PENDING_INTENT);
|
resultReceiver = getIntent().getParcelableExtra(EXTRA_RESULT_RECEIVER);
|
||||||
|
if (packageName == null || resultReceiver == null) {
|
||||||
requestId = null;
|
answered = true;
|
||||||
if (intent.hasExtra(EXTRA_KID) && intent.getStringExtra(EXTRA_KID).startsWith("|")) {
|
finish();
|
||||||
String[] kid = intent.getStringExtra(EXTRA_KID).split("\\|");
|
return;
|
||||||
if (kid.length >= 3 && "ID".equals(kid[1])) {
|
|
||||||
requestId = kid[2];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (database.getApp(packageName) != null) {
|
if (database.getApp(packageName) != null) {
|
||||||
|
resultReceiver.send(Activity.RESULT_OK, Bundle.EMPTY);
|
||||||
|
answered = true;
|
||||||
finish();
|
finish();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -61,14 +64,18 @@ public class AskPushPermission extends FragmentActivity {
|
|||||||
if (answered) return;
|
if (answered) return;
|
||||||
database.noteAppKnown(packageName, true);
|
database.noteAppKnown(packageName, true);
|
||||||
answered = true;
|
answered = true;
|
||||||
new Thread(() -> PushRegisterService.registerAndReply(AskPushPermission.this, database, intent, packageName, requestId)).start();
|
Bundle bundle = new Bundle();
|
||||||
|
bundle.putBoolean(EXTRA_EXPLICIT, true);
|
||||||
|
resultReceiver.send(Activity.RESULT_OK, bundle);
|
||||||
finish();
|
finish();
|
||||||
});
|
});
|
||||||
findViewById(R.id.permission_deny_button).setOnClickListener(v -> {
|
findViewById(R.id.permission_deny_button).setOnClickListener(v -> {
|
||||||
if (answered) return;
|
if (answered) return;
|
||||||
database.noteAppKnown(packageName, false);
|
database.noteAppKnown(packageName, false);
|
||||||
answered = true;
|
answered = true;
|
||||||
PushRegisterService.replyNotAvailable(AskPushPermission.this, intent, packageName, requestId);
|
Bundle bundle = new Bundle();
|
||||||
|
bundle.putBoolean(EXTRA_EXPLICIT, true);
|
||||||
|
resultReceiver.send(Activity.RESULT_CANCELED, bundle);
|
||||||
finish();
|
finish();
|
||||||
});
|
});
|
||||||
} catch (PackageManager.NameNotFoundException e) {
|
} catch (PackageManager.NameNotFoundException e) {
|
||||||
@ -80,8 +87,7 @@ public class AskPushPermission extends FragmentActivity {
|
|||||||
protected void onDestroy() {
|
protected void onDestroy() {
|
||||||
super.onDestroy();
|
super.onDestroy();
|
||||||
if (!answered) {
|
if (!answered) {
|
||||||
PushRegisterService.replyNotAvailable(AskPushPermission.this, intent, packageName, requestId);
|
resultReceiver.send(Activity.RESULT_CANCELED, Bundle.EMPTY);
|
||||||
answered = true;
|
|
||||||
}
|
}
|
||||||
database.close();
|
database.close();
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,363 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2020, microG Project Team
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
package org.microg.gms.gcm
|
||||||
|
|
||||||
|
import android.app.Activity
|
||||||
|
import android.app.PendingIntent
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
|
import android.os.*
|
||||||
|
import android.util.Log
|
||||||
|
import androidx.legacy.content.WakefulBroadcastReceiver
|
||||||
|
import androidx.lifecycle.Lifecycle
|
||||||
|
import androidx.lifecycle.LifecycleOwner
|
||||||
|
import androidx.lifecycle.LifecycleService
|
||||||
|
import androidx.lifecycle.lifecycleScope
|
||||||
|
import org.microg.gms.checkin.CheckinPrefs
|
||||||
|
import org.microg.gms.checkin.CheckinService
|
||||||
|
import org.microg.gms.checkin.LastCheckinInfo
|
||||||
|
import org.microg.gms.common.ForegroundServiceContext
|
||||||
|
import org.microg.gms.common.PackageUtils
|
||||||
|
import org.microg.gms.common.Utils
|
||||||
|
import org.microg.gms.gcm.GcmConstants.*
|
||||||
|
import org.microg.gms.ui.AskPushPermission
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean
|
||||||
|
import kotlin.coroutines.resume
|
||||||
|
import kotlin.coroutines.suspendCoroutine
|
||||||
|
|
||||||
|
private const val TAG = "GmsGcmRegister"
|
||||||
|
|
||||||
|
private suspend fun ensureCheckinIsUpToDate(context: Context) {
|
||||||
|
if (!CheckinPrefs.get(context).isEnabled) throw RuntimeException("Checkin disabled")
|
||||||
|
val lastCheckin = LastCheckinInfo.read(context).lastCheckin
|
||||||
|
if (lastCheckin < System.currentTimeMillis() - CheckinService.MAX_VALID_CHECKIN_AGE) {
|
||||||
|
val resultData: Bundle = suspendCoroutine { continuation ->
|
||||||
|
val intent = Intent(context, CheckinService::class.java)
|
||||||
|
val continued = AtomicBoolean(false)
|
||||||
|
intent.putExtra(CheckinService.EXTRA_RESULT_RECEIVER, object : ResultReceiver(null) {
|
||||||
|
override fun onReceiveResult(resultCode: Int, resultData: Bundle?) {
|
||||||
|
if (continued.compareAndSet(false, true)) continuation.resume(resultData ?: Bundle.EMPTY)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
ForegroundServiceContext(context).startService(intent)
|
||||||
|
Handler().postDelayed({
|
||||||
|
if (continued.compareAndSet(false, true)) continuation.resume(Bundle.EMPTY)
|
||||||
|
}, 10000L)
|
||||||
|
}
|
||||||
|
if (resultData.getLong(CheckinService.EXTRA_NEW_CHECKIN_TIME, 0L) + lastCheckin == 0L) {
|
||||||
|
throw RuntimeException("No checkin available")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private suspend fun ensureAppRegistrationAllowed(context: Context, database: GcmDatabase, packageName: String) {
|
||||||
|
if (!GcmPrefs.get(context).isEnabled) throw RuntimeException("GCM disabled")
|
||||||
|
val app = database.getApp(packageName)
|
||||||
|
if (app == null && GcmPrefs.get(context).isConfirmNewApps) {
|
||||||
|
val accepted: Boolean = suspendCoroutine { continuation ->
|
||||||
|
val i = Intent(context, AskPushPermission::class.java)
|
||||||
|
i.putExtra(AskPushPermission.EXTRA_REQUESTED_PACKAGE, packageName)
|
||||||
|
i.putExtra(AskPushPermission.EXTRA_RESULT_RECEIVER, object : ResultReceiver(null) {
|
||||||
|
override fun onReceiveResult(resultCode: Int, resultData: Bundle?) {
|
||||||
|
continuation.resume(resultCode == Activity.RESULT_OK)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||||
|
i.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS)
|
||||||
|
i.addFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT)
|
||||||
|
context.startActivity(i)
|
||||||
|
}
|
||||||
|
if (!accepted) {
|
||||||
|
throw RuntimeException("Push permission not granted to app")
|
||||||
|
}
|
||||||
|
} else if (!app.allowRegister) {
|
||||||
|
throw RuntimeException("Push permission not granted to app")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun completeRegisterRequest(context: Context, database: GcmDatabase, request: RegisterRequest, requestId: String? = null): Bundle = suspendCoroutine { continuation ->
|
||||||
|
PushRegisterManager.completeRegisterRequest(context, database, requestId, request) { continuation.resume(it) }
|
||||||
|
}
|
||||||
|
|
||||||
|
private val Intent.requestId: String?
|
||||||
|
get() {
|
||||||
|
val kidString = getStringExtra(GcmConstants.EXTRA_KID) ?: return null
|
||||||
|
if (kidString.startsWith("|")) {
|
||||||
|
val kid = kidString.split("\\|".toRegex()).toTypedArray()
|
||||||
|
if (kid.size >= 3 && "ID" == kid[1]) {
|
||||||
|
return kid[2]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
private val Intent.app: PendingIntent?
|
||||||
|
get() = getParcelableExtra(EXTRA_APP)
|
||||||
|
|
||||||
|
private val Intent.appPackageName: String?
|
||||||
|
get() = PackageUtils.packageFromPendingIntent(app)
|
||||||
|
|
||||||
|
class PushRegisterService : LifecycleService() {
|
||||||
|
private lateinit var database: GcmDatabase
|
||||||
|
override fun onCreate() {
|
||||||
|
super.onCreate()
|
||||||
|
database = GcmDatabase(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDestroy() {
|
||||||
|
super.onDestroy()
|
||||||
|
database.close()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
|
||||||
|
WakefulBroadcastReceiver.completeWakefulIntent(intent)
|
||||||
|
Log.d(TAG, "onStartCommand: $intent")
|
||||||
|
lifecycleScope.launchWhenStarted {
|
||||||
|
if (intent == null) return@launchWhenStarted
|
||||||
|
handleIntent(intent)
|
||||||
|
}
|
||||||
|
return super.onStartCommand(intent, flags, startId)
|
||||||
|
}
|
||||||
|
|
||||||
|
private suspend fun handleIntent(intent: Intent) {
|
||||||
|
try {
|
||||||
|
ensureCheckinIsUpToDate(this)
|
||||||
|
if (ACTION_C2DM_UNREGISTER == intent.action || ACTION_C2DM_REGISTER == intent.action && "1" == intent.getStringExtra(EXTRA_DELETE)) {
|
||||||
|
unregister(intent)
|
||||||
|
} else if (ACTION_C2DM_REGISTER == intent.action) {
|
||||||
|
register(intent)
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.w(TAG, e)
|
||||||
|
replyNotAvailable(intent)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun replyNotAvailable(intent: Intent) {
|
||||||
|
val outIntent = Intent(ACTION_C2DM_REGISTRATION)
|
||||||
|
outIntent.putExtra(EXTRA_ERROR, PushRegisterManager.attachRequestId(ERROR_SERVICE_NOT_AVAILABLE, intent.requestId))
|
||||||
|
sendReply(intent, intent.appPackageName, outIntent)
|
||||||
|
}
|
||||||
|
|
||||||
|
private suspend fun register(intent: Intent) {
|
||||||
|
val packageName = intent.appPackageName ?: throw RuntimeException("No package provided")
|
||||||
|
ensureAppRegistrationAllowed(this, database, packageName)
|
||||||
|
Log.d(TAG, "register[req]: " + intent.toString() + " extras=" + intent!!.extras)
|
||||||
|
val bundle = completeRegisterRequest(this, database,
|
||||||
|
RegisterRequest()
|
||||||
|
.build(Utils.getBuild(this))
|
||||||
|
.sender(intent.getStringExtra(EXTRA_SENDER))
|
||||||
|
.checkin(LastCheckinInfo.read(this))
|
||||||
|
.app(packageName)
|
||||||
|
.extraParams(intent.extras))
|
||||||
|
|
||||||
|
val outIntent = Intent(ACTION_C2DM_REGISTRATION)
|
||||||
|
outIntent.putExtras(bundle)
|
||||||
|
Log.d(TAG, "register[res]: " + outIntent.toString() + " extras=" + outIntent.extras)
|
||||||
|
sendReply(intent, packageName, outIntent)
|
||||||
|
}
|
||||||
|
|
||||||
|
private suspend fun unregister(intent: Intent) {
|
||||||
|
val packageName = intent.appPackageName ?: throw RuntimeException("No package provided")
|
||||||
|
Log.d(TAG, "unregister[req]: " + intent.toString() + " extras=" + intent.extras)
|
||||||
|
val bundle = completeRegisterRequest(this, database, RegisterRequest()
|
||||||
|
.build(Utils.getBuild(this))
|
||||||
|
.sender(intent.getStringExtra(EXTRA_SENDER))
|
||||||
|
.checkin(LastCheckinInfo.read(this))
|
||||||
|
.app(packageName)
|
||||||
|
.extraParams(intent.extras)
|
||||||
|
)
|
||||||
|
val outIntent = Intent(ACTION_C2DM_REGISTRATION)
|
||||||
|
outIntent.putExtras(bundle)
|
||||||
|
Log.d(TAG, "unregister[res]: " + outIntent.toString() + " extras=" + outIntent.extras)
|
||||||
|
sendReply(intent, packageName, outIntent)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun sendReply(intent: Intent, packageName: String?, outIntent: Intent) {
|
||||||
|
if (sendReplyToMessenger(intent, outIntent)) return
|
||||||
|
outIntent.setPackage(packageName)
|
||||||
|
sendOrderedBroadcast(outIntent, null)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun sendReplyToMessenger(intent: Intent, outIntent: Intent): Boolean {
|
||||||
|
try {
|
||||||
|
val messenger = intent.getParcelableExtra<Messenger>(EXTRA_MESSENGER) ?: return false
|
||||||
|
val message = Message.obtain()
|
||||||
|
message.obj = outIntent
|
||||||
|
messenger.send(message)
|
||||||
|
return true
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.w(TAG, e)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onBind(intent: Intent): IBinder? {
|
||||||
|
Log.d(TAG, "onBind: $intent")
|
||||||
|
super.onBind(intent)
|
||||||
|
if (ACTION_C2DM_REGISTER == intent.action) {
|
||||||
|
val messenger = Messenger(PushRegisterHandler(this, database, lifecycle))
|
||||||
|
return messenger.binder
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class PushRegisterHandler(private val context: Context, private val database: GcmDatabase, private val lifecycle: Lifecycle) : Handler(), LifecycleOwner {
|
||||||
|
override fun getLifecycle(): Lifecycle = lifecycle
|
||||||
|
|
||||||
|
private var callingUid = 0
|
||||||
|
override fun sendMessageAtTime(msg: Message, uptimeMillis: Long): Boolean {
|
||||||
|
callingUid = Binder.getCallingUid()
|
||||||
|
return super.sendMessageAtTime(msg, uptimeMillis)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun sendReplyViaMessage(what: Int, id: Int, replyTo: Messenger, messageData: Bundle) {
|
||||||
|
val response = Message.obtain()
|
||||||
|
response.what = what
|
||||||
|
response.arg1 = id
|
||||||
|
response.data = messageData
|
||||||
|
try {
|
||||||
|
replyTo.send(response)
|
||||||
|
} catch (e: RemoteException) {
|
||||||
|
Log.w(TAG, e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun sendReplyViaIntent(outIntent: Intent, replyTo: Messenger) {
|
||||||
|
val message = Message.obtain()
|
||||||
|
message.obj = outIntent
|
||||||
|
try {
|
||||||
|
replyTo.send(message)
|
||||||
|
} catch (e: RemoteException) {
|
||||||
|
Log.w(TAG, e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun sendReply(what: Int, id: Int, replyTo: Messenger, data: Bundle, oneWay: Boolean) {
|
||||||
|
if (what == 0) {
|
||||||
|
val outIntent = Intent(ACTION_C2DM_REGISTRATION)
|
||||||
|
outIntent.putExtras(data)
|
||||||
|
sendReplyViaIntent(outIntent, replyTo)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
val messageData = Bundle()
|
||||||
|
messageData.putBundle("data", data)
|
||||||
|
sendReplyViaMessage(what, id, replyTo, messageData)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun replyError(what: Int, id: Int, replyTo: Messenger, errorMessage: String, oneWay: Boolean) {
|
||||||
|
val bundle = Bundle()
|
||||||
|
bundle.putString(EXTRA_ERROR, errorMessage)
|
||||||
|
sendReply(what, id, replyTo, bundle, oneWay)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun replyNotAvailable(what: Int, id: Int, replyTo: Messenger) {
|
||||||
|
replyError(what, id, replyTo, ERROR_SERVICE_NOT_AVAILABLE, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
private val selfAuthIntent: PendingIntent
|
||||||
|
private get() {
|
||||||
|
val intent = Intent()
|
||||||
|
intent.setPackage("com.google.example.invalidpackage")
|
||||||
|
return PendingIntent.getBroadcast(context, 0, intent, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun handleMessage(msg: Message) {
|
||||||
|
var msg = msg
|
||||||
|
val obj = msg.obj
|
||||||
|
if (msg.what == 0) {
|
||||||
|
if (obj is Intent) {
|
||||||
|
val nuMsg = Message.obtain()
|
||||||
|
nuMsg.what = msg.what
|
||||||
|
nuMsg.arg1 = 0
|
||||||
|
nuMsg.replyTo = null
|
||||||
|
val packageName = obj.appPackageName
|
||||||
|
val data = Bundle()
|
||||||
|
data.putBoolean("oneWay", false)
|
||||||
|
data.putString("pkg", packageName)
|
||||||
|
data.putBundle("data", msg.data)
|
||||||
|
nuMsg.data = data
|
||||||
|
msg = nuMsg
|
||||||
|
} else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val what = msg.what
|
||||||
|
val id = msg.arg1
|
||||||
|
val replyTo = msg.replyTo
|
||||||
|
if (replyTo == null) {
|
||||||
|
Log.w(TAG, "replyTo is null")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
val data = msg.data
|
||||||
|
val packageName = data.getString("pkg") ?: return
|
||||||
|
val subdata = data.getBundle("data")
|
||||||
|
try {
|
||||||
|
PackageUtils.checkPackageUid(context, packageName, callingUid)
|
||||||
|
} catch (e: SecurityException) {
|
||||||
|
Log.w(TAG, e)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
Log.d(TAG, "handleMessage: package=$packageName what=$what id=$id")
|
||||||
|
val oneWay = data.getBoolean("oneWay", false)
|
||||||
|
when (what) {
|
||||||
|
0, 1 -> {
|
||||||
|
lifecycleScope.launchWhenStarted {
|
||||||
|
try {
|
||||||
|
val sender = subdata?.getString("sender")
|
||||||
|
val delete = subdata?.get("delete") != null
|
||||||
|
ensureCheckinIsUpToDate(context)
|
||||||
|
if (!delete) ensureAppRegistrationAllowed(context, database, packageName)
|
||||||
|
val bundle = completeRegisterRequest(context, database,
|
||||||
|
RegisterRequest()
|
||||||
|
.build(Utils.getBuild(context))
|
||||||
|
.sender(sender)
|
||||||
|
.checkin(LastCheckinInfo.read(context))
|
||||||
|
.app(packageName)
|
||||||
|
.delete(delete)
|
||||||
|
.extraParams(subdata))
|
||||||
|
sendReply(what, id, replyTo, bundle, oneWay)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.w(TAG, e)
|
||||||
|
replyNotAvailable(what, id, replyTo)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
2 -> {
|
||||||
|
val messageId = subdata!!.getString("google.message_id")
|
||||||
|
Log.d(TAG, "Ack $messageId for $packageName")
|
||||||
|
val i = Intent(context, McsService::class.java)
|
||||||
|
i.action = McsConstants.ACTION_ACK
|
||||||
|
i.putExtra(EXTRA_APP, selfAuthIntent)
|
||||||
|
ForegroundServiceContext(context).startService(i)
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
val bundle = Bundle()
|
||||||
|
bundle.putBoolean("unsupported", true)
|
||||||
|
sendReplyViaMessage(what, id, replyTo, bundle)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (oneWay) {
|
||||||
|
val bundle = Bundle()
|
||||||
|
bundle.putBoolean("ack", true)
|
||||||
|
sendReplyViaMessage(what, id, replyTo, bundle)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class PushRegisterReceiver : WakefulBroadcastReceiver() {
|
||||||
|
override fun onReceive(context: Context, intent: Intent) {
|
||||||
|
val intent2 = Intent(context, PushRegisterService::class.java)
|
||||||
|
if (intent.extras!!["delete"] != null) {
|
||||||
|
intent2.action = ACTION_C2DM_UNREGISTER
|
||||||
|
} else {
|
||||||
|
intent2.action = ACTION_C2DM_REGISTER
|
||||||
|
}
|
||||||
|
intent2.putExtras(intent.extras!!)
|
||||||
|
startWakefulService(context, intent2)
|
||||||
|
}
|
||||||
|
}
|
@ -1,12 +1,9 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<!-- Copyright (C) 2015 The Android Open Source Project
|
<!-- Copyright (C) 2015 The Android Open Source Project
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
You may obtain a copy of the License at
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
Unless required by applicable law or agreed to in writing, software
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
@ -16,37 +13,40 @@
|
|||||||
|
|
||||||
<FrameLayout
|
<FrameLayout
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
|
||||||
android:layout_width="fill_parent"
|
android:layout_width="fill_parent"
|
||||||
android:layout_height="fill_parent"
|
android:layout_height="fill_parent"
|
||||||
android:clipChildren="false"
|
android:clipChildren="false"
|
||||||
android:clipToPadding="false">
|
android:clipToPadding="false">
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
|
android:id="@+id/dialog_container"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:orientation="vertical">
|
android:orientation="vertical">
|
||||||
|
|
||||||
<FrameLayout
|
<FrameLayout
|
||||||
|
android:id="@+id/desc_container"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:paddingEnd="16dip"
|
|
||||||
android:paddingLeft="20dip"
|
|
||||||
android:paddingRight="16dip"
|
|
||||||
android:paddingStart="20dip"
|
android:paddingStart="20dip"
|
||||||
android:paddingTop="18dip">
|
android:paddingLeft="20dip"
|
||||||
|
android:paddingTop="18dip"
|
||||||
|
android:paddingEnd="16dip"
|
||||||
|
android:paddingRight="16dip">
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
|
android:id="@+id/perm_desc_root"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:orientation="horizontal">
|
android:orientation="horizontal">
|
||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
|
android:id="@+id/permission_icon"
|
||||||
android:layout_width="36dip"
|
android:layout_width="36dip"
|
||||||
android:layout_height="36dip"
|
android:layout_height="36dip"
|
||||||
android:scaleType="fitCenter"
|
android:scaleType="fitCenter"
|
||||||
android:src="@drawable/ic_cloud_bell"
|
android:src="@drawable/ic_cloud_bell"
|
||||||
app:tint="?attr/colorAccent">
|
android:tint="?attr/colorAccent">
|
||||||
</ImageView>
|
</ImageView>
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
@ -54,8 +54,8 @@
|
|||||||
style="@style/TextAppearance.AppCompat.Subhead"
|
style="@style/TextAppearance.AppCompat.Subhead"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:paddingLeft="16dip"
|
|
||||||
android:paddingStart="16dip"
|
android:paddingStart="16dip"
|
||||||
|
android:paddingLeft="16dip"
|
||||||
android:text="Allow {appName} to register for push notifications?"
|
android:text="Allow {appName} to register for push notifications?"
|
||||||
android:textSize="20sp">
|
android:textSize="20sp">
|
||||||
</TextView>
|
</TextView>
|
||||||
@ -67,62 +67,31 @@
|
|||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:orientation="vertical"
|
android:gravity="end"
|
||||||
android:paddingEnd="16dip"
|
android:orientation="horizontal"
|
||||||
|
android:paddingStart="20dip"
|
||||||
android:paddingLeft="20dip"
|
android:paddingLeft="20dip"
|
||||||
android:paddingRight="16dip"
|
android:paddingEnd="16dip"
|
||||||
android:paddingStart="20dip">
|
android:paddingRight="16dip">
|
||||||
|
|
||||||
<androidx.appcompat.widget.ButtonBarLayout
|
<Button
|
||||||
android:layout_width="match_parent"
|
android:id="@+id/permission_deny_button"
|
||||||
|
style="?attr/buttonBarButtonStyle"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:gravity="bottom"
|
android:text="@string/deny">
|
||||||
android:orientation="horizontal"
|
</Button>
|
||||||
android:paddingBottom="4dp"
|
|
||||||
android:paddingLeft="6dip"
|
|
||||||
android:paddingStart="6dip"
|
|
||||||
android:paddingTop="4dp">
|
|
||||||
|
|
||||||
<TextView
|
<Button
|
||||||
style="?android:attr/textAppearanceSmall"
|
android:id="@+id/permission_allow_button"
|
||||||
android:layout_width="wrap_content"
|
style="?attr/buttonBarButtonStyle"
|
||||||
android:layout_height="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_gravity="center_vertical"
|
android:layout_height="wrap_content"
|
||||||
android:maxLines="1"
|
android:text="@string/allow">
|
||||||
android:paddingEnd="12dp"
|
</Button>
|
||||||
android:paddingRight="12dp"
|
|
||||||
android:textColor="?android:attr/textColorSecondary"
|
|
||||||
android:visibility="invisible">
|
|
||||||
</TextView>
|
|
||||||
|
|
||||||
<View
|
|
||||||
android:id="@*android:id/spacer"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="0dp"
|
|
||||||
android:layout_weight="1"
|
|
||||||
android:visibility="invisible">
|
|
||||||
</View>
|
|
||||||
|
|
||||||
<Button
|
|
||||||
android:id="@+id/permission_deny_button"
|
|
||||||
style="?attr/buttonBarButtonStyle"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="@string/deny">
|
|
||||||
</Button>
|
|
||||||
|
|
||||||
<Button
|
|
||||||
android:id="@+id/permission_allow_button"
|
|
||||||
style="?attr/buttonBarButtonStyle"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="@string/allow">
|
|
||||||
</Button>
|
|
||||||
|
|
||||||
</androidx.appcompat.widget.ButtonBarLayout>
|
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
</FrameLayout>
|
</FrameLayout>
|
@ -25,4 +25,14 @@
|
|||||||
<item name="android:windowNoTitle">true</item>
|
<item name="android:windowNoTitle">true</item>
|
||||||
<item name="windowActionBar">false</item>
|
<item name="windowActionBar">false</item>
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
<style name="Theme.AppCompat.Light.Dialog.Alert.NoActionBar">
|
||||||
|
<item name="windowActionBar">false</item>
|
||||||
|
<item name="windowNoTitle">true</item>
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<style name="Theme.AppCompat.DayNight.Dialog.Alert.NoActionBar">
|
||||||
|
<item name="windowActionBar">false</item>
|
||||||
|
<item name="windowNoTitle">true</item>
|
||||||
|
</style>
|
||||||
</resources>
|
</resources>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user