Realm incorrect thread error from SyncAdapter - java

So I have created a very standard sync adapter (using this fantastic tutorial) and during onPerformSync I run some realm transactions in a method called syncDatastore within my DataManager class. The issue is when the sync adapter tries to perform the sync, I get
java.lang.IllegalStateException: Realm access from incorrect thread.
Realm objects can only be accessed on the thread they were created.
Here is an excerpt from my SyncAdapter:
#Override
public void onPerformSync(Account account, Bundle extras, String authority, ContentProviderClient provider, SyncResult syncResult) {
try {
// Get the auth token for the current account
String authToken = _accountManager.blockingGetAuthToken(account, AccountGeneral.AUTHTOKEN_TYPE_FULL_ACCESS, true);
// run network and database operations
dataManager.syncDatastore();
} catch (Exception e) {
e.printStackTrace();
}
}
I initialize the RealmConfiguration in my Application class with:
RealmConfiguration realmConfiguration = new RealmConfiguration.Builder(this).build();
Realm.setDefaultConfiguration(realmConfiguration);
And an example of how it is used in my DataManager:
private Realm realm = Realm.getDefaultInstance();
public void syncDatastore() {
postResources();
pushDataToServer();
getDataFromServer();
}
private void postResources() {
ArrayList<Client> clients = new ArrayList<>();
clients.addAll(realm.where(Client.class).equalTo("isSynced", false).equalTo("apiId", "0").findAll());
Log.e("clients count", String.valueOf(clients.size()));
for (Client c : clients) {
createClientResource(c);
}
}
Please note I have tried to remove android:process=":sync" from my service declaration in the manifest as outlined here but to no avail. I am also quite new to both SyncAdapters and Realm, so any help would be most appreciated.

Realm instances are thread confined, so you need a new Realm instance on your background thread (the background thread on which the sync is happening).
#Override
public void onPerformSync(Account account, Bundle extras, String authority, ContentProviderClient provider, SyncResult syncResult) {
Realm realm = null;
try {
realm = Realm.getDefaultInstance();
// Get the auth token for the current account
String authToken = _accountManager.blockingGetAuthToken(account, AccountGeneral.AUTHTOKEN_TYPE_FULL_ACCESS, true);
// run network and database operations
dataManager.syncDatastore(realm);
} catch (Exception e) {
e.printStackTrace();
} finally {
if(realm != null) {
realm.close();
}
}
}
public void syncDatastore(Realm realm) {
postResources(realm);
pushDataToServer();
getDataFromServer();
}
private void postResources(Realm realm) {
RealmResults<Client> clients = realm.where(Client.class).equalTo("isSynced", false).equalTo("apiId", "0").findAll();
Log.e("clients count", String.valueOf(clients.size()));
for (Client c : clients) {
createClientResource(c);
}
}

Related

SQL Permission with Java Authentication and Authorization Service

I have a SQL table user with 2 roles: ADMIN and USER.
I'm trying to use Java Authentication and Authorization Service(jaas) to make user with ADMIN role can read and modify other tables while USER role can read only.
I create a custom class extent java.security.Permission and a custom class extend
Policy class but I can't find any way to check permission with SecurityManager
PolicyImpl policy = new PolicyImpl();
Policy.setPolicy(policy);
Subject subject = loginContext.getSubject();
try {
Subject.doAsPrivileged(subject, (PrivilegedAction) () -> {
SecurityManager sm = System.getSecurityManager();
sm.checkSecurityAccess();
if (sm != null) {
sm.checkPermission(); // I don't know how to make this function check for
// SQL permission based on custom policy
}
return null;
}, null);
My Policy class
public class PolicyImpl extends Policy {
#Override
public PermissionCollection getPermissions(ProtectionDomain domain) {
List<Principal> principals = Arrays.asList(domain.getPrincipals());
if (principals.isEmpty()) {
return null;
}
BasicPermission basicPermission = null;
try {
for (Principal principal : principals) {
basicPermission = (BasicPermission) AccessController
.doPrivileged((PrivilegedExceptionAction) () -> Controller.getRoles(principal.getName()));
}
} catch (Exception ex) {
ex.printStackTrace();
return null;
}
return basicPermission.newPermissionCollection();
}
}
It seems that java.security.FilePermission is the most suitable one since this permission has read and write property but I don't know how can I use this class for SQL Server.

Unable to connect to the Samsung Health using Health Data Store

I am trying to connect my application with Samsung health, but i am unable to connect to the HealthDataStore.
I have enabled sHealth in developer mode.
I have added permission for the meta data.
HealthDataStore mStore = new HealthDataStore(DashboardListActivity.this,mConnectionListener);
mStore.connectService();
private final HealthDataStore.ConnectionListener mConnectionListener = new HealthDataStore.ConnectionListener() {
#Override
public void onConnected() {
Log.d(TAG, "Health data service is connected.");
/*HealthPermissionManager pmsManager = new HealthPermissionManager(mStore);
Map<HealthPermissionManager.PermissionKey, Boolean> resultMap = pmsManager.isPermissionAcquired(mKeySet);
if (resultMap.containsValue(Boolean.FALSE)) {
// Request the permission for reading step counts if it is not acquired
pmsManager.requestPermissions(mKeySet, mActivity).setResultListener(mPermissionListener);
} else {
// Get the current step count and display it
// ...
HealthDataResolver.AggregateRequest request = (HealthDataResolver.AggregateRequest) new HealthDataResolver.AggregateRequest.Builder()
.setDataType(HealthConstants.Exercise.HEALTH_DATA_TYPE);
}*/
}
#Override
public void onConnectionFailed(HealthConnectionErrorResult healthConnectionErrorResult) {
Log.d(TAG, "Health data service is not available.");
//showConnectionFailureDialog(healthConnectionErrorResult);
}
#Override
public void onDisconnected() {
Log.d(TAG, "Health data service is not Disconnected.");
}
};
I am always getting the onConnectionfailed, its not hitting the connection success.

Send message with Telegram API and store session (NOT with bot API)

I want to implement a very simple Java Telegram Client, which is capable of sending and receiving messages and store the sessions across multiple starts. I already managed to authenticate and receive messages
api = new TelegramApi(apiState, new AppInfo(API_ID, "console", "1", "1", "en"), new ApiCallback() {
#Override
public void onAuthCancelled(TelegramApi api) {
Log.d(TAG, "-----------------CANCELLED----------------");
Log.d(TAG, api.getApiContext().toString());
}
#Override
public void onUpdatesInvalidated(TelegramApi api) {
Log.d(TAG, "-----------------INVALIDATED----------------");
Log.d(TAG, api.getApiContext().toString());
}
#Override
public void onUpdate(TLAbsUpdates tlAbsUpdates) {
Log.d(TAG, "-----------------UPDATE----------------");
Log.d(TAG, tlAbsUpdates.toString());
if (tlAbsUpdates instanceof TLUpdateShortMessage) {
Log.d(TAG, "-----------------UPDATE CHAT MESSAGE----------------");
int senderId = ((TLUpdateShortMessage) tlAbsUpdates).getUserId();
Log.d(TAG, "Message from " + senderId);
String message = ((TLUpdateShortMessage) tlAbsUpdates).getMessage();
Log.d(TAG, message);
activity.appendMessage(TAG, message);
}
}
});
api.switchToDc(2);
TLConfig config = null;
try {
config = api.doRpcCallNonAuth(new TLRequestHelpGetConfig());
} catch (TimeoutException | IOException e) {
e.printStackTrace();
}
apiState.updateSettings(config);
However, I struggle to send messages to another user. For the beginning, it would be enough if I could send a message back to the user, who sent me a message before (by retrieving the senderId, as you can see in the onUpdate method before). However, if someone could also help me with retrieving the ids of my saved contacts, it would be perfect.
Furthermore, I want to store the sessions accross multiple startups, since I get a FLOOD_WAIT error (420), if I test my code to often.
For this I used https://github.com/rubenlagus/TelegramApi/blob/51713e9b6eb9e0ae0d4bbbe3d4deffff9b7f01e4/src/main/java/org/telegram/bot/kernel/engine/MemoryApiState.java and its used classes (e.g. TLPersistence), which stores and loads the ApiState. However, apparently it does not store the signin status, since I always have to authenticate my number every time I update the code.
By the way, I am using Api layer 66 (https://github.com/rubenlagus/TelegramApi/releases).
UPDATE 1:
Problems with sending messages solved myself:
private void sendMessageToUser(int userId, String message) {
TLInputPeerUser peer = new TLInputPeerUser();
peer.setUserId(userId);
TLRequestMessagesSendMessage messageRequest = new TLRequestMessagesSendMessage();
messageRequest.setFlags(0);
messageRequest.setPeer(peer);
messageRequest.setRandomId(new SecureRandom().nextLong());
messageRequest.setMessage(message);
api.doRpcCallNonAuth(messageRequest, 1500, new RpcCallback<TLAbsUpdates>() {
#Override
public void onResult(TLAbsUpdates tlAbsUpdates) {
Log.d(TAG, "-----------------------MESSAGE SENT-----------------------");
}
#Override
public void onError(int i, String s) {
Log.d(TAG, "-----------------------MESSAGE SENT ERROR-----------------------");
Log.d(TAG, String.valueOf(i));
if(s != null) {
Log.d(TAG, s);
}
}
});
}
However, now I am stuck at finding the userIds of my contacts.
After first update this is left:
Saving the session state (and signin state)
Find userIds of contacts
Update 2:
I managed to fetch the users, with which there are already dialogs. This is enough for my use case, however, loading all contacts would be perfect. This is how to load users from existing dialogs:
private int getUserId(String phone) throws InterruptedException {
TLRequestMessagesGetDialogs dialogs = new TLRequestMessagesGetDialogs();
dialogs.setOffsetId(0);
dialogs.setLimit(20);
dialogs.setOffsetPeer(new TLInputPeerUser());
CountDownLatch latch = new CountDownLatch(1);
api.doRpcCallNonAuth(dialogs, 1500, new RpcCallback<TLAbsDialogs>() {
#Override
public void onResult(TLAbsDialogs tlAbsDialogs) {
Log.d(TAG, "----------------------getUsers--------------------");
for(TLAbsUser absUser : ((TLDialogs) tlAbsDialogs).getUsers()) {
users.add((TLUser) absUser);
}
latch.countDown();
}
#Override
public void onError(int i, String s) {
Log.d(TAG, "----------------------getUsers ERROR--------------------");
latch.countDown();
}
});
latch.await();
for(TLUser user : users) {
if(user.getPhone().equals(phone)) {
return user.getId();
}
}
return 0;
}
After second update this is left:
Saving the session state (and signin state)
Get user ids from contacts instead of dialogs

Create a global Realm file for a collaborative android app

My app is a collaborative app where users should be able to write/read into a global Realm file to share data between them. I would also like to include some initial data (realmObjects) in the Realm file which will be common to all users.
User will sign up to, then, be able to create data and share the data with all the users. The initial data, created only once, should be available to all users.
I am not able to assign user permissions (write/read) to each new user once they sign-up/login in the app. The common data is created multiple time since the query (realm.where(realmObject.class).findall() returns no results, even though it has been created.
public static final String AUTH_URL = "http://" + BuildConfig.OBJECT_SERVER_IP + ":9080/auth";
public static final String REALM_URL = "realm://" + BuildConfig.OBJECT_SERVER_IP + ":9080/default";
UserManager class
public class UserManager {
// Supported authentication mode
public enum AUTH_MODE {
PASSWORD,
FACEBOOK,
GOOGLE
}
private static AUTH_MODE mode = AUTH_MODE.PASSWORD; // default
public static void setAuthMode(AUTH_MODE m) {
mode = m;
}
public static void logoutActiveUser() {
switch (mode) {
case PASSWORD: {
break;
}
case FACEBOOK: {
LoginManager.getInstance().logOut();
break;
}
case GOOGLE: {
break;
}
}
SyncUser.currentUser().logout();
}
// Configure Realm for the current active user
public static void setActiveUser(SyncUser user) {
Realm.removeDefaultConfiguration();
SyncConfiguration defaultConfig = new SyncConfiguration.Builder(user, REALM_URL).name("Realm").schemaVersion(0).build();
Realm.setDefaultConfiguration(defaultConfig);
setDefaultPermissionsRealm(user);
}
private static void setDefaultPermissionsRealm(SyncUser user){
if (user.getIdentity().toString().equals(PRIVILAGE)){
Realm realm = user.getManagementRealm();
realm.executeTransaction(new Realm.Transaction() {
#Override
public void execute(Realm realm) {
Boolean mayRead = true; // Grant read access
Boolean mayWrite = true; // Keep current permission
Boolean mayManage = false; // Revoke management access
PermissionChange change = new PermissionChange(REALM_URL,
"*",
mayRead,
mayWrite,
mayManage);
realm.insert(change);
}
});
}
}
}
SplashActivity
SyncUser.loginAsync(SyncCredentials.usernamePassword(eMail, password, true), AUTH_URL, new SyncUser.Callback() {
#Override
public void onSuccess(SyncUser syncUser) {
loginRegistrationSuccess = true;
registrationComplete(syncUser);
}
#Override
public void onError(ObjectServerError error) {
loginRegistrationSuccess = false;
}
});
facebookAuthRegistration = new FacebookAuth((LoginButton) findViewById(R.id.sign_up_facebook), this) {
#Override
public void onRegistrationComplete(final LoginResult loginResult, User userInfo) {
UserManager.setAuthMode(UserManager.AUTH_MODE.FACEBOOK);
SyncCredentials credentials = SyncCredentials.facebook(loginResult.getAccessToken().getToken());
SyncUser.loginAsync(credentials, AUTH_URL, SplashScreenActivity.this);
}
};
googleAuthRegistration = new GoogleAuth((Button) findViewById(R.id.sign_up_google), this, googleAuthLogin.getmGoogleApiClient()) {
#Override
public void onRegistrationComplete(GoogleSignInResult result) {
UserManager.setAuthMode(UserManager.AUTH_MODE.GOOGLE);
GoogleSignInAccount acct = result.getSignInAccount();
SyncCredentials credentials = SyncCredentials.google(acct.getIdToken());
SyncUser.loginAsync(credentials, AUTH_URL, SplashScreenActivity.this);
}
};
#Override
public void onSuccess(SyncUser syncUser) {
loginRegistrationSuccess = true;
showProgress(false);
registrationComplete(syncUser);
}
private void registrationComplete(SyncUser syncUser) {
UserManager.setActiveUser(syncUser);
Intent mainIntent = new Intent(SplashScreenActivity.this, MainActivity.class);
mainIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
SplashScreenActivity.this.startActivity(mainIntent);
SplashScreenActivity.this.finish();
}
It looks like if I am creating a different realm for each user even though I am not including in the URL the "~".
Could anyone tell me what I am doing wrong?
Thanks
Initially, you will need to create the Realm using the admin user, and set the permissions for all other users can access it. If you are using the Pro edition, you could create the Realm using a small node.js script. Alternatively, you could create a small internal app with the single purpose of creating the Realm using the admin user.
Users are only allowed to create Realm within their home directory (~ so to speak) but they can share it with other users. Doing so, you will have to distribute the user id of that first user.

Is right way to use thread for getting data from server and updating list view

i m new android and find the best approach to update the data on UI with thread-safety for getting data from server.
here is my code to get data from server api and update list view,
but i was think this approach may cause to degrade the performance of application.
i m using okhttp for calling server api.
public String ActionAsyncGetTeamData(final String URL, final String token,final String expected_closure_date, final HTTPVerb verb, Activity activity) {
final String[] ResponseData = new String[1];
loading_complete=false;
_context = activity;
if (networkDetector.isNetworkAvailable()) {
new Thread(new Runnable() {
public void run() {
ResponseData[0] = GET(URL, token, expected_closure_date);
leadList.clear();
meetingList.clear();
taskList.clear();
TeamSyncModel teamSyncModel=json.fromJson(ResponseData[0],TeamSyncModel.class);
for (LeadModel leadModel: teamSyncModel.lead) {
leadList.add(leadModel);
}
for (MeetingModel meetingModel: teamSyncModel.meeting) {
meetingList.add(meetingModel);
}
for (TaskModel taskModel: teamSyncModel.task) {
taskList.add(taskModel);
}
loading_complete=true;
_context.runOnUiThread(new Runnable() {
#Override
public void run() {
Planner planner=new Planner();
planner.progress.setVisibility(View.GONE);
planner.list.setVisibility(View.VISIBLE);
planner.adapterTeam(_context);
planner.closure_header.requestFocus();
}
});
}
}).start();
return ResponseData[0];
}
else {
Planner planner=new Planner();
planner.progress.setVisibility(View.GONE);
planner.list.setVisibility(View.VISIBLE);
planner.adapterTeam(_context);
leadList.clear();
meetingList.clear();
taskList.clear();
loading_complete=true;
Toast.makeText(_context.getApplicationContext(), "Internet connection not available", Toast.LENGTH_SHORT).show();
}
return null;
}
Get data from Server
private String GET(String url, String token, String lastSync) {
client.setConnectTimeout(15, TimeUnit.MINUTES);
client.setReadTimeout(15, TimeUnit.MINUTES);
try {
Request request = new Request.Builder()
.url(url + lastSync)
.addHeader("x-access-token", token)
.build();
Response response = client.newCall(request).execute();
return response.body().string();
} catch(Exception ex) {
ex.printStackTrace();
}
return null;
}
is it right way to do this tasks in android
you try the following steps
1 in UI create refer to listview, adapter and asyntask
2 from the ui send reference to adapter at the asyntask in this getData from server and in the method onPostExecute of the asynk call this method adapter.notifyDatasetChange();
3 in the adapter you manage the info what you give of the server in the asyntask

Categories