Update auth

This commit is contained in:
mar-v-in 2015-02-03 23:08:55 +01:00
parent 14379f607e
commit 8a964300d1
13 changed files with 627 additions and 144 deletions

View File

@ -113,6 +113,14 @@
</intent-filter>
</activity>
<provider
android:name="org.microg.gms.feeds.SubscribedFeedsProvider"
android:authorities="subscribedfeeds"
android:exported="true"
android:multiprocess="false"
android:readPermission="android.permission.SUBSCRIBED_FEEDS_READ"
android:writePermission="android.permission.SUBSCRIBED_FEEDS_WRITE" />
<service
android:name=".auth.GetToken"
android:exported="true" />

View File

@ -16,9 +16,13 @@
package org.microg.gms.auth;
import android.accounts.Account;
import android.content.Context;
import android.net.Uri;
import android.util.Log;
import org.microg.gms.auth.loginservice.GoogleLoginService;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
@ -49,6 +53,7 @@ public class AuthClient {
content.append("&");
content.append(Uri.encode(key)).append("=").append(Uri.encode(formContent.get(key)));
}
Log.d(TAG, "-- Request --\n" + content);
OutputStream os = connection.getOutputStream();
os.write(content.toString().getBytes());
os.close();
@ -56,6 +61,7 @@ public class AuthClient {
throw new IOException(connection.getResponseMessage());
}
String result = new String(readStreamToEnd(connection.getInputStream()));
Log.d(TAG, "-- Response --\n" + result);
return AuthResponse.parse(result);
}

View File

@ -0,0 +1,74 @@
/*
* Copyright 2013-2015 µg 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.auth;
import android.accounts.Account;
import android.accounts.AccountManager;
import android.content.Context;
import android.util.Log;
public class AuthManager {
private static final String TAG = "GmsAuthManager";
public static void storeResponse(Context context, Account account, String packageName,
String sig, String service, AuthResponse response) {
AccountManager accountManager = AccountManager.get(context);
if (response.accountId != null)
accountManager.setUserData(account, "GoogleUserId", response.accountId);
if (response.Sid != null)
accountManager.setAuthToken(account, buildTokenKey(packageName, sig, "SID"), response.Sid);
if (response.LSid != null)
accountManager.setAuthToken(account, buildTokenKey(packageName, sig, "LSID"), response.LSid);
if (response.expiry > 0)
accountManager.setUserData(account, buildExpireKey(packageName, sig, service), Long.toString(response.expiry));
if (response.auth != null && response.expiry != 0) {
accountManager.setAuthToken(account, buildTokenKey(packageName, sig, service), response.auth);
accountManager.setUserData(account, buildPermKey(packageName, sig, service), "1");
}
}
public static boolean isPermitted(Context context, Account account, String packageName,
String sig, String service) {
AccountManager accountManager = AccountManager.get(context);
String perm = accountManager.getUserData(account, buildPermKey(packageName, sig, service));
if (!"1".equals(perm))
return false;
String exp = accountManager.getUserData(account, buildExpireKey(packageName, sig, service));
if (exp != null) {
long expLong = Long.parseLong(exp);
if (expLong < System.currentTimeMillis() / 1000L) {
Log.d(TAG, "Permission for " + packageName + " / " + service + " present, but expired");
return false;
}
}
return true;
}
private static String buildTokenKey(String packageName, String sig, String service) {
return packageName + ":" + sig + ":" + service;
}
private static String buildPermKey(String packageName, String sig, String service) {
return "perm." + packageName + ":" + sig + ":" + service;
}
private static String buildExpireKey(String packageName, String sig, String service) {
return "EXP." + packageName + ":" + sig + ":" + service;
}
}

View File

@ -26,9 +26,10 @@ import android.os.Handler;
import android.os.Looper;
import android.os.RemoteException;
import android.util.Log;
import com.google.android.auth.IAuthManagerService;
import java.util.Arrays;
import org.microg.gms.common.Utils;
public class AuthManagerServiceImpl extends IAuthManagerService.Stub {
public static final String GOOGLE_ACCOUNT_TYPE = "com.google";
@ -62,23 +63,34 @@ public class AuthManagerServiceImpl extends IAuthManagerService.Stub {
public Bundle getToken(String accountName, String scope, Bundle extras) throws RemoteException {
String packageName = extras.getString(KEY_ANDROID_PACKAGE_NAME, extras.getString(KEY_CLIENT_PACKAGE_NAME, null));
int callerUid = extras.getInt(KEY_CALLER_UID, 0);
checkPackage(packageName, callerUid, getCallingUid());
Utils.checkPackage(context, packageName, callerUid, getCallingUid());
boolean notify = extras.getBoolean(KEY_HANDLE_NOTIFICATION, false);
Log.d("AuthManagerService", "getToken: account:" + accountName + " scope:" + scope + " extras:" + extras);
AccountManagerFuture<Bundle> authToken = AccountManager.get(context).getAuthToken(new Account(accountName, GOOGLE_ACCOUNT_TYPE), scope, extras, notify, null, new Handler(Looper.getMainLooper()));
//AccountManagerFuture<Bundle> authToken = AccountManager.get(context).getAuthToken(new Account(accountName, GOOGLE_ACCOUNT_TYPE), scope, extras, notify, null, new Handler(Looper.getMainLooper()));
try {
Bundle requestResult = authToken.getResult();
if (!requestResult.containsKey(AccountManager.KEY_AUTHTOKEN) && requestResult.containsKey(AccountManager.KEY_INTENT)) {
Account account = new Account(accountName, GOOGLE_ACCOUNT_TYPE);
String sig = Utils.getFirstPackageSignatureDigest(context, packageName);
AuthResponse response = new AuthRequest().fromContext(context)
.app(packageName, sig)
.callerIsApp()
.email(accountName)
.token(AccountManager.get(context).getPassword(account))
.service(scope)
.getResponse();
AuthManager.storeResponse(context, account, packageName, sig, scope, response);
//Bundle requestResult = authToken.getResult();
/*if (!requestResult.containsKey(AccountManager.KEY_AUTHTOKEN) && requestResult.containsKey(AccountManager.KEY_INTENT)) {
Intent intent = requestResult.getParcelable(AccountManager.KEY_INTENT);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(intent);
}
Log.d("getToken", requestResult.toString());
}*/
Log.d("getToken", response.auth);
Bundle result = new Bundle();
result.putString(KEY_AUTH_TOKEN, requestResult.getString(AccountManager.KEY_AUTHTOKEN));
result.putString(KEY_AUTH_TOKEN, response.auth);
result.putString(KEY_ERROR, "Unknown");
result.putParcelable(KEY_USER_RECOVERY_INTENT, requestResult.getParcelable(AccountManager.KEY_INTENT));
//result.putParcelable(KEY_USER_RECOVERY_INTENT, requestResult.getParcelable(AccountManager.KEY_INTENT));
return result;
} catch (Exception e) {
Log.w("AuthManagerService", e);
@ -86,16 +98,6 @@ public class AuthManagerServiceImpl extends IAuthManagerService.Stub {
}
}
private void checkPackage(String packageName, int callerUid, int callingUid) {
if (callerUid != 0 && callerUid != callingUid) {
throw new SecurityException("callerUid [" + callerUid + "] and real calling uid [" + callingUid + "] mismatch!");
}
String[] packagesForUid = context.getPackageManager().getPackagesForUid(callingUid);
if (packagesForUid != null && !Arrays.asList(packagesForUid).contains(packageName)) {
throw new SecurityException("callingUid [" + callingUid + "] is not related to packageName [" + packageName + "]");
}
}
@Override
public Bundle clearToken(String token, Bundle extras) throws RemoteException {
return null;

View File

@ -16,7 +16,15 @@
package org.microg.gms.auth;
import android.content.Context;
import org.microg.gms.common.Build;
import org.microg.gms.common.Constants;
import org.microg.gms.common.Utils;
import java.io.IOException;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
public class AuthRequest {
@ -33,20 +41,49 @@ public class AuthRequest {
public String countryCode;
public String operatorCountryCode;
public String locale;
public int gmsVersion;
public int gmsVersion = Constants.MAX_REFERENCE_VERSION;
public String accountType = "HOSTED_OR_GOOGLE";
public String email;
public String service;
public String source = "android";
public boolean isCalledFromAccountManager;
public String token;
public boolean isSystemPartition;
public boolean systemPartition;
public boolean getAccountId;
public boolean isAccessToken;
public String droidguardResults;
public boolean hasPermission;
public boolean addAccount;
public AuthRequest() {
}
@Deprecated
public AuthRequest(Context context, String token) {
this(Utils.getLocale(context), Utils.getBuild(context), Utils.getAndroidIdHex(context), token);
}
@Deprecated
public AuthRequest(Locale locale, Build build, String androidIdHex, String token) {
this(locale, build.sdk, Constants.MAX_REFERENCE_VERSION, build.device, build.id, androidIdHex, token);
}
@Deprecated
public AuthRequest(Locale locale, int sdkVersion, int gmsVersion, String deviceName,
String buildVersion, String androidIdHex, String token) {
this.androidIdHex = androidIdHex;
this.deviceName = deviceName;
this.buildVersion = buildVersion;
this.countryCode = locale.getCountry();
this.gmsVersion = gmsVersion;
this.operatorCountryCode = locale.getCountry();
this.locale = locale.toString();
this.sdkVersion = sdkVersion;
this.token = token;
}
public Map<String, String> getHttpHeaders() {
Map<String, String> map = new HashMap<>();
@ -65,7 +102,7 @@ public class AuthRequest {
map.put("sdk_version", Integer.toString(sdkVersion));
map.put("google_play_services_version", Integer.toString(gmsVersion));
map.put("accountType", accountType);
if (isSystemPartition) map.put("system_partition", "1");
if (systemPartition) map.put("system_partition", "1");
if (hasPermission) map.put("has_permission", "1");
if (addAccount) map.put("add_account", "1");
if (email != null) map.put("Email", email);
@ -88,4 +125,107 @@ public class AuthRequest {
if (droidguardResults != null) map.put("droidguard_results", droidguardResults);
return map;
}
public AuthRequest build(Build build) {
sdkVersion = build.sdk;
deviceName = build.device;
buildVersion = build.id;
return this;
}
public AuthRequest locale(Locale locale) {
this.locale = locale.toString();
this.countryCode = locale.getCountry();
this.operatorCountryCode = locale.getCountry();
return this;
}
public AuthRequest fromContext(Context context) {
build(Utils.getBuild(context));
locale(Utils.getLocale(context));
androidIdHex = Utils.getAndroidIdHex(context);
return this;
}
public AuthRequest email(String email) {
this.email = email;
return this;
}
public AuthRequest token(String token) {
this.token = token;
return this;
}
public AuthRequest service(String service) {
this.service = service;
return this;
}
public AuthRequest app(String app, String appSignature) {
this.app = app;
this.appSignature = appSignature;
return this;
}
public AuthRequest appIsGms() {
return app(Constants.GMS_PACKAGE_NAME, Constants.GMS_PACKAGE_SIGNATURE_SHA1);
}
public AuthRequest callerIsGms() {
return caller(Constants.GMS_PACKAGE_NAME, Constants.GMS_PACKAGE_SIGNATURE_SHA1);
}
public AuthRequest callerIsApp() {
return caller(app, appSignature);
}
public AuthRequest caller(String caller, String callerSignature) {
this.caller = caller;
this.callerSignature = callerSignature;
return this;
}
public AuthRequest calledFromAccountManager() {
isCalledFromAccountManager = true;
return this;
}
public AuthRequest addAccount() {
addAccount = true;
return this;
}
public AuthRequest systemPartition() {
systemPartition = true;
return this;
}
public AuthRequest hasPermission() {
hasPermission = true;
return this;
}
public AuthRequest getAccountId() {
getAccountId = true;
return this;
}
public AuthRequest isAccessToken() {
isAccessToken = true;
return this;
}
public AuthRequest droidguardResults(String droidguardResults) {
this.droidguardResults = droidguardResults;
return this;
}
public AuthResponse getResponse() throws IOException {
return AuthClient.request(this);
}
public void getResponseAsync(AuthClient.GmsAuthCallback callback) {
AuthClient.request(this, callback);
}
}

View File

@ -41,7 +41,7 @@ public class AuthResponse {
@ResponseField("Email")
public String email;
@ResponseField("services")
public Set<String> services = new HashSet<>();
public String services;
@ResponseField("GooglePlusUpgrade")
public boolean isGooglePlusUpgrade;
@ResponseField("PicasaUser")
@ -56,12 +56,18 @@ public class AuthResponse {
public String lastName;
@ResponseField("issueAdvice")
public String issueAdvice;
@ResponseField("accountId")
public String accountId;
@ResponseField("Expiry")
public long expiry = -1;
@ResponseField("storeConsentRemotely")
public boolean storeConsentRemotely;
public static AuthResponse parse(String result) {
AuthResponse response = new AuthResponse();
String[] entries = result.split("\n");
for (String s : entries) {
String[] keyValuePair = s.split("=");
String[] keyValuePair = s.split("=", 2);
String key = keyValuePair[0].trim();
String value = keyValuePair[1].trim();
try {
@ -72,11 +78,10 @@ public class AuthResponse {
field.set(response, value);
} else if (field.getType().equals(boolean.class)) {
field.setBoolean(response, value.equals("1"));
} else if (field.getType().equals(long.class)) {
field.setLong(response, Long.parseLong(value));
} else if (field.getType().equals(int.class)) {
field.setInt(response, Integer.parseInt(value));
} else if (field.getType().isAssignableFrom(Set.class)) {
//noinspection unchecked
((Set)field.get(response)).addAll(Arrays.asList(value.split(",")));
}
}
}

View File

@ -1,54 +0,0 @@
/*
* Copyright 2013-2015 µg 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.auth;
import android.content.Context;
import android.os.Build;
import org.microg.gms.common.Constants;
import org.microg.gms.common.Utils;
import java.util.Locale;
public class GmsAddAccountRequest extends AuthRequest {
public GmsAddAccountRequest(Context context, String token) {
this(Locale.getDefault(), Build.VERSION.SDK_INT, Constants.MAX_REFERENCE_VERSION,
Build.DEVICE, Build.ID, Utils.getAndroidIdHex(context), token);
}
public GmsAddAccountRequest(Locale locale, int sdkVersion, int gmsVersion, String deviceName,
String buildVersion, String androidIdHex, String token) {
this.service = "ac2dm";
this.addAccount = true;
this.isSystemPartition = true;
this.hasPermission = true;
this.getAccountId = true;
this.app = "com.google.android.gms";
this.appSignature = "38918a453d07199354f8b19af05ec6562ced5788";
this.androidIdHex = androidIdHex;
this.deviceName = deviceName;
this.buildVersion = buildVersion;
this.countryCode = locale.getCountry();
this.gmsVersion = gmsVersion;
this.operatorCountryCode = locale.getCountry();
this.locale = locale.toString();
this.sdkVersion = sdkVersion;
this.token = token;
}
}

View File

@ -0,0 +1,57 @@
/*
* Copyright 2013-2015 µg 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.auth.login;
import android.app.Activity;
import android.content.res.Configuration;
import android.os.Bundle;
import android.util.DisplayMetrics;
import com.google.android.gms.R;
public abstract class BaseActivity extends Activity {
private static final int TITLE_MIN_HEIGHT = 64;
private static final double TITLE_WIDTH_FACTOR = (8.0 / 18.0);
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.login_base);
formatTitle();
}
private void formatTitle() {
if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) {
double widthPixels = (double) (getResources().getDisplayMetrics().widthPixels);
findViewById(R.id.title_container).getLayoutParams().height =
(int) (dpToPx(TITLE_MIN_HEIGHT) + (TITLE_WIDTH_FACTOR * widthPixels));
} else {
findViewById(R.id.title_container).getLayoutParams().height = dpToPx(TITLE_MIN_HEIGHT);
}
}
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
formatTitle();
}
public int dpToPx(int dp) {
DisplayMetrics displayMetrics = getResources().getDisplayMetrics();
return Math.round(dp * (displayMetrics.xdpi / DisplayMetrics.DENSITY_DEFAULT));
}
}

View File

@ -18,14 +18,11 @@ package org.microg.gms.auth.login;
import android.accounts.Account;
import android.accounts.AccountManager;
import android.app.Activity;
import android.content.Context;
import android.content.res.Configuration;
import android.graphics.Color;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.KeyEvent;
import android.view.View;
@ -41,30 +38,30 @@ import android.widget.RelativeLayout;
import com.google.android.gms.R;
import org.microg.gms.auth.AuthClient;
import org.microg.gms.auth.AuthManager;
import org.microg.gms.auth.AuthRequest;
import org.microg.gms.auth.AuthResponse;
import org.microg.gms.auth.GmsAddAccountRequest;
import org.microg.gms.auth.RetrieveRtTokenRequest;
import org.microg.gms.common.Constants;
import org.microg.gms.common.Utils;
import java.util.Locale;
public class LoginActivity extends Activity {
public class LoginActivity extends BaseActivity {
public static final String TMPL_NEW_ACCOUNT = "new_account";
public static final String EXTRA_TMPL = "tmpl";
public static final String EXTRA_EMAIL = "email";
public static final String EXTRA_TOKEN = "masterToken";
private static final String TAG = "GmsAuthLoginBrowser";
private static final String EMBEDDED_SETUP_URL = "https://accounts.google.com/EmbeddedSetup";
private static final String MAGIC_USER_AGENT = " MinuteMaid";
private static final String COOKIE_OAUTH_TOKEN = "oauth_token";
private static final int TITLE_MIN_HEIGHT = 64;
public static final double TITLE_WIDTH_FACTOR = (8.0 / 18.0);
private WebView webView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.login_base);
formatTitle();
webView = createWebView(this);
webView.addJavascriptInterface(new JsBridge(), "mm");
((ViewGroup) findViewById(R.id.auth_root)).addView(webView);
@ -75,18 +72,29 @@ public class LoginActivity extends Activity {
closeWeb();
}
});
CookieManager.getInstance().setAcceptCookie(true);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
CookieManager.getInstance().removeAllCookies(new ValueCallback<Boolean>() {
@Override
public void onReceiveValue(Boolean value) {
load();
}
});
if (getIntent().hasExtra(EXTRA_TOKEN)) {
if (getIntent().hasExtra(EXTRA_EMAIL)) {
AccountManager accountManager = AccountManager.get(LoginActivity.this);
Account account = new Account(getIntent().getStringExtra(EXTRA_EMAIL), "com.google");
accountManager.addAccountExplicitly(account, getIntent().getStringExtra(EXTRA_TOKEN), null);
retrieveGmsToken(account);
} else {
retrieveRtToken(getIntent().getStringExtra(EXTRA_TOKEN));
}
} else {
//noinspection deprecation
CookieManager.getInstance().removeAllCookie();
load();
CookieManager.getInstance().setAcceptCookie(true);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
CookieManager.getInstance().removeAllCookies(new ValueCallback<Boolean>() {
@Override
public void onReceiveValue(Boolean value) {
load();
}
});
} else {
//noinspection deprecation
CookieManager.getInstance().removeAllCookie();
load();
}
}
}
@ -119,24 +127,9 @@ public class LoginActivity extends Activity {
settings.setJavaScriptCanOpenWindowsAutomatically(false);
}
private void formatTitle() {
if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) {
double widthPixels = (double) (getResources().getDisplayMetrics().widthPixels);
findViewById(R.id.title_container).getLayoutParams().height =
(int) (dpToPx(TITLE_MIN_HEIGHT) + (TITLE_WIDTH_FACTOR * widthPixels));
} else {
findViewById(R.id.title_container).getLayoutParams().height = dpToPx(TITLE_MIN_HEIGHT);
}
}
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
formatTitle();
}
private void load() {
webView.loadUrl(buildUrl(getIntent().hasExtra(EXTRA_TMPL) ? getIntent().getStringExtra(EXTRA_TMPL) : TMPL_NEW_ACCOUNT, Locale.getDefault()));
String tmpl = getIntent().hasExtra(EXTRA_TMPL) ? getIntent().getStringExtra(EXTRA_TMPL) : TMPL_NEW_ACCOUNT;
webView.loadUrl(buildUrl(tmpl, Utils.getLocale(this)));
}
private void closeWeb() {
@ -151,15 +144,23 @@ public class LoginActivity extends Activity {
for (String ar1 : temp) {
if (ar1.trim().startsWith(COOKIE_OAUTH_TOKEN + "=")) {
String[] temp1 = ar1.split("=");
sendRetrieveRtToken(temp1[1]);
retrieveRtToken(temp1[1]);
}
}
// TODO: Error message
}
private void sendRetrieveRtToken(String oAuthToken) {
AuthClient.request(new RetrieveRtTokenRequest(this, oAuthToken),
new AuthClient.GmsAuthCallback() {
private void retrieveRtToken(String oAuthToken) {
new AuthRequest().fromContext(this)
.appIsGms()
.service("ac2dm")
.token(oAuthToken).isAccessToken()
.addAccount()
.getAccountId()
.systemPartition()
.hasPermission()
.droidguardResults(null /*TODO*/)
.getResponseAsync(new AuthClient.GmsAuthCallback() {
@Override
public void onResponse(AuthResponse response) {
AccountManager accountManager = AccountManager.get(LoginActivity.this);
@ -167,9 +168,14 @@ public class LoginActivity extends Activity {
if (accountManager.addAccountExplicitly(account, response.token, null)) {
accountManager.setAuthToken(account, "SID", response.Sid);
accountManager.setAuthToken(account, "LSID", response.LSid);
accountManager.setUserData(account, "flags", "1");
accountManager.setUserData(account, "services", response.services);
accountManager.setUserData(account, "oauthAccessToken", "1");
accountManager.setUserData(account, "firstName", response.firstName);
accountManager.setUserData(account, "lastName", response.lastName);
retrieveGmsToken(account);
setResult(RESULT_OK);
// TODO: hand over to account setup
finish();
} else {
// TODO: Error message
Log.w(TAG, "Account NOT created!");
@ -184,14 +190,62 @@ public class LoginActivity extends Activity {
});
}
public int dpToPx(int dp) {
DisplayMetrics displayMetrics = getResources().getDisplayMetrics();
return Math.round(dp * (displayMetrics.xdpi / DisplayMetrics.DENSITY_DEFAULT));
private void retrieveGmsToken(final Account account) {
final String service = "ac2dm";
new AuthRequest().fromContext(this)
.appIsGms()
.service(service)
.email(account.name)
.token(AccountManager.get(this).getPassword(account))
.systemPartition()
.hasPermission()
.addAccount()
.getAccountId()
.getResponseAsync(new AuthClient.GmsAuthCallback() {
@Override
public void onResponse(AuthResponse response) {
AuthManager.storeResponse(LoginActivity.this, account,
Constants.GMS_PACKAGE_NAME, Constants.GMS_PACKAGE_SIGNATURE_SHA1,
service, response);
retrieveGmsKeyUserinfoProfile(account);
}
@Override
public void onException(Exception exception) {
Log.w(TAG, "onException: " + exception);
}
});
}
private void retrieveGmsKeyUserinfoProfile(final Account account) {
final String service = "oauth2:https://www.googleapis.com/auth/userinfo.profile";
new AuthRequest().fromContext(this)
.appIsGms().callerIsGms()
.service(service)
.email(account.name)
.token(AccountManager.get(this).getPassword(account))
.systemPartition()
.hasPermission()
.getAccountId()
.getResponseAsync(new AuthClient.GmsAuthCallback() {
@Override
public void onResponse(AuthResponse response) {
AuthManager.storeResponse(LoginActivity.this, account,
Constants.GMS_PACKAGE_NAME, Constants.GMS_PACKAGE_SIGNATURE_SHA1,
service, response);
finish();
}
@Override
public void onException(Exception exception) {
Log.w(TAG, "onException: " + exception);
}
});
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if ((keyCode == KeyEvent.KEYCODE_BACK) && webView.canGoBack()) {
if ((keyCode == KeyEvent.KEYCODE_BACK) && webView.canGoBack() && webView.getVisibility() == View.VISIBLE) {
webView.goBack();
return true;
}

View File

@ -19,39 +19,69 @@ package org.microg.gms.auth.loginservice;
import android.accounts.AbstractAccountAuthenticator;
import android.accounts.Account;
import android.accounts.AccountAuthenticatorResponse;
import android.accounts.AccountManager;
import android.accounts.NetworkErrorException;
import android.app.Service;
import android.content.Intent;
import android.os.Bundle;
import android.os.IBinder;
import android.util.Log;
import com.google.android.gms.R;
import org.microg.gms.auth.AuthClient;
import org.microg.gms.auth.AuthManager;
import org.microg.gms.auth.AuthRequest;
import org.microg.gms.auth.AuthResponse;
import org.microg.gms.auth.login.LoginActivity;
import org.microg.gms.common.Utils;
import java.io.IOException;
import java.util.Arrays;
public class GoogleLoginService extends Service {
private static final String TAG = "GmsLoginService";
private String accountType;
@Override
public void onCreate() {
super.onCreate();
accountType = getString(R.string.google_account_type);
}
@Override
public IBinder onBind(Intent intent) {
if (intent.getAction().equals(android.accounts.AccountManager.ACTION_AUTHENTICATOR_INTENT)) {
return new AbstractAccountAuthenticator(this) {
@Override
public Bundle editProperties(AccountAuthenticatorResponse response, String accountType) {
Log.d(TAG, "editProperties: " + accountType);
return null;
}
@Override
public Bundle addAccount(AccountAuthenticatorResponse response, String accountType, String authTokenType, String[] requiredFeatures, Bundle options) throws NetworkErrorException {
if (accountType.equals(GoogleLoginService.this.accountType)) {
return GoogleLoginService.this.addAccount(response, authTokenType, requiredFeatures, options);
}
return null;
}
@Override
public Bundle confirmCredentials(AccountAuthenticatorResponse response, Account account, Bundle options) throws NetworkErrorException {
Log.d(TAG, "confirmCredentials: " + account + ", " + options);
return null;
}
@Override
public Bundle getAuthToken(AccountAuthenticatorResponse response, Account account, String authTokenType, Bundle options) throws NetworkErrorException {
return null;
return GoogleLoginService.this.getAuthToken(response, account, authTokenType, options);
}
@Override
public String getAuthTokenLabel(String authTokenType) {
Log.d(TAG, "getAuthTokenLabel: " + authTokenType);
return null;
}
@ -62,10 +92,55 @@ public class GoogleLoginService extends Service {
@Override
public Bundle hasFeatures(AccountAuthenticatorResponse response, Account account, String[] features) throws NetworkErrorException {
return null;
Log.d(TAG, "hasFeatures: " + account + ", " + Arrays.toString(features));
Bundle b = new Bundle();
b.putBoolean(AccountManager.KEY_BOOLEAN_RESULT, true);
return b;
}
}.getIBinder();
}
return null;
}
private Bundle getAuthToken(AccountAuthenticatorResponse response, Account account, String authTokenType, Bundle options) {
options.keySet();
Log.d(TAG, "getAuthToken: " + account + ", " + authTokenType + ", " + options);
String app = options.getString(AccountManager.KEY_ANDROID_PACKAGE_NAME);
Utils.checkPackage(this, app, options.getInt(AccountManager.KEY_CALLER_UID), options.getInt(AccountManager.KEY_CALLER_UID));
String appSignature = Utils.getFirstPackageSignatureDigest(this, app);
try {
AuthRequest request = new AuthRequest().fromContext(this)
.email(account.name)
.token(AccountManager.get(this).getPassword(account))
.service(authTokenType)
.app(app, appSignature)
.callerIsGms()
.calledFromAccountManager();
if (AuthManager.isPermitted(this, account, app, appSignature, authTokenType)) {
request.hasPermission();
}
AuthResponse r = request.getResponse();
AuthManager.storeResponse(this, account, app, appSignature, authTokenType, r);
if (r.expiry == 0)
Log.d(TAG, "Auth: " + r.auth);
Bundle bundle = new Bundle();
bundle.putString(AccountManager.KEY_ACCOUNT_TYPE, account.type);
bundle.putString(AccountManager.KEY_ACCOUNT_NAME, account.name);
bundle.putString(AccountManager.KEY_AUTHTOKEN, r.auth);
return bundle;
} catch (IOException e) {
Log.w(TAG, e);
}
return null;
}
private Bundle addAccount(AccountAuthenticatorResponse response, String authTokenType, String[] requiredFeatures, Bundle options) {
final Intent i = new Intent(GoogleLoginService.this, LoginActivity.class);
i.putExtras(options);
i.putExtra(LoginActivity.EXTRA_TMPL, LoginActivity.TMPL_NEW_ACCOUNT);
i.putExtra(AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE, response);
final Bundle result = new Bundle();
result.putParcelable(AccountManager.KEY_INTENT, i);
return result;
}
}

View File

@ -14,18 +14,10 @@
* limitations under the License.
*/
package org.microg.gms.auth;
package org.microg.gms.common;
import android.content.Context;
/**
* This request will retrieve a rt token (oauth2rt) from an access token (oauth2)
*/
public class RetrieveRtTokenRequest extends GmsAddAccountRequest {
public RetrieveRtTokenRequest(Context context, String token) {
super(context, token);
this.isAccessToken = true;
this.droidguardResults = null; // TODO
}
public class Build {
public int sdk = android.os.Build.VERSION.SDK_INT;
public String id = android.os.Build.ID;
public String device = android.os.Build.DEVICE;
}

View File

@ -17,10 +17,78 @@
package org.microg.gms.common;
import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.Signature;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
import java.util.Locale;
public class Utils {
public static String getAndroidIdHex(Context context) {
return null;
}
public static Locale getLocale(Context context) {
return Locale.getDefault(); // TODO
}
public static Build getBuild(Context context) {
return new Build();
}
public static void checkPackage(Context context, String packageName,int callingUid) {
String[] packagesForUid = context.getPackageManager().getPackagesForUid(callingUid);
if (packagesForUid != null && !Arrays.asList(packagesForUid).contains(packageName)) {
throw new SecurityException("callingUid [" + callingUid + "] is not related to packageName [" + packageName + "]");
}
}
public static void checkPackage(Context context, String packageName, int callerUid, int callingUid) {
if (callerUid != 0 && callerUid != callingUid) {
throw new SecurityException("callerUid [" + callerUid + "] and real calling uid [" + callingUid + "] mismatch!");
}
checkPackage(context, packageName, callingUid);
}
public static String getFirstPackageSignatureDigest(Context context, String packageName) {
PackageManager packageManager = context.getPackageManager();
final PackageInfo info;
try {
info = packageManager.getPackageInfo(packageName, PackageManager.GET_SIGNATURES);
} catch (PackageManager.NameNotFoundException e) {
throw new RuntimeException(e);
}
if (info != null && info.signatures != null && info.signatures.length > 0) {
for (Signature sig : info.signatures) {
String digest = sha1sum(sig.toByteArray());
if (digest != null) {
return digest;
}
}
}
return null;
}
public static String sha1sum(byte[] bytes) {
MessageDigest md;
try {
md = MessageDigest.getInstance("SHA1");
} catch (final NoSuchAlgorithmException e) {
return null;
}
if (md != null) {
bytes = md.digest(bytes);
if (bytes != null) {
StringBuilder sb = new StringBuilder(2 * bytes.length);
for (byte b : bytes) {
sb.append(String.format("%02x", b));
}
return sb.toString();
}
}
return null;
}
}

View File

@ -0,0 +1,56 @@
/*
* Copyright 2013-2015 µg 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.feeds;
import android.content.ContentProvider;
import android.content.ContentValues;
import android.database.Cursor;
import android.database.MatrixCursor;
import android.net.Uri;
public class SubscribedFeedsProvider extends ContentProvider{
@Override
public boolean onCreate() {
return false;
}
@Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
return new MatrixCursor(new String[0]);
}
@Override
public String getType(Uri uri) {
return null;
}
@Override
public Uri insert(Uri uri, ContentValues values) {
return null;
}
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
return 0;
}
@Override
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
return 0;
}
}