Keep Streaming service running over MediaBrowserService when Doze Mode - java

I am working on a Android Streaming App using MediaBrowserServiceCompat based on this great article https://medium.com/androiddevelopers/mediabrowserservicecompat-and-the-modern-media-playback-app-7959a5196d90. My app works pretty good except when phone goes to Doze Mode. I have been looking over internet and see some possible solutions to keep my service alive. One of those is running the UI and the Music Service in different Process. However since I am using the MediaBrowserService I don't know how to start the service from UI using AIDL.
This is how I connected the service with the Main Activity in the same process:
MainActivity.java
private class MediaBrowserConnection extends MediaBrowserHelper {
private MediaBrowserConnection(Context context) {
super(context, MusicService.class);
}
#Override
protected void onConnected(#NonNull MediaControllerCompat mediaController) {
mMediaController = mediaController;
}
#Override
protected void onChildrenLoaded(#NonNull String parentId,
#NonNull List<MediaBrowserCompat.MediaItem> children) {
super.onChildrenLoaded(parentId, children);
}
}
MediaBrowserHelper.java
public MediaBrowserHelper(Context context,
Class<? extends MediaBrowserServiceCompat> serviceClass) {
mContext = context;
mMediaBrowserServiceClass = serviceClass;
mMediaBrowserConnectionCallback = new MediaBrowserConnectionCallback();
mMediaControllerCallback = new MediaControllerCallback();
mMediaBrowserSubscriptionCallback = new MediaBrowserSubscriptionCallback();
}
public void onStart() {
if (mMediaBrowser == null) {
mMediaBrowser =
new MediaBrowserCompat(
mContext,
new ComponentName(mContext, mMediaBrowserServiceClass),
mMediaBrowserConnectionCallback,
null);
mMediaBrowser.connect();
}
Log.d(TAG, "onStart: Creating MediaBrowser, and connecting");
}
public void onStop() {
if (mMediaController != null) {
mMediaController.unregisterCallback(mMediaControllerCallback);
mMediaController = null;
}
if (mMediaBrowser != null && mMediaBrowser.isConnected()) {
mMediaBrowser.disconnect();
mMediaBrowser = null;
}
resetState();
Log.d(TAG, "onStop: Releasing MediaController, Disconnecting from MediaBrowser");
}
Anyone knows how to do this or is there any other reliable solution to keep streaming alive?
Thanks

Related

Android ContentResolver not reliable

I am currently developing an Application to consistently running a Service which gets all received Messages and pushes them to Google Sheets. My Service includes an Async Task which is created once a Message is received. Here is the AsyncTask which is inside my Service class. The SMSListener is a Broadcast Receiver class.
SmsListener
public class SmsListener extends BroadcastReceiver {
private boolean RECEIVED = false;
#Override
public void onReceive(Context context, Intent intent) {
if (Telephony.Sms.Intents.SMS_RECEIVED_ACTION.equals(intent.getAction())) {
setRECEIVED(true);
Log.d("DEBUGSMSLISTENER", "SMS is received.");
}
}
public boolean isRECEIVED() {
return RECEIVED;
}
public void setRECEIVED(boolean RECEIVED) {
this.RECEIVED = RECEIVED;
}
}
AsyncTask
static class AsyncPushTask extends AsyncTask<Void, Integer, Uri> {
//Fields
private AsyncPushTask(SmsListener listener, Context context, ContactManager manager, Context application) {
smsListener = listener;
contentResolver = context.getContentResolver();
contactManager = manager;
applicationContext = application;
}
#Override
protected Uri doInBackground(Void... voids) {
Log.d(TAG, "ASYNC BACKGROUND started");
Log.d(TAG, "is received " + smsListener.isRECEIVED());
//if (isPermitted() & smsListener.isRECEIVED()) {
//if (true) {
Log.d(TAG, "is permitted and received SMS TASK");
Cursor cursor = contentResolver.query(Uri.parse("content://sms/inbox"), null, null, null, null);
if (cursor.moveToFirst()) { // must check the result to prevent exception
//Do the work to push SMS to Google Sheets
The Code works as expected but sometimes, I cannot recreate the behavior, the First SMS in the inbox isnt recognized correctly, so that the pushed message equals the one that was pushed in the last cycle.
TY for the help.

Disable VPN upon application exit

So I'm working for a company that requires a VPN in order to connect to the database server. I'm facing an issue with disconnecting programmatically from the VPN service (or disabling it) upon onPause / onStop.
In order to ensure the user is indeed connected to a VPN, I'm using a network listener and if the user is not connected, a dialog is being shown and navigates the user to the VPN Settings Configuration. Once the user connects and resumes the application, the listener recognizes the VPN IP and everything runs great.
My issue is that I want to disable the VPN connection once the user has stopped using the application. Therefore, I've been trying to search for a solution that disables the VPN connection without requesting the user to go to the VPN Settings again. Is there an option to toggle the VPN off programmatically without navigating to the VPN Settings page?
Network Service:
public class NetworkSchedulerService extends JobService implements
ConnectivityReceiver.ConnectivityReceiverListener {
private ConnectivityReceiver mConnectivityReceiver;
#Override
public void onCreate() {
super.onCreate();
mConnectivityReceiver = new ConnectivityReceiver(this);
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
return START_NOT_STICKY;
}
#Override
public boolean onStartJob(JobParameters params) {
registerReceiver(mConnectivityReceiver, new IntentFilter(Constants.CONNECTIVITY_ACTION));
return true;
}
#Override
public boolean onStopJob(JobParameters params) {
unregisterReceiver(mConnectivityReceiver);
return true;
}
#Override
public void onNetworkConnectionChanged(boolean isConnected) {
String message = isConnected ? "מחובר לרשת" : "אין חיבור פעיל לרשת";
Toast.makeText(getApplicationContext(), message, Toast.LENGTH_SHORT).show();
}
}
Listener:
private void scheduleJob() {
JobInfo myJob = new JobInfo.Builder(0, new ComponentName(this, NetworkSchedulerService.class))
.setRequiresCharging(true)
.setMinimumLatency(1000)
.setOverrideDeadline(2000)
.setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY)
.setPersisted(true)
.build();
JobScheduler jobScheduler = (JobScheduler) getSystemService(Context.JOB_SCHEDULER_SERVICE);
jobScheduler.schedule(myJob);
}
#Override
protected void onStop() {
stopService(new Intent(this, NetworkSchedulerService.class));
super.onStop();
}
#Override
protected void onStart() {
super.onStart();
Intent startServiceIntent = new Intent(this, NetworkSchedulerService.class);
startService(startServiceIntent); // INTERNET LISTENER
}
Dialog:
public void dialogVPN() {
builder = new AlertDialog.Builder(LoadingSplash.this);
builder.setMessage("Please ensure VPN Connection");
builder.setPositiveButton("OK", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
Intent intent = new Intent("android.net.vpn.SETTINGS");
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivityForResult(intent, 10);
}
});
builder.show();
}
And the returnConnType:
public String returnConnType() {
ConnectivityManager connectivityManager = (ConnectivityManager) getSystemService(this.CONNECTIVITY_SERVICE);
String result = "None";
if (connectivityManager != null) {
Network network = connectivityManager.getActiveNetwork();
NetworkCapabilities capabilities = connectivityManager.getNetworkCapabilities(network);
if (capabilities == null) {
result = "None";
}
if (capabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)) {
result = "WIFI";
} else if (capabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)) {
result = "MOBILE";
} else if (capabilities.hasTransport(NetworkCapabilities.TRANSPORT_VPN)) {
result = "VPN";
}
}
return result;
}
Any suggestions as to how to solve this? A proper solution or an alternative one would be appreciated.
I don't think you can disable the VPN settings from inside the app using an API provided by Android, as I do not know about any such APIs. However, as a workaround, you can consider doing the following.
While exiting the application (on a back button press), you can use the same listener to pop up another dialog saying the user to turn off the VPN. Hence, turning off the VPN will follow the same tasks that the user had to do while turning on the VPN.
When a user exits the application using a home button press, you might consider using a JobScheduler in your onDestroy function of the exiting activity, so that you can check if the VPN connection is alive in a background service when the application is not running and create a notification which will tell the user that, the VPN is alive. Then on clicking the notification, redirect the user to the VPN configuration settings and guide the user to turn it off.
Hope that helps!
So thanks to #Reaz I've managed to find the proper solution for now.
Tested on Oreo
In case someone else is intrested..
app
implementation 'android.arch.lifecycle:extensions:1.1.1'
AppLifecycleObserver
public class AppLifecycleObserver extends MultiDexApplication implements LifecycleObserver {
public static final String TAG = AppLifecycleObserver.class.getName();
Notifications notif = new Notifications();
Context mContext;
public AppLifecycleObserver(Context context) {
mContext = context;
}
#OnLifecycleEvent(Lifecycle.Event.ON_START)
public void onEnterForeground() {
Log.v(TAG,"FOREGROUND");
}
#OnLifecycleEvent(Lifecycle.Event.ON_STOP)
public void onEnterBackground() {
Log.v(TAG,"BACKGROUND");
notif.createVPNNotification(mContext,"VPN","Please make sure to turn VPN off");
}
}
on Any activity you wish to implement # onCreate:
AppLifecycleObserver appLifecycleObserver = new AppLifecycleObserver(this);
ProcessLifecycleOwner.get().getLifecycle().addObserver(appLifecycleObserver);

How can i keep the job service running when the app is closed from recent task list by user

I am using Job Scheduler API in my app to schedule a job for me after specific time interval. It runs fine when the app is running. But whenever the user closes the app or clears it from the recent task list the app stops and the scheduled job never executes afterwards until you open the app and it is rescheduled again from the time it is opened.
Now i want someone to help me to keep the jobs on executing even if the app is closed or cleared from the recent task list.
If there is any alternative solution please tell me.
i am looking for the solution from the past 3 days. Tried everything said by developers on StackOverFlow and other sites and none of them worked for me.
This is where is schedule the job!
ComponentName componentName = new
ComponentName(getActivity().getBaseContext(),WallpaperJobService.class);
JobInfo jobInfo = new JobInfo.Builder(777,componentName)
.setRequiresCharging(sharedPreferences.getBoolean("Charging",false))
.setRequiredNetworkType(sharedPreferences.getBoolean("Wifi",false) ?
JobInfo.NETWORK_TYPE_UNMETERED : JobInfo.NETWORK_TYPE_ANY)
.setPeriodic(sharedPreferences.getInt("Duration",15) * 60 *
1000)
.setPersisted(true)
.build();
JobScheduler scheduler = (JobScheduler)
getContext().getSystemService(Context.JOB_SCHEDULER_SERVICE);
scheduler.schedule(jobInfo);
My Job Service Class:
public class WallpaperJobService extends JobService {
private boolean jobCancelled;
private SharedPreferences sharedPreferences;
private SharedPreferences.Editor editor;
#Override
public boolean onStartJob(JobParameters params) {
Log.i("WallpaperJobService", "Job started!");
changeWallpaper(params);
return true;
}
private void changeWallpaper(final JobParameters params) {
final ArrayList<Image> images = (ArrayList<Image>)
MainActivity.favoritesRoomDatabase.roomDao().getAllFavoriteWallpapers();
sharedPreferences = getSharedPreferences("GridSize", MODE_PRIVATE);
editor = sharedPreferences.edit();
if (images != null && images.size() != 0) {
if (sharedPreferences.getInt("Index", 0) == images.size()) {
editor.putInt("Index", 0);
editor.commit();
}
Picasso.get().load(Constants.domain +
images.get(sharedPreferences.getInt("Index", 0)).getImage_url()).into(new
Target() {
#Override
public void onBitmapLoaded(final Bitmap bitmap,
Picasso.LoadedFrom from) {
new Thread(new Runnable() {
#Override
public void run() {
if (jobCancelled) {
Log.i("WallpaperJobService","Returned");
return;
}
try {
//Doing some work here
} catch (IOException e) {
e.printStackTrace();
}
Log.i("WallpaperJobService", "Job finished!");
jobFinished(params, false);
}
}).start();
}
#Override
public void onBitmapFailed(Exception e, Drawable errorDrawable)
{
Log.i("WallpaperJobService", "Bitmap load failed " +
e.getMessage());
}
#Override
public void onPrepareLoad(Drawable placeHolderDrawable) {
}
});
} else {
Log.i("WallpaperJobService", "Favorite database is null!");
}
}
#Override
public boolean onStopJob(JobParameters params) {
Log.i("WallpaperJobService", "Job cancelled before completion!");
jobCancelled = true;
return true;
}
}
When doing stuff periodically in the background — JobScheduler, WorkManager, AlarmManager, FCM push messages, etc. — you have to take into account that your process might not be around when it is time for you to do your work. Android will fork a process for you, but it is "starting from scratch". Anything that your UI might have set up in memory, such as a database, would have been for some prior process and might not be set up in the new process.

Android Service only runs whilst debugging

Background
I am creating a service that syncs a local Realm database (stored on phone) with an online database. The database stores users and measurements.
I initialise the service by calling 'startService(new Intent(this, SyncService.class));' during the splash activity's onCreate() method, and specify in the manifest that the service should run for the length of the application.
The service has a broadcast receiver. When the receiver detects a network change from 'not connected' to 'connected', it fires a method called syncDatabases().
This method finds all measurements recorded locally after the timestamp of the last API callback, and sends them to the database. The API responds to a request by returning the object + a unique ID.
When a measurement is made whilst the device is offline, it is stored locally. When an internet connection is made, the syncDatabases() method should be called in order to update the online server with the local measurements.
My steps...
Steps when debugging the project:
With wifi I open the app and with an external device make a new measurement. This appears on both the app and in the database. I then turn wifi off and make another measurement - this appears on the device.
I attach the debugger.
I turn back on wifi and this triggers the services' receivers' onReceive() method. I step through this and it all works according to plan. I reach the syncDatabases() method, and from there I receive the callback from the API, and it then updates the Realm database with the new ID value.
The problem...
If I don't attach the debugger, nothing happens. The new measurements aren't pushed to the database, and none of my Log.e calls are printed.
Why is this happening? And is there an alternative solution / fix for this problem?
Code
Service class
public class SyncService extends Service {
private static final String TAG = "SYNCSERVICE";
private boolean mConnected = false;
private BroadcastReceiver mReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
if (intent.getExtras() != null) {
final ConnectivityManager connectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
final NetworkInfo netInfo = connectivityManager.getActiveNetworkInfo();
if (netInfo != null) {
switch (netInfo.getState()) {
case CONNECTED:
if (!mConnected) {
Log.e("NETWORK", "Network " + netInfo.getTypeName() + " now connected");
syncDatabases();
mConnected = true;
}
break;
default:
mConnected = false;
break;
}
} else mConnected = false;
}
}
};
#Override
public void onCreate() {
super.onCreate();
initReceiver();
ConnectivityManager connectivityManager = (ConnectivityManager) getApplicationContext().getSystemService(Context.CONNECTIVITY_SERVICE);
if (connectivityManager.getActiveNetworkInfo() != null) {
mConnected = true;
}
android.util.Log.e(TAG, "onCreate: SyncService created");
}
#Override
public void onDestroy() {
super.onDestroy();
unInitReceiver();
android.util.Log.e(TAG, "onDestroy: SyncService destroyed");
}
#Nullable
#Override
public IBinder onBind(Intent intent) {
android.util.Log.e(TAG, "onBind: SyncService bound");
return null;
}
#Override
public boolean onUnbind(Intent intent) {
android.util.Log.e(TAG, "onUnbind: SyncService unbound");
return super.onUnbind(intent);
}
#Override
public void onRebind(Intent intent) {
super.onRebind(intent);
android.util.Log.e(TAG, "onRebind: SyncService rebound");
}
private void initReceiver() {
IntentFilter filters = new IntentFilter();
filters.addAction("android.net.wifi.WIFI_STATE_CHANGED");
filters.addAction("android.net.wifi.STATE_CHANGE");
registerReceiver(mReceiver, filters);
}
private void unInitReceiver() {
unregisterReceiver(mReceiver);
}
public void syncDatabases() {
RealmResults<UserDB> users = RealmDB.getInstance(getApplicationContext()).where(UserDB.class).findAll();
if (users.size() > 0) {
int userId = users.get(0).getmUserID();
Log.e("MESSAGE", PreferenceUtils.getInstance().getLastSyncDate());
Date lastSync = null;
SimpleDateFormat sdf = new SimpleDateFormat("EEE MMM dd HH:mm:ss zzz yyyy", Locale.getDefault());
try {
lastSync = sdf.parse(PreferenceUtils.getInstance().getLastSyncDate());
}
catch (ParseException e) {
e.printStackTrace();
try {
lastSync = BaseFragment.FORMAT.parse(PreferenceUtils.getInstance().getLastSyncDate());
}
catch (ParseException e1) {
e1.printStackTrace();
}
}
if (lastSync != null) {
Date lastSyncOffset = new Date(lastSync.getTime() + 1000);
/** Get all local results which have been made after the last sync date
 **/
RealmResults<MeasurementDB> newLocalMeasurements = RealmDB.getInstance(getApplicationContext())
.where(MeasurementDB.class).equalTo("user_ID", userId)
.greaterThan("dateCreated", lastSyncOffset)
.findAll();
/** For each measurement made after the last sync, add it to the server
 **/
for (MeasurementDB measurement : newLocalMeasurements) {
TemperatureListener mListener = new TemperatureListener(measurement);
ApiRequest.getInstance(getApplicationContext()).registerNewMeasurement(measurement.getAverage(),
measurement.getDateCreated().toString(), mListener, mListener);
}
}
}
}
/**
* Temperature listener receives the local copy of the temperature item. onResponse can then
* directly mutate the object instead of searching local db
*/
private class TemperatureListener implements Response.Listener<Measurement>, Response.ErrorListener {
private MeasurementDB measurement;
public TemperatureListener(MeasurementDB measurement) {
this.measurement = measurement;
}
#Override
public void onErrorResponse(VolleyError error) {
Log.e("OnResponse", "Failure");
}
#Override
public void onResponse(Measurement response) {
Log.e("OnResponse", "Success");
/** Update our local measurement's ID value (supplied by server)
 **/
RealmDB.getInstance(getApplicationContext()).beginTransaction();
measurement.setMeasurement_ID(response.getmMeasurementId());
RealmDB.getInstance(getApplicationContext()).commitTransaction();
/** Update the last sync date
 **/
PreferenceUtils.getInstance().setLastSyncDate(response.getmDateCreated());
}
}
}
Initialisation of Service in splash activity
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_splash);
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
mTimedOut = true;
finishActivity();
}
}, DURATION);
/** Will sync application / cloud databases in background of app when network connected. **/
startService(new Intent(this, SyncService.class));
doApiWork();
}
Manifest entry
Stop with task to kill the service at the same time as the app.
Exported 'false' stops other apps from using the service.
<service
android:name=".network.SyncService"
android:stopWithTask="true"
android:enabled="true"
android:exported="false"/>
EDIT
I removed the service and left a receiver class, registered in the manifest, which triggers methods on another class when needed. However the receiver is only triggered in debug mode.

Bluetooth Client-Server Example in Android

I am making a simple client-server application which can be used to transfer integers, strings or characters.
My application compiled successfully, but no client-server connection is there.
When I try to search for a server, it gives the NullPointerException on the line
bluetooth.startDiscovery();
However, when I start the server it gives no exception.
Also, when I start a server on one device and look for a server on another, a connection is not established either due to a problem in server running correctly or a problem in the client.
I have already got an instance of BluetoothAdapter using
bluetooth = BluetoothAdapter.getDefaultAdapter();
I think that probably a null pointer is given because i am calling the method startDiscovery() inside an onclickListener, this one,
private void setupSearchButton() {
Button searchButton = (Button) findViewById(R.id.button_search);
searchButton.setOnClickListener(new OnClickListener() {
public void onClick(View view) {
registerReceiver(discoveryResult, new IntentFilter(BluetoothDevice.ACTION_FOUND));
if (!bluetooth.isDiscovering()) {
try { foundDevices.clear();
bluetooth.startDiscovery(); } catch(Exception e)
{
Context c = getApplicationContext();
Toast.makeText(c, e.toString(),Toast.LENGTH_LONG).show();
}
}
}
});
}
The code for declaring a server is posted here :
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == DISCOVERY_REQUEST) {
boolean isDiscoverable = resultCode > 0;
if (isDiscoverable) {
String name = "bluetoothserver";
try {
final BluetoothServerSocket btserver = bluetooth.listenUsingRfcommWithServiceRecord(name, uuid);
AsyncTask<Integer, Void, BluetoothSocket> acceptThread = new AsyncTask<Integer, Void, BluetoothSocket>() {
#Override
protected BluetoothSocket doInBackground(Integer... params) {
try {
socket = btserver.accept(params[0] * 1000);
return socket;
} catch (Exception e) {
Log.d("BLUETOOTH SERVER EXCEPTION : ", e.getMessage());
}
return null;
}
#Override
protected void onPostExecute(BluetoothSocket result) {
if (result != null) {
switchUI();
}
}
};
acceptThread.execute(resultCode);
} catch (IOException e) {
Log.d("BLUETOOTH", e.getMessage());
}
}
}
}
Can someone tell me what is the problem ? I am using two buttons, one to start a bluetooth server and other to search for a bluetooth server. The method that runs on clicking the start bluetooth server button triggers startActivityForResult, as a result of which the method onActivityResult is executed as shown here. The OnClickListener for search server button is already shown here.

Categories

Resources