Skip to content

Commit 3da964f

Browse files
authored
Merge pull request #2335 from brandonpage/screen-lock-timeout
Add Screen Lock Timeout
2 parents b3f4c22 + 186a8b9 commit 3da964f

File tree

7 files changed

+165
-44
lines changed

7 files changed

+165
-44
lines changed

libs/SalesforceSDK/src/com/salesforce/androidsdk/app/SalesforceSDKUpgradeManager.java

+63-3
Original file line numberDiff line numberDiff line change
@@ -28,15 +28,24 @@
2828

2929
import static com.salesforce.androidsdk.security.ScreenLockManager.MOBILE_POLICY_PREF;
3030
import static com.salesforce.androidsdk.security.ScreenLockManager.SCREEN_LOCK;
31+
import static com.salesforce.androidsdk.security.ScreenLockManager.SCREEN_LOCK_TIMEOUT;
3132

3233
import android.content.Context;
3334
import android.content.SharedPreferences;
3435

3536
import com.salesforce.androidsdk.accounts.UserAccount;
3637
import com.salesforce.androidsdk.accounts.UserAccountManager;
38+
import com.salesforce.androidsdk.analytics.SalesforceAnalyticsManager;
39+
import com.salesforce.androidsdk.auth.HttpAccess;
40+
import com.salesforce.androidsdk.auth.OAuth2;
3741
import com.salesforce.androidsdk.util.SalesforceSDKLogger;
3842

43+
import java.io.IOException;
44+
import java.net.URI;
45+
import java.net.URISyntaxException;
46+
import java.util.HashMap;
3947
import java.util.List;
48+
import java.util.concurrent.Executors;
4049

4150
/**
4251
* This class handles upgrades from one version to another.
@@ -94,6 +103,10 @@ protected synchronized void upgradeAccMgr() {
94103
if (installedVerDouble < 9.2) {
95104
upgradeTo9Dot2();
96105
}
106+
// Already incorporated into 9.2 upgrade.
107+
if (installedVerDouble > 9.2 && installedVerDouble <= 10.1) {
108+
upgradeTo10Dot1Dot1();
109+
}
97110
} catch (Exception e) {
98111
SalesforceSDKLogger.e(TAG, "Failed to parse installed version.");
99112
}
@@ -146,8 +159,10 @@ private void upgradeTo9Dot2() {
146159
if (globalPrefs.contains(KEY_TIMEOUT) && globalPrefs.contains(KEY_PASSCODE_LENGTH)) {
147160
SharedPreferences.Editor globalEditor = globalPrefs.edit();
148161
// Check that Passcode was enabled
149-
if (globalPrefs.getInt(KEY_TIMEOUT, 0) != 0) {
162+
int timeout = globalPrefs.getInt(KEY_TIMEOUT, 0);
163+
if (timeout != 0) {
150164
globalEditor.putBoolean(SCREEN_LOCK, true);
165+
globalEditor.putInt(SCREEN_LOCK_TIMEOUT, timeout);
151166
}
152167

153168
globalEditor.remove(KEY_PASSCODE);
@@ -170,11 +185,12 @@ private void upgradeTo9Dot2() {
170185
+ account.getOrgLevelFilenameSuffix(), Context.MODE_PRIVATE);
171186
if (orgPrefs.contains(KEY_TIMEOUT) && orgPrefs.contains(KEY_PASSCODE_LENGTH)) {
172187
// Check that Passcode was enabled
173-
if (orgPrefs.getInt(KEY_TIMEOUT, 0) != 0) {
188+
int userTimeout = orgPrefs.getInt(KEY_TIMEOUT, 0);
189+
if (userTimeout != 0) {
174190
// Set screen lock key at user level
175191
final SharedPreferences userPrefs = ctx.getSharedPreferences(MOBILE_POLICY_PREF
176192
+ account.getUserLevelFilenameSuffix(), Context.MODE_PRIVATE);
177-
userPrefs.edit().putBoolean(SCREEN_LOCK, true).apply();
193+
userPrefs.edit().putBoolean(SCREEN_LOCK, true).putInt(SCREEN_LOCK_TIMEOUT, userTimeout).apply();
178194
}
179195

180196
// Delete passcode keys at org level
@@ -193,4 +209,48 @@ private void upgradeTo9Dot2() {
193209
}
194210
}
195211
}
212+
213+
// TODO: Remove upgrade step in Mobile SDK 12.0
214+
private void upgradeTo10Dot1Dot1() {
215+
final Context ctx = SalesforceSDKManager.getInstance().getAppContext();
216+
final SharedPreferences globalPrefs = ctx.getSharedPreferences(MOBILE_POLICY_PREF, Context.MODE_PRIVATE);
217+
if (globalPrefs.contains(SCREEN_LOCK)) {
218+
final UserAccountManager manager = SalesforceSDKManager.getInstance().getUserAccountManager();
219+
final List<UserAccount> accounts = manager.getAuthenticatedUsers();
220+
221+
if (accounts != null) {
222+
Executors.newSingleThreadExecutor().execute(() -> {
223+
int lowestTimeout = Integer.MAX_VALUE;
224+
225+
// Get and set connected app mobile policy timeout per user.
226+
for (UserAccount account : accounts) {
227+
try {
228+
final OAuth2.IdServiceResponse response = OAuth2.callIdentityService(HttpAccess.DEFAULT,
229+
account.getIdUrl(), account.getAuthToken());
230+
231+
if (response.mobilePolicy && response.screenLockTimeout != -1) {
232+
final SharedPreferences userPrefs = ctx.getSharedPreferences(MOBILE_POLICY_PREF
233+
+ account.getUserLevelFilenameSuffix(), Context.MODE_PRIVATE);
234+
int timeoutInMills = response.screenLockTimeout * 1000 * 60;
235+
userPrefs.edit().putInt(SCREEN_LOCK_TIMEOUT, timeoutInMills).apply();
236+
237+
if (lowestTimeout == Integer.MAX_VALUE || timeoutInMills < lowestTimeout) {
238+
lowestTimeout = timeoutInMills;
239+
}
240+
}
241+
} catch (IOException e) {
242+
SalesforceSDKLogger.e(TAG, "Exception throw retrieving mobile policy", e);
243+
}
244+
}
245+
246+
// Set timeout or remove block.
247+
if (lowestTimeout < Integer.MAX_VALUE && lowestTimeout > 0) {
248+
globalPrefs.edit().putInt(SCREEN_LOCK_TIMEOUT, lowestTimeout).apply();
249+
} else {
250+
globalPrefs.edit().remove(SCREEN_LOCK).apply();
251+
}
252+
});
253+
}
254+
}
255+
}
196256
}

libs/SalesforceSDK/src/com/salesforce/androidsdk/auth/OAuth2.java

+7-2
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ public class OAuth2 {
9090
private static final String INSTANCE_URL = "instance_url";
9191
private static final String JSON = "json";
9292
private static final String MOBILE_POLICY = "mobile_policy";
93+
private static final String SCREEN_LOCK_TIMEOUT = "screen_lock";
9394
private static final String REFRESH_TOKEN = "refresh_token";
9495
private static final String HYBRID_REFRESH = "hybrid_refresh";
9596
private static final String RESPONSE_TYPE = "response_type";
@@ -527,7 +528,7 @@ public static class IdServiceResponse {
527528
public String pictureUrl;
528529
public String thumbnailUrl;
529530
public boolean mobilePolicy;
530-
public int pinLength = -1;
531+
@Deprecated public int pinLength = -1;
531532
public int screenLockTimeout = -1;
532533
public boolean biometricUnlockAllowed = true;
533534
public JSONObject customAttributes;
@@ -553,7 +554,11 @@ public IdServiceResponse(Response response) {
553554
}
554555
customAttributes = parsedResponse.optJSONObject(CUSTOM_ATTRIBUTES);
555556
customPermissions = parsedResponse.optJSONObject(CUSTOM_PERMISSIONS);
556-
mobilePolicy = parsedResponse.has(MOBILE_POLICY);
557+
if (parsedResponse.has(MOBILE_POLICY)) {
558+
JSONObject mobilePolicyObject = parsedResponse.getJSONObject(MOBILE_POLICY);
559+
mobilePolicy = mobilePolicyObject.has(SCREEN_LOCK_TIMEOUT);
560+
screenLockTimeout = mobilePolicyObject.getInt(SCREEN_LOCK_TIMEOUT);
561+
}
557562
} catch (Exception e) {
558563
SalesforceSDKLogger.w(TAG, "Could not parse identity response", e);
559564
}

libs/SalesforceSDK/src/com/salesforce/androidsdk/security/ScreenLockManager.java

+49-19
Original file line numberDiff line numberDiff line change
@@ -48,28 +48,46 @@ public class ScreenLockManager {
4848

4949
public static final String MOBILE_POLICY_PREF = "mobile_policy";
5050
public static final String SCREEN_LOCK = "screen_lock";
51+
public static final String SCREEN_LOCK_TIMEOUT = "screen_lock_timeout";
5152

52-
private boolean backgroundedSinceUnlock = true;
53+
private long lastLockedTimestamp = 0;
5354

5455
/**
5556
* Stores the mobile policy for the org upon user login.
5657
*
5758
* @param account the newly add account
5859
* @param screenLockRequired if the account requires screen lock or not
60+
*
61+
* @deprecated Timeout is now required. This method will be removed in 11.0.
62+
*/
63+
@Deprecated
64+
public void storeMobilePolicy(UserAccount account, boolean screenLockRequired) { }
65+
66+
/**
67+
* Stores the mobile policy for the org upon user login.
68+
*
69+
* @param account the newly add account
70+
* @param screenLockRequired if the account requires screen lock or not
71+
* @param timeout timeout in milliseconds
5972
*/
60-
public void storeMobilePolicy(UserAccount account, boolean screenLockRequired) {
73+
public void storeMobilePolicy(UserAccount account, boolean screenLockRequired, int timeout) {
6174
Context ctx = SalesforceSDKManager.getInstance().getAppContext();
6275
SharedPreferences accountSharedPrefs = ctx.getSharedPreferences(MOBILE_POLICY_PREF
6376
+ account.getUserLevelFilenameSuffix(), Context.MODE_PRIVATE);
64-
accountSharedPrefs.edit().putBoolean(SCREEN_LOCK, screenLockRequired).apply();
77+
accountSharedPrefs.edit().putBoolean(SCREEN_LOCK, screenLockRequired).putInt(SCREEN_LOCK_TIMEOUT, timeout).apply();
6578

6679
SharedPreferences globalPrefs = ctx.getSharedPreferences(MOBILE_POLICY_PREF, Context.MODE_PRIVATE);
6780
if (screenLockRequired) {
68-
globalPrefs.edit().putBoolean(SCREEN_LOCK, true).apply();
81+
int currentTimeout = globalPrefs.getInt(SCREEN_LOCK_TIMEOUT, 0);
82+
SharedPreferences.Editor globalPrefsEditor = globalPrefs.edit();
83+
84+
globalPrefsEditor.putBoolean(SCREEN_LOCK, true);
85+
if (currentTimeout == 0 || timeout < currentTimeout) {
86+
globalPrefsEditor.putInt(SCREEN_LOCK_TIMEOUT, timeout);
87+
}
88+
globalPrefsEditor.apply();
6989

70-
// This is needed to block access to the app immediately on login
71-
backgroundedSinceUnlock = true;
72-
onAppForegrounded();
90+
lock();
7391
}
7492
}
7593

@@ -86,7 +104,7 @@ public void onAppForegrounded() {
86104
* To be called by the protected activity is paused to denote that the app should lock.
87105
*/
88106
public void onAppBackgrounded() {
89-
backgroundedSinceUnlock = true;
107+
lastLockedTimestamp = System.currentTimeMillis();
90108
}
91109

92110
/**
@@ -95,7 +113,7 @@ public void onAppBackgrounded() {
95113
public void reset() {
96114
Context ctx = SalesforceSDKManager.getInstance().getAppContext();
97115
SharedPreferences globalPrefs = ctx.getSharedPreferences(MOBILE_POLICY_PREF, Context.MODE_PRIVATE);
98-
globalPrefs.edit().remove(SCREEN_LOCK).apply();
116+
globalPrefs.edit().remove(SCREEN_LOCK).remove(SCREEN_LOCK_TIMEOUT).apply();
99117
}
100118

101119
/**
@@ -110,23 +128,34 @@ public void cleanUp(UserAccount account) {
110128
if (account != null) {
111129
final SharedPreferences accountPrefs = ctx.getSharedPreferences(MOBILE_POLICY_PREF
112130
+ account.getUserLevelFilenameSuffix(), Context.MODE_PRIVATE);
113-
accountPrefs.edit().remove(SCREEN_LOCK).apply();
131+
accountPrefs.edit().remove(SCREEN_LOCK).remove(SCREEN_LOCK_TIMEOUT).apply();
114132
}
115133

116134
// Determine if any other users still need Screen Lock.
117135
final List<UserAccount> accounts = SalesforceSDKManager.getInstance()
118136
.getUserAccountManager().getAuthenticatedUsers();
137+
int lowestTimeout = Integer.MAX_VALUE;
138+
119139
if (accounts != null) {
120140
accounts.remove(account);
121141
for (final UserAccount mAccount : accounts) {
122142
if (mAccount != null) {
123143
final SharedPreferences accountPrefs = ctx.getSharedPreferences(MOBILE_POLICY_PREF
124144
+ mAccount.getUserLevelFilenameSuffix(), Context.MODE_PRIVATE);
125145
if (accountPrefs.getBoolean(SCREEN_LOCK, false)) {
126-
return;
146+
int timeout = accountPrefs.getInt(SCREEN_LOCK_TIMEOUT, Integer.MAX_VALUE);
147+
if (timeout < lowestTimeout) {
148+
lowestTimeout = timeout;
149+
}
127150
}
128151
}
129152
}
153+
154+
if (lowestTimeout < Integer.MAX_VALUE) {
155+
SharedPreferences globalPrefs = ctx.getSharedPreferences(MOBILE_POLICY_PREF, Context.MODE_PRIVATE);
156+
globalPrefs.edit().putInt(SCREEN_LOCK_TIMEOUT, lowestTimeout).apply();
157+
return;
158+
}
130159
}
131160

132161
// If we have returned, no other accounts require Screen Lock.
@@ -135,20 +164,21 @@ public void cleanUp(UserAccount account) {
135164

136165
/**
137166
* Unlocks the app.
167+
*
168+
* @deprecated This method will be removed in 11.0.
138169
*/
139-
public void unlock() {
140-
backgroundedSinceUnlock = false;
141-
}
170+
@Deprecated
171+
public void unlock() { }
142172

143173
@VisibleForTesting
144174
protected boolean shouldLock() {
145-
return backgroundedSinceUnlock && readMobilePolicy();
146-
}
147-
148-
private boolean readMobilePolicy() {
175+
long elapsedTime = System.currentTimeMillis() - lastLockedTimestamp;
149176
Context ctx = SalesforceSDKManager.getInstance().getAppContext();
150177
SharedPreferences sharedPrefs = ctx.getSharedPreferences(MOBILE_POLICY_PREF, Context.MODE_PRIVATE);
151-
return sharedPrefs.getBoolean(SCREEN_LOCK, false);
178+
boolean hasLock = sharedPrefs.getBoolean(SCREEN_LOCK, false);
179+
int timeout = sharedPrefs.getInt(SCREEN_LOCK_TIMEOUT, 0);
180+
181+
return hasLock && (elapsedTime > timeout);
152182
}
153183

154184
private void lock() {

libs/SalesforceSDK/src/com/salesforce/androidsdk/ui/OAuthWebviewHelper.java

+6-2
Original file line numberDiff line numberDiff line change
@@ -621,8 +621,12 @@ protected void onPostExecute(OAuth2.TokenEndpointResponse tr) {
621621
// Save the user account
622622
addAccount(account);
623623

624-
final ScreenLockManager screenLockManager = mgr.getScreenLockManager();
625-
screenLockManager.storeMobilePolicy(account, id.mobilePolicy);
624+
// Screen lock required by mobile policy.
625+
if (id.screenLockTimeout > 0) {
626+
int timeoutInMills = id.screenLockTimeout * 1000 * 60;
627+
final ScreenLockManager screenLockManager = mgr.getScreenLockManager();
628+
screenLockManager.storeMobilePolicy(account, id.mobilePolicy, timeoutInMills);
629+
}
626630

627631
// All done
628632
callback.finish(account);

libs/SalesforceSDK/src/com/salesforce/androidsdk/ui/ScreenLockActivity.java

-1
Original file line numberDiff line numberDiff line change
@@ -214,7 +214,6 @@ private void onAuthError(CharSequence errString) {
214214
private void finishSuccess() {
215215
resetUI();
216216
sendAccessibilityEvent(getString(R.string.sf__screen_lock_auth_success));
217-
SalesforceSDKManager.getInstance().getScreenLockManager().unlock();
218217
finish();
219218
}
220219

libs/test/SalesforceSDKTest/src/com/salesforce/androidsdk/auth/OAuth2Test.java

-1
Original file line numberDiff line numberDiff line change
@@ -286,7 +286,6 @@ public void testCallIdentityService() throws IOException, OAuthFailedException,
286286
IdServiceResponse id = OAuth2.callIdentityService(httpAccess, TestCredentials.INSTANCE_URL +
287287
"/id/" + TestCredentials.ORG_ID + "/" + TestCredentials.USER_ID, refreshResponse.authToken);
288288
Assert.assertEquals("Wrong username returned", TestCredentials.USERNAME, id.username);
289-
Assert.assertEquals("Wrong pinLength returned", -1, id.pinLength);
290289
Assert.assertEquals("Wrong screenLockTimeout returned", -1, id.screenLockTimeout);
291290
Assert.assertTrue("Wrong biometricUnlockAllowed returned", id.biometricUnlockAllowed);
292291
}

0 commit comments

Comments
 (0)