Which NSXPCConnection related objects do I have to retain myself? - cocoa

I cannot find any hint in the docs regarding object lifecycle management.
In the XPC service, do I have to keep a strong reference to the NSXPCListener, or does the resume call take care of this effectively?
I'm using Swift and a connection creation object to get most of the stuff out of the main.swift file:
// main.swift
if let dependencies = Dependencies().setUp() {
// Actually run the service code (and never return)
NSRunLoop.currentRunLoop().run()
}
I have the hunch that the dependencies object (which creates the NSXPCListener during set up) should keep a strong reference to the listener object. But the resume method is said to work like operation queues.
Conversely, does the client need to keep the NSXPCConnection around?
In the XPC service, upon incoming connection, does setting exportedObject retain that object for the duration of the connection, or do I have to keep a strong ref to it myself?
Consequently: When multiple connections come in, should I maintain a list of exportedObjects?
In either the client of service, should I obtain a remoteObjectProxy once and keep it around, or should I obtain a proxy object anew for every call?
My partcular XPC service is a launchd process running all the time, not a one-off thing, and the client app itself might run for a few hours in the background, too. I worry whether it's safe to keep a proxy object to the background service for a potentially long-running communication.
If background services crash, launchd is said to restart them. Now if my service was a "launch on demand" service instead, will message calls to proxy objects issue a relaunch if necessary, will obtaining a proxy object do, or will only reconnecting achieve that?
Thanks for helping me sort this out!

Related

How to authenticate NSConnection requests?

(Let's ignore the fact that NSConnection is now deprecated.)
I have a tool that accepts connections to NSConnection over a service port. I have an application that launches the tool and then connects to it. That part works.
Now, I like to make sure that only my particular app can talk to the tool and that the tool rejects connections from any other tool/app.
How do I best accomplish this?
One idea I had:
Since the app starts the tool, it could pass a "secret" to the tool as an argument, and then I pass the same secret to the tool whenever I invoke one of its functions as an NSDistributedObject. However, that would mean I have to pass that extra argument to every call I make, and I think that's unnecessary overhead.
I would think that I could accept or reject the connection right when the app opens the connection to the tool, i.e. only once. There is the NSConnectionDelegate, and I suspect that I'd have to implement the authentication check in its authenticateComponents:withData: handler, but I cannot find any examples that would explain how to do that. I mean, is there any data in that connection attempt that would identify the app that's requesting the connection, such as its PID, for instance?
Do you establish a connection for every call? I wouldn't think so but, if not, why do you think you'd have to pass the secret for every call? It's pretty common for the server to have a check-in method that clients have to call. You could validate the secret in that check-in method.
A malicious client might try to just skip the check-in method. You can use the -connection:handleRequest: method of NSConnectionDelegate to force them to call the check-in method. Keep a flag for every connection indicating if you've seen the check-in method. If you have, that method can just return NO. If you haven't, then examine the NSDistantObjectRequest's invocation's selector. If it's the check-in method, set your flag and return NO. If it's not, then terminate the connection.
I know the underlying ports (Mach or socket) have mechanisms for authenticating peers, but I don't see a way to get at that with the abstractions of NSConnection laid over them.
Finally, you are apparently wedded to NSConnection but this is exactly the sort of thing that the NSXPCConnection API is for. Among other things, it will ensure that the service is only visible to the parent app.

What is the correct way to self-terminate a MacOS XPC service?

I've successfully followed the Daemons and Service Guide - Creating XPC Services tutorial, along with the SandboxedFetch sample code and have a working Client / Service setup that is using the new NSXPCConnection class.
What still isn't entirely clear to me is how to properly self-terminate the service application once it has finished its job. I recognize that in many cases, the service is expected to remain alive, but in the use-case I have in mind, the service will be used to only to do some processing that I'd otherwise not perform in the main application. Once that processing is finished, there's no reason for the service application to remain. If the client needs another service at a later date, it can just re-create a new one.
Since the service is a lightweight, non-nibbed, NSApplication, I was trying to self-terminate it by calling invalidate from inside applicationWillTerminate, but that triggers an EXC_BAD_ACCESS exception almost every time. Calling invalidate on the service's [NSXPCListener serviceListener] generates slightly less reliable crashes, but it still crashes.
Calling invalidate from within the client application on its NSXPCConnection also generates an EXC_BAD_ACCESS exception almost every time.
So I'm curious what the correct sequence of steps is to cleanly shutdown the XPC service and then quit the service application. Ideally, the service would self-terminate after it has made its last XPC call to the client.
Attached is a small screenshot of what one of the exception's stacktrace looks like. (Yes, that's a webview that's loading in the service. Once the webview has finished loading, I want the service to self-terminate)
My first reaction is that you should not bother to terminate. When memory pressure occurs and your service is idle, launchd will kill your service. Exiting probably isn't in anyone's best interest because your service will take time to launch again. Don't terminate and you won't have to figure out why your attempt crashes.
But if for some reason you are determined to terminate, don't try so hard. Just do whatever you need to do to clean up (flush buffers, close network connections gracefully so the server doesn't suffer, whatever) and call exit. Although you seem to be using NSApplication, your service is not an application in any sense that the user cares about and there is no compelling reason to act like one in this respect. The host application needs to be able to cope with your service crashing anyway, so your deliberately exiting unceremoniously is just fine.
By the way, using NSApplication in an XPC service probably isn't the best idea because there's no supported way to declare that you want that. This might help explain why it isn't working as well as you'd like, though this paragraph should not be construed as a proper analysis of the crash. :-)

Can I run Android GeoFencing entirely within a background service?

I have an app which needs almost no user interaction, but requires Geofences. Can I run this entirely within a background service?
There will be an Activity when the service is first run. This Activity will start a service and register a BroadcastReceiver for BOOT_COMPLETED, so the service will start at boot. It's unlikely that this Activity will ever be run again.
The service will set an Alarm to go off periodically, which will cause an IntentService to download a list of locations from the network. This IntentService will then set up Geofences around those locations, and create PendingIntents which will fire when the locations are approached. In turn, those PendingIntents will cause another IntentService to take some action.
All this needs to happen in the background, with no user interaction apart from starting the Activity for the first time after installation. Hence, the Activity will not interact with LocationClient or any location services.
I've actually got this set up with proximityAlerts, but wish to move to the new Geofencing API for battery life reasons. However, I have heard that there can be a few problems with using LocationClient from within a service. Specifically, what I've heard (sorry, no references, just hearsay claims):
location client relies on ui availability for error handling
when called from background thread, LocationClient.connect() assumes that it is called from main ui thread (or other thread with event looper), so connection callback is never called, if we call this method from service running in background thread
When I've investigated, I can't see any reason why this would be the case, or why it would stop my doing what I want. I was hoping it would be almost a drop-in replacement for proximityAlerts...
Can anyone shed some light on things here?
The best thing would be to just try it out, right? Your strategy seems sound.
when called from background thread, LocationClient.connect() assumes that it is called from main ui thread (or other thread with event looper), so connection callback is never called, if we call this method from service running in background thread.
I know this to be not true. I have a Service that is started from an Activity, and the connection callback is called.
I dont know about proximity alerts; but I cant seem to find an API to list my GeoFences. I am worried that my database (sqlite) and the actual fences might get out of sync. That is a design flaw in my opinion.
The reason LocationClient needs UI, is that the device may not have Google Play Services installed. Google has deviced a cunning and complex mechanism that allows your app to prompt the user to download it. The whole thing is horrible and awful in my opinion. Its all "what-if what-if" programming.
(They rushed a lot of stuff out the door for google IO 2013. Not all of it are well documented, and some of it seems a bit "rough around the edges").

What benefit does MSDN article on CoRevokeClassObject talk about?

MSDN article on CoRevokeGetClassObject() says that when the COM server calls it the class object referenced by clients is not released. Then the following comes:
If other clients still have pointers to the class object and have caused the reference >count to be incremented by calls to IUnknown::AddRef, the reference count will not be >zero. When this occurs, applications may benefit if subsequent calls (with the obvious >exceptions of IUnknown::AddRef and IUnknown::Release) to the class object fail.
What is meant by "applications may benefit"? The class object is not released, but creation requests fail. Sounds reasonable but where's the benefit?
Yeah, it's a pretty strange turn of words...
I think what they're trying to say is that clients may end up in a tricky situation if they create objects from a server that just called CoRevokeClassObjects, because it's likely it'll disappear very soon (CoRevokeClassObjects is routinely called when a server is shut down.)
So, if the activation calls (IClassFactory::CreateInstance) don't fail, the client will get an interface pointer back, and as soon as they call a method on it, they'll get an error from the RPC layer that the server is gone.
I suppose that's 'beneficial' in some way :-)
That said, I'm not sure how to detect the case where IUnknown::Release is called via CoRevokeClassObjects vs some other client, but I suppose the code revoking the factories could set some global state or per-factory state that they can check before letting creation requests come through.

Cocoa Distributed Objects, GC client, non-GC server

I have a setup where there are two Cocoa processes, communicating with Distributed Objects (DO). The client is using garbage collection, the server is not.
It seems that the client hangs on to the distant objects outside of my direct references to them. This means that even after I don't have references to the objects, they hang around owned by NSDistantObjectTableEntry. Obviously they don't get deallocated on the server.
Only when the client is quit it lets go of all the distant objects. Breaking the connection manually would probably also work, but I don't want to do that while the client is running.
Is there a way to tell a GC'd DO client to let go of the distant objects that aren't referenced locally anymore?
There may be a retain cycle that spans the client and server - i.e. a client object is retaining a proxy of a server object, which is in turn retaining the proxy of the client's object.
That's a very simple example of a retain cycle, when more that two objects are involved it gets more complicated to diagnose.
See The Subtle Dangers Of Distributed Objects for example of other DO related gotchas.

Resources